<commit_before>//Copyright 2015 TF2Stadium. All rights reserved.
//Use of this source code is governed by the MIT
//that can be found in the LICENSE file.

//Package wsevent implements thread-safe event-driven communication similar to socket.IO,
//on the top of Gorilla's WebSocket implementation.
package wsevent

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"log"
	"net/http"
	"reflect"
	"sync"

	ws "github.com/gorilla/websocket"
)

//Client
type Client struct {
	//Session ID
	id string

	conn     *ws.Conn
	connLock *sync.RWMutex
	request  *http.Request
}

type Handler func(*Server, *Client, []byte) []byte

//Server
type Server struct {
	//maps room string to a list of clients in it
	rooms     map[string]([]*Client)
	roomsLock *sync.RWMutex

	//maps client IDs to the list of rooms the corresponding client has joined
	joinedRooms     map[string][]string
	joinedRoomsLock *sync.RWMutex

	//The extractor function reads the byte array and the message type
	//and returns the event represented by the message.
	Extractor func([]byte) string
	//Called when the websocket connection closes. The disconnected client's
	//session ID is sent as an argument
	OnDisconnect func(string)
	//Called when no event handler for a specific event exists
	DefaultHandler Handler

	handlers     map[string]Handler
	handlersLock *sync.RWMutex

	newClient chan *Client
}

func genID() string {
	bytes := make([]byte, 32)
	rand.Read(bytes)

	return base64.URLEncoding.EncodeToString(bytes)
}

//Returns the client's unique session ID
func (c *Client) Id() string {
	return c.id
}

// Returns the first http request when established connection.
func (c *Client) Request() *http.Request {
	return c.request
}

func (s *Server) NewClientWithID(upgrader ws.Upgrader, w http.ResponseWriter, r *http.Request, id string) (*Client, error) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return nil, err
	}

	client := &Client{
		id:       id,
		conn:     conn,
		connLock: new(sync.RWMutex),
		request:  r,
	}
	s.newClient <- client

	return client, nil
}

func (s *Server) NewClient(upgrader ws.Upgrader, w http.ResponseWriter, r *http.Request) (*Client, error) {
	return s.NewClientWithID(upgrader, w, r, genID())
}

func (c *Client) Close() error {
	c.connLock.Lock()
	defer c.connLock.Unlock()
	return c.conn.Close()
}

//A thread-safe variant of WriteMessage
func (c *Client) Emit(data string) error {
	c.connLock.Lock()
	defer c.connLock.Unlock()
	return c.conn.WriteMessage(ws.TextMessage, []byte(data))
}

type emitJS struct {
	Id   int         `json:"id"`
	Data interface{} `json:"data"`
}

var emitPool = &sync.Pool{New: func() interface{} { return emitJS{} }}

//A thread-safe variant of EmitJSON
func (c *Client) EmitJSON(v interface{}) error {
	c.connLock.Lock()
	defer c.connLock.Unlock()

	js := emitPool.Get().(emitJS)
	defer emitPool.Put(js)

	js.Id = -1
	js.Data = v

	return c.conn.WriteJSON(js)
}

//Return a new server object
func NewServer() *Server {
	s := &Server{
		rooms:     make(map[string]([]*Client)),
		roomsLock: new(sync.RWMutex),

		//Maps socket ID -> list of rooms the client is in
		joinedRooms:     make(map[string][]string),
		joinedRoomsLock: new(sync.RWMutex),

		handlers:     make(map[string]Handler),
		handlersLock: new(sync.RWMutex),

		newClient: make(chan *Client),
	}

	go s.listener()
	return s
}

//Add a client c to room r
func (s *Server) AddClient(c *Client, r string) {
	s.joinedRoomsLock.RLock()
	for _, room := range s.joinedRooms[c.id] {
		if r == room {
			//log.Printf("%s already in room %s", c.id, r)
			s.joinedRoomsLock.RUnlock()
			return
		}
	}
	s.joinedRoomsLock.RUnlock()

	s.roomsLock.Lock()
	s.rooms[r] = append(s.rooms[r], c)
	s.roomsLock.Unlock()

	s.joinedRoomsLock.Lock()
	defer s.joinedRoomsLock.Unlock()
	s.joinedRooms[c.id] = append(s.joinedRooms[c.id], r)
	//log.Printf("Added %s to room %s", c.id, r)
}

//Remove client c from room r
func (s *Server) RemoveClient(id, r string) {
	index := -1
	s.roomsLock.RLock()
	for i, client := range s.rooms[r] {
		if id == client.id {
			index = i
			break
		}
	}
	s.roomsLock.RUnlock()
	if index == -1 {
		//log.Printf("Client %s not found in room %s", id, r)
		return
	}

	s.roomsLock.Lock()
	s.rooms[r] = append(s.rooms[r][:index], s.rooms[r][index+1:]...)
	s.roomsLock.Unlock()

	index = -1
	s.joinedRoomsLock.RLock()
	for i, room := range s.joinedRooms[id] {
		if room == r {
			index = i
		}
	}
	s.joinedRoomsLock.RUnlock()
	if index == -1 {
		return
	}

	s.joinedRoomsLock.Lock()
	defer s.joinedRoomsLock.Unlock()

	s.joinedRooms[id] = append(s.joinedRooms[id][:index], s.joinedRooms[id][index+1:]...)
}

//Send all clients in room room data
func (s *Server) Broadcast(room string, data string) {
	s.roomsLock.RLock()
	for _, client := range s.rooms[room] {
		//log.Printf("sending to %s in room %s\n", client.id, room)
		go func(c *Client) {
			c.Emit(data)
		}(client)
	}
	s.roomsLock.RUnlock()
}

func (s *Server) BroadcastJSON(room string, v interface{}) {
	s.roomsLock.RLock()
	for _, client := range s.rooms[room] {
		//log.Printf("sending to %s %s\n", client.id, room)
		go func(c *Client) {
			c.EmitJSON(v)
		}(client)
	}
	s.roomsLock.RUnlock()
}

func (c *Client) cleanup(s *Server) {
	c.conn.Close()

	s.joinedRoomsLock.RLock()
	for _, room := range s.joinedRooms[c.id] {
		//log.Println(room)
		index := -1

		s.roomsLock.Lock()
		for i, client := range s.rooms[room] {
			if client.id == c.id {
				index = i
			}
		}

		s.rooms[room] = append(s.rooms[room][:index], s.rooms[room][index+1:]...)
		s.roomsLock.Unlock()
	}
	s.joinedRoomsLock.RUnlock()

	s.joinedRoomsLock.Lock()
	delete(s.joinedRooms, c.id)
	s.joinedRoomsLock.Unlock()

	if s.OnDisconnect != nil {
		s.OnDisconnect(c.id)
	}
}

//Returns an array of rooms the client c has been added to
func (s *Server) RoomsJoined(id string) []string {
	rooms := make([]string, len(s.joinedRooms[id]))
	s.joinedRoomsLock.RLock()
	defer s.joinedRoomsLock.RUnlock()

	copy(rooms, s.joinedRooms[id])

	return rooms
}

type request struct {
	Id   string
	Data json.RawMessage
}

type reply struct {
	Id   string `json:"id"`
	Data string `json:"data,string"`
}

var (
	reqPool   = &sync.Pool{New: func() interface{} { return request{} }}
	replyPool = &sync.Pool{New: func() interface{} { return reply{} }}
)

func (c *Client) listener(s *Server) {
	for {
		mtype, data, err := c.conn.ReadMessage()
		if err != nil {
			c.cleanup(s)
			return
		}

		js := reqPool.Get().(request)
		err = json.Unmarshal(data, &js)

		if err != nil || mtype != ws.TextMessage {
			log.Println(err)
			continue
		}

		callName := s.Extractor(js.Data)

		s.handlersLock.RLock()
		f, ok := s.handlers[callName]
		s.handlersLock.RUnlock()

		if !ok {
			if s.DefaultHandler != nil {
				f = s.DefaultHandler
				goto call
			}
			continue
		}
	call:
		go func() {
			rtrn := f(s, c, js.Data)

			replyJs := replyPool.Get().(reply)
			replyJs.Id = js.Id
			replyJs.Data = string(rtrn)

			bytes, _ := json.Marshal(replyJs)
			c.Emit(string(bytes))

			reqPool.Put(js)
			replyPool.Put(replyJs)
		}()
	}
}

func (s *Server) listener() {
	for {
		c := <-s.newClient
		go c.listener(s)
	}
}

//Registers a callback for the event string. The callback must take 2 arguments,
//The client from which the message was received and the string message itself.
func (s *Server) On(event string, f Handler) {
	s.handlersLock.Lock()
	s.handlers[event] = f
	s.handlersLock.Unlock()
}

//A Receiver interface implements the Name method, which returns a name for the
//event, given a Handler's name
type Receiver interface {
	Name(string) string
}

//Similar to net/rpc's Register, expect that rcvr needs to implement the
//Receiver interface
func (s *Server) Register(rcvr Receiver) {
	rval := reflect.ValueOf(rcvr)
	rtype := reflect.TypeOf(rcvr)

	for i := 0; i < rval.NumMethod(); i++ {
		method := rval.Method(i)
		name := rtype.Method(i).Name
		if name == "Name" {
			continue
		}

		s.On(rcvr.Name(name), func(_ *Server, c *Client, b []byte) []byte {
			in := []reflect.Value{
				reflect.ValueOf(s),
				reflect.ValueOf(c),
				reflect.ValueOf(b)}

			rtrn := method.Call(in)
			return rtrn[0].Bytes()
		})

	}
}
<commit_msg>Use TypeOf<commit_after>//Copyright 2015 TF2Stadium. All rights reserved.
//Use of this source code is governed by the MIT
//that can be found in the LICENSE file.

//Package wsevent implements thread-safe event-driven communication similar to socket.IO,
//on the top of Gorilla's WebSocket implementation.
package wsevent

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"log"
	"net/http"
	"reflect"
	"sync"

	ws "github.com/gorilla/websocket"
)

//Client
type Client struct {
	//Session ID
	id string

	conn     *ws.Conn
	connLock *sync.RWMutex
	request  *http.Request
}

type Handler func(*Server, *Client, []byte) []byte

//Server
type Server struct {
	//maps room string to a list of clients in it
	rooms     map[string]([]*Client)
	roomsLock *sync.RWMutex

	//maps client IDs to the list of rooms the corresponding client has joined
	joinedRooms     map[string][]string
	joinedRoomsLock *sync.RWMutex

	//The extractor function reads the byte array and the message type
	//and returns the event represented by the message.
	Extractor func([]byte) string
	//Called when the websocket connection closes. The disconnected client's
	//session ID is sent as an argument
	OnDisconnect func(string)
	//Called when no event handler for a specific event exists
	DefaultHandler Handler

	handlers     map[string]Handler
	handlersLock *sync.RWMutex

	newClient chan *Client
}

func genID() string {
	bytes := make([]byte, 32)
	rand.Read(bytes)

	return base64.URLEncoding.EncodeToString(bytes)
}

//Returns the client's unique session ID
func (c *Client) Id() string {
	return c.id
}

