<commit_before>package shell

import (
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"os/exec"
	"syscall"
	"time"

	"github.com/gorilla/websocket"

	"github.com/zenoss/glog"
	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"
)

const (
	FORK   = "FORK"
	EXEC   = "EXEC"
	SIGNAL = "SIGNAL"
)

// Client->Agent protocol
type request struct {
	Action    string
	ServiceId string
	Env       []string
	Cmd       string
	Signal    int
}

// Agent->Client protocol
type response struct {
	Stdin  string
	Stdout string
	Stderr string
	Result string
}

// Defines commands to be run in an object's container
type Process struct {
	ServiceId string              // The service id of the container to start
	IsTTY     bool                // Describes the type of connection needed
	Envv      []string            // Environment variables
	Command   string              // Command to run
	Error     error               `json:"-"`
	Stdin     chan string         `json:"-"`
	Stdout    chan string         `json:"-"`
	Stderr    chan string         `json:"-"`
	Exited    chan bool           `json:"-"`
	Signal    chan syscall.Signal `json:"-"`
	whenDone  chan bool
}

func NewProcess(serviceId, command string, envv []string, istty bool) *Process {
	return &Process{
		ServiceId: serviceId,
		IsTTY:     istty,
		Envv:      envv,
		Command:   command,
		Stdin:     make(chan string),
		Stdout:    make(chan string),
		Stderr:    make(chan string),
		Signal:    make(chan syscall.Signal),
		Exited:    make(chan bool),
		whenDone:  make(chan bool),
	}
}

// Starts a container shell
func Exec(p *Process, s *dao.Service) error {
	var runner Runner

	// Bind mount on /serviced
	dir, bin, err := serviced.ExecPath()
	if err != nil {
		return err
	}
	servicedVolume := fmt.Sprintf("%s:/serviced", dir)

	// Bind mount the pwd
	dir, err = os.Getwd()
	pwdVolume := fmt.Sprintf("%s:/mnt/pwd", dir)

	// Get the shell command
	var shellCmd string
	if p.Command != "" {
		shellCmd = p.Command
	} else {
		shellCmd = "su -"
	}

	// Get the proxy Command
	proxyCmd := []string{fmt.Sprintf("/serviced/%s", bin), "-logtostderr=false", "proxy", "-logstash=false", "-autorestart=false", s.Id, shellCmd}
	// Get the docker start command
	docker, err := exec.LookPath("docker")
	if err != nil {
		return err
	}
	argv := []string{"run", "-rm", "-v", servicedVolume, "-v", pwdVolume}
	argv = append(argv, p.Envv...)

	if p.IsTTY {
		argv = append(argv, "-i", "-t")
	}

	argv = append(argv, s.ImageId)
	argv = append(argv, proxyCmd...)

	runner, err = CreateCommand(docker, argv)

	if err != nil {
		return err
	}

	// @see http://dave.cheney.net/tag/golang-3
	p.Stdout = runner.StdoutPipe()
	p.Stderr = runner.StderrPipe()

	go p.send(runner)
	return nil
}

func (p *Process) send(r Runner) {
	exited := r.ExitedPipe()
	go r.Reader(8192)

	defer func() {
		close(p.Stdin)
	}()

	for {
		select {
		case i := <-p.Stdin:
			r.Write([]byte(i))
		case s := <-p.Signal:
			r.Signal(s)
		case m := <-exited:
			if e := r.Error(); e == nil {
				p.Error = errors.New("0")
			} else {
				p.Error = e
			}
			p.Exited <- m
			p.whenDone <- true
			return
		}
	}
}

func (p *Process) Wait() {
	<-p.whenDone
}

// Describes streams from an agent-executed process to a client
type ProcessStream interface {

	// Initiate client-side communication and create Process
	StreamClient(http.ResponseWriter, *http.Request, chan *Process)

	// Initiate agent-side communication and kick off shell
	StreamAgent()

	// Wait for the process to end
	Wait()
}

type baseProcessStream struct {
	agent   *websocket.Conn
	process *Process
	addr    string
}

type WebsocketProcessStream struct {
	*baseProcessStream
	client *websocket.Conn
}

type HTTPProcessStream struct {
	*baseProcessStream
	client *net.Conn
}

func NewWebsocketProcessStream(addr string) *WebsocketProcessStream {
	return &WebsocketProcessStream{
		baseProcessStream: &baseProcessStream{addr: addr},
	}
}

func NewHTTPProcessStream(addr string) *HTTPProcessStream {
	return &HTTPProcessStream{
		baseProcessStream: &baseProcessStream{addr: addr},
	}
}

type WebsocketProcessHandler struct {
	Addr string
}

type OSProcessHandler struct {
	Port string
}

type HTTPProcessHandler struct {
	Addr string
}

// Implement http.Handler
func (h *WebsocketProcessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	stream := NewWebsocketProcessStream(h.Addr)

	// Create a client and wait for the process packet
	pc := make(chan bool)

	// Set up everything to start the connection to agent once a process is
	// defined.
	go func() {
		<-pc
		// Now that we have the process, connect to the agent
		stream.StreamAgent()
	}()

	// Now start pulling from the client until we receive a process, then
	// hook it all up
	go stream.StreamClient(w, r, pc)

	// Wait for the process to die
	stream.Wait()
}

func (h *HTTPProcessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	//stream := NewHTTPProcessStream(h.Addr)
}

// Read the first packet from the client and deserialize to Process
func readProcessPacket(ws *websocket.Conn) *Process {
	var (
		req   request
		istty bool
	)
	if err := ws.ReadJSON(&req); err != nil {
		return nil
	}
	switch req.Action {
	case FORK:
		istty = true
	case EXEC:
		istty = false
	default:
		return nil
	}
	proc := NewProcess(req.ServiceId, req.Cmd, req.Env, istty)
	if proc.Envv == nil {
		proc.Envv = []string{}
	}
	return proc
}

func (s *WebsocketProcessStream) StreamClient(w http.ResponseWriter, r *http.Request, pc chan bool) {
	ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
	if _, ok := err.(websocket.HandshakeError); ok {
		http.Error(w, "Not a websocket handshake", 400)
		return
	} else if err != nil {
		return
	}
	s.client = ws
	s.process = readProcessPacket(ws)
	pc <- true
	forwardToClient(s.client, s.process)
}

func (s *baseProcessStream) StreamAgent() {
	// TODO: Proper ws scheme validation
	ws, _, _ := websocket.DefaultDialer.Dial("ws://"+s.addr, nil)
	s.agent = ws

	action := "EXEC"
	if s.process.IsTTY {
		action = "FORK"
	}

	// Recreate the request from the process and send it up the pipe
	s.agent.WriteJSON(request{
		Cmd:       s.process.Command,
		Action:    action,
		ServiceId: s.process.ServiceId,
		Env:       s.process.Envv,
	})

	s.forwardFromAgent()
}

func (s *baseProcessStream) Wait() {
	for {
		if s.process != nil {
			s.process.Wait()
			return
		}
		time.Sleep(10)
	}
}

// Wire up the Process to the agent connection
func (s *baseProcessStream) forwardFromAgent() {
	defer func() {
		s.agent.Close()
		if s.process.Error == nil {
			s.process.Error = errors.New("Connection closed unexpectedly")
			s.process.Exited <- true
		}
	}()

	// Writer
	go func() {
		for {
			select {
			case m := <-s.process.Stdin:
				s.agent.WriteJSON(request{Action: EXEC, Cmd: m})
			case m := <-s.process.Signal:
				s.agent.WriteJSON(request{Action: SIGNAL, Signal: int(m)})
			}
		}
	}()

	// Reader
	for {
		var res response
		if err := s.agent.ReadJSON(&res); err == io.EOF {
			break
		} else if err != nil {
			// Bad read send message
		}

		if res.Stdout != "" {
			s.process.Stdout <- res.Stdout
		}

		if res.Stderr != "" {
			s.process.Stderr <- res.Stderr
		}

		if res.Result != "" {
			s.process.Error = errors.New(res.Result)
			s.process.Exited <- true
			break
		}
	}
}

// Wire up the Process to the client connection
func forwardToClient(ws *websocket.Conn, proc *Process) {
	defer func() {
		ws.Close()
		proc.Signal <- syscall.SIGKILL // Does nothing if process exited
	}()

	// Reader
	go func() {
		for {
			var req request
			if err := ws.ReadJSON(&req); err == io.EOF {
				break
			} else if err != nil {
				// Bad read send message
			}

			if req.Cmd != "" {
				proc.Stdin <- req.Cmd
			}

			if req.Signal != 0 {
				proc.Signal <- syscall.Signal(req.Signal)
			}
		}
	}()

	// Writer
	for {
		select {
		case m := <-proc.Stdout:
			ws.WriteJSON(response{Stdout: m})
		case m := <-proc.Stderr:
			ws.WriteJSON(response{Stderr: m})
		case <-proc.Exited:
			ws.WriteJSON(response{Result: fmt.Sprint(proc.Error)})
			break
		}
	}

}

// This is the handler on the agent that receives the connection from the proxy
func (h *OSProcessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Establish the websocket connection with proxy
	ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
	if _, ok := err.(websocket.HandshakeError); ok {
		http.Error(w, "Not a websocket handshake", 400)
		return
	} else if err != nil {
		return
	}
	defer ws.Close()

	// Read the process off the websocket
	proc := readProcessPacket(ws)

	// Make it go
	controlplane, err := serviced.NewControlClient(h.Port)
	if err != nil {
		glog.Fatalf("Could not create a control plane client %v", err)
	}
	service := &dao.Service{}
	controlplane.GetService(proc.ServiceId, service)

	if err := Exec(proc, service); err != nil {
	}

	// Wire it up
	go forwardToClient(ws, proc)

	proc.Wait()

}
<commit_msg>Send signals semi-properly<commit_after>package shell

import (
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"os/exec"
	"syscall"
	"time"

	"github.com/gorilla/websocket"

	"github.com/zenoss/glog"
	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"
)

const (
	FORK   = "FORK"
	EXEC   = "EXEC"
	SIGNAL = "SIGNAL"
)

// Client->Agent protocol
type request struct {
	Action    string
	ServiceId string
	Env       []string
	Cmd       string
	Signal    int
}

// Agent->Client protocol
type response struct {
	Stdin  string
	Stdout string
	Stderr string
	Result string
}

// Defines commands to be run in an object's container
type Process struct {
	ServiceId string              // The service id of the container to start
	IsTTY     bool                // Describes the type of connection needed
	Envv      []string            // Environment variables
	Command   string              // Command to run
	Error     error               `json:"-"`
	Stdin     chan string         `json:"-"`
	Stdout    chan string         `json:"-"`
	Stderr    chan string         `json:"-"`
	Exited    chan bool           `json:"-"`
	Signal    chan syscall.Signal `json:"-"`
	whenDone  chan bool
}

func NewProcess(serviceId, command string, envv []string, istty bool) *Process {
	return &Process{
		ServiceId: serviceId,
		IsTTY:     istty,
		Envv:      envv,
		Command:   command,
		Stdin:     make(chan string),
		Stdout:    make(chan string),
		Stderr:    make(chan string),
		Signal:    make(chan syscall.Signal),
		Exited:    make(chan bool),
		whenDone:  make(chan bool),
	}
}