// Returns the first http request when established connection.
func (c *Client) Request() *http.Request {
	return c.request
}

func (s *Server) NewClientWithID(upgrader ws.Upgrader, w http.ResponseWriter, r *http.Request, id string) (*Client, error) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return nil, err
	}

	client := &Client{
		id:       id,
		conn:     conn,
		connLock: new(sync.RWMutex),
		request:  r,
	}
	s.newClient <- client

	return client, nil
}

func (s *Server) NewClient(upgrader ws.Upgrader, w http.ResponseWriter, r *http.Request) (*Client, error) {
	return s.NewClientWithID(upgrader, w, r, genID())
}

func (c *Client) Close() error {
	c.connLock.Lock()
	defer c.connLock.Unlock()
	return c.conn.Close()
}

//A thread-safe variant of WriteMessage
func (c *Client) Emit(data string) error {
	c.connLock.Lock()
	defer c.connLock.Unlock()
	return c.conn.WriteMessage(ws.TextMessage, []byte(data))
}

type emitJS struct {
	Id   int         `json:"id"`
	Data interface{} `json:"data"`
}

var emitPool = &sync.Pool{New: func() interface{} { return emitJS{} }}

//A thread-safe variant of EmitJSON
func (c *Client) EmitJSON(v interface{}) error {
	c.connLock.Lock()
	defer c.connLock.Unlock()

	js := emitPool.Get().(emitJS)
	defer emitPool.Put(js)

	js.Id = -1
	js.Data = v

	return c.conn.WriteJSON(js)
}

//Return a new server object
func NewServer() *Server {
	s := &Server{
		rooms:     make(map[string]([]*Client)),
		roomsLock: new(sync.RWMutex),

		//Maps socket ID -> list of rooms the client is in
		joinedRooms:     make(map[string][]string),
		joinedRoomsLock: new(sync.RWMutex),

		handlers:     make(map[string]Handler),
		handlersLock: new(sync.RWMutex),

		newClient: make(chan *Client),
	}

	go s.listener()
	return s
}

//Add a client c to room r
func (s *Server) AddClient(c *Client, r string) {
	s.joinedRoomsLock.RLock()
	for _, room := range s.joinedRooms[c.id] {
		if r == room {
			//log.Printf("%s already in room %s", c.id, r)
			s.joinedRoomsLock.RUnlock()
			return
		}
	}
	s.joinedRoomsLock.RUnlock()

	s.roomsLock.Lock()
	s.rooms[r] = append(s.rooms[r], c)
	s.roomsLock.Unlock()

	s.joinedRoomsLock.Lock()
	defer s.joinedRoomsLock.Unlock()
	s.joinedRooms[c.id] = append(s.joinedRooms[c.id], r)
	//log.Printf("Added %s to room %s", c.id, r)
}

//Remove client c from room r
func (s *Server) RemoveClient(id, r string) {
	index := -1
	s.roomsLock.RLock()
	for i, client := range s.rooms[r] {
		if id == client.id {
			index = i
			break
		}
	}
	s.roomsLock.RUnlock()
	if index == -1 {
		//log.Printf("Client %s not found in room %s", id, r)
		return
	}

	s.roomsLock.Lock()
	s.rooms[r] = append(s.rooms[r][:index], s.rooms[r][index+1:]...)
	s.roomsLock.Unlock()

	index = -1
	s.joinedRoomsLock.RLock()
	for i, room := range s.joinedRooms[id] {
		if room == r {
			index = i
		}
	}
	s.joinedRoomsLock.RUnlock()
	if index == -1 {
		return
	}

	s.joinedRoomsLock.Lock()
	defer s.joinedRoomsLock.Unlock()

	s.joinedRooms[id] = append(s.joinedRooms[id][:index], s.joinedRooms[id][index+1:]...)
}

//Send all clients in room room data
func (s *Server) Broadcast(room string, data string) {
	s.roomsLock.RLock()
	for _, client := range s.rooms[room] {
		//log.Printf("sending to %s in room %s\n", client.id, room)
		go func(c *Client) {
			c.Emit(data)
		}(client)
	}
	s.roomsLock.RUnlock()
}

func (s *Server) BroadcastJSON(room string, v interface{}) {
	s.roomsLock.RLock()
	for _, client := range s.rooms[room] {
		//log.Printf("sending to %s %s\n", client.id, room)
		go func(c *Client) {
			c.EmitJSON(v)
		}(client)
	}
	s.roomsLock.RUnlock()
}

func (c *Client) cleanup(s *Server) {
	c.conn.Close()

	s.joinedRoomsLock.RLock()
	for _, room := range s.joinedRooms[c.id] {
		//log.Println(room)
		index := -1

		s.roomsLock.Lock()
		for i, client := range s.rooms[room] {
			if client.id == c.id {
				index = i
			}
		}

		s.rooms[room] = append(s.rooms[room][:index], s.rooms[room][index+1:]...)
		s.roomsLock.Unlock()
	}
	s.joinedRoomsLock.RUnlock()

	s.joinedRoomsLock.Lock()
	delete(s.joinedRooms, c.id)
	s.joinedRoomsLock.Unlock()

	if s.OnDisconnect != nil {
		s.OnDisconnect(c.id)
	}
}

//Returns an array of rooms the client c has been added to
func (s *Server) RoomsJoined(id string) []string {
	rooms := make([]string, len(s.joinedRooms[id]))
	s.joinedRoomsLock.RLock()
	defer s.joinedRoomsLock.RUnlock()

	copy(rooms, s.joinedRooms[id])

	return rooms
}

type request struct {
	Id   string
	Data json.RawMessage
}

type reply struct {
	Id   string `json:"id"`
	Data string `json:"data,string"`
}

var (
	reqPool   = &sync.Pool{New: func() interface{} { return request{} }}
	replyPool = &sync.Pool{New: func() interface{} { return reply{} }}
)

func (c *Client) listener(s *Server) {
	for {
		mtype, data, err := c.conn.ReadMessage()
		if err != nil {
			c.cleanup(s)
			return
		}

		js := reqPool.Get().(request)
		err = json.Unmarshal(data, &js)

		if err != nil || mtype != ws.TextMessage {
			log.Println(err)
			continue
		}

		callName := s.Extractor(js.Data)

		s.handlersLock.RLock()
		f, ok := s.handlers[callName]
		s.handlersLock.RUnlock()

		if !ok {
			if s.DefaultHandler != nil {
				f = s.DefaultHandler
				goto call
			}
			continue
		}
	call:
		go func() {
			rtrn := f(s, c, js.Data)

			replyJs := replyPool.Get().(reply)
			replyJs.Id = js.Id
			replyJs.Data = string(rtrn)

			bytes, _ := json.Marshal(replyJs)
			c.Emit(string(bytes))

			reqPool.Put(js)
			replyPool.Put(replyJs)
		}()
	}
}

func (s *Server) listener() {
	for {
		c := <-s.newClient
		go c.listener(s)
	}
}

//Registers a callback for the event string. The callback must take 2 arguments,
//The client from which the message was received and the string message itself.
func (s *Server) On(event string, f Handler) {
	s.handlersLock.Lock()
	s.handlers[event] = f
	s.handlersLock.Unlock()
}

//A Receiver interface implements the Name method, which returns a name for the
//event, given a Handler's name
type Receiver interface {
	Name(string) string
}

//Similar to net/rpc's Register, expect that rcvr needs to implement the
//Receiver interface
func (s *Server) Register(rcvr Receiver) {
	rtype := reflect.TypeOf(rcvr)

	for i := 0; i < rtype.NumMethod(); i++ {
		method := rtype.Method(i)
		if method.Name == "Name" {
			continue
		}

		s.On(rcvr.Name(method.Name), func(_ *Server, c *Client, b []byte) []byte {
			in := []reflect.Value{
				reflect.ValueOf(rcvr),
				reflect.ValueOf(s),
				reflect.ValueOf(c),
				reflect.ValueOf(b)}

			rtrn := method.Func.Call(in)
			return rtrn[0].Bytes()
		})

	}
}
<|endoftext|>


<commit_before>package flavorcommands

import (
	"fmt"
	"os"

	"github.com/codegangsta/cli"
	"github.com/fatih/structs"
	"github.com/jrperritt/rackcli/auth"
	"github.com/jrperritt/rackcli/output"
	"github.com/jrperritt/rackcli/util"
	"github.com/olekukonko/tablewriter"
	osFlavors "github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
	"github.com/rackspace/gophercloud/rackspace/compute/v2/flavors"
)

var list = cli.Command{
	Name:        "list",
	Usage:       fmt.Sprintf("%s %s list [flags]", util.Name, commandPrefix),
	Description: "Lists flavors",
	Action:      commandList,
	Flags:       flagsList(),
}

func flagsList() []cli.Flag {
	return []cli.Flag{
		cli.IntFlag{
			Name:  "minDisk",
			Usage: "Only list flavors that have at least this much disk storage (in GB).",
		},
		cli.IntFlag{
			Name:  "minRam",
			Usage: "Only list flavors that have at least this much RAM (in GB).",
		},

		cli.StringFlag{
			Name:  "marker",
			Usage: "Start listing flavors at this flavor ID.",
		},
		cli.IntFlag{
			Name:  "limit",
			Usage: "Only return this many flavors at most.",
		},
	}
}

func commandList(c *cli.Context) {
	util.CheckArgNum(c, 0)
	client := auth.NewClient("compute")
	opts := flavors.ListOpts{
		MinDisk: c.Int("minDisk"),
		MinRAM:  c.Int("minRam"),
		Marker:  c.String("marker"),
		Limit:   c.Int("limit"),
	}
	allPages, err := flavors.ListDetail(client, opts).AllPages()
	if err != nil {
		fmt.Printf("Error listing flavors: %s\n", err)
		os.Exit(1)
	}
	o, err := osFlavors.ExtractFlavors(allPages)
	if err != nil {
		fmt.Printf("Error listing flavors: %s\n", err)
		os.Exit(1)
	}
	output.Print(c, o, tableList)
}

func tableList(c *cli.Context, i interface{}) {
	flavors, ok := i.([]osFlavors.Flavor)
	if !ok {
		fmt.Fprintf(c.App.Writer, "Could not type assert interface\n%+v\nto []osFlavors.Flavor\n", i)
		os.Exit(1)
	}
	t := tablewriter.NewWriter(c.App.Writer)
	t.SetAlignment(tablewriter.ALIGN_LEFT)
	keys := []string{"ID", "Name", "RAM", "Disk", "Swap", "VCPUs", "RxTxFactor"}
	t.SetHeader(keys)
	for _, flavor := range flavors {
		m := structs.Map(flavor)
		f := []string{}
		for _, key := range keys {
			f = append(f, fmt.Sprint(m[key]))
		}
		t.Append(f)
	}
	t.Render()
}
<commit_msg>more info for list flavors flags<commit_after>package flavorcommands

import (
	"fmt"
	"os"

	"github.com/codegangsta/cli"
	"github.com/fatih/structs"
	"github.com/jrperritt/rackcli/auth"
	"github.com/jrperritt/rackcli/output"
	"github.com/jrperritt/rackcli/util"
	"github.com/olekukonko/tablewriter"
	osFlavors "github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
	"github.com/rackspace/gophercloud/rackspace/compute/v2/flavors"
)