// Starts a container shell
func Exec(p *Process, s *dao.Service) error {
	var runner Runner

	// Bind mount on /serviced
	dir, bin, err := serviced.ExecPath()
	if err != nil {
		return err
	}
	servicedVolume := fmt.Sprintf("%s:/serviced", dir)

	// Bind mount the pwd
	dir, err = os.Getwd()
	pwdVolume := fmt.Sprintf("%s:/mnt/pwd", dir)

	// Get the shell command
	var shellCmd string
	if p.Command != "" {
		shellCmd = p.Command
	} else {
		shellCmd = "su -"
	}

	// Get the proxy Command
	proxyCmd := []string{fmt.Sprintf("/serviced/%s", bin), "-logtostderr=false", "proxy", "-logstash=false", "-autorestart=false", s.Id, shellCmd}
	// Get the docker start command
	docker, err := exec.LookPath("docker")
	if err != nil {
		return err
	}
	argv := []string{"run", "-rm", "-v", servicedVolume, "-v", pwdVolume}
	argv = append(argv, p.Envv...)

	if p.IsTTY {
		argv = append(argv, "-i", "-t")
	}

	argv = append(argv, s.ImageId)
	argv = append(argv, proxyCmd...)

	runner, err = CreateCommand(docker, argv)

	if err != nil {
		return err
	}

	// @see http://dave.cheney.net/tag/golang-3
	p.Stdout = runner.StdoutPipe()
	p.Stderr = runner.StderrPipe()

	go p.send(runner)
	return nil
}

func (p *Process) send(r Runner) {
	exited := r.ExitedPipe()
	go r.Reader(8192)

	defer func() {
		close(p.Stdin)
	}()

	for {
		select {
		case i := <-p.Stdin:
			r.Write([]byte(i))
		case s := <-p.Signal:
			r.Signal(s)
		case m := <-exited:
			if e := r.Error(); e == nil {
				p.Error = errors.New("0")
			} else {
				p.Error = e
			}
			p.Exited <- m
			p.whenDone <- true
			return
		}
	}
}

func (p *Process) Wait() {
	<-p.whenDone
}

// Describes streams from an agent-executed process to a client
type ProcessStream interface {

	// Initiate client-side communication and create Process
	StreamClient(http.ResponseWriter, *http.Request, chan *Process)

	// Initiate agent-side communication and kick off shell
	StreamAgent()

	// Wait for the process to end
	Wait()
}

type baseProcessStream struct {
	agent   *websocket.Conn
	process *Process
	addr    string
}

type WebsocketProcessStream struct {
	*baseProcessStream
	client *websocket.Conn
}

type HTTPProcessStream struct {
	*baseProcessStream
	client *net.Conn
}

func NewWebsocketProcessStream(addr string) *WebsocketProcessStream {
	return &WebsocketProcessStream{
		baseProcessStream: &baseProcessStream{addr: addr},
	}
}

func NewHTTPProcessStream(addr string) *HTTPProcessStream {
	return &HTTPProcessStream{
		baseProcessStream: &baseProcessStream{addr: addr},
	}
}

type WebsocketProcessHandler struct {
	Addr string
}

type OSProcessHandler struct {
	Port string
}

type HTTPProcessHandler struct {
	Addr string
}

// Implement http.Handler
func (h *WebsocketProcessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	stream := NewWebsocketProcessStream(h.Addr)

	// Create a client and wait for the process packet
	pc := make(chan bool)

	// Set up everything to start the connection to agent once a process is
	// defined.
	go func() {
		<-pc
		// Now that we have the process, connect to the agent
		stream.StreamAgent()
	}()

	// Now start pulling from the client until we receive a process, then
	// hook it all up
	go stream.StreamClient(w, r, pc)

	// Wait for the process to die
	stream.Wait()
}

func (h *HTTPProcessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	//stream := NewHTTPProcessStream(h.Addr)
}

// Read the first packet from the client and deserialize to Process
func readProcessPacket(ws *websocket.Conn) *Process {
	var (
		req   request
		istty bool
	)
	if err := ws.ReadJSON(&req); err != nil {
		return nil
	}
	switch req.Action {
	case FORK:
		istty = true
	case EXEC:
		istty = false
	default:
		return nil
	}
	proc := NewProcess(req.ServiceId, req.Cmd, req.Env, istty)
	if proc.Envv == nil {
		proc.Envv = []string{}
	}
	return proc
}

func (s *WebsocketProcessStream) StreamClient(w http.ResponseWriter, r *http.Request, pc chan bool) {
	ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
	if _, ok := err.(websocket.HandshakeError); ok {
		http.Error(w, "Not a websocket handshake", 400)
		return
	} else if err != nil {
		return
	}
	s.client = ws
	s.process = readProcessPacket(ws)
	pc <- true
	forwardToClient(s.client, s.process)
}

func (s *baseProcessStream) StreamAgent() {
	// TODO: Proper ws scheme validation
	ws, _, _ := websocket.DefaultDialer.Dial("ws://"+s.addr, nil)
	s.agent = ws

	action := "EXEC"
	if s.process.IsTTY {
		action = "FORK"
	}

	// Recreate the request from the process and send it up the pipe
	s.agent.WriteJSON(request{
		Cmd:       s.process.Command,
		Action:    action,
		ServiceId: s.process.ServiceId,
		Env:       s.process.Envv,
	})

	s.forwardFromAgent()
}

func (s *baseProcessStream) Wait() {
	for {
		if s.process != nil {
			s.process.Wait()
			return
		}
		time.Sleep(10)
	}
}

// Wire up the Process to the agent connection
func (s *baseProcessStream) forwardFromAgent() {
	defer func() {
		s.agent.Close()
		if s.process.Error == nil {
			s.process.Error = errors.New("Connection closed unexpectedly")
			s.process.Exited <- true
		}
	}()

	// Writer
	go func() {
		for {
			select {
			case m := <-s.process.Stdin:
				s.agent.WriteJSON(request{Action: EXEC, Cmd: m})
			case m := <-s.process.Signal:
				s.agent.WriteJSON(request{Action: SIGNAL, Signal: int(m)})
			}
		}
	}()

	// Reader
	for {
		var res response
		if err := s.agent.ReadJSON(&res); err == io.EOF {
			break
		} else if err != nil {
			// Bad read send message
		}

		if res.Stdout != "" {
			s.process.Stdout <- res.Stdout
		}

		if res.Stderr != "" {
			s.process.Stderr <- res.Stderr
		}

		if res.Result != "" {
			s.process.Error = errors.New(res.Result)
			s.process.Exited <- true
			break
		}
	}
}

// Wire up the Process to the client connection
func forwardToClient(ws *websocket.Conn, proc *Process) {
	defer func() {
		ws.Close()
		proc.Signal <- syscall.SIGKILL // Does nothing if process exited
	}()

	// Reader
	go func() {
		for {
			var req request
			if err := ws.ReadJSON(&req); err == io.EOF {
				break
			} else if err != nil {
				// Bad read send message
			}

			switch req.Action {
			// TODO: Defend against invalid requests?
			case SIGNAL:
				proc.Signal <- syscall.Signal(req.Signal)
			case EXEC:
				proc.Stdin <- req.Cmd
			}
		}
	}()

	// Writer
	for {
		select {
		case m := <-proc.Stdout:
			ws.WriteJSON(response{Stdout: m})
		case m := <-proc.Stderr:
			ws.WriteJSON(response{Stderr: m})
		case <-proc.Exited:
			ws.WriteJSON(response{Result: fmt.Sprint(proc.Error)})
			break
		}
	}

}

// This is the handler on the agent that receives the connection from the proxy
func (h *OSProcessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Establish the websocket connection with proxy
	ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
	if _, ok := err.(websocket.HandshakeError); ok {
		http.Error(w, "Not a websocket handshake", 400)
		return
	} else if err != nil {
		return
	}
	defer ws.Close()

	// Read the process off the websocket
	proc := readProcessPacket(ws)

	// Make it go
	controlplane, err := serviced.NewControlClient(h.Port)
	if err != nil {
		glog.Fatalf("Could not create a control plane client %v", err)
	}
	service := &dao.Service{}
	controlplane.GetService(proc.ServiceId, service)

	if err := Exec(proc, service); err != nil {
	}

	// Wire it up
	go forwardToClient(ws, proc)

	proc.Wait()

}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"runtime"
	"syscall"
	"unsafe"
)

// For calculating the size of the console window; this is pretty important when we're writing
// arbitrary-length log messages around the interactive display.
type winsize struct {
	Row    uint16
	Col    uint16
	Xpixel uint16
	Ypixel uint16
}

// WindowSize finds and returns the size of the console window as (rows, columns)
func WindowSize() (int, int, error) {
	ws := winsize{}
	if ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
		uintptr(syscall.Stderr),
		uintptr(tiocgwinsz()),
		uintptr(unsafe.Pointer(&ws)),
	); int(ret) == -1 {
		return 25, 80, fmt.Errorf("error %d getting window size", int(errno))
	}
	return int(ws.Row), int(ws.Col), nil
}