var list = cli.Command{
	Name:        "list",
	Usage:       fmt.Sprintf("%s %s list [flags]", util.Name, commandPrefix),
	Description: "Lists flavors",
	Action:      commandList,
	Flags:       flagsList(),
}

func flagsList() []cli.Flag {
	return []cli.Flag{
		cli.IntFlag{
			Name:  "minDisk",
			Usage: "[optional] Only list flavors that have at least this much disk storage (in GB).",
		},
		cli.IntFlag{
			Name:  "minRam",
			Usage: "[optional] Only list flavors that have at least this much RAM (in GB).",
		},

		cli.StringFlag{
			Name:  "marker",
			Usage: "[optional] Start listing flavors at this flavor ID.",
		},
		cli.IntFlag{
			Name:  "limit",
			Usage: "[optional] Only return this many flavors at most.",
		},
	}
}

func commandList(c *cli.Context) {
	util.CheckArgNum(c, 0)
	client := auth.NewClient("compute")
	opts := flavors.ListOpts{
		MinDisk: c.Int("minDisk"),
		MinRAM:  c.Int("minRam"),
		Marker:  c.String("marker"),
		Limit:   c.Int("limit"),
	}
	allPages, err := flavors.ListDetail(client, opts).AllPages()
	if err != nil {
		fmt.Printf("Error listing flavors: %s\n", err)
		os.Exit(1)
	}
	o, err := osFlavors.ExtractFlavors(allPages)
	if err != nil {
		fmt.Printf("Error listing flavors: %s\n", err)
		os.Exit(1)
	}
	output.Print(c, o, tableList)
}

func tableList(c *cli.Context, i interface{}) {
	flavors, ok := i.([]osFlavors.Flavor)
	if !ok {
		fmt.Fprintf(c.App.Writer, "Could not type assert interface\n%+v\nto []osFlavors.Flavor\n", i)
		os.Exit(1)
	}
	t := tablewriter.NewWriter(c.App.Writer)
	t.SetAlignment(tablewriter.ALIGN_LEFT)
	keys := []string{"ID", "Name", "RAM", "Disk", "Swap", "VCPUs", "RxTxFactor"}
	t.SetHeader(keys)
	for _, flavor := range flavors {
		m := structs.Map(flavor)
		f := []string{}
		for _, key := range keys {
			f = append(f, fmt.Sprint(m[key]))
		}
		t.Append(f)
	}
	t.Render()
}
<|endoftext|>


<commit_before>package stun

import (
	"errors"
	"fmt"
	"net"
	"strconv"
)

const (
	familyIPv4 uint16 = 0x01
	familyIPv6 uint16 = 0x02
)

// XORMappedAddress implements XOR-MAPPED-ADDRESS attribute.
//
// https://tools.ietf.org/html/rfc5389#section-15.2
type XORMappedAddress struct {
	IP   net.IP
	Port int
}

func (a XORMappedAddress) String() string {
	return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
}

// isIPv4 returns true if ip with len of net.IPv6Len seems to be ipv4.
func isIPv4(ip net.IP) bool {
	// Optimized for performance. Copied from net.IP.To4.
	return isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff
}

// Is p all zeros?
func isZeros(p net.IP) bool {
	for i := 0; i < len(p); i++ {
		if p[i] != 0 {
			return false
		}
	}
	return true
}

// ErrBadIPLength means that len(IP) is not net.{IPv6len,IPv4len}.
var ErrBadIPLength = errors.New("invalid length of IP value")

// AddTo adds XOR-MAPPED-ADDRESS to m. Can return ErrBadIPLength
// if len(a.IP) is invalid.
func (a *XORMappedAddress) AddTo(m *Message) error {
	var (
		family = familyIPv4
		ip     = a.IP
	)
	if len(a.IP) == net.IPv6len {
		if isIPv4(ip) {
			ip = ip[12:16] // like in ip.To4()
		} else {
			family = familyIPv6
		}
	} else if len(ip) != net.IPv4len {
		return ErrBadIPLength
	}
	value := make([]byte, 32+128)
	value[0] = 0 // first 8 bits are zeroes
	xorValue := make([]byte, net.IPv6len)
	copy(xorValue[4:], m.TransactionID[:])
	bin.PutUint32(xorValue[0:4], magicCookie)
	bin.PutUint16(value[0:2], family)
	bin.PutUint16(value[2:4], uint16(a.Port^magicCookie>>16))
	xorBytes(value[4:4+len(ip)], ip, xorValue)
	m.Add(AttrXORMappedAddress, value[:4+len(ip)])
	return nil
}

// GetFrom decodes XOR-MAPPED-ADDRESS attribute in message and returns
// error if any. While decoding, a.IP is reused if possible and can be
// rendered to invalid state (e.g. if a.IP was set to IPv6 and then
// IPv4 value were decoded into it), be careful.
//
// Example:
//
//  expectedIP := net.ParseIP("213.141.156.236")
//  expectedIP.String() // 213.141.156.236, 16 bytes, first 12 of them are zeroes
//  expectedPort := 21254
//  addr := &XORMappedAddress{
//    IP:   expectedIP,
//    Port: expectedPort,
//  }
//  // addr were added to message that is decoded as newMessage
//  // ...
//
//  addr.GetFrom(newMessage)
//  addr.IP.String()    // 213.141.156.236, net.IPv4Len
//  expectedIP.String() // d58d:9cec::ffff:d58d:9cec, 16 bytes, first 4 are IPv4
//  // now we have len(expectedIP) = 16 and len(addr.IP) = 4.
func (a *XORMappedAddress) GetFrom(m *Message) error {
	v, err := m.Get(AttrXORMappedAddress)
	if err != nil {
		return err
	}
	family := bin.Uint16(v[0:2])
	if family != familyIPv6 && family != familyIPv4 {
		return newDecodeErr("xor-mapped address", "family",
			fmt.Sprintf("bad value %d", family),
		)
	}
	ipLen := net.IPv4len
	if family == familyIPv6 {
		ipLen = net.IPv6len
	}
	// Ensuring len(a.IP) == ipLen and reusing a.IP.
	if len(a.IP) < ipLen {
		a.IP = a.IP[:cap(a.IP)]
		for len(a.IP) < ipLen {
			a.IP = append(a.IP, 0)
		}
	}
	a.IP = a.IP[:ipLen]
	for i := range a.IP {
		a.IP[i] = 0
	}
	a.Port = int(bin.Uint16(v[2:4])) ^ (magicCookie >> 16)
	xorValue := make([]byte, 4+transactionIDSize)
	bin.PutUint32(xorValue[0:4], magicCookie)
	copy(xorValue[4:], m.TransactionID[:])
	xorBytes(a.IP, v[4:], xorValue)
	return nil
}
<commit_msg>a:xoraddr: expose AddToAs and GetFromAs<commit_after>package stun

import (
	"errors"
	"fmt"
	"net"
	"strconv"
)

const (
	familyIPv4 uint16 = 0x01
	familyIPv6 uint16 = 0x02
)

// XORMappedAddress implements XOR-MAPPED-ADDRESS attribute.
//
// https://tools.ietf.org/html/rfc5389#section-15.2
type XORMappedAddress struct {
	IP   net.IP
	Port int
}

func (a XORMappedAddress) String() string {
	return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
}

// isIPv4 returns true if ip with len of net.IPv6Len seems to be ipv4.
func isIPv4(ip net.IP) bool {
	// Optimized for performance. Copied from net.IP.To4.
	return isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff
}

// Is p all zeros?
func isZeros(p net.IP) bool {
	for i := 0; i < len(p); i++ {
		if p[i] != 0 {
			return false
		}
	}
	return true
}

// ErrBadIPLength means that len(IP) is not net.{IPv6len,IPv4len}.
var ErrBadIPLength = errors.New("invalid length of IP value")

// AddToAs adds XOR-MAPPED-ADDRESS value to m as t attribute.
func (a *XORMappedAddress) AddToAs(m *Message, t AttrType) error {
	var (
		family = familyIPv4
		ip     = a.IP
	)
	if len(a.IP) == net.IPv6len {
		if isIPv4(ip) {
			ip = ip[12:16] // like in ip.To4()
		} else {
			family = familyIPv6
		}
	} else if len(ip) != net.IPv4len {
		return ErrBadIPLength
	}
	value := make([]byte, 32+128)
	value[0] = 0 // first 8 bits are zeroes
	xorValue := make([]byte, net.IPv6len)
	copy(xorValue[4:], m.TransactionID[:])
	bin.PutUint32(xorValue[0:4], magicCookie)
	bin.PutUint16(value[0:2], family)
	bin.PutUint16(value[2:4], uint16(a.Port^magicCookie>>16))
	xorBytes(value[4:4+len(ip)], ip, xorValue)
	m.Add(t, value[:4+len(ip)])
	return nil
}

// AddTo adds XOR-MAPPED-ADDRESS to m. Can return ErrBadIPLength
// if len(a.IP) is invalid.
func (a *XORMappedAddress) AddTo(m *Message) error {
	return a.AddToAs(m, AttrXORMappedAddress)
}

// GetFrom decodes XOR-MAPPED-ADDRESS attribute value in message
// getting it as for t type.
func (a *XORMappedAddress) GetFromAs(m *Message, t AttrType) error {
	v, err := m.Get(t)
	if err != nil {
		return err
	}
	family := bin.Uint16(v[0:2])
	if family != familyIPv6 && family != familyIPv4 {
		return newDecodeErr("xor-mapped address", "family",
			fmt.Sprintf("bad value %d", family),
		)
	}
	ipLen := net.IPv4len
	if family == familyIPv6 {
		ipLen = net.IPv6len
	}
	// Ensuring len(a.IP) == ipLen and reusing a.IP.
	if len(a.IP) < ipLen {
		a.IP = a.IP[:cap(a.IP)]
		for len(a.IP) < ipLen {
			a.IP = append(a.IP, 0)
		}
	}
	a.IP = a.IP[:ipLen]
	for i := range a.IP {
		a.IP[i] = 0
	}
	a.Port = int(bin.Uint16(v[2:4])) ^ (magicCookie >> 16)
	xorValue := make([]byte, 4+transactionIDSize)
	bin.PutUint32(xorValue[0:4], magicCookie)
	copy(xorValue[4:], m.TransactionID[:])
	xorBytes(a.IP, v[4:], xorValue)
	return nil
}

// GetFrom decodes XOR-MAPPED-ADDRESS attribute in message and returns
// error if any. While decoding, a.IP is reused if possible and can be
// rendered to invalid state (e.g. if a.IP was set to IPv6 and then
// IPv4 value were decoded into it), be careful.
//
// Example:
//
//  expectedIP := net.ParseIP("213.141.156.236")
//  expectedIP.String() // 213.141.156.236, 16 bytes, first 12 of them are zeroes
//  expectedPort := 21254
//  addr := &XORMappedAddress{
//    IP:   expectedIP,
//    Port: expectedPort,
//  }
//  // addr were added to message that is decoded as newMessage
//  // ...
//
//  addr.GetFrom(newMessage)
//  addr.IP.String()    // 213.141.156.236, net.IPv4Len
//  expectedIP.String() // d58d:9cec::ffff:d58d:9cec, 16 bytes, first 4 are IPv4
//  // now we have len(expectedIP) = 16 and len(addr.IP) = 4.
func (a *XORMappedAddress) GetFrom(m *Message) error {
	return a.GetFromAs(m, AttrXORMappedAddress)
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package lxd_client

import (
	"github.com/juju/loggo"
	"github.com/lxc/lxd/shared"
)

// The various status values used for LXD.
const (
	StatusStarting = "Starting"
	StatusStarted  = "Started"
	StatusRunning  = "Running"
	StatusFreezing = "Freezing"
	StatusFrozen   = "Frozen"
	StatusThawed   = "Thawed"
	StatusStopping = "Stopping"
	StatusStopped  = "Stopped"

	StatusOK         = "OK"
	StatusPending    = "Pending"
	StatusAborting   = "Aborting"
	StatusCancelling = "Canceling"
	StatusCancelled  = "Canceled"
	StatusSuccess    = "Success"
	StatusFailure    = "Failure"
)

var allStatuses = map[string]shared.State{
	StatusStarting: shared.STARTING,
	StatusRunning:  shared.RUNNING,
	StatusThawed:   shared.THAWED,

	// TODO(ericsnow) Use the newer status codes:
	//StatusStarting:   shared.Starting,
	//StatusStarted:    shared.Started,
	//StatusRunning:    shared.Running,
	//StatusFreezing:   shared.Freezing,
	//StatusFrozen:     shared.Frozen,
	//StatusThawed:     shared.Thawed,
	//StatusStopping:   shared.Stopping,
	//StatusStopped:    shared.Stopped,
	//StatusOK:         shared.OK,
	//StatusPending:    shared.Pending,
	//StatusAborting:   shared.Aborting,
	//StatusCancelling: shared.Cancelling,
	//StatusCancelled:  shared.Cancelled,
	//StatusSuccess:    shared.Success,
	//StatusFailure:    shared.Failure,
}

var (
	logger = loggo.GetLogger("juju.provider.lxd.lxd_client")
)
<commit_msg>Do not use StatusThawed.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package lxd_client

import (
	"github.com/juju/loggo"
	"github.com/lxc/lxd/shared"
)

// The various status values used for LXD.
const (
	StatusStarting = "Starting"
	StatusStarted  = "Started"
	StatusRunning  = "Running"
	StatusFreezing = "Freezing"
	StatusFrozen   = "Frozen"
	StatusThawed   = "Thawed"
	StatusStopping = "Stopping"
	StatusStopped  = "Stopped"

	StatusOK         = "OK"
	StatusPending    = "Pending"
	StatusAborting   = "Aborting"
	StatusCancelling = "Canceling"
	StatusCancelled  = "Canceled"
	StatusSuccess    = "Success"
	StatusFailure    = "Failure"
)

var allStatuses = map[string]shared.State{
	StatusStarting: shared.STARTING,
	StatusRunning:  shared.RUNNING,
	//StatusThawed:   shared.THAWED,

	// TODO(ericsnow) Use the newer status codes:
	//StatusStarting:   shared.Starting,
	//StatusStarted:    shared.Started,
	//StatusRunning:    shared.Running,
	//StatusFreezing:   shared.Freezing,
	//StatusFrozen:     shared.Frozen,
	//StatusThawed:     shared.Thawed,
	//StatusStopping:   shared.Stopping,
	//StatusStopped:    shared.Stopped,
	//StatusOK:         shared.OK,
	//StatusPending:    shared.Pending,
	//StatusAborting:   shared.Aborting,
	//StatusCancelling: shared.Cancelling,
	//StatusCancelled:  shared.Cancelled,
	//StatusSuccess:    shared.Success,
	//StatusFailure:    shared.Failure,
}

var (
	logger = loggo.GetLogger("juju.provider.lxd.lxd_client")
)
<|endoftext|>


<commit_before>/*
Copyright 2018 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package composite

import (
	"fmt"
	"reflect"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

	"k8s.io/metacontroller/apis/metacontroller/v1alpha1"
	dynamicdiscovery "k8s.io/metacontroller/dynamic/discovery"
	dynamicobject "k8s.io/metacontroller/dynamic/object"
)

func (pc *parentController) syncRollingUpdate(parentRevisions []*parentRevision, observedChildren childMap) error {
	// Reconcile the set of existing child claims in ControllerRevisions.
	claimed := pc.syncRevisionClaims(parentRevisions)

	// Give the latest revision any children it desires that aren't claimed yet.
	latest := parentRevisions[0]
	for gvk, objects := range latest.desiredChildMap {
		apiVersion, kind := parseChildMapKey(gvk)
		// Ignore the API version, because the 'claimed' map is version-agnostic.
		apiGroup, _ := parseAPIVersion(apiVersion)
		key := claimMapKey(apiGroup, kind)

		// Skip if rolling update isn't enabled for this child type.
		if !pc.updateStrategy.isRolling(apiGroup, kind) {
			continue
		}

		claimMap := claimed[key]
		for name := range objects {
			if _, found := claimMap[name]; !found {
				latest.addChild(apiGroup, kind, name)
			}
		}
	}

	// Look for the next child to update, if any.
	// We go one by one, in the order in which the controller returned them
	// in the latest sync hook result.
	for _, child := range latest.desiredChildList {
		apiGroup, _ := parseAPIVersion(child.GetAPIVersion())
		kind := child.GetKind()
		name := child.GetName()
		key := claimMapKey(apiGroup, kind)

		// Skip if rolling update isn't enabled for this child type.
		if !pc.updateStrategy.isRolling(apiGroup, kind) {
			continue
		}

		// Look up which revision claims this child, if any.
		var pr *parentRevision
		if claimMap := claimed[key]; claimMap != nil {
			pr = claimMap[name]
		}

		// Move the first child that isn't in the latest revision to the latest.
		if pr != latest {
			// We only continue to push more children into the latest revision if all
			// the children already in the latest revision are happy, where "happy" is
			// defined by the statusChecks in each child type's updateStrategy.
			if err := pc.shouldContinueRolling(latest, observedChildren); err != nil {
				// Add status condition to explain what we're waiting for.
				updatedCondition := &dynamicobject.StatusCondition{
					Type:    "Updated",
					Status:  "False",
					Reason:  "RolloutWaiting",
					Message: err.Error(),
				}
				dynamicobject.SetCondition(latest.status, updatedCondition)
				return nil
			}

			latest.addChild(apiGroup, kind, name)
			// Remove it from all other revisions.
			for _, pr := range parentRevisions[1:] {
				pr.removeChild(apiGroup, kind, name)
			}

			// We've done our one move for this sync pass.
			// Add status condition to explain what we're doing next.
			updatedCondition := &dynamicobject.StatusCondition{
				Type:    "Updated",
				Status:  "False",
				Reason:  "RolloutProgressing",
				Message: fmt.Sprintf("updating %v %v", kind, name),
			}
			dynamicobject.SetCondition(latest.status, updatedCondition)
			return nil
		}
	}

	// Everything is already on the latest revision.
	updatedCondition := &dynamicobject.StatusCondition{
		Type:    "Updated",
		Status:  "True",
		Reason:  "OnLatestRevision",
		Message: fmt.Sprintf("latest ControllerRevision: %v", latest.revision.Name),
	}
	dynamicobject.SetCondition(latest.status, updatedCondition)
	return nil
}

func (pc *parentController) shouldContinueRolling(latest *parentRevision, observedChildren childMap) error {
	// We continue rolling only if all children claimed by the latest revision
	// are updated and were observed in a "happy" state, according to the
	// user-supplied, resource-specific status checks.
	for _, ck := range latest.revision.Children {
		strategy := pc.updateStrategy.get(ck.APIGroup, ck.Kind)
		if !isRollingStrategy(strategy) {
			// We don't need to check children that don't use rolling update.
			continue
		}

		for _, name := range ck.Names {
			child := observedChildren.findGroupKindName(ck.APIGroup, ck.Kind, name)
			if child == nil {
				// We didn't observe this child at all, so it's not happy.
				return fmt.Errorf("missing child %v %v", ck.Kind, name)
			}
			// Is this child up-to-date with what the latest revision wants?
			// Apply the latest update to it and see if anything changes.
			update := latest.desiredChildMap.findGroupKindName(ck.APIGroup, ck.Kind, name)
			updated, err := applyUpdate(child, update)
			if err != nil {
				return fmt.Errorf("can't check if child %v %v is updated: %v", ck.Kind, name, err)
			}
			if !reflect.DeepEqual(child, updated) {
				return fmt.Errorf("child %v %v is not updated yet", ck.Kind, name)
			}
			// For RollingInPlace, we should check ObservedGeneration (if possible)
			// before checking status, to make sure status reflects the latest spec.
			if strategy.Method == v1alpha1.ChildUpdateRollingInPlace {
				// Ideally every controller would support ObservedGeneration, but not
				// all do, so we have to ignore it if it's not present.
				if observedGeneration := dynamicobject.GetObservedGeneration(child.UnstructuredContent()); observedGeneration > 0 {
					// Ideally we would remember the Generation from our own last Update,
					// but we don't have a good place to persist that.
					// Instead, we compare with the latest Generation, which should be
					// fine as long as the object spec is not updated frequently.
					if observedGeneration < child.GetGeneration() {
						return fmt.Errorf("child %v %v with RollingInPlace update strategy hasn't observed latest spec", ck.Kind, name)
					}
				}
			}
			// Check the child status according to the updateStrategy.
			if err := childStatusCheck(&strategy.StatusChecks, child); err != nil {
				// If any child already on the latest revision fails the status check,
				// pause the rollout.
				return fmt.Errorf("child %v %v failed status check: %v", ck.Kind, name, err)
			}
		}
	}
	return nil
}

func (pc *parentController) syncRevisionClaims(parentRevisions []*parentRevision) childClaimMap {
	// The latest revision is always the first item.
	latest := parentRevisions[0]

	// Build a map for lookup from a child to the parentRevision that claims it.
	claimed := make(map[string]map[string]*parentRevision)

	for _, pr := range parentRevisions {
		children := make([]v1alpha1.ControllerRevisionChildren, 0, len(pr.revision.Children))

		for _, ck := range pr.revision.Children {
			if !pc.updateStrategy.isRolling(ck.APIGroup, ck.Kind) {
				// Remove claims for any child kinds that no longer use rolling update.
				continue
			}

			key := claimMapKey(ck.APIGroup, ck.Kind)
			names := make([]string, 0, len(ck.Names))

			for _, name := range ck.Names {
				// Remove claims for any children that the latest revision no longer desires.
				// Such children will be deleted immediately, so we can forget the claim.
				if latest.desiredChildMap.findGroupKindName(ck.APIGroup, ck.Kind, name) == nil {
					continue
				}

				// Get the sub-map for this child kind.
				claimMap := claimed[key]
				if _, exists := claimMap[name]; exists {
					// Another revision already claimed this child, so drop it from here.
					// The only precedence rule we care about is that the latest revision
					// wins over any other, which is ensured by the fact that the latest
					// revision is always first in the list.
					continue
				}
				// Create a new sub-map if necessary.
				if claimMap == nil {
					claimMap = make(map[string]*parentRevision)
					claimed[key] = claimMap
				}
				claimMap[name] = pr
				names = append(names, name)
			}

			if len(names) == 0 {
				// Remove the whole child kind if there are no names left.
				continue
			}

			children = append(children, ck)
		}

		pr.revision.Children = children
	}
	return claimed
}

func childStatusCheck(checks *v1alpha1.ChildUpdateStatusChecks, child *unstructured.Unstructured) error {
	if checks == nil {
		// Nothing to check.
		return nil
	}

	for _, condCheck := range checks.Conditions {
		cond := dynamicobject.GetStatusCondition(child.UnstructuredContent(), condCheck.Type)
		if cond == nil {
			return fmt.Errorf("required condition type missing: %q", condCheck.Type)
		}
		if condCheck.Status != nil {
			if cond.Status != *condCheck.Status {
				return fmt.Errorf("%q condition status is %q (want %q)", condCheck.Type, cond.Status, *condCheck.Status)
			}
		}
		if condCheck.Reason != nil {
			if cond.Reason != *condCheck.Reason {
				return fmt.Errorf("%q condition reason is %q (want %q)", condCheck.Type, cond.Reason, *condCheck.Reason)
			}
		}
	}
	return nil
}

type updateStrategyMap map[string]*v1alpha1.ChildUpdateStrategy

func (m updateStrategyMap) get(apiGroup, kind string) *v1alpha1.ChildUpdateStrategy {
	return m[claimMapKey(apiGroup, kind)]
}

func (m updateStrategyMap) isRolling(apiGroup, kind string) bool {
	return isRollingStrategy(m.get(apiGroup, kind))
}

func (m updateStrategyMap) anyRolling() bool {
	for _, strategy := range m {
		if isRollingStrategy(strategy) {
			return true
		}
	}
	return false
}

func isRollingStrategy(strategy *v1alpha1.ChildUpdateStrategy) bool {
	if strategy == nil {
		// This child kind uses OnDelete (don't update at all).
		return false
	}
	switch strategy.Method {
	case v1alpha1.ChildUpdateRollingInPlace, v1alpha1.ChildUpdateRollingRecreate:
		return true
	}
	return false
}

func makeUpdateStrategyMap(resources *dynamicdiscovery.ResourceMap, cc *v1alpha1.CompositeController) (updateStrategyMap, error) {
	m := make(updateStrategyMap)
	for _, child := range cc.Spec.ChildResources {
		if child.UpdateStrategy != nil && child.UpdateStrategy.Method != v1alpha1.ChildUpdateOnDelete {
			// Map resource name to kind name.
			resource := resources.Get(child.APIVersion, child.Resource)
			if resource == nil {
				return nil, fmt.Errorf("can't find child resource %q in %v", child.Resource, child.APIVersion)
			}
			// Ignore API version.
			apiGroup, _ := parseAPIVersion(child.APIVersion)
			key := claimMapKey(apiGroup, resource.Kind)
			m[key] = child.UpdateStrategy
		}
	}
	return m, nil
}
<commit_msg>CompositeController: Skip no-op syncs during rolling update.<commit_after>/*
Copyright 2018 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package composite

import (
	"fmt"
	"reflect"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

	"k8s.io/metacontroller/apis/metacontroller/v1alpha1"
	dynamicdiscovery "k8s.io/metacontroller/dynamic/discovery"
	dynamicobject "k8s.io/metacontroller/dynamic/object"
)

func (pc *parentController) syncRollingUpdate(parentRevisions []*parentRevision, observedChildren childMap) error {
	// Reconcile the set of existing child claims in ControllerRevisions.
	claimed := pc.syncRevisionClaims(parentRevisions)

	// Give the latest revision any children it desires that aren't claimed yet,
	// or that don't need any changes to match the desired state.
	latest := parentRevisions[0]
	for gvk, objects := range latest.desiredChildMap {
		apiVersion, kind := parseChildMapKey(gvk)
		// Ignore the API version, because the 'claimed' map is version-agnostic.
		apiGroup, _ := parseAPIVersion(apiVersion)
		key := claimMapKey(apiGroup, kind)

		// Skip if rolling update isn't enabled for this child type.
		if !pc.updateStrategy.isRolling(apiGroup, kind) {
			continue
		}

		claimMap := claimed[key]
		for name, desiredChild := range objects {
			pr, found := claimMap[name]
			if !found {
				// No revision claims this child, so give it to the latest revision.
				latest.addChild(apiGroup, kind, name)
				continue
			}
			if pr == latest {
				// It's already owned by the latest revision. Nothing to do.
				continue
			}
			// This child is claimed by another revision, but if it already matches
			// the desired state in the latest revision, we can move it immediately.
			child := observedChildren.findGroupKindName(apiGroup, kind, name)
			if child == nil {
				// The child wasn't observed, so we don't know if it'll match latest.
				continue
			}
			updated, err := applyUpdate(child, desiredChild)
			if err != nil {
				// We can't prove it'll be a no-op, so don't move it to latest.
				continue
			}
			if reflect.DeepEqual(child, updated) {
				// This will be a no-op update, so move it immediately instead of
				// waiting until the next sync. In addition to reducing unnecessary
				// ControllerRevision updates, this helps ensure that the overall sync
				// won't be a no-op, which would mean there's nothing changing that
				// would trigger a resync to continue the rollout.
				latest.addChild(apiGroup, kind, name)
				pr.removeChild(apiGroup, kind, name)
			}
		}
	}

	// Look for the next child to update, if any.
	// We go one by one, in the order in which the controller returned them
	// in the latest sync hook result.
	for _, child := range latest.desiredChildList {
		apiGroup, _ := parseAPIVersion(child.GetAPIVersion())
		kind := child.GetKind()
		name := child.GetName()
		key := claimMapKey(apiGroup, kind)

		// Skip if rolling update isn't enabled for this child type.
		if !pc.updateStrategy.isRolling(apiGroup, kind) {
			continue
		}

		// Look up which revision claims this child, if any.
		var pr *parentRevision
		if claimMap := claimed[key]; claimMap != nil {
			pr = claimMap[name]
		}

		// Move the first child that isn't in the latest revision to the latest.
		if pr != latest {
			// We only continue to push more children into the latest revision if all
			// the children already in the latest revision are happy, where "happy" is
			// defined by the statusChecks in each child type's updateStrategy.
			if err := pc.shouldContinueRolling(latest, observedChildren); err != nil {
				// Add status condition to explain what we're waiting for.
				updatedCondition := &dynamicobject.StatusCondition{
					Type:    "Updated",
					Status:  "False",
					Reason:  "RolloutWaiting",
					Message: err.Error(),
				}
				dynamicobject.SetCondition(latest.status, updatedCondition)
				return nil
			}

			latest.addChild(apiGroup, kind, name)
			// Remove it from all other revisions.
			for _, pr := range parentRevisions[1:] {
				pr.removeChild(apiGroup, kind, name)
			}

			// We've done our one move for this sync pass.
			// Add status condition to explain what we're doing next.
			updatedCondition := &dynamicobject.StatusCondition{
				Type:    "Updated",
				Status:  "False",
				Reason:  "RolloutProgressing",
				Message: fmt.Sprintf("updating %v %v", kind, name),
			}
			dynamicobject.SetCondition(latest.status, updatedCondition)
			return nil
		}
	}

	// Everything is already on the latest revision.
	updatedCondition := &dynamicobject.StatusCondition{
		Type:    "Updated",
		Status:  "True",
		Reason:  "OnLatestRevision",
		Message: fmt.Sprintf("latest ControllerRevision: %v", latest.revision.Name),
	}
	dynamicobject.SetCondition(latest.status, updatedCondition)
	return nil
}

func (pc *parentController) shouldContinueRolling(latest *parentRevision, observedChildren childMap) error {
	// We continue rolling only if all children claimed by the latest revision
	// are updated and were observed in a "happy" state, according to the
	// user-supplied, resource-specific status checks.
	for _, ck := range latest.revision.Children {
		strategy := pc.updateStrategy.get(ck.APIGroup, ck.Kind)
		if !isRollingStrategy(strategy) {
			// We don't need to check children that don't use rolling update.
			continue
		}

		for _, name := range ck.Names {
			child := observedChildren.findGroupKindName(ck.APIGroup, ck.Kind, name)
			if child == nil {
				// We didn't observe this child at all, so it's not happy.
				return fmt.Errorf("missing child %v %v", ck.Kind, name)
			}
			// Is this child up-to-date with what the latest revision wants?
			// Apply the latest update to it and see if anything changes.
			update := latest.desiredChildMap.findGroupKindName(ck.APIGroup, ck.Kind, name)
			updated, err := applyUpdate(child, update)
			if err != nil {
				return fmt.Errorf("can't check if child %v %v is updated: %v", ck.Kind, name, err)
			}
			if !reflect.DeepEqual(child, updated) {
				return fmt.Errorf("child %v %v is not updated yet", ck.Kind, name)
			}
			// For RollingInPlace, we should check ObservedGeneration (if possible)
			// before checking status, to make sure status reflects the latest spec.
			if strategy.Method == v1alpha1.ChildUpdateRollingInPlace {
				// Ideally every controller would support ObservedGeneration, but not
				// all do, so we have to ignore it if it's not present.
				if observedGeneration := dynamicobject.GetObservedGeneration(child.UnstructuredContent()); observedGeneration > 0 {
					// Ideally we would remember the Generation from our own last Update,
					// but we don't have a good place to persist that.
					// Instead, we compare with the latest Generation, which should be
					// fine as long as the object spec is not updated frequently.
					if observedGeneration < child.GetGeneration() {
						return fmt.Errorf("child %v %v with RollingInPlace update strategy hasn't observed latest spec", ck.Kind, name)
					}
				}
			}
			// Check the child status according to the updateStrategy.
			if err := childStatusCheck(&strategy.StatusChecks, child); err != nil {
				// If any child already on the latest revision fails the status check,
				// pause the rollout.
				return fmt.Errorf("child %v %v failed status check: %v", ck.Kind, name, err)
			}
		}
	}
	return nil
}

func (pc *parentController) syncRevisionClaims(parentRevisions []*parentRevision) childClaimMap {
	// The latest revision is always the first item.
	latest := parentRevisions[0]

	// Build a map for lookup from a child to the parentRevision that claims it.
	claimed := make(map[string]map[string]*parentRevision)

	for _, pr := range parentRevisions {
		children := make([]v1alpha1.ControllerRevisionChildren, 0, len(pr.revision.Children))

		for _, ck := range pr.revision.Children {
			if !pc.updateStrategy.isRolling(ck.APIGroup, ck.Kind) {
				// Remove claims for any child kinds that no longer use rolling update.
				continue
			}

			key := claimMapKey(ck.APIGroup, ck.Kind)
			names := make([]string, 0, len(ck.Names))

			for _, name := range ck.Names {
				// Remove claims for any children that the latest revision no longer desires.
				// Such children will be deleted immediately, so we can forget the claim.
				if latest.desiredChildMap.findGroupKindName(ck.APIGroup, ck.Kind, name) == nil {
					continue
				}

				// Get the sub-map for this child kind.
				claimMap := claimed[key]
				if _, exists := claimMap[name]; exists {
					// Another revision already claimed this child, so drop it from here.
					// The only precedence rule we care about is that the latest revision
					// wins over any other, which is ensured by the fact that the latest
					// revision is always first in the list.
					continue
				}
				// Create a new sub-map if necessary.
				if claimMap == nil {
					claimMap = make(map[string]*parentRevision)
					claimed[key] = claimMap
				}
				claimMap[name] = pr
				names = append(names, name)
			}

			if len(names) == 0 {
				// Remove the whole child kind if there are no names left.
				continue
			}

			children = append(children, ck)
		}

		pr.revision.Children = children
	}
	return claimed
}

func childStatusCheck(checks *v1alpha1.ChildUpdateStatusChecks, child *unstructured.Unstructured) error {
	if checks == nil {
		// Nothing to check.
		return nil
	}

	for _, condCheck := range checks.Conditions {
		cond := dynamicobject.GetStatusCondition(child.UnstructuredContent(), condCheck.Type)
		if cond == nil {
			return fmt.Errorf("required condition type missing: %q", condCheck.Type)
		}
		if condCheck.Status != nil {
			if cond.Status != *condCheck.Status {
				return fmt.Errorf("%q condition status is %q (want %q)", condCheck.Type, cond.Status, *condCheck.Status)
			}
		}
		if condCheck.Reason != nil {
			if cond.Reason != *condCheck.Reason {
				return fmt.Errorf("%q condition reason is %q (want %q)", condCheck.Type, cond.Reason, *condCheck.Reason)
			}
		}
	}
	return nil
}

type updateStrategyMap map[string]*v1alpha1.ChildUpdateStrategy

func (m updateStrategyMap) get(apiGroup, kind string) *v1alpha1.ChildUpdateStrategy {
	return m[claimMapKey(apiGroup, kind)]
}

func (m updateStrategyMap) isRolling(apiGroup, kind string) bool {
	return isRollingStrategy(m.get(apiGroup, kind))
}

func (m updateStrategyMap) anyRolling() bool {
	for _, strategy := range m {
		if isRollingStrategy(strategy) {
			return true
		}
	}
	return false
}

func isRollingStrategy(strategy *v1alpha1.ChildUpdateStrategy) bool {
	if strategy == nil {
		// This child kind uses OnDelete (don't update at all).
		return false
	}
	switch strategy.Method {
	case v1alpha1.ChildUpdateRollingInPlace, v1alpha1.ChildUpdateRollingRecreate:
		return true
	}
	return false
}

func makeUpdateStrategyMap(resources *dynamicdiscovery.ResourceMap, cc *v1alpha1.CompositeController) (updateStrategyMap, error) {
	m := make(updateStrategyMap)
	for _, child := range cc.Spec.ChildResources {
		if child.UpdateStrategy != nil && child.UpdateStrategy.Method != v1alpha1.ChildUpdateOnDelete {
			// Map resource name to kind name.
			resource := resources.Get(child.APIVersion, child.Resource)
			if resource == nil {
				return nil, fmt.Errorf("can't find child resource %q in %v", child.Resource, child.APIVersion)
			}
			// Ignore API version.
			apiGroup, _ := parseAPIVersion(child.APIVersion)
			key := claimMapKey(apiGroup, resource.Kind)
			m[key] = child.UpdateStrategy
		}
	}
	return m, nil
}
<|endoftext|>


<commit_before>package stars

import (
	"fmt"
	"log"
	"math/rand"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func pagesCount(s string) (int, error) {
	if s == "" {
		return 1, nil
	}

	m := make(map[string]int)

	pages := regexp.MustCompile(`.*[\?&]page\=(?P<page>\d+).*rel=\"(?P<rel>.*)\"`)

	for _, link := range strings.Split(s, ",") {
		var rel string
		var number int

		match := pages.FindStringSubmatch(link)

		for i, name := range pages.SubexpNames() {
			switch name {
			case "rel":
				rel = match[i]
			case "page":
				number, _ = strconv.Atoi(match[i])
			}
		}

		if rel != "" && number > 0 {
			m[rel] = number
		}
	}

	if m["last"] == 0 && m["prev"] > 0 {
		return m["prev"] + 1, nil
	}
	return m["last"], nil
}

func randomPageNumber(i int) int {
	const shortForm = "2006-January-02"

	year, month, day := time.Now().Date()

	date := fmt.Sprintf("%v-%v-%v", year, month, day)
	t, _ := time.Parse(shortForm, date)
	rand.Seed(t.Unix())

	randomNumber := rand.Intn(i)
	log.Printf("Random: %v\n", randomNumber)

	return randomNumber
}
<commit_msg>Fix padding in front of date to prevent wrong parsing of date<commit_after>package stars

import (
	"fmt"
	"log"
	"math/rand"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func pagesCount(s string) (int, error) {
	if s == "" {
		return 1, nil
	}

	m := make(map[string]int)

	pages := regexp.MustCompile(`.*[\?&]page\=(?P<page>\d+).*rel=\"(?P<rel>.*)\"`)

	for _, link := range strings.Split(s, ",") {
		var rel string
		var number int

		match := pages.FindStringSubmatch(link)

		for i, name := range pages.SubexpNames() {
			switch name {
			case "rel":
				rel = match[i]
			case "page":
				number, _ = strconv.Atoi(match[i])
			}
		}

		if rel != "" && number > 0 {
			m[rel] = number
		}
	}

	if m["last"] == 0 && m["prev"] > 0 {
		return m["prev"] + 1, nil
	}
	return m["last"], nil
}

func randomPageNumber(i int) int {
	const shortForm = "2006-January-02"

	year, month, day := time.Now().Date()

	date := fmt.Sprintf("%v-%v-%02d", year, month, day)
	t, _ := time.Parse(shortForm, date)
	rand.Seed(t.Unix())

	randomNumber := rand.Intn(i)
	log.Printf("Random: %v\n", randomNumber)

	return randomNumber
}
<|endoftext|>


<commit_before>package seven5

import (
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

const (
	prefix = "gopath"
)

//SimpleStaticFileServer is a simple implementation of a file server
//for static files that is sufficient for most simple applications.  It
//defaults to serving "/" from "static" (child of the current directory)
//but this can be changed with the environment variable STATIC_DIR.
type SimpleStaticFilesServer struct {
	testMode  bool
	mountedAt string
	staticDir string
	fs        http.Handler
}

//NewSimpleStaticFilesServer returns a new file server and if isTestMode
//is true and the environment variable GOPATH is set, it will
//also serve up go source files from the GOPATH.  It expects that the
//prefix "/gopath" will be used for gopath requests.  You should supply
//the place this has been "mounted" in the URL space (usually "/"") in
//the first parameter.
func NewSimpleStaticFilesServer(mountedAt string, isTestMode bool) *SimpleStaticFilesServer {
	staticDir := "static"
	env := os.Getenv("STATIC_DIR")
	if env != "" {
		log.Printf("STATIC_DIR is set, using  %s for static files", env)
		staticDir = env
	}
	return &SimpleStaticFilesServer{
		testMode:  isTestMode,
		mountedAt: mountedAt,
		staticDir: staticDir,
		fs:        http.StripPrefix(mountedAt, http.FileServer(http.Dir(staticDir))),
	}
}

func (s *SimpleStaticFilesServer) gopath(w http.ResponseWriter, r *http.Request, desired string) {
	gopathSegments := strings.Split(os.Getenv("GOPATH"), ":")
	for _, gopath := range gopathSegments {
		_, err := os.Stat(filepath.Join(gopath, desired))
		if err != nil {
			continue
		}
		log.Printf("serving gopath content  %s: /%v", gopath, desired)
		http.ServeFile(w, r, filepath.Join(gopath, desired))
		return
	}
	http.NotFound(w, r)
}

//ServeHTTP retuns a static file or a not found error. This function meets
//the requirement of net/http#Handler.
func (s *SimpleStaticFilesServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if s.testMode && strings.HasPrefix(r.URL.String(), prefix) {
		s.gopath(w, r, r.URL.String()[len(prefix):])
		return
	}
	log.Printf("[STATIC CONTENT (%s)]: %v", s.staticDir, r.URL.String())
	s.fs.ServeHTTP(w, r)
}
<commit_msg>debug prin<commit_after>package seven5

import (
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

const (
	prefix = "gopath"
)

//SimpleStaticFileServer is a simple implementation of a file server
//for static files that is sufficient for most simple applications.  It
//defaults to serving "/" from "static" (child of the current directory)
//but this can be changed with the environment variable STATIC_DIR.
type SimpleStaticFilesServer struct {
	testMode  bool
	mountedAt string
	staticDir string
	fs        http.Handler
}

//NewSimpleStaticFilesServer returns a new file server and if isTestMode
//is true and the environment variable GOPATH is set, it will
//also serve up go source files from the GOPATH.  It expects that the
//prefix "/gopath" will be used for gopath requests.  You should supply
//the place this has been "mounted" in the URL space (usually "/"") in
//the first parameter.
func NewSimpleStaticFilesServer(mountedAt string, isTestMode bool) *SimpleStaticFilesServer {
	staticDir := "static"
	env := os.Getenv("STATIC_DIR")
	if env != "" {
		log.Printf("STATIC_DIR is set, using  %s for static files", env)
		staticDir = env
	}
	return &SimpleStaticFilesServer{
		testMode:  isTestMode,
		mountedAt: mountedAt,
		staticDir: staticDir,
		fs:        http.StripPrefix(mountedAt, http.FileServer(http.Dir(staticDir))),
	}
}

func (s *SimpleStaticFilesServer) gopath(w http.ResponseWriter, r *http.Request, desired string) {
	gopathSegments := strings.Split(os.Getenv("GOPATH"), ":")
	for _, gopath := range gopathSegments {
		_, err := os.Stat(filepath.Join(gopath, desired))
		if err != nil {
			continue
		}
		log.Printf("serving gopath content  %s: /%v", gopath, desired)
		http.ServeFile(w, r, filepath.Join(gopath, desired))
		return
	}
	http.NotFound(w, r)
}

//ServeHTTP retuns a static file or a not found error. This function meets
//the requirement of net/http#Handler.
func (s *SimpleStaticFilesServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Printf("test=%v, %s, prefix=%", s.testMode, r.URL.String(), strings.HasPrefix(r.URL.String(), prefix))
	if s.testMode && strings.HasPrefix(r.URL.String(), prefix) {
		s.gopath(w, r, r.URL.String()[len(prefix):])
		return
	}
	log.Printf("[STATIC CONTENT (%s)]: %v", s.staticDir, r.URL.String())
	s.fs.ServeHTTP(w, r)
}
<|endoftext|>


<commit_before>package storage

import (
	"io"
	"log"
	"os"
	"path/filepath"

	"github.com/anacrolix/missinggo"
	"github.com/anacrolix/torrent/common"
	"github.com/anacrolix/torrent/segments"

	"github.com/anacrolix/torrent/metainfo"
)

// File-based storage for torrents, that isn't yet bound to a particular
// torrent.
type fileClientImpl struct {
	baseDir   string
	pathMaker func(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string
	pc        PieceCompletion
}

// The Default path maker just returns the current path
func defaultPathMaker(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string {
	return baseDir
}

func infoHashPathMaker(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string {
	return filepath.Join(baseDir, infoHash.HexString())
}

// All Torrent data stored in this baseDir
func NewFile(baseDir string) ClientImplCloser {
	return NewFileWithCompletion(baseDir, pieceCompletionForDir(baseDir))
}

func NewFileWithCompletion(baseDir string, completion PieceCompletion) *fileClientImpl {
	return newFileWithCustomPathMakerAndCompletion(baseDir, nil, completion)
}

// File storage with data partitioned by infohash.
func NewFileByInfoHash(baseDir string) ClientImpl {
	return NewFileWithCustomPathMaker(baseDir, infoHashPathMaker)
}

// Allows passing a function to determine the path for storing torrent data
func NewFileWithCustomPathMaker(baseDir string, pathMaker func(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string) ClientImpl {
	return newFileWithCustomPathMakerAndCompletion(baseDir, pathMaker, pieceCompletionForDir(baseDir))
}

func newFileWithCustomPathMakerAndCompletion(baseDir string, pathMaker func(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string, completion PieceCompletion) *fileClientImpl {
	if pathMaker == nil {
		pathMaker = defaultPathMaker
	}
	return &fileClientImpl{
		baseDir:   baseDir,
		pathMaker: pathMaker,
		pc:        completion,
	}
}

func (me *fileClientImpl) Close() error {
	return me.pc.Close()
}

func (fs *fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
	dir := fs.pathMaker(fs.baseDir, info, infoHash)
	err := CreateNativeZeroLengthFiles(info, dir)
	if err != nil {
		return nil, err
	}
	upvertedFiles := info.UpvertedFiles()
	return &fileTorrentImpl{
		dir,
		info.Name,
		upvertedFiles,
		segments.NewIndex(common.LengthIterFromUpvertedFiles(upvertedFiles)),
		infoHash,
		fs.pc,
	}, nil
}

type fileTorrentImpl struct {
	dir            string
	infoName       string
	upvertedFiles  []metainfo.FileInfo
	segmentLocater segments.Index
	infoHash       metainfo.Hash
	completion     PieceCompletion
}

func (fts *fileTorrentImpl) Piece(p metainfo.Piece) PieceImpl {
	// Create a view onto the file-based torrent storage.
	_io := fileTorrentImplIO{fts}
	// Return the appropriate segments of this.
	return &filePieceImpl{
		fts,
		p,
		missinggo.NewSectionWriter(_io, p.Offset(), p.Length()),
		io.NewSectionReader(_io, p.Offset(), p.Length()),
	}
}

func (fs *fileTorrentImpl) Close() error {
	return nil
}

// Creates natives files for any zero-length file entries in the info. This is
// a helper for file-based storages, which don't address or write to zero-
// length files because they have no corresponding pieces.
func CreateNativeZeroLengthFiles(info *metainfo.Info, dir string) (err error) {
	for _, fi := range info.UpvertedFiles() {
		if fi.Length != 0 {
			continue
		}
		name := filepath.Join(append([]string{dir, info.Name}, fi.Path...)...)
		os.MkdirAll(filepath.Dir(name), 0777)
		var f io.Closer
		f, err = os.Create(name)
		if err != nil {
			break
		}
		f.Close()
	}
	return
}

// Exposes file-based storage of a torrent, as one big ReadWriterAt.
type fileTorrentImplIO struct {
	fts *fileTorrentImpl
}

// Returns EOF on short or missing file.
func (fst *fileTorrentImplIO) readFileAt(fi metainfo.FileInfo, b []byte, off int64) (n int, err error) {
	f, err := os.Open(fst.fts.fileInfoName(fi))
	if os.IsNotExist(err) {
		// File missing is treated the same as a short file.
		err = io.EOF
		return
	}
	if err != nil {
		return
	}
	defer f.Close()
	// Limit the read to within the expected bounds of this file.
	if int64(len(b)) > fi.Length-off {
		b = b[:fi.Length-off]
	}
	for off < fi.Length && len(b) != 0 {
		n1, err1 := f.ReadAt(b, off)
		b = b[n1:]
		n += n1
		off += int64(n1)
		if n1 == 0 {
			err = err1
			break
		}
	}
	return
}

// Only returns EOF at the end of the torrent. Premature EOF is ErrUnexpectedEOF.
func (fst fileTorrentImplIO) ReadAt(b []byte, off int64) (n int, err error) {
	fst.fts.segmentLocater.Locate(segments.Extent{off, int64(len(b))}, func(i int, e segments.Extent) bool {
		n1, err1 := fst.readFileAt(fst.fts.upvertedFiles[i], b[:e.Length], e.Start)
		n += n1
		b = b[n1:]
		err = err1
		return err == nil // && int64(n1) == e.Length
	})
	if len(b) != 0 && err == nil {
		err = io.EOF
	}
	return
}

func (fst fileTorrentImplIO) WriteAt(p []byte, off int64) (n int, err error) {
	log.Printf("write at %v: %v bytes", off, len(p))
	fst.fts.segmentLocater.Locate(segments.Extent{off, int64(len(p))}, func(i int, e segments.Extent) bool {
		name := fst.fts.fileInfoName(fst.fts.upvertedFiles[i])
		os.MkdirAll(filepath.Dir(name), 0777)
		var f *os.File
		f, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			return false
		}
		var n1 int
		n1, err = f.WriteAt(p[:e.Length], e.Start)
		log.Printf("%v %v wrote %v: %v", i, e, n1, err)
		closeErr := f.Close()
		n += n1
		p = p[n1:]
		if err == nil {
			err = closeErr
		}
		//if err == nil && int64(n1) != e.Length {
		//	err = io.ErrShortWrite
		//}
		return err == nil
	})
	return
}

func (fts *fileTorrentImpl) fileInfoName(fi metainfo.FileInfo) string {
	return filepath.Join(append([]string{fts.dir, fts.infoName}, fi.Path...)...)
}
<commit_msg>storage file implementation: Error on short writes<commit_after>package storage

import (
	"io"
	"log"
	"os"
	"path/filepath"

	"github.com/anacrolix/missinggo"
	"github.com/anacrolix/torrent/common"
	"github.com/anacrolix/torrent/segments"

	"github.com/anacrolix/torrent/metainfo"
)

// File-based storage for torrents, that isn't yet bound to a particular
// torrent.
type fileClientImpl struct {
	baseDir   string
	pathMaker func(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string
	pc        PieceCompletion
}

// The Default path maker just returns the current path
func defaultPathMaker(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string {
	return baseDir
}

func infoHashPathMaker(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string {
	return filepath.Join(baseDir, infoHash.HexString())
}

// All Torrent data stored in this baseDir
func NewFile(baseDir string) ClientImplCloser {
	return NewFileWithCompletion(baseDir, pieceCompletionForDir(baseDir))
}

func NewFileWithCompletion(baseDir string, completion PieceCompletion) *fileClientImpl {
	return newFileWithCustomPathMakerAndCompletion(baseDir, nil, completion)
}

// File storage with data partitioned by infohash.
func NewFileByInfoHash(baseDir string) ClientImpl {
	return NewFileWithCustomPathMaker(baseDir, infoHashPathMaker)
}

// Allows passing a function to determine the path for storing torrent data
func NewFileWithCustomPathMaker(baseDir string, pathMaker func(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string) ClientImpl {
	return newFileWithCustomPathMakerAndCompletion(baseDir, pathMaker, pieceCompletionForDir(baseDir))
}

func newFileWithCustomPathMakerAndCompletion(baseDir string, pathMaker func(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string, completion PieceCompletion) *fileClientImpl {
	if pathMaker == nil {
		pathMaker = defaultPathMaker
	}
	return &fileClientImpl{
		baseDir:   baseDir,
		pathMaker: pathMaker,
		pc:        completion,
	}
}

func (me *fileClientImpl) Close() error {
	return me.pc.Close()
}

func (fs *fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
	dir := fs.pathMaker(fs.baseDir, info, infoHash)
	err := CreateNativeZeroLengthFiles(info, dir)
	if err != nil {
		return nil, err
	}
	upvertedFiles := info.UpvertedFiles()
	return &fileTorrentImpl{
		dir,
		info.Name,
		upvertedFiles,
		segments.NewIndex(common.LengthIterFromUpvertedFiles(upvertedFiles)),
		infoHash,
		fs.pc,
	}, nil
}

type fileTorrentImpl struct {
	dir            string
	infoName       string
	upvertedFiles  []metainfo.FileInfo
	segmentLocater segments.Index
	infoHash       metainfo.Hash
	completion     PieceCompletion
}

func (fts *fileTorrentImpl) Piece(p metainfo.Piece) PieceImpl {
	// Create a view onto the file-based torrent storage.
	_io := fileTorrentImplIO{fts}
	// Return the appropriate segments of this.
	return &filePieceImpl{
		fts,
		p,
		missinggo.NewSectionWriter(_io, p.Offset(), p.Length()),
		io.NewSectionReader(_io, p.Offset(), p.Length()),
	}
}

func (fs *fileTorrentImpl) Close() error {
	return nil
}

// Creates natives files for any zero-length file entries in the info. This is
// a helper for file-based storages, which don't address or write to zero-
// length files because they have no corresponding pieces.
func CreateNativeZeroLengthFiles(info *metainfo.Info, dir string) (err error) {
	for _, fi := range info.UpvertedFiles() {
		if fi.Length != 0 {
			continue
		}
		name := filepath.Join(append([]string{dir, info.Name}, fi.Path...)...)
		os.MkdirAll(filepath.Dir(name), 0777)
		var f io.Closer
		f, err = os.Create(name)
		if err != nil {
			break
		}
		f.Close()
	}
	return
}

// Exposes file-based storage of a torrent, as one big ReadWriterAt.
type fileTorrentImplIO struct {
	fts *fileTorrentImpl
}

// Returns EOF on short or missing file.
func (fst *fileTorrentImplIO) readFileAt(fi metainfo.FileInfo, b []byte, off int64) (n int, err error) {
	f, err := os.Open(fst.fts.fileInfoName(fi))
	if os.IsNotExist(err) {
		// File missing is treated the same as a short file.
		err = io.EOF
		return
	}
	if err != nil {
		return
	}
	defer f.Close()
	// Limit the read to within the expected bounds of this file.
	if int64(len(b)) > fi.Length-off {
		b = b[:fi.Length-off]
	}
	for off < fi.Length && len(b) != 0 {
		n1, err1 := f.ReadAt(b, off)
		b = b[n1:]
		n += n1
		off += int64(n1)
		if n1 == 0 {
			err = err1
			break
		}
	}
	return
}

// Only returns EOF at the end of the torrent. Premature EOF is ErrUnexpectedEOF.
func (fst fileTorrentImplIO) ReadAt(b []byte, off int64) (n int, err error) {
	fst.fts.segmentLocater.Locate(segments.Extent{off, int64(len(b))}, func(i int, e segments.Extent) bool {
		n1, err1 := fst.readFileAt(fst.fts.upvertedFiles[i], b[:e.Length], e.Start)
		n += n1
		b = b[n1:]
		err = err1
		return err == nil // && int64(n1) == e.Length
	})
	if len(b) != 0 && err == nil {
		err = io.EOF
	}
	return
}

func (fst fileTorrentImplIO) WriteAt(p []byte, off int64) (n int, err error) {
	log.Printf("write at %v: %v bytes", off, len(p))
	fst.fts.segmentLocater.Locate(segments.Extent{off, int64(len(p))}, func(i int, e segments.Extent) bool {
		name := fst.fts.fileInfoName(fst.fts.upvertedFiles[i])
		os.MkdirAll(filepath.Dir(name), 0777)
		var f *os.File
		f, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			return false
		}
		var n1 int
		n1, err = f.WriteAt(p[:e.Length], e.Start)
		log.Printf("%v %v wrote %v: %v", i, e, n1, err)
		closeErr := f.Close()
		n += n1
		p = p[n1:]
		if err == nil {
			err = closeErr
		}
		if err == nil && int64(n1) != e.Length {
			err = io.ErrShortWrite
		}
		return err == nil
	})
	return
}

func (fts *fileTorrentImpl) fileInfoName(fi metainfo.FileInfo) string {
	return filepath.Join(append([]string{fts.dir, fts.infoName}, fi.Path...)...)
}
<|endoftext|>


<commit_before>package widget_test

import (
	"fmt"
	"testing"

	"net/http"
	"strings"

	"github.com/fatih/color"
	"github.com/jinzhu/gorm"
	"github.com/qor/admin"
	"github.com/qor/qor"
	"github.com/qor/qor/test/utils"
	"github.com/qor/widget"
)

var db *gorm.DB
var Widgets *widget.Widgets
var Admin *admin.Admin

type bannerArgument struct {
	Title    string
	SubTitle string
}

func init() {
	db = utils.TestDB()
}

// Runner
func TestRender(t *testing.T) {
	if err := db.DropTableIfExists(&widget.QorWidgetSetting{}).Error; err != nil {
		panic(err)
	}
	db.AutoMigrate(&widget.QorWidgetSetting{})

	Widgets = widget.New(&widget.Config{
		DB: db,
	})
	Widgets.RegisterViewPath("github.com/qor/widget/test")

	Admin = admin.New(&qor.Config{DB: db})
	Admin.AddResource(Widgets)
	Admin.MountTo("/admin", http.NewServeMux())

	Widgets.RegisterWidget(&widget.Widget{
		Name:      "Banner",
		Templates: []string{"banner"},
		Setting:   Admin.NewResource(&bannerArgument{}),
		Context: func(context *widget.Context, setting interface{}) *widget.Context {
			if setting != nil {
				argument := setting.(*bannerArgument)
				context.Options["Title"] = argument.Title
				context.Options["SubTitle"] = argument.SubTitle
			}
			return context
		},
	})
}

func reset() {
	db.DropTable(&widget.QorWidgetSetting{})
	db.AutoMigrate(&widget.QorWidgetSetting{})
}

// Test DB's record after call Render
func TestRenderRecord(t *testing.T) {
	reset()
	var count int
	db.Model(&widget.QorWidgetSetting{}).Where(widget.QorWidgetSetting{Name: "HomeBanner", WidgetType: "Banner", Scope: "default", GroupName: "Banner"}).Count(&count)
	if count != 0 {
		t.Errorf(color.RedString(fmt.Sprintf("\nWidget Render Record TestCase: should don't exist widget setting")))
	}

	widgetContext := Widgets.NewContext(&widget.Context{})
	widgetContext.Render("HomeBanner", "Banner")
	db.Model(&widget.QorWidgetSetting{}).Where(widget.QorWidgetSetting{Name: "HomeBanner", WidgetType: "Banner", Scope: "default", GroupName: "Banner"}).Count(&count)
	if count == 0 {
		t.Errorf(color.RedString(fmt.Sprintf("\nWidget Render Record TestCase: should have default widget setting")))
	}
}

// Runner
func TestRenderContext(t *testing.T) {
	reset()
	setting := &widget.QorWidgetSetting{}
	db.Where(widget.QorWidgetSetting{Name: "HomeBanner", WidgetType: "Banner"}).FirstOrInit(setting)
	db.Create(setting)

	html := Widgets.Render("HomeBanner", "Banner")
	if !strings.Contains(string(html), "Hello, \n

\n

\n") {
		t.Errorf(color.RedString(fmt.Sprintf("\nWidget Render TestCase #%d: Failure Result:\n %s\n", 1, html)))
	}

	widgetContext := Widgets.NewContext(&widget.Context{
		Options: map[string]interface{}{"CurrentUser": "Qortex"},
	})
	html = widgetContext.Render("HomeBanner", "Banner")
	if !strings.Contains(string(html), "Hello, Qortex\n

\n

\n") {
		t.Errorf(color.RedString(fmt.Sprintf("\nWidget Render TestCase #%d: Failure Result:\n %s\n", 2, html)))
	}

	db.Where(widget.QorWidgetSetting{Name: "HomeBanner", WidgetType: "Banner"}).FirstOrInit(setting)
	setting.SetSerializableArgumentValue(&bannerArgument{Title: "Title", SubTitle: "SubTitle"})
	db.Save(setting)

	html = widgetContext.Render("HomeBanner", "Banner")
	if !strings.Contains(string(html), "Hello, Qortex\n
Title
\n
SubTitle
\n") {
		t.Errorf(color.RedString(fmt.Sprintf("\nWidget Render TestCase #%d: Failure Result:\n %s\n", 3, html)))
	}
}
<commit_msg>Add testcase for create a widget setting with new scope<commit_after>package widget_test

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"

	"github.com/fatih/color"
	"github.com/jinzhu/gorm"
	"github.com/qor/admin"
	"github.com/qor/qor"
	"github.com/qor/qor/test/utils"
	"github.com/qor/widget"
)

var db *gorm.DB
var Widgets *widget.Widgets
var Admin *admin.Admin
var Server *httptest.Server

type bannerArgument struct {
	Title    string
	SubTitle string
}

func init() {
	db = utils.TestDB()
}

// Runner
func TestRender(t *testing.T) {
	if err := db.DropTableIfExists(&widget.QorWidgetSetting{}).Error; err != nil {
		panic(err)
	}
	db.AutoMigrate(&widget.QorWidgetSetting{})
	mux := http.NewServeMux()
	Server = httptest.NewServer(mux)

	Widgets = widget.New(&widget.Config{
		DB: db,
	})
	Widgets.RegisterViewPath("github.com/qor/widget/test")

	Admin = admin.New(&qor.Config{DB: db})
	Admin.AddResource(Widgets)
	Admin.MountTo("/admin", mux)

	Widgets.RegisterWidget(&widget.Widget{
		Name:      "Banner",
		Templates: []string{"banner"},
		Setting:   Admin.NewResource(&bannerArgument{}),
		Context: func(context *widget.Context, setting interface{}) *widget.Context {
			if setting != nil {
				argument := setting.(*bannerArgument)
				context.Options["Title"] = argument.Title
				context.Options["SubTitle"] = argument.SubTitle
			}
			return context
		},
	})

	Widgets.RegisterScope(&widget.Scope{
		Name: "From Google",
		Visible: func(context *widget.Context) bool {
			if request, ok := context.Get("Request"); ok {
				_, ok := request.(*http.Request).URL.Query()["from_google"]
				return ok
			}
			return false
		},
	})
}

func reset() {
	db.DropTable(&widget.QorWidgetSetting{})
	db.AutoMigrate(&widget.QorWidgetSetting{})
}

// Test DB's record after call Render
func TestRenderRecord(t *testing.T) {
	reset()
	var count int
	db.Model(&widget.QorWidgetSetting{}).Where(widget.QorWidgetSetting{Name: "HomeBanner", WidgetType: "Banner", Scope: "default", GroupName: "Banner"}).Count(&count)
	if count != 0 {
		t.Errorf(color.RedString(fmt.Sprintf("\nWidget Render Record TestCase: should don't exist widget setting")))
	}

	widgetContext := Widgets.NewContext(&widget.Context{})
	widgetContext.Render("HomeBanner", "Banner")
	db.Model(&widget.QorWidgetSetting{}).Where(widget.QorWidgetSetting{Name: "HomeBanner", WidgetType: "Banner", Scope: "default", GroupName: "Banner"}).Count(&count)
	if count == 0 {
		t.Errorf(color.RedString(fmt.Sprintf("\nWidget Render Record TestCase: should have default widget setting")))
	}

	http.PostForm(Server.URL+"/admin/widgets/HomeBanner",
		url.Values{"_method": {"PUT"},
			"QorResource.Scope":       {"from_google"},
			"QorResource.ActivatedAt": {"2016-07-14 10:10:42.433372925 +0800 CST"},
			"QorResource.Widgets":     {"Banner"},
			"QorResource.Template":    {"banner"},
			"QorResource.Kind":        {"Banner"},
		})
	db.Model(&widget.QorWidgetSetting{}).Where(widget.QorWidgetSetting{Name: "HomeBanner", WidgetType: "Banner", Scope: "from_google"}).Count(&count)
	if count == 0 {
		t.Errorf(color.RedString(fmt.Sprintf("\nWidget Render Record TestCase: should have from_google widget setting")))
	}
}

// Runner
func TestRenderContext(t *testing.T) {
	reset()
	setting := &widget.QorWidgetSetting{}
	db.Where(widget.QorWidgetSetting{Name: "HomeBanner", WidgetType: "Banner"}).FirstOrInit(setting)
	db.Create(setting)

	html := Widgets.Render("HomeBanner", "Banner")
	if !strings.Contains(string(html), "Hello, \n

\n

\n") {
		t.Errorf(color.RedString(fmt.Sprintf("\nWidget Render TestCase #%d: Failure Result:\n %s\n", 1, html)))
	}

	widgetContext := Widgets.NewContext(&widget.Context{
		Options: map[string]interface{}{"CurrentUser": "Qortex"},
	})
	html = widgetContext.Render("HomeBanner", "Banner")
	if !strings.Contains(string(html), "Hello, Qortex\n

\n

\n") {
		t.Errorf(color.RedString(fmt.Sprintf("\nWidget Render TestCase #%d: Failure Result:\n %s\n", 2, html)))
	}

	db.Where(widget.QorWidgetSetting{Name: "HomeBanner", WidgetType: "Banner"}).FirstOrInit(setting)
	setting.SetSerializableArgumentValue(&bannerArgument{Title: "Title", SubTitle: "SubTitle"})
	db.Save(setting)

	html = widgetContext.Render("HomeBanner", "Banner")
	if !strings.Contains(string(html), "Hello, Qortex\n
Title
\n
SubTitle
\n") {
		t.Errorf(color.RedString(fmt.Sprintf("\nWidget Render TestCase #%d: Failure Result:\n %s\n", 3, html)))
	}
}
<|endoftext|>