// tiocgwinsz returns the ioctl number corresponding to TIOCGWINSZ.
// We could determine this using cgo which would be more robust, but I'd really
// rather not invoke cgo for something as static as this.
func tiocgwinsz() int {
	if runtime.GOOS == "linux" {
		return 0x5413
	}
	return 1074295912 // OSX and FreeBSD.
}
<commit_msg>Replace WindowSize impl with terminal.GetSize (#711)<commit_after>package cli

import (
	"golang.org/x/crypto/ssh/terminal"
	"runtime"
)

// WindowSize finds and returns the size of the console window as (rows, columns)
func WindowSize() (int, int, error) {
	cols, rows, err := terminal.GetSize(tiocgwinsz())
	if err != nil {
		return 25, 80, err
	}
	return rows, cols, err
}

// tiocgwinsz returns the ioctl number corresponding to TIOCGWINSZ.
// We could determine this using cgo which would be more robust, but I'd really
// rather not invoke cgo for something as static as this.
func tiocgwinsz() int {
	if runtime.GOOS == "linux" {
		return 0x5413
	}
	return 1074295912 // OSX and FreeBSD.
}
<|endoftext|>


<commit_before>package types

type Version struct {
	ID                string            `json:"id,omitempty"`
	AppID             string            `json:"appID,omitempty"`
	PreviousVersionID string            `json:"previousVersionId,omitempty"`
	Command           string            `json:"cmd,omitempty"`
	Args              []string          `json:"args,omitempty"`
	CPUs              float64           `json:"cpus,omitempty"`
	Mem               float64           `json:"mem,omitempty"`
	Disk              float64           `json:"disk,omitempty"`
	Instances         int32             `json:"instances,omitempty"`
	RunAs             string            `json:"runAs,omitempty"`
	Priority          int32             `json:"priority,omitempty"`
	Container         *Container        `json:"container,omitempty"`
	Labels            map[string]string `json:"labels,omitempty"`
	HealthChecks      []*HealthCheck    `json:"healthChecks,omitempty"`
	Env               map[string]string `json:"env,omitempty"`
	KillPolicy        *KillPolicy       `json:"killPolicy,omitempty"`
	UpdatePolicy      *UpdatePolicy     `json:"updatPolicy,omitempty"`
	Constraints       []string          `json:"constraints,omitempty"`
	URIs              []string          `json:"uris,omitempty"`
	IP                []string          `json:"ip,omitempty"`
	Mode              string            `json:"mode,omitempty"`
}

type Container struct {
	Type    string    `json:"type,omitempty"`
	Docker  *Docker   `json:"docker,omitempty"`
	Volumes []*Volume `json:"volumes,omitempty"`
}

type Docker struct {
	ForcePullImage bool           `json:"forcePullImage,omitempty"`
	Image          string         `json:"image,omitempty"`
	Network        string         `json:"network,omitempty"`
	Parameters     []*Parameter   `json:"parameters,omitempty"`
	PortMappings   []*PortMapping `json:"portMappings,omitempty"`
	Privileged     bool           `json:"privileged,omitempty"`
}

type Parameter struct {
	Key   string `json:"key,omitempty"`
	Value string `json:"value,omitempty"`
}

type PortMapping struct {
	ContainerPort int32  `json:"containerPort,omitempty"`
	Name          string `json:"name,omitempty"`
	Protocol      string `json:"protocol,omitempty"`
}

type Volume struct {
	ContainerPath string `json:"containerPath,omitempty"`
	HostPath      string `json:"hostPath,omitempty"`
	Mode          string `json:"mode,omitempty"`
}

type KillPolicy struct {
	Duration int64 `json:"duration,omitempty"`
}

type UpdatePolicy struct {
	UpdateDelay  int32  `json:"updateDelay,omitempty"`
	MaxRetries   int32  `json:"maxRetries,omitempty"`
	MaxFailovers int32  `json:"maxFailovers,omitempty"`
	Action       string `json:"action,omitempty"`
}

type HealthCheck struct {
	ID                  string  `json:"id,omitempty"`
	Address             string  `json:"address,omitempty"`
	TaskID              string  `json:"taskID,omitempty"`
	AppID               string  `json:"appID,omitempty"`
	Protocol            string  `json:"protocol,omitempty"`
	PortName            string  `json:"portName,omitempty"`
	Value               string  `json:"value,omitempty"`
	Path                string  `json:"path,omitempty"`
	ConsecutiveFailures uint32  `json:"consecutiveFailures,omitempty"`
	GracePeriodSeconds  float64 `json:"gracePeriodSeconds,omitempty"`
	IntervalSeconds     float64 `json:"intervalSeconds,omitempty"`
	TimeoutSeconds      float64 `json:"timeoutSeconds,omitempty"`
}
<commit_msg>helper methods related to label can not be removed<commit_after>package types

type Version struct {
	ID                string            `json:"id,omitempty"`
	AppID             string            `json:"appID,omitempty"`
	PreviousVersionID string            `json:"previousVersionId,omitempty"`
	Command           string            `json:"cmd,omitempty"`
	Args              []string          `json:"args,omitempty"`
	CPUs              float64           `json:"cpus,omitempty"`
	Mem               float64           `json:"mem,omitempty"`
	Disk              float64           `json:"disk,omitempty"`
	Instances         int32             `json:"instances,omitempty"`
	RunAs             string            `json:"runAs,omitempty"`
	Priority          int32             `json:"priority,omitempty"`
	Container         *Container        `json:"container,omitempty"`
	Labels            map[string]string `json:"labels,omitempty"`
	HealthChecks      []*HealthCheck    `json:"healthChecks,omitempty"`
	Env               map[string]string `json:"env,omitempty"`
	KillPolicy        *KillPolicy       `json:"killPolicy,omitempty"`
	UpdatePolicy      *UpdatePolicy     `json:"updatPolicy,omitempty"`
	Constraints       []string          `json:"constraints,omitempty"`
	URIs              []string          `json:"uris,omitempty"`
	IP                []string          `json:"ip,omitempty"`
	Mode              string            `json:"mode,omitempty"`
}

type Container struct {
	Type    string    `json:"type,omitempty"`
	Docker  *Docker   `json:"docker,omitempty"`
	Volumes []*Volume `json:"volumes,omitempty"`
}

type Docker struct {
	ForcePullImage bool           `json:"forcePullImage,omitempty"`
	Image          string         `json:"image,omitempty"`
	Network        string         `json:"network,omitempty"`
	Parameters     []*Parameter   `json:"parameters,omitempty"`
	PortMappings   []*PortMapping `json:"portMappings,omitempty"`
	Privileged     bool           `json:"privileged,omitempty"`
}

type Parameter struct {
	Key   string `json:"key,omitempty"`
	Value string `json:"value,omitempty"`
}

type PortMapping struct {
	ContainerPort int32  `json:"containerPort,omitempty"`
	Name          string `json:"name,omitempty"`
	Protocol      string `json:"protocol,omitempty"`
}

type Volume struct {
	ContainerPath string `json:"containerPath,omitempty"`
	HostPath      string `json:"hostPath,omitempty"`
	Mode          string `json:"mode,omitempty"`
}

type KillPolicy struct {
	Duration int64 `json:"duration,omitempty"`
}

type UpdatePolicy struct {
	UpdateDelay  int32  `json:"updateDelay,omitempty"`
	MaxRetries   int32  `json:"maxRetries,omitempty"`
	MaxFailovers int32  `json:"maxFailovers,omitempty"`
	Action       string `json:"action,omitempty"`
}

type HealthCheck struct {
	ID                  string  `json:"id,omitempty"`
	Address             string  `json:"address,omitempty"`
	TaskID              string  `json:"taskID,omitempty"`
	AppID               string  `json:"appID,omitempty"`
	Protocol            string  `json:"protocol,omitempty"`
	PortName            string  `json:"portName,omitempty"`
	Value               string  `json:"value,omitempty"`
	Path                string  `json:"path,omitempty"`
	ConsecutiveFailures uint32  `json:"consecutiveFailures,omitempty"`
	GracePeriodSeconds  float64 `json:"gracePeriodSeconds,omitempty"`
	IntervalSeconds     float64 `json:"intervalSeconds,omitempty"`
	TimeoutSeconds      float64 `json:"timeoutSeconds,omitempty"`
}

// AddLabel adds a label to the application
//		name:	the name of the label
//		value: value for this label
func (v *Version) AddLabel(name, value string) *Version {
	if v.Labels == nil {
		v.EmptyLabels()
	}
	v.Labels[name] = value

	return v
}

// EmptyLabels explicitly empties the labels -- use this if you need to empty
// the labels of an application that already has labels set (setting labels to nil will
// keep the current value)
func (v *Version) EmptyLabels() *Version {
	v.Labels = map[string]string{}

	return v
}
<|endoftext|>


<commit_before>package realtime

import (
	"encoding/json"
	"fmt"
	"koding/db/mongodb/modelhelper"
	"socialapi/models"
	"strconv"
	"github.com/koding/logging"
	"github.com/koding/rabbitmq"
	"github.com/koding/worker"

	"github.com/streadway/amqp"
)

type Action func(*RealtimeWorkerController, []byte) error

type RealtimeWorkerController struct {
	routes  map[string]Action
	log     logging.Logger
	rmqConn *amqp.Connection
}

func (r *RealtimeWorkerController) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	r.log.Error("an error occured deleting realtime event", err)
	delivery.Ack(false)
	return false
}

func NewRealtimeWorkerController(rmq *rabbitmq.RabbitMQ, log logging.Logger) (*RealtimeWorkerController, error) {
	rmqConn, err := rmq.Connect("NewRealtimeWorkerController")
	if err != nil {
		return nil, err
	}

	ffc := &RealtimeWorkerController{
		log:     log,
		rmqConn: rmqConn.Conn(),
	}

	routes := map[string]Action{
		"api.channel_message_created": (*RealtimeWorkerController).MessageSaved,
		"api.channel_message_updated": (*RealtimeWorkerController).MessageUpdated,
		"api.channel_message_deleted": (*RealtimeWorkerController).MessageDeleted,

		"api.interaction_created": (*RealtimeWorkerController).InteractionSaved,
		"api.interaction_deleted": (*RealtimeWorkerController).InteractionDeleted,

		"api.message_reply_created": (*RealtimeWorkerController).MessageReplySaved,
		"api.message_reply_deleted": (*RealtimeWorkerController).MessageReplyDeleted,

		"api.channel_message_list_created": (*RealtimeWorkerController).MessageListSaved,
		"api.channel_message_list_updated": (*RealtimeWorkerController).MessageListUpdated,
		"api.channel_message_list_deleted": (*RealtimeWorkerController).MessageListDeleted,

		"api.channel_participant_created": (*RealtimeWorkerController).ChannelParticipantAdded,
		"api.channel_participant_deleted": (*RealtimeWorkerController).ChannelParticipantRemoved,
	}

	ffc.routes = routes

	return ffc, nil
}

func (f *RealtimeWorkerController) HandleEvent(event string, data []byte) error {
	f.log.Debug("New Event Recieved %s", event)
	handler, ok := f.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return handler(f, data)
}

func mapMessageToChannelMessage(data []byte) (*models.ChannelMessage, error) {
	cm := models.NewChannelMessage()
	if err := json.Unmarshal(data, cm); err != nil {
		return nil, err
	}

	return cm, nil
}

func mapMessageToChannelMessageList(data []byte) (*models.ChannelMessageList, error) {
	cm := models.NewChannelMessageList()
	if err := json.Unmarshal(data, cm); err != nil {
		return nil, err
	}

	return cm, nil
}

func mapMessageToInteraction(data []byte) (*models.Interaction, error) {
	i := models.NewInteraction()
	if err := json.Unmarshal(data, i); err != nil {
		return nil, err
	}

	return i, nil
}

func mapMessageToMessageReply(data []byte) (*models.MessageReply, error) {
	i := models.NewMessageReply()
	if err := json.Unmarshal(data, i); err != nil {
		return nil, err
	}

	return i, nil
}

// no operation for message save for now
func (f *RealtimeWorkerController) MessageSaved(data []byte) error {
	return nil
}

// no operation for message delete for now
// channel_message_delete will handle message deletions from the
func (f *RealtimeWorkerController) MessageDeleted(data []byte) error {
	return nil
}

func (f *RealtimeWorkerController) MessageUpdated(data []byte) error {
	cm, err := mapMessageToChannelMessage(data)
	if err != nil {
		return err
	}

	// this is here for sending
	// old account id in message updated event
	container, err := cm.BuildEmptyMessageContainer()
	if err != nil {
		return err
	}

	err = f.sendInstanceEvent(cm.GetId(), container, "updateInstance")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) ChannelParticipantAdded(data []byte) error {
	return f.handleChannelParticipantEvent("AddedToChannel", data)
}

func (f *RealtimeWorkerController) ChannelParticipantRemoved(data []byte) error {
	return f.handleChannelParticipantEvent("RemovedFromChannel", data)
}

func (f *RealtimeWorkerController) handleChannelParticipantEvent(eventName string, data []byte) error {
	cp := models.NewChannelParticipant()
	if err := json.Unmarshal(data, cp); err != nil {
		return err
	}

	c := models.NewChannel()
	if err := c.ById(cp.ChannelId); err != nil {
		return err
	}

	return f.sendNotification(cp.AccountId, eventName, c)
}

func (f *RealtimeWorkerController) InteractionSaved(data []byte) error {
	return f.handleInteractionEvent("InteractionAdded", data)
}

func (f *RealtimeWorkerController) InteractionDeleted(data []byte) error {
	return f.handleInteractionEvent("InteractionRemoved", data)
}

func (f *RealtimeWorkerController) handleInteractionEvent(eventName string, data []byte) error {
	i, err := mapMessageToInteraction(data)
	if err != nil {
		return err
	}

	count, err := i.Count(i.TypeConstant)
	if err != nil {
		return err
	}

	res := map[string]interface{}{
		"messageId":    i.MessageId,
		"accountId":    i.AccountId,
		"typeConstant": i.TypeConstant,
		"count":        count,
	}

	err = f.sendInstanceEvent(i.MessageId, res, eventName)
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) MessageReplySaved(data []byte) error {
	i, err := mapMessageToMessageReply(data)
	if err != nil {
		return err
	}

	reply := models.NewChannelMessage()
	if err := reply.ById(i.ReplyId); err != nil {
		return err
	}

	err = f.sendInstanceEvent(i.MessageId, reply, "ReplyAdded")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) MessageReplyDeleted(data []byte) error {
	i, err := mapMessageToMessageReply(data)
	if err != nil {
		return err
	}

	err = f.sendInstanceEvent(i.MessageId, i, "ReplyRemoved")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

// send message to the channel
func (f *RealtimeWorkerController) MessageListSaved(data []byte) error {
	cml, err := mapMessageToChannelMessageList(data)
	if err != nil {
		return err
	}

	err = f.sendChannelEvent(cml, "MessageAdded")
	if err != nil {
		return err
	}

	return nil
}

// no operation for channel_message_list_updated event
func (f *RealtimeWorkerController) MessageListUpdated(data []byte) error {
	return nil
}

func (f *RealtimeWorkerController) MessageListDeleted(data []byte) error {
	cml, err := mapMessageToChannelMessageList(data)
	if err != nil {
		return err
	}

	err = f.sendChannelEvent(cml, "MessageRemoved")
	if err != nil {
		return err
	}
	return nil
}

func (f *RealtimeWorkerController) sendInstanceEvent(instanceId int64, message interface{}, eventName string) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	routingKey := "oid." + strconv.FormatInt(instanceId, 10) + ".event." + eventName

	updateMessage, err := json.Marshal(message)
	if err != nil {
		return err
	}

	updateArr := make([]string, 1)
	if eventName == "updateInstance" {
		updateArr[0] = fmt.Sprintf("{\"$set\":%s}", string(updateMessage))
	} else {
		updateArr[0] = string(updateMessage)
	}

	msg, err := json.Marshal(updateArr)
	if err != nil {
		return err
	}

	return channel.Publish(
		"updateInstances", // exchange name
		routingKey,        // routing key
		false,             // mandatory
		false,             // immediate
		amqp.Publishing{Body: msg}, // message
	)
}

func (f *RealtimeWorkerController) sendChannelEvent(cml *models.ChannelMessageList, eventName string) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	secretNames, err := fetchSecretNames(cml.ChannelId)
	if err != nil {
		return err
	}

	// if we dont have any secret names, just return
	if len(secretNames) < 1 {
		f.log.Info("Channel %d doest have any secret name", cml.ChannelId)
		return nil
	}

	cm := models.NewChannelMessage()
	if err := cm.ById(cml.MessageId); err != nil {
		return err
	}

	byteMessage, err := json.Marshal(cm)
	if err != nil {
		return err
	}

	for _, secretName := range secretNames {
		routingKey := "socialapi.channelsecret." + secretName + "." + eventName

		if err := channel.Publish(
			"broker",   // exchange name
			routingKey, // routing key
			false,      // mandatory
			false,      // immediate
			amqp.Publishing{Body: byteMessage}, // message
		); err != nil {
			return err
		}
	}
	return nil
}

func fetchSecretNames(channelId int64) ([]string, error) {
	names := make([]string, 0)
	c, err := fetchChannel(channelId)
	if err != nil {
		return names, err
	}

	name := fmt.Sprintf(
		"socialapi-group-%s-type-%s-name-%s",
		c.GroupName,
		c.TypeConstant,
		c.Name,
	)

	names, err = modelhelper.FetchFlattenedSecretName(name)
	return names, nil
}

func fetchChannel(channelId int64) (*models.Channel, error) {
	c := models.NewChannel()
	if err := c.ById(channelId); err != nil {
		return nil, err
	}
	return c, nil
}

func (f *RealtimeWorkerController) sendNotification(accountId int64, eventName string, data interface{}) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	oldAccount, err := modelhelper.GetAccountBySocialApiId(accountId)
	if err != nil {
		return err
	}

	notification := map[string]interface{}{
		"event":    eventName,
		"contents": data,
	}

	byteNotification, err := json.Marshal(notification)
	if err != nil {
		return err
	}

	return channel.Publish(
		"notification",
		oldAccount.Profile.Nickname, // this is routing key
		false,
		false,
		amqp.Publishing{Body: byteNotification},
	)
}
<commit_msg>social: send old account id while sending the interaction events<commit_after>package realtime

import (
	"encoding/json"
	"fmt"
	"koding/db/mongodb/modelhelper"
	"socialapi/models"
	"strconv"
	"github.com/koding/logging"
	"github.com/koding/rabbitmq"
	"github.com/koding/worker"

	"github.com/streadway/amqp"
)

type Action func(*RealtimeWorkerController, []byte) error

type RealtimeWorkerController struct {
	routes  map[string]Action
	log     logging.Logger
	rmqConn *amqp.Connection
}

func (r *RealtimeWorkerController) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	r.log.Error("an error occured deleting realtime event", err)
	delivery.Ack(false)
	return false
}

func NewRealtimeWorkerController(rmq *rabbitmq.RabbitMQ, log logging.Logger) (*RealtimeWorkerController, error) {
	rmqConn, err := rmq.Connect("NewRealtimeWorkerController")
	if err != nil {
		return nil, err
	}

	ffc := &RealtimeWorkerController{
		log:     log,
		rmqConn: rmqConn.Conn(),
	}

	routes := map[string]Action{
		"api.channel_message_created": (*RealtimeWorkerController).MessageSaved,
		"api.channel_message_updated": (*RealtimeWorkerController).MessageUpdated,
		"api.channel_message_deleted": (*RealtimeWorkerController).MessageDeleted,

		"api.interaction_created": (*RealtimeWorkerController).InteractionSaved,
		"api.interaction_deleted": (*RealtimeWorkerController).InteractionDeleted,

		"api.message_reply_created": (*RealtimeWorkerController).MessageReplySaved,
		"api.message_reply_deleted": (*RealtimeWorkerController).MessageReplyDeleted,

		"api.channel_message_list_created": (*RealtimeWorkerController).MessageListSaved,
		"api.channel_message_list_updated": (*RealtimeWorkerController).MessageListUpdated,
		"api.channel_message_list_deleted": (*RealtimeWorkerController).MessageListDeleted,

		"api.channel_participant_created": (*RealtimeWorkerController).ChannelParticipantAdded,
		"api.channel_participant_deleted": (*RealtimeWorkerController).ChannelParticipantRemoved,
	}

	ffc.routes = routes

	return ffc, nil
}

func (f *RealtimeWorkerController) HandleEvent(event string, data []byte) error {
	f.log.Debug("New Event Recieved %s", event)
	handler, ok := f.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return handler(f, data)
}

func mapMessageToChannelMessage(data []byte) (*models.ChannelMessage, error) {
	cm := models.NewChannelMessage()
	if err := json.Unmarshal(data, cm); err != nil {
		return nil, err
	}

	return cm, nil
}

func mapMessageToChannelMessageList(data []byte) (*models.ChannelMessageList, error) {
	cm := models.NewChannelMessageList()
	if err := json.Unmarshal(data, cm); err != nil {
		return nil, err
	}

	return cm, nil
}

func mapMessageToInteraction(data []byte) (*models.Interaction, error) {
	i := models.NewInteraction()
	if err := json.Unmarshal(data, i); err != nil {
		return nil, err
	}

	return i, nil
}

func mapMessageToMessageReply(data []byte) (*models.MessageReply, error) {
	i := models.NewMessageReply()
	if err := json.Unmarshal(data, i); err != nil {
		return nil, err
	}

	return i, nil
}

// no operation for message save for now
func (f *RealtimeWorkerController) MessageSaved(data []byte) error {
	return nil
}

// no operation for message delete for now
// channel_message_delete will handle message deletions from the
func (f *RealtimeWorkerController) MessageDeleted(data []byte) error {
	return nil
}

func (f *RealtimeWorkerController) MessageUpdated(data []byte) error {
	cm, err := mapMessageToChannelMessage(data)
	if err != nil {
		return err
	}

	// this is here for sending
	// old account id in message updated event
	container, err := cm.BuildEmptyMessageContainer()
	if err != nil {
		return err
	}

	err = f.sendInstanceEvent(cm.GetId(), container, "updateInstance")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) ChannelParticipantAdded(data []byte) error {
	return f.handleChannelParticipantEvent("AddedToChannel", data)
}

func (f *RealtimeWorkerController) ChannelParticipantRemoved(data []byte) error {
	return f.handleChannelParticipantEvent("RemovedFromChannel", data)
}

func (f *RealtimeWorkerController) handleChannelParticipantEvent(eventName string, data []byte) error {
	cp := models.NewChannelParticipant()
	if err := json.Unmarshal(data, cp); err != nil {
		return err
	}

	c := models.NewChannel()
	if err := c.ById(cp.ChannelId); err != nil {
		return err
	}

	return f.sendNotification(cp.AccountId, eventName, c)
}

func (f *RealtimeWorkerController) InteractionSaved(data []byte) error {
	return f.handleInteractionEvent("InteractionAdded", data)
}

func (f *RealtimeWorkerController) InteractionDeleted(data []byte) error {
	return f.handleInteractionEvent("InteractionRemoved", data)
}

func (f *RealtimeWorkerController) handleInteractionEvent(eventName string, data []byte) error {
	i, err := mapMessageToInteraction(data)
	if err != nil {
		return err
	}

	count, err := i.Count(i.TypeConstant)
	if err != nil {
		return err
	}

	oldId, err := models.AccountOldIdById(i.AccountId)
	if err != nil {
		return err
	}

	res := map[string]interface{}{
		"messageId":    i.MessageId,
		"accountId":    i.AccountId,
		"accountOldId": oldId,
		"typeConstant": i.TypeConstant,
		"count":        count,
	}

	err = f.sendInstanceEvent(i.MessageId, res, eventName)
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) MessageReplySaved(data []byte) error {
	i, err := mapMessageToMessageReply(data)
	if err != nil {
		return err
	}

	reply := models.NewChannelMessage()
	if err := reply.ById(i.ReplyId); err != nil {
		return err
	}

	err = f.sendInstanceEvent(i.MessageId, reply, "ReplyAdded")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) MessageReplyDeleted(data []byte) error {
	i, err := mapMessageToMessageReply(data)
	if err != nil {
		return err
	}

	err = f.sendInstanceEvent(i.MessageId, i, "ReplyRemoved")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

// send message to the channel
func (f *RealtimeWorkerController) MessageListSaved(data []byte) error {
	cml, err := mapMessageToChannelMessageList(data)
	if err != nil {
		return err
	}

	err = f.sendChannelEvent(cml, "MessageAdded")
	if err != nil {
		return err
	}

	return nil
}

// no operation for channel_message_list_updated event
func (f *RealtimeWorkerController) MessageListUpdated(data []byte) error {
	return nil
}

func (f *RealtimeWorkerController) MessageListDeleted(data []byte) error {
	cml, err := mapMessageToChannelMessageList(data)
	if err != nil {
		return err
	}

	err = f.sendChannelEvent(cml, "MessageRemoved")
	if err != nil {
		return err
	}
	return nil
}

func (f *RealtimeWorkerController) sendInstanceEvent(instanceId int64, message interface{}, eventName string) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	routingKey := "oid." + strconv.FormatInt(instanceId, 10) + ".event." + eventName

	updateMessage, err := json.Marshal(message)
	if err != nil {
		return err
	}

	updateArr := make([]string, 1)
	if eventName == "updateInstance" {
		updateArr[0] = fmt.Sprintf("{\"$set\":%s}", string(updateMessage))
	} else {
		updateArr[0] = string(updateMessage)
	}

	msg, err := json.Marshal(updateArr)
	if err != nil {
		return err
	}

	return channel.Publish(
		"updateInstances", // exchange name
		routingKey,        // routing key
		false,             // mandatory
		false,             // immediate
		amqp.Publishing{Body: msg}, // message
	)
}

func (f *RealtimeWorkerController) sendChannelEvent(cml *models.ChannelMessageList, eventName string) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	secretNames, err := fetchSecretNames(cml.ChannelId)
	if err != nil {
		return err
	}

	// if we dont have any secret names, just return
	if len(secretNames) < 1 {
		f.log.Info("Channel %d doest have any secret name", cml.ChannelId)
		return nil
	}

	cm := models.NewChannelMessage()
	if err := cm.ById(cml.MessageId); err != nil {
		return err
	}

	byteMessage, err := json.Marshal(cm)
	if err != nil {
		return err
	}

	for _, secretName := range secretNames {
		routingKey := "socialapi.channelsecret." + secretName + "." + eventName

		if err := channel.Publish(
			"broker",   // exchange name
			routingKey, // routing key
			false,      // mandatory
			false,      // immediate
			amqp.Publishing{Body: byteMessage}, // message
		); err != nil {
			return err
		}
	}
	return nil
}

func fetchSecretNames(channelId int64) ([]string, error) {
	names := make([]string, 0)
	c, err := fetchChannel(channelId)
	if err != nil {
		return names, err
	}

	name := fmt.Sprintf(
		"socialapi-group-%s-type-%s-name-%s",
		c.GroupName,
		c.TypeConstant,
		c.Name,
	)

	names, err = modelhelper.FetchFlattenedSecretName(name)
	return names, nil
}

func fetchChannel(channelId int64) (*models.Channel, error) {
	c := models.NewChannel()
	if err := c.ById(channelId); err != nil {
		return nil, err
	}
	return c, nil
}

func (f *RealtimeWorkerController) sendNotification(accountId int64, eventName string, data interface{}) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	oldAccount, err := modelhelper.GetAccountBySocialApiId(accountId)
	if err != nil {
		return err
	}

	notification := map[string]interface{}{
		"event":    eventName,
		"contents": data,
	}

	byteNotification, err := json.Marshal(notification)
	if err != nil {
		return err
	}

	return channel.Publish(
		"notification",
		oldAccount.Profile.Nickname, // this is routing key
		false,
		false,
		amqp.Publishing{Body: byteNotification},
	)
}
<|endoftext|>


<commit_before>package monitor

import (
	"context"
	"errors"
	"sync"
	"time"

	"github.com/micro/go-micro/client"
	pb "github.com/micro/go-micro/debug/proto"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/registry/cache"
)

type monitor struct {
	options Options

	exit     chan bool
	registry cache.Cache
	client   client.Client

	sync.RWMutex
	running  bool
	services map[string]*Status
}

func (m *monitor) Check(service string) error {
	status, err := m.check(service)
	if err != nil {
		return err
	}
	m.Lock()
	m.services[service] = status
	m.Unlock()

	if status.Code != StatusRunning {
		return errors.New(status.Info)
	}

	return nil
}

// check provides binary running/failed status.
// In the event Debug.Health cannot be called on a service we reap the node.
func (m *monitor) check(service string) (*Status, error) {
	services, err := m.registry.GetService(service)
	if err != nil {
		return nil, err
	}

	// create debug client
	debug := pb.NewDebugService(service, m.client)

	var status *Status
	var gerr error

	// iterate through multiple versions of a service
	for _, service := range services {
		for _, node := range service.Nodes {
			// TODO: checks that are not just RPC based
			// TODO: better matching of the protocol
			// TODO: maybe everything has to be a go-micro service?
			if node.Metadata["server"] != m.client.String() {
				continue
			}
			// check the transport matches
			if node.Metadata["transport"] != m.client.Options().Transport.String() {
				continue
			}

			rsp, err := debug.Health(
				context.Background(),
				// empty health request
				&pb.HealthRequest{},
				// call this specific node
				client.WithAddress(node.Address),
				// retry in the event of failure
				client.WithRetries(3),
			)
			if err != nil {
				// reap the dead node
				m.registry.Deregister(&registry.Service{
					Name:    service.Name,
					Version: service.Version,
					Nodes:   []*registry.Node{node},
				})

				// save the error
				gerr = err
				continue
			}

			// expecting ok response status
			if rsp.Status != "ok" {
				gerr = errors.New(rsp.Status)
				continue
			}

			// no error set status
			status = &Status{
				Code: StatusRunning,
				Info: "running",
			}
		}
	}

	// if we got the success case return it
	if status != nil {
		return status, nil
	}

	// if gerr is not nil return it
	if gerr != nil {
		return &Status{
			Code:  StatusFailed,
			Info:  "not running",
			Error: gerr.Error(),
		}, nil
	}

	// otherwise unknown status
	return &Status{
		Code: StatusUnknown,
		Info: "unknown status",
	}, nil
}

func (m *monitor) reap() {
	services, err := m.registry.ListServices()
	if err != nil {
		return
	}

	serviceMap := make(map[string]bool)
	for _, service := range services {
		serviceMap[service.Name] = true
	}

	m.Lock()
	defer m.Unlock()

	// range over our watched services
	for service := range m.services {
		// check if the service exists in the registry
		if !serviceMap[service] {
			// if not, delete it in our status map
			delete(m.services, service)
		}
	}
}

func (m *monitor) run() {
	// check the status every tick
	t := time.NewTicker(time.Minute)
	defer t.Stop()

	// reap dead services
	t2 := time.NewTicker(time.Hour)
	defer t2.Stop()

	// list the known services
	services, _ := m.registry.ListServices()

	// create a check chan of same length
	check := make(chan string, len(services))

	// front-load the services to watch
	for _, service := range services {
		check <- service.Name
	}

	for {
		select {
		// exit if we're told to
		case <-m.exit:
			return
		// check a service when told to
		case service := <-check:
			// check the status
			status, err := m.check(service)
			if err != nil {
				status = &Status{
					Code: StatusUnknown,
					Info: "unknown status",
				}
			}

			// save the status
			m.Lock()
			m.services[service] = status
			m.Unlock()
		// on the tick interval get all services and issue a check
		case <-t.C:
			// create a list of services
			serviceMap := make(map[string]bool)

			m.RLock()
			for service := range m.services {
				serviceMap[service] = true
			}
			m.RUnlock()

			go func() {
				// check the status of all watched services
				for service := range serviceMap {
					select {
					case <-m.exit:
						return
					case check <- service:
					default:
						// barf if we block
					}
				}

				// list services
				services, _ := m.registry.ListServices()

				for _, service := range services {
					// start watching the service
					if ok := serviceMap[service.Name]; !ok {
						m.Watch(service.Name)
					}
				}
			}()
		case <-t2.C:
			// reap any dead/non-existent services
			m.reap()
		}
	}
}

func (m *monitor) Reap(service string) error {
	services, err := m.registry.GetService(service)
	if err != nil {
		return nil
	}
	m.Lock()
	defer m.Unlock()
	delete(m.services, service)
	for _, service := range services {
		m.registry.Deregister(service)
	}
	return nil
}

func (m *monitor) Status(service string) (Status, error) {
	m.RLock()
	defer m.RUnlock()
	if status, ok := m.services[service]; ok {
		return *status, nil
	}
	return Status{}, ErrNotWatching
}

func (m *monitor) Watch(service string) error {
	m.Lock()
	defer m.Unlock()

	// check if we're watching
	if _, ok := m.services[service]; ok {
		return nil
	}

	// get the status
	status, err := m.check(service)
	if err != nil {
		return err
	}

	// set the status
	m.services[service] = status
	return nil
}

func (m *monitor) Run() error {
	m.Lock()
	defer m.Unlock()

	if m.running {
		return nil
	}

	// reset the exit channel
	m.exit = make(chan bool)
	// setup a new cache
	m.registry = cache.New(m.options.Registry)

	// start running
	go m.run()

	// set to running
	m.running = true

	return nil
}

func (m *monitor) Stop() error {
	m.Lock()
	defer m.Unlock()

	if !m.running {
		return nil
	}

	select {
	case <-m.exit:
		return nil
	default:
		close(m.exit)
		for s := range m.services {
			delete(m.services, s)
		}
		m.registry.Stop()
		m.running = false
		return nil
	}

	return nil
}

func newMonitor(opts ...Option) Monitor {
	options := Options{
		Client:   client.DefaultClient,
		Registry: registry.DefaultRegistry,
	}

	for _, o := range opts {
		o(&options)
	}

	return &monitor{
		options:  options,
		exit:     make(chan bool),
		client:   options.Client,
		registry: cache.New(options.Registry),
		services: make(map[string]*Status),
	}
}
<commit_msg>Do not deregister services in the monitor unless Reap is called<commit_after>package monitor

import (
	"context"
	"errors"
	"sync"
	"time"

	"github.com/micro/go-micro/client"
	pb "github.com/micro/go-micro/debug/proto"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/registry/cache"
)

type monitor struct {
	options Options

	exit     chan bool
	registry cache.Cache
	client   client.Client

	sync.RWMutex
	running  bool
	services map[string]*Status
}

func (m *monitor) Check(service string) error {
	status, err := m.check(service)
	if err != nil {
		return err
	}
	m.Lock()
	m.services[service] = status
	m.Unlock()

	if status.Code != StatusRunning {
		return errors.New(status.Info)
	}

	return nil
}

// check provides binary running/failed status.
// In the event Debug.Health cannot be called on a service we reap the node.
func (m *monitor) check(service string) (*Status, error) {
	services, err := m.registry.GetService(service)
	if err != nil {
		return nil, err
	}

	// create debug client
	debug := pb.NewDebugService(service, m.client)

	var status *Status
	var gerr error

	// iterate through multiple versions of a service
	for _, service := range services {
		for _, node := range service.Nodes {
			// TODO: checks that are not just RPC based
			// TODO: better matching of the protocol
			// TODO: maybe everything has to be a go-micro service?
			if node.Metadata["server"] != m.client.String() {
				continue
			}
			// check the transport matches
			if node.Metadata["transport"] != m.client.Options().Transport.String() {
				continue
			}

			rsp, err := debug.Health(
				context.Background(),
				// empty health request
				&pb.HealthRequest{},
				// call this specific node
				client.WithAddress(node.Address),
				// retry in the event of failure
				client.WithRetries(3),
			)
			if err != nil {
				// save the error
				gerr = err
				continue
			}

			// expecting ok response status
			if rsp.Status != "ok" {
				gerr = errors.New(rsp.Status)
				continue
			}

			// no error set status
			status = &Status{
				Code: StatusRunning,
				Info: "running",
			}
		}
	}

	// if we got the success case return it
	if status != nil {
		return status, nil
	}

	// if gerr is not nil return it
	if gerr != nil {
		return &Status{
			Code:  StatusFailed,
			Info:  "not running",
			Error: gerr.Error(),
		}, nil
	}

	// otherwise unknown status
	return &Status{
		Code: StatusUnknown,
		Info: "unknown status",
	}, nil
}

func (m *monitor) reap() {
	services, err := m.registry.ListServices()
	if err != nil {
		return
	}

	serviceMap := make(map[string]bool)
	for _, service := range services {
		serviceMap[service.Name] = true
	}

	m.Lock()
	defer m.Unlock()

	// range over our watched services
	for service := range m.services {
		// check if the service exists in the registry
		if !serviceMap[service] {
			// if not, delete it in our status map
			delete(m.services, service)
		}
	}
}

func (m *monitor) run() {
	// check the status every tick
	t := time.NewTicker(time.Minute)
	defer t.Stop()

	// reap dead services
	t2 := time.NewTicker(time.Hour)
	defer t2.Stop()

	// list the known services
	services, _ := m.registry.ListServices()

	// create a check chan of same length
	check := make(chan string, len(services))

	// front-load the services to watch
	for _, service := range services {
		check <- service.Name
	}

	for {
		select {
		// exit if we're told to
		case <-m.exit:
			return
		// check a service when told to
		case service := <-check:
			// check the status
			status, err := m.check(service)
			if err != nil {
				status = &Status{
					Code: StatusUnknown,
					Info: "unknown status",
				}
			}

			// save the status
			m.Lock()
			m.services[service] = status
			m.Unlock()
		// on the tick interval get all services and issue a check
		case <-t.C:
			// create a list of services
			serviceMap := make(map[string]bool)

			m.RLock()
			for service := range m.services {
				serviceMap[service] = true
			}
			m.RUnlock()

			go func() {
				// check the status of all watched services
				for service := range serviceMap {
					select {
					case <-m.exit:
						return
					case check <- service:
					default:
						// barf if we block
					}
				}

				// list services
				services, _ := m.registry.ListServices()

				for _, service := range services {
					// start watching the service
					if ok := serviceMap[service.Name]; !ok {
						m.Watch(service.Name)
					}
				}
			}()
		case <-t2.C:
			// reap any dead/non-existent services
			m.reap()
		}
	}
}

func (m *monitor) Reap(service string) error {
	services, err := m.registry.GetService(service)
	if err != nil {
		return nil
	}
	m.Lock()
	defer m.Unlock()
	delete(m.services, service)
	for _, service := range services {
		m.registry.Deregister(service)
	}
	return nil
}

func (m *monitor) Status(service string) (Status, error) {
	m.RLock()
	defer m.RUnlock()
	if status, ok := m.services[service]; ok {
		return *status, nil
	}
	return Status{}, ErrNotWatching
}

func (m *monitor) Watch(service string) error {
	m.Lock()
	defer m.Unlock()

	// check if we're watching
	if _, ok := m.services[service]; ok {
		return nil
	}

	// get the status
	status, err := m.check(service)
	if err != nil {
		return err
	}

	// set the status
	m.services[service] = status
	return nil
}

func (m *monitor) Run() error {
	m.Lock()
	defer m.Unlock()

	if m.running {
		return nil
	}

	// reset the exit channel
	m.exit = make(chan bool)
	// setup a new cache
	m.registry = cache.New(m.options.Registry)

	// start running
	go m.run()

	// set to running
	m.running = true

	return nil
}

func (m *monitor) Stop() error {
	m.Lock()
	defer m.Unlock()

	if !m.running {
		return nil
	}

	select {
	case <-m.exit:
		return nil
	default:
		close(m.exit)
		for s := range m.services {
			delete(m.services, s)
		}
		m.registry.Stop()
		m.running = false
		return nil
	}

	return nil
}

func newMonitor(opts ...Option) Monitor {
	options := Options{
		Client:   client.DefaultClient,
		Registry: registry.DefaultRegistry,
	}

	for _, o := range opts {
		o(&options)
	}

	return &monitor{
		options:  options,
		exit:     make(chan bool),
		client:   options.Client,
		registry: cache.New(options.Registry),
		services: make(map[string]*Status),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"

	"github.com/NebulousLabs/Sia-Ant-Farm/ant"
	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// getAddrs returns n free listening ports by leveraging the
// behaviour of net.Listen(":0").  Addresses are returned in the format of
// ":port"
func getAddrs(n int) ([]string, error) {
	var addrs []string

	for i := 0; i < n; i++ {
		l, err := net.Listen("tcp", ":0")
		if err != nil {
			return nil, err
		}
		defer l.Close()
		addrs = append(addrs, fmt.Sprintf(":%v", l.Addr().(*net.TCPAddr).Port))
	}
	return addrs, nil
}

// connectAnts connects two or more ants to the first ant in the slice,
// effectively bootstrapping the antfarm.
func connectAnts(ants ...*ant.Ant) error {
	if len(ants) < 2 {
		return errors.New("you must call connectAnts with at least two ants.")
	}
	targetAnt := ants[0]
	c := api.NewClient(targetAnt.APIAddr, "")
	for _, ant := range ants[1:] {
		connectQuery := fmt.Sprintf("/gateway/connect/%v", ant.RPCAddr)
		addr := modules.NetAddress(ant.RPCAddr)
		if addr.Host() == "" {
			connectQuery = fmt.Sprintf("/gateway/connect/%v", "127.0.0.1"+ant.RPCAddr)
		}
		err := c.Post(connectQuery, "", nil)
		if err != nil {
			return err
		}
	}
	return nil
}

// antConsensusGroups iterates through all of the ants known to the antFarm
// and returns the different consensus groups that have been formed between the
// ants.
//
// The outer slice is the list of gorups, and the inner slice is a list of ants
// in each group.
func antConsensusGroups(ants ...*ant.Ant) (groups [][]*ant.Ant, err error) {
	for _, a := range ants {
		c := api.NewClient(a.APIAddr, "")
		var cg api.ConsensusGET
		if err := c.Get("/consensus", &cg); err != nil {
			return nil, err
		}
		a.SeenBlocks[cg.Height] = cg.CurrentBlock

		// Compare this ant to all of the other groups. If the ant fits in a
		// group, insert it. If not, add it to the next group.
		found := false
		for gi, group := range groups {
			for i := types.BlockHeight(0); i < 8; i++ {
				id1, exists1 := a.SeenBlocks[cg.Height-i]
				id2, exists2 := group[0].SeenBlocks[cg.Height-i] // no group should have a length of zero
				if exists1 && exists2 && id1 == id2 {
					groups[gi] = append(groups[gi], a)
					found = true
					break
				}
			}
			if found {
				break
			}
		}
		if !found {
			groups = append(groups, []*ant.Ant{a})
		}
	}
	return groups, nil
}

// startAnts starts the ants defined by configs and blocks until every API
// has loaded.
func startAnts(configs ...ant.AntConfig) ([]*ant.Ant, error) {
	var ants []*ant.Ant
	for i, config := range configs {
		cfg, err := parseConfig(config)
		if err != nil {
			return nil, err
		}
		fmt.Printf("[INFO] starting ant %v with config %v\n", i, cfg)
		ant, err := ant.New(cfg)
		if err != nil {
			return nil, err
		}
		defer func() {
			if err != nil {
				ant.Close()
			}
		}()
		ants = append(ants, ant)
	}

	return ants, nil
}

// parseConfig takes an input `config` and fills it with default values if
// required.
func parseConfig(config ant.AntConfig) (ant.AntConfig, error) {
	// if config.SiaDirectory isn't set, use ioutil.TempDir to create a new
	// temporary directory.
	if config.SiaDirectory == "" {
		tempdir, err := ioutil.TempDir("./antfarm-data", "ant")
		if err != nil {
			return ant.AntConfig{}, err
		}
		config.SiaDirectory = tempdir
	}

	if config.SiadPath == "" {
		config.SiadPath = "siad"
	}

	// Automatically generate 3 free operating system ports for the Ant's api,
	// rpc, and host addresses
	addrs, err := getAddrs(3)
	if err != nil {
		return ant.AntConfig{}, err
	}
	if config.APIAddr == "" {
		config.APIAddr = "localhost" + addrs[0]
	}
	if config.RPCAddr == "" {
		config.RPCAddr = addrs[1]
	}
	if config.HostAddr == "" {
		config.HostAddr = addrs[2]
	}

	return config, nil
}
<commit_msg>clearer defer in startAnts<commit_after>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"

	"github.com/NebulousLabs/Sia-Ant-Farm/ant"
	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// getAddrs returns n free listening ports by leveraging the
// behaviour of net.Listen(":0").  Addresses are returned in the format of
// ":port"
func getAddrs(n int) ([]string, error) {
	var addrs []string

	for i := 0; i < n; i++ {
		l, err := net.Listen("tcp", ":0")
		if err != nil {
			return nil, err
		}
		defer l.Close()
		addrs = append(addrs, fmt.Sprintf(":%v", l.Addr().(*net.TCPAddr).Port))
	}
	return addrs, nil
}

// connectAnts connects two or more ants to the first ant in the slice,
// effectively bootstrapping the antfarm.
func connectAnts(ants ...*ant.Ant) error {
	if len(ants) < 2 {
		return errors.New("you must call connectAnts with at least two ants.")
	}
	targetAnt := ants[0]
	c := api.NewClient(targetAnt.APIAddr, "")
	for _, ant := range ants[1:] {
		connectQuery := fmt.Sprintf("/gateway/connect/%v", ant.RPCAddr)
		addr := modules.NetAddress(ant.RPCAddr)
		if addr.Host() == "" {
			connectQuery = fmt.Sprintf("/gateway/connect/%v", "127.0.0.1"+ant.RPCAddr)
		}
		err := c.Post(connectQuery, "", nil)
		if err != nil {
			return err
		}
	}
	return nil
}

// antConsensusGroups iterates through all of the ants known to the antFarm
// and returns the different consensus groups that have been formed between the
// ants.
//
// The outer slice is the list of gorups, and the inner slice is a list of ants
// in each group.
func antConsensusGroups(ants ...*ant.Ant) (groups [][]*ant.Ant, err error) {
	for _, a := range ants {
		c := api.NewClient(a.APIAddr, "")
		var cg api.ConsensusGET
		if err := c.Get("/consensus", &cg); err != nil {
			return nil, err
		}
		a.SeenBlocks[cg.Height] = cg.CurrentBlock

		// Compare this ant to all of the other groups. If the ant fits in a
		// group, insert it. If not, add it to the next group.
		found := false
		for gi, group := range groups {
			for i := types.BlockHeight(0); i < 8; i++ {
				id1, exists1 := a.SeenBlocks[cg.Height-i]
				id2, exists2 := group[0].SeenBlocks[cg.Height-i] // no group should have a length of zero
				if exists1 && exists2 && id1 == id2 {
					groups[gi] = append(groups[gi], a)
					found = true
					break
				}
			}
			if found {
				break
			}
		}
		if !found {
			groups = append(groups, []*ant.Ant{a})
		}
	}
	return groups, nil
}

// startAnts starts the ants defined by configs and blocks until every API
// has loaded.
func startAnts(configs ...ant.AntConfig) ([]*ant.Ant, error) {
	var ants []*ant.Ant
	var err error

	// Ensure that, if an error occurs, all the ants that have been started are
	// closed before returning.
	defer func() {
		if err != nil {
			for _, ant := range ants {
				ant.Close()
			}
		}
	}()

	for i, config := range configs {
		cfg, err := parseConfig(config)
		if err != nil {
			return nil, err
		}
		fmt.Printf("[INFO] starting ant %v with config %v\n", i, cfg)
		ant, err := ant.New(cfg)
		if err != nil {
			return nil, err
		}
		defer func() {
			if err != nil {
				ant.Close()
			}
		}()
		ants = append(ants, ant)
	}

	return ants, nil
}

// parseConfig takes an input `config` and fills it with default values if
// required.
func parseConfig(config ant.AntConfig) (ant.AntConfig, error) {
	// if config.SiaDirectory isn't set, use ioutil.TempDir to create a new
	// temporary directory.
	if config.SiaDirectory == "" {
		tempdir, err := ioutil.TempDir("./antfarm-data", "ant")
		if err != nil {
			return ant.AntConfig{}, err
		}
		config.SiaDirectory = tempdir
	}

	if config.SiadPath == "" {
		config.SiadPath = "siad"
	}

	// Automatically generate 3 free operating system ports for the Ant's api,
	// rpc, and host addresses
	addrs, err := getAddrs(3)
	if err != nil {
		return ant.AntConfig{}, err
	}
	if config.APIAddr == "" {
		config.APIAddr = "localhost" + addrs[0]
	}
	if config.RPCAddr == "" {
		config.RPCAddr = addrs[1]
	}
	if config.HostAddr == "" {
		config.HostAddr = addrs[2]
	}

	return config, nil
}
<|endoftext|>


<commit_before>package msp

import (
	"bytes"
	"crypto/rand"
	"testing"
)

func TestFieldElemMultiplicationOne(t *testing.T) {
	x := FieldElem(make([]byte, ModulusSize))
	rand.Read(x)

	xy, yx := x.Mul(One), One.Mul(x)

	if !One.IsOne() {
		t.Fatalf("One is not one?")
	}

	if bytes.Compare(xy, x) != 0 || bytes.Compare(yx, x) != 0 {
		t.Fatalf("Multiplication by 1 failed!\nx = %x\n1*x = %x\nx*1 = %x", x, yx, xy)
	}
}

func TestFieldElemMultiplicationZero(t *testing.T) {
	x := FieldElem(make([]byte, ModulusSize))
	rand.Read(x)

	xy, yx := x.Mul(Zero), Zero.Mul(x)

	if !Zero.IsZero() {
		t.Fatalf("Zero is not zero?")
	}

	if !xy.IsZero() || !yx.IsZero() {
		t.Fatalf("Multiplication by 0 failed!\nx = %x\n0*x = %x\nx*0 = %x", x, yx, xy)
	}
}

func TestFieldElemInvert(t *testing.T) {
	x := FieldElem(make([]byte, ModulusSize))
	rand.Read(x)

	xInv := x.Invert()

	xy, yx := x.Mul(xInv), xInv.Mul(x)

	if !xy.IsOne() || !yx.IsOne() {
		t.Fatalf("Multiplication by inverse failed!\nx = %x\nxInv = %x\nxInv*x = %x\nx*xInv = %x", x, yx, xy)
	}
}
<commit_msg>Bugfix: Not enough arguments to Fatalf.<commit_after>package msp

import (
	"bytes"
	"crypto/rand"
	"testing"
)

func TestFieldElemMultiplicationOne(t *testing.T) {
	x := FieldElem(make([]byte, ModulusSize))
	rand.Read(x)

	xy, yx := x.Mul(One), One.Mul(x)

	if !One.IsOne() {
		t.Fatalf("One is not one?")
	}

	if bytes.Compare(xy, x) != 0 || bytes.Compare(yx, x) != 0 {
		t.Fatalf("Multiplication by 1 failed!\nx = %x\n1*x = %x\nx*1 = %x", x, yx, xy)
	}
}

func TestFieldElemMultiplicationZero(t *testing.T) {
	x := FieldElem(make([]byte, ModulusSize))
	rand.Read(x)

	xy, yx := x.Mul(Zero), Zero.Mul(x)

	if !Zero.IsZero() {
		t.Fatalf("Zero is not zero?")
	}

	if !xy.IsZero() || !yx.IsZero() {
		t.Fatalf("Multiplication by 0 failed!\nx = %x\n0*x = %x\nx*0 = %x", x, yx, xy)
	}
}

func TestFieldElemInvert(t *testing.T) {
	x := FieldElem(make([]byte, ModulusSize))
	rand.Read(x)

	xInv := x.Invert()

	xy, yx := x.Mul(xInv), xInv.Mul(x)

	if !xy.IsOne() || !yx.IsOne() {
		t.Fatalf("Multiplication by inverse failed!\nx = %x\nxInv = %x\nxInv*x = %x\nx*xInv = %x", x, xInv, yx, xy)
	}
}
<|endoftext|>


<commit_before>package mumble

import (
	"errors"
	"fmt"
	"log"
	"strconv"
	"strings"

	"github.com/TF2Stadium/fumble/database"
	"github.com/layeh/gumble/gumble"
)

var ErrChanNotFound = errors.New("channel not found")

func printchannels(c gumble.Channels) {
	for _, channel := range c {
		log.Println(channel.Name)
	}
}

func channelManage(conn *Conn) {
	for {
		select {
		case lobbyID := <-conn.Create:
			name := fmt.Sprintf("Lobby #%d", lobbyID)

			conn.wait.Add(1)
			conn.client.Do(func() { conn.client.Channels[0].Add(name, false) })
			conn.wait.Wait()

			conn.client.Do(func() {
				channel := conn.client.Channels[0].Find(name)
				channel.SetDescription("Mumble channel for TF2Stadium " + name)

				conn.wait.Add(2)
				log.Printf("#%d: Creating RED and BLU", lobbyID)
				channel.Add("RED", false)
				channel.Add("BLU", false)
			})
			conn.wait.Wait()
			log.Printf("#%d: Done", lobbyID)
		case lobbyID := <-conn.Remove:
			name := fmt.Sprintf("Lobby #%d", lobbyID)

			conn.client.Do(func() {
				root := conn.client.Channels[0].Find(name) // root lobby channel
				if root == nil {
					log.Printf("Couldn't find channel `%s`", name)
					return
				}

				totalUsers := 0
				for _, channel := range root.Children {
					totalUsers += len(channel.Users)

					conn.wait.Add(1)
					channel.Remove()
				}

				if totalUsers == 0 { // no users in both channels, remove it entirely
					conn.wait.Add(1)
					root.Remove()
				}
				return
			})
			conn.wait.Wait()
			log.Printf("#%d: Deleted channels", lobbyID)
		}
	}
}

func getLobbyID(channel *gumble.Channel) uint {
	name := channel.Name
	if name[0] != 'L' { // channel name is either "RED" or "BLU"
		name = channel.Parent.Name
	}

	id, _ := strconv.ParseUint(name[strings.Index(name, "#")+1:], 10, 32)
	return uint(id)
}

func isUserAllowed(user *gumble.User, channel *gumble.Channel) (bool, string) {
	if channel.IsRoot() {
		return true, ""
	}

	lobbyID := getLobbyID(channel)

	return database.IsAllowed(user.UserID, lobbyID, channel.Name)
}

func (conn Conn) removeEmptyChannels() {
	conn.client.Do(func() {
		for _, c := range conn.client.Channels {
			if len(c.Users) == 0 && !database.IsLobbyClosed(getLobbyID(c)) {
				conn.wait.Add(1)
				c.Remove()
			}
		}
	})

	conn.wait.Wait()
}
<commit_msg>Remove channels for closed lobbies after 10 minutes<commit_after>package mumble

import (
	"errors"
	"fmt"
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/TF2Stadium/fumble/database"
	"github.com/layeh/gumble/gumble"
)

var ErrChanNotFound = errors.New("channel not found")

func printchannels(c gumble.Channels) {
	for _, channel := range c {
		log.Println(channel.Name)
	}
}

func channelManage(conn *Conn) {
	for {
		select {
		case lobbyID := <-conn.Create:
			name := fmt.Sprintf("Lobby #%d", lobbyID)

			conn.wait.Add(1)
			conn.client.Do(func() { conn.client.Channels[0].Add(name, false) })
			conn.wait.Wait()

			conn.client.Do(func() {
				channel := conn.client.Channels[0].Find(name)
				channel.SetDescription("Mumble channel for TF2Stadium " + name)

				conn.wait.Add(2)
				log.Printf("#%d: Creating RED and BLU", lobbyID)
				channel.Add("RED", false)
				channel.Add("BLU", false)
			})
			conn.wait.Wait()
			log.Printf("#%d: Done", lobbyID)
		case lobbyID := <-conn.Remove:
			name := fmt.Sprintf("Lobby #%d", lobbyID)

			conn.client.Do(func() {
				root := conn.client.Channels[0].Find(name) // root lobby channel
				if root == nil {
					log.Printf("Couldn't find channel `%s`", name)
					return
				}

				totalUsers := 0
				for _, channel := range root.Children {
					totalUsers += len(channel.Users)

					conn.wait.Add(1)
					channel.Remove()
				}

				if totalUsers == 0 { // no users in both channels, remove it entirely
					conn.wait.Add(1)
					root.Remove()
				} else {
					root.Send("Removing channel after 10 minutes", false)
					time.AfterFunc(10*time.Minute, func() {
						conn.client.Do(func() {
							root := conn.client.Channels[0].Find(name)
							if root == nil {
								log.Printf("Couldn't find channel `%s`", name)
								return
							}
							conn.wait.Add(1)
							root.Remove()
						})
						conn.wait.Wait()
					})
				}
				return
			})
			conn.wait.Wait()
			log.Printf("#%d: Deleted channels", lobbyID)
		}
	}
}

func getLobbyID(channel *gumble.Channel) uint {
	name := channel.Name
	if name[0] != 'L' { // channel name is either "RED" or "BLU"
		name = channel.Parent.Name
	}

	id, _ := strconv.ParseUint(name[strings.Index(name, "#")+1:], 10, 32)
	return uint(id)
}

func isUserAllowed(user *gumble.User, channel *gumble.Channel) (bool, string) {
	if channel.IsRoot() {
		return true, ""
	}

	lobbyID := getLobbyID(channel)

	return database.IsAllowed(user.UserID, lobbyID, channel.Name)
}
<|endoftext|>


<commit_before>// Copyright 2015 Pikkpoiss
//
// 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
//
//     http://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 main

import (
	twodee "../lib/twodee"
	"github.com/go-gl/mathgl/mgl32"
	"io/ioutil"
	"time"
)

const (
	PxPerUnit = 32
)

type GameLayer struct {
	cameraBounds  twodee.Rectangle
	camera        *twodee.Camera
	sprite        *twodee.SpriteRenderer
	batch         *twodee.BatchRenderer
	app           *Application
	spritesheet   *twodee.Spritesheet
	spritetexture *twodee.Texture
	level         *Level
}

func NewGameLayer(winb twodee.Rectangle, app *Application) (layer *GameLayer, err error) {
	var (
		camera       *twodee.Camera
		cameraBounds = twodee.Rect(-10, -10, 10, 10)
	)
	if camera, err = twodee.NewCamera(cameraBounds, winb); err != nil {
		return
	}
	layer = &GameLayer{
		camera:       camera,
		cameraBounds: cameraBounds,
		app:          app,
	}
	err = layer.Reset()
	return
}

func (l *GameLayer) Reset() (err error) {
	l.Delete()
	if l.batch, err = twodee.NewBatchRenderer(l.camera); err != nil {
		return
	}
	if l.sprite, err = twodee.NewSpriteRenderer(l.camera); err != nil {
		return
	}
	if err = l.loadSpritesheet(); err != nil {
		return
	}
	if l.level, err = NewLevel("resources/background.tmx"); err != nil {
		return
	}
	l.updateCamera(1.0)
	l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PlayMusic))
	return
}

func (l *GameLayer) Delete() {
	if l.batch != nil {
		l.batch.Delete()
		l.batch = nil
	}
	if l.sprite != nil {
		l.sprite.Delete()
		l.sprite = nil
	}
	if l.spritetexture != nil {
		l.spritetexture.Delete()
		l.spritetexture = nil
	}
}

func (l *GameLayer) Render() {
	if l.level != nil {
		l.batch.Bind()
		if err := l.batch.Draw(l.level.Background, 0, 0, 0); err != nil {
			panic(err)
		}
		l.batch.Unbind()
		l.spritetexture.Bind()
		l.sprite.Draw([]twodee.SpriteConfig{
			l.level.Player.SpriteConfig(l.spritesheet),
		})
		l.spritetexture.Unbind()
	}
}

func (l *GameLayer) Update(elapsed time.Duration) {
	l.updateCamera(0.05)
	if l.level != nil {
		l.level.Update(elapsed)
	}
}

func (l *GameLayer) updateCamera(scale float32) {
	var (
		pPt     = l.level.Player.Pos()
		cRect   = l.camera.WorldBounds
		cWidth  = cRect.Max.X - cRect.Min.X
		cHeight = cRect.Max.Y - cRect.Min.Y
		cMidX   = cRect.Min.X + (cWidth / 2.0)
		cMidY   = cRect.Min.Y + (cHeight / 2.0)
		pVec    = mgl32.Vec2{pPt.X, pPt.Y}
		cVec    = mgl32.Vec2{cMidX, cMidY}
		diff    = pVec.Sub(cVec)
		bounds  twodee.Rectangle
	)
	if diff.Len() > 1 {
		adj := diff.Mul(scale)
		bounds = twodee.Rect(
			cRect.Min.X+adj[0],
			cRect.Min.Y+adj[1],
			cRect.Max.X+adj[0],
			cRect.Max.Y+adj[1],
		)
		l.camera.SetWorldBounds(bounds)
	}
}

func (l *GameLayer) HandleEvent(evt twodee.Event) bool {
	switch event := evt.(type) {
	case *twodee.MouseMoveEvent:
		break
	case *twodee.MouseButtonEvent:
		break
	case *twodee.KeyEvent:
		l.handleMovement(event)
		if event.Type == twodee.Release {
			break
		}
		switch event.Code {
		case twodee.KeyX:
			l.app.State.Exit = true
		case twodee.KeyZ:
			l.level.Player.Roll()
		case twodee.KeyM:
			if twodee.MusicIsPaused() {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(ResumeMusic))
			} else {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PauseMusic))
			}
		}
	}
	return true
}

func (l *GameLayer) handleMovement(evt *twodee.KeyEvent) {
	var (
		value = float32(1.0)
	)
	if evt.Type == twodee.Release {
		value = float32(0.0)
	}
	switch evt.Code {
	case twodee.KeyDown:
		l.level.Player.MoveY(-value)
	case twodee.KeyLeft:
		l.level.Player.MoveX(-value)
	case twodee.KeyRight:
		l.level.Player.MoveX(value)
	case twodee.KeyUp:
		l.level.Player.MoveY(value)
	case twodee.KeyZ:
		l.level.Player.Roll()
	}
}

func (l *GameLayer) loadSpritesheet() (err error) {
	var (
		data []byte
	)
	if data, err = ioutil.ReadFile("resources/spritesheet.json"); err != nil {
		return
	}
	if l.spritesheet, err = twodee.ParseTexturePackerJSONArrayString(
		string(data),
		PxPerUnit,
	); err != nil {
		return
	}
	if l.spritetexture, err = twodee.LoadTexture(
		"resources/"+l.spritesheet.TexturePath,
		twodee.Nearest,
	); err != nil {
		return
	}
	return
}
<commit_msg>Got screen shake with roll<commit_after>// Copyright 2015 Pikkpoiss
//
// 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
//
//     http://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 main

import (
	twodee "../lib/twodee"
	"github.com/go-gl/mathgl/mgl32"
	"io/ioutil"
	"time"
)

const (
	PxPerUnit = 32
)

type GameLayer struct {
	shake         *twodee.ContinuousAnimation
	cameraBounds  twodee.Rectangle
	camera        *twodee.Camera
	sprite        *twodee.SpriteRenderer
	batch         *twodee.BatchRenderer
	app           *Application
	spritesheet   *twodee.Spritesheet
	spritetexture *twodee.Texture
	level         *Level
}

func NewGameLayer(winb twodee.Rectangle, app *Application) (layer *GameLayer, err error) {
	var (
		camera       *twodee.Camera
		cameraBounds = twodee.Rect(-10, -10, 10, 10)
	)
	if camera, err = twodee.NewCamera(cameraBounds, winb); err != nil {
		return
	}
	layer = &GameLayer{
		camera:       camera,
		cameraBounds: cameraBounds,
		app:          app,
	}
	err = layer.Reset()
	return
}

func (l *GameLayer) Reset() (err error) {
	l.Delete()
	if l.batch, err = twodee.NewBatchRenderer(l.camera); err != nil {
		return
	}
	if l.sprite, err = twodee.NewSpriteRenderer(l.camera); err != nil {
		return
	}
	if err = l.loadSpritesheet(); err != nil {
		return
	}
	if l.level, err = NewLevel("resources/background.tmx"); err != nil {
		return
	}
	l.updateCamera(1.0)
	l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PlayMusic))
	return
}

func (l *GameLayer) Delete() {
	if l.batch != nil {
		l.batch.Delete()
		l.batch = nil
	}
	if l.sprite != nil {
		l.sprite.Delete()
		l.sprite = nil
	}
	if l.spritetexture != nil {
		l.spritetexture.Delete()
		l.spritetexture = nil
	}
}

func (l *GameLayer) Render() {
	if l.level != nil {
		l.batch.Bind()
		if err := l.batch.Draw(l.level.Background, 0, 0, 0); err != nil {
			panic(err)
		}
		l.batch.Unbind()
		l.spritetexture.Bind()
		l.sprite.Draw([]twodee.SpriteConfig{
			l.level.Player.SpriteConfig(l.spritesheet),
		})
		l.spritetexture.Unbind()
	}
}

func (l *GameLayer) Update(elapsed time.Duration) {
	if l.shake != nil {
		l.shake.Update(elapsed)
	}
	l.updateCamera(0.05)
	if l.level != nil {
		l.level.Update(elapsed)
	}
}

func (l *GameLayer) updateCamera(scale float32) {
	var (
		pPt     = l.level.Player.Pos()
		cRect   = l.camera.WorldBounds
		cWidth  = cRect.Max.X - cRect.Min.X
		cHeight = cRect.Max.Y - cRect.Min.Y
		cMidX   = cRect.Min.X + (cWidth / 2.0)
		cMidY   = cRect.Min.Y + (cHeight / 2.0)
		pVec    = mgl32.Vec2{pPt.X, pPt.Y}
		cVec    = mgl32.Vec2{cMidX, cMidY}
		diff    = pVec.Sub(cVec)
		bounds  twodee.Rectangle
		adj     mgl32.Vec2
	)
	if diff.Len() > 1 {
		adj = diff.Mul(scale)
	} else {
		adj = mgl32.Vec2{0, 0}
	}
	if l.shake != nil {
		adj[1] += l.shake.Value()
	}
	bounds = twodee.Rect(
		cRect.Min.X+adj[0],
		cRect.Min.Y+adj[1],
		cRect.Max.X+adj[0],
		cRect.Max.Y+adj[1],
	)
	l.camera.SetWorldBounds(bounds)
}

func (l *GameLayer) ShakeCamera() {
	if l.shake == nil {
		decay := twodee.SineDecayFunc(
			time.Duration(500)*time.Millisecond,
			0.08, // Amplitude
			4.0,  // Frequency
			1.0,  // Decay
		)
		l.shake = twodee.NewContinuousAnimation(decay)
	}
	l.shake.Reset()
}

func (l *GameLayer) HandleEvent(evt twodee.Event) bool {
	switch event := evt.(type) {
	case *twodee.MouseMoveEvent:
		break
	case *twodee.MouseButtonEvent:
		break
	case *twodee.KeyEvent:
		l.handleMovement(event)
		if event.Type == twodee.Release {
			break
		}
		switch event.Code {
		case twodee.KeyX:
			l.app.State.Exit = true
		case twodee.KeyZ:
			l.level.Player.Roll()
			l.ShakeCamera()
		case twodee.KeyM:
			if twodee.MusicIsPaused() {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(ResumeMusic))
			} else {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PauseMusic))
			}
		}
	}
	return true
}

func (l *GameLayer) handleMovement(evt *twodee.KeyEvent) {
	var (
		value = float32(1.0)
	)
	if evt.Type == twodee.Release {
		value = float32(0.0)
	}
	switch evt.Code {
	case twodee.KeyDown:
		l.level.Player.MoveY(-value)
	case twodee.KeyLeft:
		l.level.Player.MoveX(-value)
	case twodee.KeyRight:
		l.level.Player.MoveX(value)
	case twodee.KeyUp:
		l.level.Player.MoveY(value)
	}
}

func (l *GameLayer) loadSpritesheet() (err error) {
	var (
		data []byte
	)
	if data, err = ioutil.ReadFile("resources/spritesheet.json"); err != nil {
		return
	}
	if l.spritesheet, err = twodee.ParseTexturePackerJSONArrayString(
		string(data),
		PxPerUnit,
	); err != nil {
		return
	}
	if l.spritetexture, err = twodee.LoadTexture(
		"resources/"+l.spritesheet.TexturePath,
		twodee.Nearest,
	); err != nil {
		return
	}
	return
}
<|endoftext|>


<commit_before>package dropp

import (
	"regexp"
	"strconv"
	"strings"
)

// AvaComp is the compounded availability of an item that contains the
// availability type and the item count if present
type AvaComp struct {
	Availability AvaType
	ItemCount    int
}

// AvaType indicates the availability of an item from the source
type AvaType int

const (
	// Available if the item count is above a certain threshold
	Available AvaType = iota

	// Low if the item count is below a certain threshold
	Low

	// Out if there are no items available
	Out

	// Unknown is used when the update could not be performed
	Unknown
)

var threshold int

func init() {
	threshold = 10
}

// NewAva returns a new compounded availability structure starting from a
// line scraped from the website page
func NewAva(avaString string) AvaComp {
	outPredicate := avaString == "Currently out of stock" ||
		avaString == "Usually dispatched in 6-9 business days" ||
		strings.Contains(avaString, "Expected restock on")

	avaPredicate := avaString == "In stock, usually dispatched in 1 business day" ||
		avaString == "In stock, usually dispatched in 1-3 business days"

	switch {
	// This usually happens when the URL is wrong or the item was removed
	case avaString == "":
		return AvaComp{
			Availability: Unknown,
			ItemCount:    0,
		}
	case outPredicate:
		return AvaComp{
			Availability: Out,
			ItemCount:    0,
		}
	case avaPredicate:
		return AvaComp{
			Availability: Available,
			ItemCount:    0,
		}
	case strings.Contains(avaString, "usually dispatched in 1 business day"):
		rx, _ := regexp.Compile("[0-9]+")
		avaCount, _ := strconv.Atoi(rx.FindStringSubmatch(avaString)[0])
		if avaCount <= threshold {
			return AvaComp{
				Availability: Low,
				ItemCount:    avaCount,
			}
		}
		return AvaComp{
			Availability: Available,
			ItemCount:    avaCount,
		}
	default:
		return AvaComp{
			Availability: Unknown,
			ItemCount:    0,
		}
	}
}

// String returns a well formatted string for any availability case
func (av AvaComp) String() string {
	switch av.Availability {
	case Available:
		if av.ItemCount == 0 {
			return "Item available"
		}
		return strconv.Itoa(av.ItemCount) + " Items available"

	case Low:
		return "Item low: only " + strconv.Itoa(av.ItemCount) + " items left"
	case Out:
		return "Out of stock"
	default:
		return "Could not update availability"
	}
}

// AvaColor returns the HTML color with witch to display the availility
func AvaColor(av AvaComp) string {
	switch av.Availability {
	case Available:
		return "green"
	case Low:
		return "orange"
	case Out:
		return "red"
	default:
		return "blue"
	}
}
<commit_msg>[DPP-101] Modified availability algorithm<commit_after>package dropp

import (
	"regexp"
	"strconv"
	"strings"
)

// AvaComp is the compounded availability of an item that contains the
// availability type and the item count if present
type AvaComp struct {
	Availability AvaType
	ItemCount    int
}

// AvaType indicates the availability of an item from the source
type AvaType int

const (
	// Available if the item count is above a certain threshold
	Available AvaType = iota

	// Low if the item count is below a certain threshold
	Low

	// Out if there are no items available
	Out

	// Unknown is used when the update could not be performed
	Unknown
)

var threshold int

func init() {
	threshold = 10
}

// NewAva returns a new compounded availability structure starting from a
// line scraped from the website page
func NewAva(avaString string) AvaComp {
	outPredicate := avaString == "Currently out of stock" ||
		avaString == "Usually dispatched in 6-9 business days" ||
		strings.Contains(avaString, "Expected restock on")

	avaPredicate := avaString == "In stock, usually dispatched in 1 business day" ||
		avaString == "In stock, usually dispatched in 1-3 business days" ||
		avaString == "In stock , usually dispatched in 1-3 business days"

	switch {
	case outPredicate:
		return AvaComp{
			Availability: Out,
			ItemCount:    0,
		}
	case avaPredicate:
		return AvaComp{
			Availability: Available,
			ItemCount:    0,
		}
	case strings.Contains(avaString, "Only"):
		rx, _ := regexp.Compile("[0-9]+")
		avaCount, _ := strconv.Atoi(rx.FindStringSubmatch(avaString)[0])
		if avaCount <= threshold {
			return AvaComp{
				Availability: Low,
				ItemCount:    avaCount,
			}
		}
		return AvaComp{
			Availability: Available,
			ItemCount:    avaCount,
		}
	default:
		return AvaComp{
			Availability: Unknown,
			ItemCount:    0,
		}
	}
}

// String returns a well formatted string for any availability case
func (av AvaComp) String() string {
	switch av.Availability {
	case Available:
		if av.ItemCount == 0 {
			return "Item available"
		}
		return strconv.Itoa(av.ItemCount) + " Items available"

	case Low:
		return "Item low: only " + strconv.Itoa(av.ItemCount) + " items left"
	case Out:
		return "Out of stock"
	default:
		return "Could not update availability"
	}
}

// AvaColor returns the HTML color with witch to display the availility
func AvaColor(av AvaComp) string {
	switch av.Availability {
	case Available:
		return "green"
	case Low:
		return "orange"
	case Out:
		return "red"
	default:
		return "blue"
	}
}
<|endoftext|>