<commit_before>package main

import (
	"bufio"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"strings"

	"github.com/cfstras/go-utils/color"
	"github.com/kr/pty"
	"github.com/renstrom/fuzzysearch/fuzzy"
	"golang.org/x/net/html/charset"
)

type Configuration struct {
	XMLName xml.Name  `xml:"configuration"`
	Root    Container `xml:"root"`

	AllConnections map[string]Connection
}
type Container struct {
	Type        string       `xml:"type,attr"`
	Name        string       `xml:"name,attr"`
	expanded    bool         `xml:"expanded, attr"`
	Containers  []Container  `xml:"container"`
	Connections []Connection `xml:"connection"`
}
type Connection struct {
	Type string `xml:"type,attr"`
	Name string `xml:"name,attr"`

	Info    Info    `xml:"connection_info"`
	Login   Login   `xml:"login"`
	Timeout Timeout `xml:"timeout"`
	Command Command `xml:"command"`
	//TODO Options
}

type Info struct {
	Name        string `xml:"name"`
	Protocol    string `xml:"protocol"`
	Host        string `xml:"host"`
	Port        uint16 `xml:"port"`
	Session     string `xml:"session"`
	Commandline string `xml:"commandline"`
	Description string `xml:"description"`
}

type Login struct {
	User     string `xml:"login"`
	Password string `xml:"password"`
}

type Timeout struct {
	ConnectionTimeout int `xml:"connectiontimeout"`
	LoginTimeout      int `xml:"logintimeout"`
	PasswordTimeout   int `xml:"passwordtimeout"`
	CommandTimeout    int `xml:"commandtimeout"`
}

type Command struct {
	Commands []string `xml:",any"`
}

var (
	connectionsPath string = "~/Downloads/connections.xml"
	conns           Configuration
)

func main() {
	defer func() {
		if err := recover(); err != nil {
			color.Redln(err)
			os.Exit(1)
		}
	}()

	pathP := flag.String("connectionsPath", connectionsPath, "Path to PuTTY connections.xml")

	flag.Parse()
	if pathP != nil {
		connectionsPath = *pathP
	}

	if flag.NArg() > 1 {
		flag.Usage()
		color.Yellowln("Usage: pcm [search term]")
		panic("Only one arg allowed.")
	}
	var searchFor string
	if flag.NArg() == 1 {
		searchFor = flag.Arg(0)
	}

	conns = loadConns()
	listConnections(&conns)
	//treePrint(conns)
	words := listWords(&conns)

	reader := bufio.NewReader(os.Stdin)
	var found string
	for {
		color.Yellow("Search for: ")
		var input string
		var err error
		if searchFor != "" {
			input = searchFor
			searchFor = ""
		} else {
			input, err = reader.ReadString('\n')
			p(err, "reading stdin")
			input = strings.Trim(input, "\r\n ")
		}
		suggs := fuzzy.Find(input, words)
		if len(suggs) > 1 {
			color.Yellowln("Suggestions:")
			for _, v := range suggs {
				fmt.Println(v)
			}
			color.Redln("Please try again.")
		} else if len(suggs) == 0 {
			color.Redln("Nothing found for", input+". Please try again.")
		} else {
			found = suggs[0]
			break
		}
	}
	color.Yellowln("Using", found)

	conn := conns.AllConnections[found]
	fmt.Println(conn.Info)
	fmt.Println(conn.Login)
	fmt.Println(conn.Command)
	connect(conn)
	color.Redln("TODO")
}

func connect(c Connection) {
	cmd := &exec.Cmd{}
	cmd.Path = "/usr/bin/ssh"
	cmd.Args = []string{"-T", fmt.Sprint("-p", c.Info.Port), "-l" + c.Login.User, c.Info.Host}
	color.Yellowln(cmd.Path, cmd.Args)

	outFunc := func(pipe *os.File, name string, nextCommand func() string) {
		buf := make([]byte, 1024)
		for {
			n, err := pipe.Read(buf)
			str := string(buf[:n])
			fmt.Print(str)
			if strings.HasSuffix(str, "assword: ") || strings.HasSuffix(str, "$ ") ||
				strings.HasSuffix(str, "# ") {
				answer := nextCommand()
				if answer != "" {
					//fmt.Println("writing", answer)
					pipe.Write([]byte(answer))
					pipe.Write([]byte("\n"))
				}
			}
			if err != nil {
				fmt.Println("pipe", name, "error", err)
				fmt.Println("closing pipe", name, pipe.Close())
				return
			}
		}
	}

	inFunc := func(pipe io.WriteCloser) {
		buf := make([]byte, 1024)
		for {
			n, err := os.Stdin.Read(buf)
			//fmt.Println("my stdin got", n, string(buf[:n]))
			if err != nil {
				fmt.Println("my stdin got error", err)
				fmt.Println("closing stdIn:", pipe.Close())
				return
			}
			_, err = pipe.Write(buf[:n])

			if err != nil {
				fmt.Println("stdin got error", err)
				fmt.Println("closing stdIn:", pipe.Close())
				return
			}
		}
	}

	state := 0
	nextCommand := func() string {
		defer func() { state++ }()
		if state == 0 {
			return c.Login.Password
		}
		ind := state - 1
		if len(c.Command.Commands) > ind {
			return c.Command.Commands[ind]
		}
		return ""
	}

	pty, err := pty.Start(cmd)
	p(err, "starting ssh")

	go outFunc(pty, "pty", nextCommand)
	go inFunc(pty)

	err = cmd.Wait()
	p(err, "waiting for command")
}

func DummyReader(label string, input io.Reader) (io.Reader, error) {
	return input, nil
}

func loadConns() (result Configuration) {
	u, err := user.Current()
	p(err, "getting user")
	filename := strings.Replace(connectionsPath, "~", u.HomeDir, -1)
	rd, err := os.Open(filename)
	p(err, "opening "+filename)
	defer rd.Close()
	rd2, err := charset.NewReaderLabel("utf-16", rd)
	p(err, "loading charset")

	decoder := xml.NewDecoder(rd2)
	decoder.CharsetReader = DummyReader
	p(decoder.Decode(&result), "decoding xml")

	return
}

func p(err error, where string) {
	if err != nil {
		color.Yellow("When " + where + ": ")
		panic(err)
	}
}

func treePrint(conns Configuration) {
	treeDescend(0, conns.Root)
}
func treeDescend(depth int, node Container) {
	for i, nextCont := range node.Containers {
		var nodeSym string
		if i == 0 {
			nodeSym = "┣"
		} else if i == len(node.Containers)-1 {
			if len(node.Connections) > 0 {
				nodeSym = "┡"
			} else {
				nodeSym = "┗"
			}
		} else {
			nodeSym = "┣"
		}
		fmt.Print(strings.Repeat("  ", depth), nodeSym, "━┓ ", nextCont.Name, "\n")
		treeDescend(depth+1, nextCont)
	}
	for i, conn := range node.Connections {
		var nodeSym string
		if i == len(node.Connections)-1 {
			nodeSym = "└"
		} else if len(node.Containers) != 0 {
			nodeSym = "├"
		} else {
			nodeSym = "┌"
		}
		fmt.Print(strings.Repeat("  ", depth), nodeSym, "─ ", conn.Name, "\n")
	}
}

func listConnections(conns *Configuration) {
	conns.AllConnections = make(map[string]Connection)
	descendConnections("", conns.Root, conns)
}

func descendConnections(prefix string, node Container, conns *Configuration) {
	for _, c := range node.Connections {
		key := prefix + "/" + c.Name
		conns.AllConnections[key] = c
	}
	for _, n := range node.Containers {
		descendConnections(prefix+"/"+n.Name, n, conns)
	}
}

func listWords(conns *Configuration) []string {
	words := make([]string, 0, len(conns.AllConnections))
	for k := range conns.AllConnections {
		words = append(words, k)
	}
	return words
}
<commit_msg>now using a proper tty, catching ctrl-C and EOF<commit_after>package main

import (
	"bufio"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"os/user"
	"strings"

	"github.com/cfstras/go-utils/color"
	"github.com/kr/pty"
	"github.com/renstrom/fuzzysearch/fuzzy"
	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/net/html/charset"
)

type Configuration struct {
	XMLName xml.Name  `xml:"configuration"`
	Root    Container `xml:"root"`

	AllConnections map[string]Connection
}
type Container struct {
	Type        string       `xml:"type,attr"`
	Name        string       `xml:"name,attr"`
	expanded    bool         `xml:"expanded, attr"`
	Containers  []Container  `xml:"container"`
	Connections []Connection `xml:"connection"`
}
type Connection struct {
	Type string `xml:"type,attr"`
	Name string `xml:"name,attr"`

	Info    Info    `xml:"connection_info"`
	Login   Login   `xml:"login"`
	Timeout Timeout `xml:"timeout"`
	Command Command `xml:"command"`
	//TODO Options
}

type Info struct {
	Name        string `xml:"name"`
	Protocol    string `xml:"protocol"`
	Host        string `xml:"host"`
	Port        uint16 `xml:"port"`
	Session     string `xml:"session"`
	Commandline string `xml:"commandline"`
	Description string `xml:"description"`
}

type Login struct {
	User     string `xml:"login"`
	Password string `xml:"password"`
}

type Timeout struct {
	ConnectionTimeout int `xml:"connectiontimeout"`
	LoginTimeout      int `xml:"logintimeout"`
	PasswordTimeout   int `xml:"passwordtimeout"`
	CommandTimeout    int `xml:"commandtimeout"`
}

type Command struct {
	Commands []string `xml:",any"`
}

var (
	connectionsPath string = "~/Downloads/connections.xml"
	conns           Configuration
)

func main() {
	defer func() {
		if err := recover(); err != nil {
			color.Redln(err)
			os.Exit(1)
		}
	}()

	pathP := flag.String("connectionsPath", connectionsPath, "Path to PuTTY connections.xml")

	flag.Parse()
	if pathP != nil {
		connectionsPath = *pathP
	}

	if flag.NArg() > 1 {
		flag.Usage()
		color.Yellowln("Usage: pcm [search term]")
		panic("Only one arg allowed.")
	}
	var searchFor string
	if flag.NArg() == 1 {
		searchFor = flag.Arg(0)
	}

	conns = loadConns()
	listConnections(&conns)
	//treePrint(conns)
	words := listWords(&conns)

	reader := bufio.NewReader(os.Stdin)
	var found string
	for {
		color.Yellow("Search for: ")
		var input string
		var err error
		if searchFor != "" {
			input = searchFor
			searchFor = ""
		} else {
			input, err = reader.ReadString('\n')
			p(err, "reading stdin")
			input = strings.Trim(input, "\r\n ")
		}
		suggs := fuzzy.Find(input, words)
		if len(suggs) > 1 {
			color.Yellowln("Suggestions:")
			for _, v := range suggs {
				fmt.Println(v)
			}
			color.Redln("Please try again.")
		} else if len(suggs) == 0 {
			color.Redln("Nothing found for", input+". Please try again.")
		} else {
			found = suggs[0]
			break
		}
	}
	color.Yellowln("Using", found)

	conn := conns.AllConnections[found]
	fmt.Println(conn.Info)
	fmt.Println(conn.Login)
	fmt.Println(conn.Command)
	connect(conn)
}

func connect(c Connection) {
	cmd := &exec.Cmd{}
	cmd.Path = "/usr/bin/ssh"
	cmd.Args = []string{fmt.Sprint("-p", c.Info.Port), "-l" + c.Login.User, c.Info.Host}
	color.Yellowln(cmd.Path, cmd.Args)

	outFunc := func(pipe *os.File, name string, nextCommand func() string) {
		buf := make([]byte, 1024)
		for {
			n, err := pipe.Read(buf)
			str := string(buf[:n])
			fmt.Print(str)
			if strings.HasSuffix(str, "assword: ") || strings.HasSuffix(str, "$ ") ||
				strings.HasSuffix(str, "# ") {
				answer := nextCommand()
				if answer != "" {
					//fmt.Println("writing", answer)
					pipe.Write([]byte(answer))
					pipe.Write([]byte("\n"))
				}
			}
			if err != nil {
				fmt.Println("pipe", name, "error", err)
				fmt.Println("closing pipe", name, pipe.Close())
				return
			}
		}
	}

	inFunc := func(pipe io.WriteCloser) {
		buf := make([]byte, 1024)
		for {
			n, err := os.Stdin.Read(buf)
			//fmt.Println("my stdin got", n, string(buf[:n]))
			if err == io.EOF {
				pipe.Write([]byte{0x04})
			}
			if err != nil {
				fmt.Println("my stdin got error", err)
				fmt.Println("closing stdIn:", pipe.Close())
				return
			}
			_, err = pipe.Write(buf[:n])

			if err != nil {
				fmt.Println("stdin got error", err)
				fmt.Println("closing stdIn:", pipe.Close())
				return
			}
		}
	}

	state := 0
	nextCommand := func() string {
		defer func() { state++ }()
		if state == 0 {
			return c.Login.Password
		}
		ind := state - 1
		if len(c.Command.Commands) > ind {
			return c.Command.Commands[ind]
		}
		return ""
	}

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	signalWatcher := func(out *os.File) {
		for _ = range signals {
			out.Write([]byte{0x03})
		}
	}

	pty, err := pty.Start(cmd)
	p(err, "starting ssh")
	oldState, err := terminal.MakeRaw(0)
	p(err, "making terminal raw")
	defer terminal.Restore(0, oldState)

	go outFunc(pty, "pty", nextCommand)
	go inFunc(pty)
	go signalWatcher(pty)

	err = cmd.Wait()
	if err != nil {
		fmt.Println(err)
	}
}

func DummyReader(label string, input io.Reader) (io.Reader, error) {
	return input, nil
}

func loadConns() (result Configuration) {
	u, err := user.Current()
	p(err, "getting user")
	filename := strings.Replace(connectionsPath, "~", u.HomeDir, -1)
	rd, err := os.Open(filename)
	p(err, "opening "+filename)
	defer rd.Close()
	rd2, err := charset.NewReaderLabel("utf-16", rd)
	p(err, "loading charset")

	decoder := xml.NewDecoder(rd2)
	decoder.CharsetReader = DummyReader
	p(decoder.Decode(&result), "decoding xml")

	return
}

func p(err error, where string) {
	if err != nil {
		color.Yellow("When " + where + ": ")
		panic(err)
	}
}

func treePrint(conns Configuration) {
	treeDescend(0, conns.Root)
}
func treeDescend(depth int, node Container) {
	for i, nextCont := range node.Containers {
		var nodeSym string
		if i == 0 {
			nodeSym = "┣"
		} else if i == len(node.Containers)-1 {
			if len(node.Connections) > 0 {
				nodeSym = "┡"
			} else {
				nodeSym = "┗"
			}
		} else {
			nodeSym = "┣"
		}
		fmt.Print(strings.Repeat("  ", depth), nodeSym, "━┓ ", nextCont.Name, "\n")
		treeDescend(depth+1, nextCont)
	}
	for i, conn := range node.Connections {
		var nodeSym string
		if i == len(node.Connections)-1 {
			nodeSym = "└"
		} else if len(node.Containers) != 0 {
			nodeSym = "├"
		} else {
			nodeSym = "┌"
		}
		fmt.Print(strings.Repeat("  ", depth), nodeSym, "─ ", conn.Name, "\n")
	}
}

func listConnections(conns *Configuration) {
	conns.AllConnections = make(map[string]Connection)
	descendConnections("", conns.Root, conns)
}

func descendConnections(prefix string, node Container, conns *Configuration) {
	for _, c := range node.Connections {
		key := prefix + "/" + c.Name
		conns.AllConnections[key] = c
	}
	for _, n := range node.Containers {
		descendConnections(prefix+"/"+n.Name, n, conns)
	}
}

func listWords(conns *Configuration) []string {
	words := make([]string, 0, len(conns.AllConnections))
	for k := range conns.AllConnections {
		words = append(words, k)
	}
	return words
}
<|endoftext|>


<commit_before>package api

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/go-gorp/gorp"

	"github.com/ovh/cds/engine/api/cache"
	"github.com/ovh/cds/engine/api/permission"
	"github.com/ovh/cds/engine/api/sessionstore"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

// lastUpdateBrokerSubscribe is the information need to to subscribe
type lastUpdateBrokerSubscribe struct {
	UIID  string
	User  *sdk.User
	Queue chan string
}

// lastUpdateBroker keeps connected client of the current route,
type lastUpdateBroker struct {
	clients    map[string]*lastUpdateBrokerSubscribe
	newClients chan *lastUpdateBrokerSubscribe
	messages   chan string
	mutex      *sync.Mutex
}

//Init the lastUpdateBroker
func (b *lastUpdateBroker) Init(c context.Context, DBFunc func() *gorp.DbMap, store cache.Store) {
	// Start cache Subscription
	go CacheSubscribe(c, b.messages, store)

	// Start processing events
	go b.Start(c, DBFunc, store)
}

// CacheSubscribe subscribe to a channel and push received message in a channel
func CacheSubscribe(c context.Context, cacheMsgChan chan<- string, store cache.Store) {
	pubSub := store.Subscribe("lastUpdates")
	tick := time.NewTicker(250 * time.Millisecond)
	defer tick.Stop()
	for {
		select {
		case <-c.Done():
			if c.Err() != nil {
				log.Error("lastUpdate.CacheSubscribe> Exiting: %v", c.Err())
				return
			}
		case <-tick.C:
			msg, err := store.GetMessageFromSubscription(c, pubSub)
			if err != nil {
				log.Warning("lastUpdate.CacheSubscribe> Cannot get message %s: %s", msg, err)
				time.Sleep(5 * time.Second)
				continue
			}
			cacheMsgChan <- msg
		}
	}
}

// Start the broker
func (b *lastUpdateBroker) Start(c context.Context, DBFunc func() *gorp.DbMap, store cache.Store) {
	for {
		select {
		case <-c.Done():
			// Close all channels
			b.mutex.Lock()
			for c := range b.clients {
				delete(b.clients, c)
			}
			b.mutex.Unlock()
			if c.Err() != nil {
				log.Error("lastUpdate.CacheSubscribe> Exiting: %v", c.Err())
				return
			}
		case s := <-b.newClients:
			// Register new client
			b.mutex.Lock()
			b.clients[s.UIID] = s
			b.mutex.Unlock()
		case msg := <-b.messages:
			var lastModif sdk.LastModification
			if err := json.Unmarshal([]byte(msg), &lastModif); err != nil {
				log.Warning("lastUpdate.CacheSubscribe> Cannot unmarshal message: %s", msg)
				continue
			}

			//Receive new message
			b.mutex.Lock()
			for _, i := range b.clients {
				if err := loadUserPermissions(DBFunc(), store, i.User); err != nil {
					log.Warning("lastUpdate.CacheSubscribe> Cannot load user permission: %s", err)
					continue
				}

				if i.User.Admin {
					i.Queue <- msg
					continue
				}

				switch strings.Split(lastModif.Type, ".")[0] {
				case "project":
					if permission.ProjectPermission(lastModif.Key, i.User) >= permission.PermissionRead {
						i.Queue <- msg
						continue
					}
				case "application":
					if permission.ApplicationPermission(lastModif.Key, lastModif.Name, i.User) >= permission.PermissionRead {
						i.Queue <- msg
						continue
					}
				case "pipeline":
					if permission.PipelinePermission(lastModif.Key, lastModif.Name, i.User) >= permission.PermissionRead {
						i.Queue <- msg
						continue
					}
				}
			}
			b.mutex.Unlock()
		}
	}
}

func (b *lastUpdateBroker) ServeHTTP() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Make sure that the writer supports flushing.
		f, ok := w.(http.Flusher)
		if !ok {
			http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
			return nil
		}

		uuid, errS := sessionstore.NewSessionKey()
		if errS != nil {
			return sdk.WrapError(errS, "lastUpdateBroker.Serve> Cannot generate UUID")
		}
		messageChan := &lastUpdateBrokerSubscribe{
			UIID:  string(uuid),
			User:  getUser(ctx),
			Queue: make(chan string),
		}

		// Add this client to the map of those that should receive updates
		b.newClients <- messageChan

		// Set the headers related to event streaming.
		w.Header().Set("Content-Type", "text/event-stream")
		w.Header().Set("Cache-Control", "no-cache")
		w.Header().Set("Connection", "keep-alive")
		w.Header().Set("X-Accel-Buffering", "no")

		fmt.Fprint(w, "data: ACK\n\n")
		f.Flush()

	leave:
		for {
			select {
			case <-w.(http.CloseNotifier).CloseNotify():
				b.mutex.Lock()
				delete(b.clients, messageChan.UIID)
				b.mutex.Unlock()
				break leave
			case msg := <-messageChan.Queue:
				fmt.Fprintf(w, "data: %s\n\n", msg)
				f.Flush()
			}
		}
		return nil
	}
}
<commit_msg>fix(api): return when request context is done (#2197)<commit_after>package api

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/go-gorp/gorp"

	"github.com/ovh/cds/engine/api/cache"
	"github.com/ovh/cds/engine/api/permission"
	"github.com/ovh/cds/engine/api/sessionstore"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

// lastUpdateBrokerSubscribe is the information need to to subscribe
type lastUpdateBrokerSubscribe struct {
	UIID  string
	User  *sdk.User
	Queue chan string
}

// lastUpdateBroker keeps connected client of the current route,
type lastUpdateBroker struct {
	clients    map[string]*lastUpdateBrokerSubscribe
	newClients chan *lastUpdateBrokerSubscribe
	messages   chan string
	mutex      *sync.Mutex
}

//Init the lastUpdateBroker
func (b *lastUpdateBroker) Init(c context.Context, DBFunc func() *gorp.DbMap, store cache.Store) {
	// Start cache Subscription
	go CacheSubscribe(c, b.messages, store)

	// Start processing events
	go b.Start(c, DBFunc, store)
}

// CacheSubscribe subscribe to a channel and push received message in a channel
func CacheSubscribe(c context.Context, cacheMsgChan chan<- string, store cache.Store) {
	pubSub := store.Subscribe("lastUpdates")
	tick := time.NewTicker(250 * time.Millisecond)
	defer tick.Stop()
	for {
		select {
		case <-c.Done():
			if c.Err() != nil {
				log.Error("lastUpdate.CacheSubscribe> Exiting: %v", c.Err())
				return
			}
		case <-tick.C:
			msg, err := store.GetMessageFromSubscription(c, pubSub)
			if err != nil {
				log.Warning("lastUpdate.CacheSubscribe> Cannot get message %s: %s", msg, err)
				time.Sleep(5 * time.Second)
				continue
			}
			cacheMsgChan <- msg
		}
	}
}

// Start the broker
func (b *lastUpdateBroker) Start(c context.Context, DBFunc func() *gorp.DbMap, store cache.Store) {
	for {
		select {
		case <-c.Done():
			// Close all channels
			b.mutex.Lock()
			for c := range b.clients {
				delete(b.clients, c)
			}
			b.mutex.Unlock()
			if c.Err() != nil {
				log.Error("lastUpdate.CacheSubscribe> Exiting: %v", c.Err())
				return
			}
		case s := <-b.newClients:
			// Register new client
			b.mutex.Lock()
			b.clients[s.UIID] = s
			b.mutex.Unlock()
		case msg := <-b.messages:
			var lastModif sdk.LastModification
			if err := json.Unmarshal([]byte(msg), &lastModif); err != nil {
				log.Warning("lastUpdate.CacheSubscribe> Cannot unmarshal message: %s", msg)
				continue
			}

			//Receive new message
			b.mutex.Lock()
			for _, i := range b.clients {
				if err := loadUserPermissions(DBFunc(), store, i.User); err != nil {
					log.Warning("lastUpdate.CacheSubscribe> Cannot load user permission: %s", err)
					continue
				}

				if i.User.Admin {
					i.Queue <- msg
					continue
				}

				switch strings.Split(lastModif.Type, ".")[0] {
				case "project":
					if permission.ProjectPermission(lastModif.Key, i.User) >= permission.PermissionRead {
						i.Queue <- msg
						continue
					}
				case "application":
					if permission.ApplicationPermission(lastModif.Key, lastModif.Name, i.User) >= permission.PermissionRead {
						i.Queue <- msg
						continue
					}
				case "pipeline":
					if permission.PipelinePermission(lastModif.Key, lastModif.Name, i.User) >= permission.PermissionRead {
						i.Queue <- msg
						continue
					}
				}
			}
			b.mutex.Unlock()
		}
	}
}

func (b *lastUpdateBroker) ServeHTTP() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Make sure that the writer supports flushing.
		f, ok := w.(http.Flusher)
		if !ok {
			http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
			return nil
		}

		uuid, errS := sessionstore.NewSessionKey()
		if errS != nil {
			return sdk.WrapError(errS, "lastUpdateBroker.Serve> Cannot generate UUID")
		}
		messageChan := &lastUpdateBrokerSubscribe{
			UIID:  string(uuid),
			User:  getUser(ctx),
			Queue: make(chan string),
		}

		// Add this client to the map of those that should receive updates
		b.newClients <- messageChan

		// Set the headers related to event streaming.
		w.Header().Set("Content-Type", "text/event-stream")
		w.Header().Set("Cache-Control", "no-cache")
		w.Header().Set("Connection", "keep-alive")
		w.Header().Set("X-Accel-Buffering", "no")

		fmt.Fprint(w, "data: ACK\n\n")
		f.Flush()

	leave:
		for {
			select {
			case <-ctx.Done():
				b.mutex.Lock()
				delete(b.clients, messageChan.UIID)
				b.mutex.Unlock()
				break leave
			case <-w.(http.CloseNotifier).CloseNotify():
				b.mutex.Lock()
				delete(b.clients, messageChan.UIID)
				b.mutex.Unlock()
				break leave
			case msg := <-messageChan.Queue:
				fmt.Fprintf(w, "data: %s\n\n", msg)
				f.Flush()
			}
		}
		return nil
	}
}
<|endoftext|>


<commit_before>package sqldb

import (
	"database/sql"

	"github.com/xiaonanln/goworld/engine/async"
	"golang.org/x/net/context"
)

const (
	_SQLDB_ASYNC_JOB_GROUP = "_sqldb"
)

type DB struct {
	db *sql.DB
}

type Tx struct {
	*sql.Tx
}

func Open(driverName, dataSourceName string, ac async.AsyncCallback) {
	async.AppendAsyncJob(_SQLDB_ASYNC_JOB_GROUP, func() (res interface{}, err error) {
		db, err := sql.Open(driverName, dataSourceName)
		if err != nil {
			return nil, err
		}

		return &DB{db}, err
	}, ac)
}

func (db *DB) Close(ac async.AsyncCallback) {
	async.AppendAsyncJob(_SQLDB_ASYNC_JOB_GROUP, func() (res interface{}, err error) {
		err = db.db.Close()
		return
	}, ac)
}

func (db *DB) Ping(ac async.AsyncCallback) {
	async.AppendAsyncJob(_SQLDB_ASYNC_JOB_GROUP, func() (res interface{}, err error) {
		err = db.db.Ping()
		return
	}, ac)
}

func (db *DB) BeginTx(ctx context.Context, opts *sql.TxOptions, ac async.AsyncCallback) {
	async.AppendAsyncJob(_SQLDB_ASYNC_JOB_GROUP, func() (res interface{}, err error) {
		tx, err := db.db.BeginTx(ctx, opts)
		if err != nil {
			return nil, err
		} else {
			return &Tx{tx}, err
		}
	}, ac)
}
<commit_msg>pause on sqldb<commit_after><|endoftext|>


<commit_before>/*

sudokustate manages modifications to a given grid, allowing easy undo/redo,
and keeping track of all moves.

Moves can be made either by setting a number on a given cell or setting
multiple marks on a given cell.

When multiple modifications are logically part of the same move (for the
purposes of undo/redo), a group can be created. Call `model.StartGroup`, make
the changes, then `model.FinishGroupAndExecute` to make all of the changes in
one 'move'.

*/
package sudokustate

import (
	"github.com/jkomoros/sudoku"
)

//Model maintains all of the state and modifications to the grid. The zero-
//state is valid; create a new Model with sudokustate.Model{}.
type Model struct {
	grid                   sudoku.MutableGrid
	currentCommand         *commandList
	commands               *commandList
	inProgressMultiCommand *multiCommand
}

type commandList struct {
	c    command
	next *commandList
	prev *commandList
}

type command interface {
	Apply(m *Model)
	Undo(m *Model)
	ModifiedCells(m *Model) sudoku.CellSlice
}

type baseCommand struct {
	ref sudoku.CellRef
}

func (b *baseCommand) ModifiedCells(m *Model) sudoku.CellSlice {
	if m == nil || m.grid == nil {
		return nil
	}
	return sudoku.CellSlice{b.ref.Cell(m.grid)}
}

func (m *multiCommand) ModifiedCells(model *Model) sudoku.CellSlice {
	var result sudoku.CellSlice

	for _, command := range m.commands {
		result = append(result, command.ModifiedCells(model)...)
	}

	return result
}

func (m *multiCommand) AddCommand(c command) {
	m.commands = append(m.commands, c)
}

type markCommand struct {
	baseCommand
	marksToggle map[int]bool
}

type numberCommand struct {
	baseCommand
	number int
	//Necessary so we can undo.
	oldNumber int
}

type multiCommand struct {
	commands []command
}

//Grid returns the underlying Grid managed by this Model. It's a non-mutable
//reference to emphasize that all mutations should be done by the Model
//itself.
func (m *Model) Grid() sudoku.Grid {
	return m.grid
}

func (m *Model) executeCommand(c command) {
	listItem := &commandList{
		c:    c,
		next: nil,
		prev: m.currentCommand,
	}

	m.commands = listItem
	if m.currentCommand != nil {
		m.currentCommand.next = listItem
	}
	m.currentCommand = listItem

	c.Apply(m)
}

func (m *Model) LastModifiedCells() sudoku.CellSlice {
	if m.currentCommand == nil {
		return nil
	}

	return m.currentCommand.c.ModifiedCells(m)
}

//Undo returns true if there was something to undo.
func (m *Model) Undo() bool {
	if m.currentCommand == nil {
		return false
	}

	m.currentCommand.c.Undo(m)

	m.currentCommand = m.currentCommand.prev

	return true
}

//Redo returns true if there was something to redo.
func (m *Model) Redo() bool {

	if m.commands == nil {
		return false
	}

	var commandToApply *commandList

	if m.currentCommand == nil {
		//If there is a non-nil commands, go all the way to the beginning,
		//because we're currently pointing at state 0
		commandToApply = m.commands
		for commandToApply.prev != nil {
			commandToApply = commandToApply.prev
		}
	} else {
		//Normaly operation is just to move to the next command in the list
		//and apply it.
		commandToApply = m.currentCommand.next
	}

	if commandToApply == nil {
		return false
	}

	m.currentCommand = commandToApply

	m.currentCommand.c.Apply(m)

	return true
}

func (m *Model) StartGroup() {
	m.inProgressMultiCommand = &multiCommand{
		nil,
	}
}

func (m *Model) FinishGroupAndExecute() {
	if m.inProgressMultiCommand == nil {
		return
	}
	m.executeCommand(m.inProgressMultiCommand)
	m.inProgressMultiCommand = nil
}

func (m *Model) CancelGroup() {
	m.inProgressMultiCommand = nil
}

func (m *Model) InGroup() bool {
	return m.inProgressMultiCommand != nil
}

//Reset resets the current grid back to its fully unfilled states and discards
//all commands.
func (m *Model) Reset() {
	//TODO: test this.
	m.commands = nil
	m.currentCommand = nil
	if m.grid != nil {
		m.grid.ResetUnlockedCells()
	}
}

func (m *Model) SetGrid(grid sudoku.MutableGrid) {
	m.grid = grid
	m.Reset()
}

func (m *Model) SetMarks(ref sudoku.CellRef, marksToggle map[int]bool) {
	command := m.newMarkCommand(ref, marksToggle)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressMultiCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newMarkCommand(ref sudoku.CellRef, marksToggle map[int]bool) *markCommand {
	//Only keep marks in the toggle that won't be a no-op
	newMarksToggle := make(map[int]bool)

	cell := ref.Cell(m.grid)

	if cell == nil {
		return nil
	}
	for key, value := range marksToggle {
		if cell.Mark(key) != value {
			//Good, keep it
			newMarksToggle[key] = value
		}
	}

	if len(newMarksToggle) == 0 {
		//The command would be a no op!
		return nil
	}

	return &markCommand{baseCommand{ref}, newMarksToggle}
}

func (m *Model) SetNumber(ref sudoku.CellRef, num int) {
	command := m.newNumberCommand(ref, num)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressMultiCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newNumberCommand(ref sudoku.CellRef, num int) *numberCommand {
	cell := ref.Cell(m.grid)

	if cell == nil {
		return nil
	}

	if cell.Number() == num {
		return nil
	}

	return &numberCommand{baseCommand{ref}, num, cell.Number()}
}

func (m *markCommand) Apply(model *Model) {
	cell := m.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		cell.SetMark(key, value)
	}
}

func (m *markCommand) Undo(model *Model) {
	cell := m.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		//Set the opposite since we're undoing.
		cell.SetMark(key, !value)
	}
}

func (n *numberCommand) Apply(model *Model) {
	cell := n.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	cell.SetNumber(n.number)
}

func (n *numberCommand) Undo(model *Model) {
	cell := n.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	cell.SetNumber(n.oldNumber)
}

func (m *multiCommand) Apply(model *Model) {
	for _, command := range m.commands {
		command.Apply(model)
	}
}

func (m *multiCommand) Undo(model *Model) {
	for i := len(m.commands) - 1; i >= 0; i-- {
		m.commands[i].Undo(model)
	}
}
<commit_msg>Document all of hte public methods on Model.<commit_after>/*

sudokustate manages modifications to a given grid, allowing easy undo/redo,
and keeping track of all moves.

Moves can be made either by setting a number on a given cell or setting
multiple marks on a given cell.

When multiple modifications are logically part of the same move (for the
purposes of undo/redo), a group can be created. Call `model.StartGroup`, make
the changes, then `model.FinishGroupAndExecute` to make all of the changes in
one 'move'.

*/
package sudokustate

import (
	"github.com/jkomoros/sudoku"
)

//Model maintains all of the state and modifications to the grid. The zero-
//state is valid; create a new Model with sudokustate.Model{}.
type Model struct {
	grid                   sudoku.MutableGrid
	currentCommand         *commandList
	commands               *commandList
	inProgressMultiCommand *multiCommand
}

type commandList struct {
	c    command
	next *commandList
	prev *commandList
}

type command interface {
	Apply(m *Model)
	Undo(m *Model)
	ModifiedCells(m *Model) sudoku.CellSlice
}

type baseCommand struct {
	ref sudoku.CellRef
}

func (b *baseCommand) ModifiedCells(m *Model) sudoku.CellSlice {
	if m == nil || m.grid == nil {
		return nil
	}
	return sudoku.CellSlice{b.ref.Cell(m.grid)}
}

func (m *multiCommand) ModifiedCells(model *Model) sudoku.CellSlice {
	var result sudoku.CellSlice

	for _, command := range m.commands {
		result = append(result, command.ModifiedCells(model)...)
	}

	return result
}

func (m *multiCommand) AddCommand(c command) {
	m.commands = append(m.commands, c)
}

type markCommand struct {
	baseCommand
	marksToggle map[int]bool
}

type numberCommand struct {
	baseCommand
	number int
	//Necessary so we can undo.
	oldNumber int
}

type multiCommand struct {
	commands []command
}

//Grid returns the underlying Grid managed by this Model. It's an immutable
//reference to emphasize that all mutations should be done by the Model
//itself.
func (m *Model) Grid() sudoku.Grid {
	return m.grid
}

func (m *Model) executeCommand(c command) {
	listItem := &commandList{
		c:    c,
		next: nil,
		prev: m.currentCommand,
	}

	m.commands = listItem
	if m.currentCommand != nil {
		m.currentCommand.next = listItem
	}
	m.currentCommand = listItem

	c.Apply(m)
}

//LastModifiedCells returns the cells that were modified in the last action
//that was taken on the grid.
func (m *Model) LastModifiedCells() sudoku.CellSlice {
	if m.currentCommand == nil {
		return nil
	}

	return m.currentCommand.c.ModifiedCells(m)
}

//Undo rolls back a single action. It returns true if there was something to
//undo.
func (m *Model) Undo() bool {
	if m.currentCommand == nil {
		return false
	}

	m.currentCommand.c.Undo(m)

	m.currentCommand = m.currentCommand.prev

	return true
}

//Redo re-applies a single, previously undone, action. It returns true if
//there was something to redo.
func (m *Model) Redo() bool {

	if m.commands == nil {
		return false
	}

	var commandToApply *commandList

	if m.currentCommand == nil {
		//If there is a non-nil commands, go all the way to the beginning,
		//because we're currently pointing at state 0
		commandToApply = m.commands
		for commandToApply.prev != nil {
			commandToApply = commandToApply.prev
		}
	} else {
		//Normaly operation is just to move to the next command in the list
		//and apply it.
		commandToApply = m.currentCommand.next
	}

	if commandToApply == nil {
		return false
	}

	m.currentCommand = commandToApply

	m.currentCommand.c.Apply(m)

	return true
}

//StartGroup begins a new group of actions. After calling StartGroup, all
//calls to SetMarks or SetNumber will be grouped into a single logical group
//of actions--that is, if they are undone they will all be undone at once.
//When a group is active, the modifications aren't actually made to the grid
//until FinishGroupAndExecute is called.
func (m *Model) StartGroup() {
	m.inProgressMultiCommand = &multiCommand{
		nil,
	}
}

//FinishGroupAndExecute applies all of the modifications made inside of this
//group.
func (m *Model) FinishGroupAndExecute() {
	if m.inProgressMultiCommand == nil {
		return
	}
	m.executeCommand(m.inProgressMultiCommand)
	m.inProgressMultiCommand = nil
}

//CancelGroup throws out the in-progress group of modifications. None of the
//modifications will actually be made to the grid.
func (m *Model) CancelGroup() {
	m.inProgressMultiCommand = nil
}

//InGroup returns true if a group is currently being built (that is,
//StartGroup was called, and neither FinishGroupAndExecute nor CancelGroup
//have been called.)
func (m *Model) InGroup() bool {
	return m.inProgressMultiCommand != nil
}

//Reset resets the current grid back to its fully unfilled state (resetting
//all state in non-locked cells)and discards all actions taken on the grid so
//far.
func (m *Model) Reset() {
	//TODO: test this.
	m.commands = nil
	m.currentCommand = nil
	if m.grid != nil {
		m.grid.ResetUnlockedCells()
	}
}

//SetGrid installs a new grid into the model, and then resets it. Note that
//the grid you set into the model will be used directly; modifications you
//make to it after installing into the model may get it in an odd state.
func (m *Model) SetGrid(grid sudoku.MutableGrid) {
	m.grid = grid
	m.Reset()
}

//SetMarks will set the specified marks onto a cell. An entry of True will set
//the mark, and entry of False will remove the mark in that slot. If InGroup()
//is false, the change will be done immediately; if InGroup() is true the
//change will not be applied until FinishGroupAndExecute() is called.
func (m *Model) SetMarks(ref sudoku.CellRef, marksToggle map[int]bool) {
	command := m.newMarkCommand(ref, marksToggle)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressMultiCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newMarkCommand(ref sudoku.CellRef, marksToggle map[int]bool) *markCommand {
	//Only keep marks in the toggle that won't be a no-op
	newMarksToggle := make(map[int]bool)

	cell := ref.Cell(m.grid)

	if cell == nil {
		return nil
	}
	for key, value := range marksToggle {
		if cell.Mark(key) != value {
			//Good, keep it
			newMarksToggle[key] = value
		}
	}

	if len(newMarksToggle) == 0 {
		//The command would be a no op!
		return nil
	}

	return &markCommand{baseCommand{ref}, newMarksToggle}
}

//SetNumber will set the specified number in a cell. If InGroup() is false,
//the change will be done immediately; if InGroup() is true the change will
//not be applied until FinishGroupAndExecute() is called.
func (m *Model) SetNumber(ref sudoku.CellRef, num int) {
	command := m.newNumberCommand(ref, num)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressMultiCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newNumberCommand(ref sudoku.CellRef, num int) *numberCommand {
	cell := ref.Cell(m.grid)

	if cell == nil {
		return nil
	}

	if cell.Number() == num {
		return nil
	}

	return &numberCommand{baseCommand{ref}, num, cell.Number()}
}

func (m *markCommand) Apply(model *Model) {
	cell := m.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		cell.SetMark(key, value)
	}
}

func (m *markCommand) Undo(model *Model) {
	cell := m.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		//Set the opposite since we're undoing.
		cell.SetMark(key, !value)
	}
}

func (n *numberCommand) Apply(model *Model) {
	cell := n.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	cell.SetNumber(n.number)
}

func (n *numberCommand) Undo(model *Model) {
	cell := n.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	cell.SetNumber(n.oldNumber)
}

func (m *multiCommand) Apply(model *Model) {
	for _, command := range m.commands {
		command.Apply(model)
	}
}

func (m *multiCommand) Undo(model *Model) {
	for i := len(m.commands) - 1; i >= 0; i-- {
		m.commands[i].Undo(model)
	}
}
<|endoftext|>


<commit_before>/*

sudokustate manages modifications to a given grid, allowing easy undo/redo,
and keeping track of all moves.

Moves can be made either by setting a number on a given cell or setting
multiple marks on a given cell.

When multiple modifications are logically part of the same move (for the
purposes of undo/redo), a group can be created. Call `model.StartGroup`, make
the changes, then `model.FinishGroupAndExecute` to make all of the changes in
one 'move'.

*/
package sudokustate

import (
	"github.com/jkomoros/sudoku"
)

//Model maintains all of the state and modifications to the grid. The zero-
//state is valid; create a new Model with sudokustate.Model{}.
type Model struct {
	grid sudoku.MutableGrid
	//The place in the command list that we currently are (this could move
	//left or right down the list due to calls to undo or redo)
	currentCommand *commandList
	//The last command in the list of commands. currentCommand might not be
	//the same as commands if the user has called Undo some number of times.
	commands               *commandList
	inProgressMultiCommand *multiCommand
	//snapshot is a Diagram(true) of what the grid looked like when it was reset.
	snapshot    string
	nextGroupID int
}

type commandList struct {
	c    command
	next *commandList
	prev *commandList
}

type groupInfo struct {
	ID          int
	Description string
}

type command interface {
	Apply(m *Model)
	Undo(m *Model)
	ModifiedCells(m *Model) sudoku.CellRefSlice
	//one of 'number', 'marks', 'group'
	Type() string
	//TODO: consider removing Type; the type can be derived by which Extra it has.

	//TODO: I HATE this way of approaching it--it seems so messy and backwards
	//to expose all of the guts of each command, but in a messy way where most
	//of the methods don't apply to most of the command types. Perhaps another
	//way of doing this is to have each command have a digest() method that
	//produces an object with all of its state.

	//All sub-commands related to this command. For basic commands it's just
	//self; for group it's all sub-commands in order.
	SubCommands() []command
	//Returns the group info if this command is a containing group, nil if
	//not.
	GroupInfo() *groupInfo
	//The Marks (if this is a mark command). Returns nil otherwise.
	Marks() map[int]bool
	//The number associated with this command, or nil if not a number command.
	Number() *int
}

type baseCommand struct {
	ref sudoku.CellRef
}

func (b *baseCommand) GroupInfo() *groupInfo {
	return nil
}

func (b *baseCommand) Marks() map[int]bool {
	return nil
}

func (b *baseCommand) Number() *int {
	return nil
}

func (b *baseCommand) ModifiedCells(m *Model) sudoku.CellRefSlice {
	if m == nil || m.grid == nil {
		return nil
	}
	return sudoku.CellRefSlice{b.ref}
}

func (m *multiCommand) ModifiedCells(model *Model) sudoku.CellRefSlice {
	var result sudoku.CellRefSlice

	for _, command := range m.commands {
		result = append(result, command.ModifiedCells(model)...)
	}

	return result
}

func (m *multiCommand) GroupInfo() *groupInfo {
	return m.groupInfo
}

func (m *multiCommand) Marks() map[int]bool {
	return nil
}

func (m *multiCommand) Number() *int {
	return nil
}

func (m *multiCommand) AddCommand(c command) {
	m.commands = append(m.commands, c)
}

type markCommand struct {
	baseCommand
	marksToggle map[int]bool
}

type numberCommand struct {
	baseCommand
	number int
	//Necessary so we can undo.
	oldNumber int
}

type multiCommand struct {
	commands  []command
	groupInfo *groupInfo
}

func (m *markCommand) Marks() map[int]bool {
	return m.marksToggle
}

func (n *numberCommand) Number() *int {
	//Indirect, since we're technically giving consumers a handle to change
	//it, which we don't want.
	result := n.number
	return &result
}

func (m *markCommand) Type() string {
	return "marks"
}

func (n *numberCommand) Type() string {
	return "number"
}

func (m *multiCommand) Type() string {
	return "group"
}

func (m *markCommand) SubCommands() []command {
	return []command{m}
}

func (n *numberCommand) SubCommands() []command {
	return []command{n}
}

func (m *multiCommand) SubCommands() []command {
	return m.commands
}

//Grid returns the underlying Grid managed by this Model. It's an immutable
//reference to emphasize that all mutations should be done by the Model
//itself.
func (m *Model) Grid() sudoku.Grid {
	return m.grid
}

func (m *Model) executeCommand(c command) {
	listItem := &commandList{
		c:    c,
		next: nil,
		prev: m.currentCommand,
	}

	m.commands = listItem
	if m.currentCommand != nil {
		m.currentCommand.next = listItem
	}
	m.currentCommand = listItem

	c.Apply(m)
}

//LastModifiedCells returns the cells that were modified in the last action
//that was taken on the grid.
func (m *Model) LastModifiedCells() sudoku.CellRefSlice {
	if m.currentCommand == nil {
		return nil
	}

	return m.currentCommand.c.ModifiedCells(m)
}

//Undo rolls back a single action. It returns true if there was something to
//undo.
func (m *Model) Undo() bool {
	if m.currentCommand == nil {
		return false
	}

	m.currentCommand.c.Undo(m)

	m.currentCommand = m.currentCommand.prev

	return true
}

//Redo re-applies a single, previously undone, action. It returns true if
//there was something to redo.
func (m *Model) Redo() bool {

	if m.commands == nil {
		return false
	}

	var commandToApply *commandList

	if m.currentCommand == nil {
		//If there is a non-nil commands, go all the way to the beginning,
		//because we're currently pointing at state 0
		commandToApply = m.commands
		for commandToApply.prev != nil {
			commandToApply = commandToApply.prev
		}
	} else {
		//Normaly operation is just to move to the next command in the list
		//and apply it.
		commandToApply = m.currentCommand.next
	}

	if commandToApply == nil {
		return false
	}

	m.currentCommand = commandToApply

	m.currentCommand.c.Apply(m)

	return true
}

//StartGroup begins a new group of actions. After calling StartGroup, all
//calls to SetMarks or SetNumber will be grouped into a single logical group
//of actions--that is, if they are undone they will all be undone at once.
//When a group is active, the modifications aren't actually made to the grid
//until FinishGroupAndExecute is called. Description is a description of what
//the group logically represents, primarily just for what will be shown in the
//digest.
func (m *Model) StartGroup(description string) {
	//TODO: allow setting a name when the group is created.
	m.inProgressMultiCommand = &multiCommand{
		nil,
		&groupInfo{
			m.nextGroupID,
			description,
		},
	}
	m.nextGroupID++
}

//FinishGroupAndExecute applies all of the modifications made inside of this
//group.
func (m *Model) FinishGroupAndExecute() {
	if m.inProgressMultiCommand == nil {
		return
	}
	m.executeCommand(m.inProgressMultiCommand)
	m.inProgressMultiCommand = nil
}

//CancelGroup throws out the in-progress group of modifications. None of the
//modifications will actually be made to the grid.
func (m *Model) CancelGroup() {
	m.inProgressMultiCommand = nil
}

//InGroup returns true if a group is currently being built (that is,
//StartGroup was called, and neither FinishGroupAndExecute nor CancelGroup
//have been called.)
func (m *Model) InGroup() bool {
	return m.inProgressMultiCommand != nil
}

//Reset resets the current grid back to its fully unfilled state (resetting
//all state in non-locked cells)and discards all actions taken on the grid so
//far.
func (m *Model) Reset() {
	//TODO: test this.
	m.commands = nil
	m.currentCommand = nil
	if m.grid != nil {
		m.grid.ResetUnlockedCells()
		m.snapshot = m.grid.Diagram(true)
	}
}

//SetGrid installs a new grid into the model, and then resets it. Note that
//the grid you set into the model will be used directly; modifications you
//make to it after installing into the model may get it in an odd state.
func (m *Model) SetGrid(grid sudoku.MutableGrid) {
	m.grid = grid
	m.Reset()
}

//SetMarks will set the specified marks onto a cell. An entry of True will set
//the mark, and entry of False will remove the mark in that slot. If InGroup()
//is false, the change will be done immediately; if InGroup() is true the
//change will not be applied until FinishGroupAndExecute() is called.
func (m *Model) SetMarks(ref sudoku.CellRef, marksToggle map[int]bool) {
	command := m.newMarkCommand(ref, marksToggle)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressMultiCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newMarkCommand(ref sudoku.CellRef, marksToggle map[int]bool) *markCommand {
	//Only keep marks in the toggle that won't be a no-op
	newMarksToggle := make(map[int]bool)

	cell := ref.Cell(m.grid)

	if cell == nil {
		return nil
	}
	for key, value := range marksToggle {
		if cell.Mark(key) != value {
			//Good, keep it
			newMarksToggle[key] = value
		}
	}

	if len(newMarksToggle) == 0 {
		//The command would be a no op!
		return nil
	}

	return &markCommand{baseCommand{ref}, newMarksToggle}
}

//SetNumber will set the specified number in a cell. If InGroup() is false,
//the change will be done immediately; if InGroup() is true the change will
//not be applied until FinishGroupAndExecute() is called.
func (m *Model) SetNumber(ref sudoku.CellRef, num int) {
	command := m.newNumberCommand(ref, num)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressMultiCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newNumberCommand(ref sudoku.CellRef, num int) *numberCommand {
	cell := ref.Cell(m.grid)

	if cell == nil {
		return nil
	}

	if cell.Number() == num {
		return nil
	}

	return &numberCommand{baseCommand{ref}, num, cell.Number()}
}

func (m *markCommand) Apply(model *Model) {
	cell := m.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		cell.SetMark(key, value)
	}
}

func (m *markCommand) Undo(model *Model) {
	cell := m.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		//Set the opposite since we're undoing.
		cell.SetMark(key, !value)
	}
}

func (n *numberCommand) Apply(model *Model) {
	cell := n.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	cell.SetNumber(n.number)
}

func (n *numberCommand) Undo(model *Model) {
	cell := n.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	cell.SetNumber(n.oldNumber)
}

func (m *multiCommand) Apply(model *Model) {
	for _, command := range m.commands {
		command.Apply(model)
	}
}

func (m *multiCommand) Undo(model *Model) {
	for i := len(m.commands) - 1; i >= 0; i-- {
		m.commands[i].Undo(model)
	}
}
<commit_msg>multiCommand (and related fields) --> groupCommand<commit_after>/*

sudokustate manages modifications to a given grid, allowing easy undo/redo,
and keeping track of all moves.

Moves can be made either by setting a number on a given cell or setting
multiple marks on a given cell.

When multiple modifications are logically part of the same move (for the
purposes of undo/redo), a group can be created. Call `model.StartGroup`, make
the changes, then `model.FinishGroupAndExecute` to make all of the changes in
one 'move'.

*/
package sudokustate

import (
	"github.com/jkomoros/sudoku"
)

//Model maintains all of the state and modifications to the grid. The zero-
//state is valid; create a new Model with sudokustate.Model{}.
type Model struct {
	grid sudoku.MutableGrid
	//The place in the command list that we currently are (this could move
	//left or right down the list due to calls to undo or redo)
	currentCommand *commandList
	//The last command in the list of commands. currentCommand might not be
	//the same as commands if the user has called Undo some number of times.
	commands               *commandList
	inProgressGroupCommand *groupCommand
	//snapshot is a Diagram(true) of what the grid looked like when it was reset.
	snapshot    string
	nextGroupID int
}

type commandList struct {
	c    command
	next *commandList
	prev *commandList
}

type groupInfo struct {
	ID          int
	Description string
}

type command interface {
	Apply(m *Model)
	Undo(m *Model)
	ModifiedCells(m *Model) sudoku.CellRefSlice
	//one of 'number', 'marks', 'group'
	Type() string
	//TODO: consider removing Type; the type can be derived by which Extra it has.

	//TODO: I HATE this way of approaching it--it seems so messy and backwards
	//to expose all of the guts of each command, but in a messy way where most
	//of the methods don't apply to most of the command types. Perhaps another
	//way of doing this is to have each command have a digest() method that
	//produces an object with all of its state.

	//All sub-commands related to this command. For basic commands it's just
	//self; for group it's all sub-commands in order.
	SubCommands() []command
	//Returns the group info if this command is a containing group, nil if
	//not.
	GroupInfo() *groupInfo
	//The Marks (if this is a mark command). Returns nil otherwise.
	Marks() map[int]bool
	//The number associated with this command, or nil if not a number command.
	Number() *int
}

type baseCommand struct {
	ref sudoku.CellRef
}

func (b *baseCommand) GroupInfo() *groupInfo {
	return nil
}

func (b *baseCommand) Marks() map[int]bool {
	return nil
}

func (b *baseCommand) Number() *int {
	return nil
}

func (b *baseCommand) ModifiedCells(m *Model) sudoku.CellRefSlice {
	if m == nil || m.grid == nil {
		return nil
	}
	return sudoku.CellRefSlice{b.ref}
}

func (m *groupCommand) ModifiedCells(model *Model) sudoku.CellRefSlice {
	var result sudoku.CellRefSlice

	for _, command := range m.commands {
		result = append(result, command.ModifiedCells(model)...)
	}

	return result
}

func (m *groupCommand) GroupInfo() *groupInfo {
	return m.groupInfo
}

func (m *groupCommand) Marks() map[int]bool {
	return nil
}

func (m *groupCommand) Number() *int {
	return nil
}

func (m *groupCommand) AddCommand(c command) {
	m.commands = append(m.commands, c)
}

type markCommand struct {
	baseCommand
	marksToggle map[int]bool
}

type numberCommand struct {
	baseCommand
	number int
	//Necessary so we can undo.
	oldNumber int
}

type groupCommand struct {
	commands  []command
	groupInfo *groupInfo
}

func (m *markCommand) Marks() map[int]bool {
	return m.marksToggle
}

func (n *numberCommand) Number() *int {
	//Indirect, since we're technically giving consumers a handle to change
	//it, which we don't want.
	result := n.number
	return &result
}

func (m *markCommand) Type() string {
	return "marks"
}

func (n *numberCommand) Type() string {
	return "number"
}

func (m *groupCommand) Type() string {
	return "group"
}

func (m *markCommand) SubCommands() []command {
	return []command{m}
}

func (n *numberCommand) SubCommands() []command {
	return []command{n}
}

func (m *groupCommand) SubCommands() []command {
	return m.commands
}

//Grid returns the underlying Grid managed by this Model. It's an immutable
//reference to emphasize that all mutations should be done by the Model
//itself.
func (m *Model) Grid() sudoku.Grid {
	return m.grid
}

func (m *Model) executeCommand(c command) {
	listItem := &commandList{
		c:    c,
		next: nil,
		prev: m.currentCommand,
	}

	m.commands = listItem
	if m.currentCommand != nil {
		m.currentCommand.next = listItem
	}
	m.currentCommand = listItem

	c.Apply(m)
}

//LastModifiedCells returns the cells that were modified in the last action
//that was taken on the grid.
func (m *Model) LastModifiedCells() sudoku.CellRefSlice {
	if m.currentCommand == nil {
		return nil
	}

	return m.currentCommand.c.ModifiedCells(m)
}

//Undo rolls back a single action. It returns true if there was something to
//undo.
func (m *Model) Undo() bool {
	if m.currentCommand == nil {
		return false
	}

	m.currentCommand.c.Undo(m)

	m.currentCommand = m.currentCommand.prev

	return true
}

//Redo re-applies a single, previously undone, action. It returns true if
//there was something to redo.
func (m *Model) Redo() bool {

	if m.commands == nil {
		return false
	}

	var commandToApply *commandList

	if m.currentCommand == nil {
		//If there is a non-nil commands, go all the way to the beginning,
		//because we're currently pointing at state 0
		commandToApply = m.commands
		for commandToApply.prev != nil {
			commandToApply = commandToApply.prev
		}
	} else {
		//Normaly operation is just to move to the next command in the list
		//and apply it.
		commandToApply = m.currentCommand.next
	}

	if commandToApply == nil {
		return false
	}

	m.currentCommand = commandToApply

	m.currentCommand.c.Apply(m)

	return true
}

//StartGroup begins a new group of actions. After calling StartGroup, all
//calls to SetMarks or SetNumber will be grouped into a single logical group
//of actions--that is, if they are undone they will all be undone at once.
//When a group is active, the modifications aren't actually made to the grid
//until FinishGroupAndExecute is called. Description is a description of what
//the group logically represents, primarily just for what will be shown in the
//digest.
func (m *Model) StartGroup(description string) {
	//TODO: allow setting a name when the group is created.
	m.inProgressGroupCommand = &groupCommand{
		nil,
		&groupInfo{
			m.nextGroupID,
			description,
		},
	}
	m.nextGroupID++
}

//FinishGroupAndExecute applies all of the modifications made inside of this
//group.
func (m *Model) FinishGroupAndExecute() {
	if m.inProgressGroupCommand == nil {
		return
	}
	m.executeCommand(m.inProgressGroupCommand)
	m.inProgressGroupCommand = nil
}

//CancelGroup throws out the in-progress group of modifications. None of the
//modifications will actually be made to the grid.
func (m *Model) CancelGroup() {
	m.inProgressGroupCommand = nil
}

//InGroup returns true if a group is currently being built (that is,
//StartGroup was called, and neither FinishGroupAndExecute nor CancelGroup
//have been called.)
func (m *Model) InGroup() bool {
	return m.inProgressGroupCommand != nil
}

//Reset resets the current grid back to its fully unfilled state (resetting
//all state in non-locked cells)and discards all actions taken on the grid so
//far.
func (m *Model) Reset() {
	//TODO: test this.
	m.commands = nil
	m.currentCommand = nil
	if m.grid != nil {
		m.grid.ResetUnlockedCells()
		m.snapshot = m.grid.Diagram(true)
	}
}

//SetGrid installs a new grid into the model, and then resets it. Note that
//the grid you set into the model will be used directly; modifications you
//make to it after installing into the model may get it in an odd state.
func (m *Model) SetGrid(grid sudoku.MutableGrid) {
	m.grid = grid
	m.Reset()
}

//SetMarks will set the specified marks onto a cell. An entry of True will set
//the mark, and entry of False will remove the mark in that slot. If InGroup()
//is false, the change will be done immediately; if InGroup() is true the
//change will not be applied until FinishGroupAndExecute() is called.
func (m *Model) SetMarks(ref sudoku.CellRef, marksToggle map[int]bool) {
	command := m.newMarkCommand(ref, marksToggle)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressGroupCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newMarkCommand(ref sudoku.CellRef, marksToggle map[int]bool) *markCommand {
	//Only keep marks in the toggle that won't be a no-op
	newMarksToggle := make(map[int]bool)

	cell := ref.Cell(m.grid)

	if cell == nil {
		return nil
	}
	for key, value := range marksToggle {
		if cell.Mark(key) != value {
			//Good, keep it
			newMarksToggle[key] = value
		}
	}

	if len(newMarksToggle) == 0 {
		//The command would be a no op!
		return nil
	}

	return &markCommand{baseCommand{ref}, newMarksToggle}
}

//SetNumber will set the specified number in a cell. If InGroup() is false,
//the change will be done immediately; if InGroup() is true the change will
//not be applied until FinishGroupAndExecute() is called.
func (m *Model) SetNumber(ref sudoku.CellRef, num int) {
	command := m.newNumberCommand(ref, num)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressGroupCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newNumberCommand(ref sudoku.CellRef, num int) *numberCommand {
	cell := ref.Cell(m.grid)

	if cell == nil {
		return nil
	}

	if cell.Number() == num {
		return nil
	}

	return &numberCommand{baseCommand{ref}, num, cell.Number()}
}

func (m *markCommand) Apply(model *Model) {
	cell := m.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		cell.SetMark(key, value)
	}
}

func (m *markCommand) Undo(model *Model) {
	cell := m.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		//Set the opposite since we're undoing.
		cell.SetMark(key, !value)
	}
}

func (n *numberCommand) Apply(model *Model) {
	cell := n.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	cell.SetNumber(n.number)
}

func (n *numberCommand) Undo(model *Model) {
	cell := n.ref.MutableCell(model.grid)
	if cell == nil {
		return
	}
	cell.SetNumber(n.oldNumber)
}

func (m *groupCommand) Apply(model *Model) {
	for _, command := range m.commands {
		command.Apply(model)
	}
}

func (m *groupCommand) Undo(model *Model) {
	for i := len(m.commands) - 1; i >= 0; i-- {
		m.commands[i].Undo(model)
	}
}
<|endoftext|>


<commit_before>package google

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/hashicorp/go-retryablehttp"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

func buildClient() *retryablehttp.Client {
	httpClient := retryablehttp.NewClient()

	// Stop debug logger
	httpClient.Logger = nil
	httpClient.RetryMax = 1
	httpClient.RetryWaitMin = 30 * time.Second
	httpClient.RetryWaitMax = 2 * time.Minute

	return httpClient
}

func buildRequest(langFrom, langTo, inputText string) (*retryablehttp.Request, error) {
	var URL *url.URL
	URL, err := url.Parse("https://translate.googleapis.com/translate_a/single")

	parameters := url.Values{}
	parameters.Add("client", "gtx") // Google translate extension
	parameters.Add("dt", "t")       // Translate text
	parameters.Add("hl", "en")      // Interface language
	parameters.Add("sl", langFrom)  // Source language or "auto"
	parameters.Add("tl", langTo)    // Target language
	parameters.Add("ie", "UTF-8")   // Input encoding
	parameters.Add("oe", "UTF-8")   // Output encoding
	parameters.Add("q", inputText)  // Source text

	URL.RawQuery = parameters.Encode()

	r, err := retryablehttp.NewRequest("GET", URL.String(), nil)
	if err != nil {
		return r, errors.Wrap(err, "Failed to create request")
	}

	return r, err
}

func (q *BatchTranslator) translateBatch(items []inputObject) {
	if len(items) < 1 {
		log.Debug("nothing to do")
		return
	}

	log.Infof("processing %d items", len(items))

	q.lastBatch = time.Now()

	go func() {
		// TODO: Add support for different language pairs
		err := q.translateItems(items)
		if err != nil {
			// Send error to all items
			for _, i := range items {
				i.outChan <- returnObject{
					text: "",
					err:  err,
				}
			}
		}
	}()
}

func (q *BatchTranslator) translateItems(items []inputObject) error {
	// Join every input separated by newline
	var reqText string
	for _, i := range items {
		reqText += i.req.Text + "\n"
	}

	// FIXME: Properly batch multiple languages
	r, err := buildRequest(items[0].req.From, items[0].req.To, reqText)
	if err != nil {
		return err
	}

	resp, err := q.client.Do(r)
	if err != nil {
		log.Fatal(err)

		return errors.Wrapf(err, "Failed to do request (%s delay)", q.batchDelay)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		if contents, err := ioutil.ReadAll(resp.Body); err == nil {
			return fmt.Errorf("%s - %s", resp.Status, contents)
		}

		return fmt.Errorf("%s", resp.Status)
	}

	contents, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return errors.Wrap(err, "Failed to read response body")
	}

	response, err := decodeResponse(string(contents))
	if err != nil {
		log.Errorf("Unknown response: %q", string(contents))
		return errors.Wrap(err, "Failed to decode response json")
	}

	/* Google sometimes splits sentences in input to multiple output objects
	attempt to merge them back into one */
	if len(items) != len(response) {
		log.Debug("Response pair count don't match input")
		response = mergeOutput(items, response)
	}

	for i, pair := range response {
		if strings.TrimSpace(pair.input) != strings.TrimSpace(items[i].req.Text) {
			items[i].outChan <- returnObject{
				text: pair.input,
				err:  fmt.Errorf("mismatched input text! %q != %q", items[i].req.Text, pair.input),
			}

			continue
		}

		items[i].outChan <- returnObject{
			text: pair.output,
			err:  nil,
		}
	}

	return nil
}
<commit_msg>Debug print request url to access raw files<commit_after>package google

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/hashicorp/go-retryablehttp"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

func buildClient() *retryablehttp.Client {
	httpClient := retryablehttp.NewClient()

	// Stop debug logger
	httpClient.Logger = nil
	httpClient.RetryMax = 1
	httpClient.RetryWaitMin = 30 * time.Second
	httpClient.RetryWaitMax = 2 * time.Minute

	return httpClient
}

func buildRequest(langFrom, langTo, inputText string) (*retryablehttp.Request, error) {
	var URL *url.URL
	URL, err := url.Parse("https://translate.googleapis.com/translate_a/single")

	parameters := url.Values{}
	parameters.Add("client", "gtx") // Google translate extension
	parameters.Add("dt", "t")       // Translate text
	parameters.Add("hl", "en")      // Interface language
	parameters.Add("sl", langFrom)  // Source language or "auto"
	parameters.Add("tl", langTo)    // Target language
	parameters.Add("ie", "UTF-8")   // Input encoding
	parameters.Add("oe", "UTF-8")   // Output encoding
	parameters.Add("q", inputText)  // Source text

	URL.RawQuery = parameters.Encode()

	log.Debugf("%s", URL)

	r, err := retryablehttp.NewRequest("GET", URL.String(), nil)
	if err != nil {
		return r, errors.Wrap(err, "Failed to create request")
	}

	return r, err
}

func (q *BatchTranslator) translateBatch(items []inputObject) {
	if len(items) < 1 {
		log.Debug("nothing to do")
		return
	}

	log.Infof("processing %d items", len(items))

	q.lastBatch = time.Now()

	go func() {
		// TODO: Add support for different language pairs
		err := q.translateItems(items)
		if err != nil {
			// Send error to all items
			for _, i := range items {
				i.outChan <- returnObject{
					text: "",
					err:  err,
				}
			}
		}
	}()
}

func (q *BatchTranslator) translateItems(items []inputObject) error {
	// Join every input separated by newline
	var reqText string
	for _, i := range items {
		reqText += i.req.Text + "\n"
	}

	// FIXME: Properly batch multiple languages
	r, err := buildRequest(items[0].req.From, items[0].req.To, reqText)
	if err != nil {
		return err
	}

	resp, err := q.client.Do(r)
	if err != nil {
		log.Fatal(err)

		return errors.Wrapf(err, "Failed to do request (%s delay)", q.batchDelay)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		if contents, err := ioutil.ReadAll(resp.Body); err == nil {
			return fmt.Errorf("%s - %s", resp.Status, contents)
		}

		return fmt.Errorf("%s", resp.Status)
	}

	contents, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return errors.Wrap(err, "Failed to read response body")
	}

	response, err := decodeResponse(string(contents))
	if err != nil {
		log.Errorf("Unknown response: %q", string(contents))
		return errors.Wrap(err, "Failed to decode response json")
	}

	/* Google sometimes splits sentences in input to multiple output objects
	attempt to merge them back into one */
	if len(items) != len(response) {
		log.Debug("Response pair count don't match input")
		response = mergeOutput(items, response)
	}

	for i, pair := range response {
		if strings.TrimSpace(pair.input) != strings.TrimSpace(items[i].req.Text) {
			items[i].outChan <- returnObject{
				text: pair.input,
				err:  fmt.Errorf("mismatched input text! %q != %q", items[i].req.Text, pair.input),
			}

			continue
		}

		items[i].outChan <- returnObject{
			text: pair.output,
			err:  nil,
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	log "github.com/GameGophers/nsq-logger"
	"google.golang.org/grpc"
	"net"
	"os"
	pb "proto"
)

const (
	_port = ":50003"
)

func main() {
	log.SetPrefix(SERVICE)
	// 监听
	lis, err := net.Listen("tcp", _port)
	if err != nil {
		log.Critical(err)
		os.Exit(-1)
	}
	log.Info("listening on ", lis.Addr())

	// 注册服务
	s := grpc.NewServer()
	ins := &server{}
	ins.init()
	pb.RegisterSnowflakeServiceServer(s, ins)

	// 开始服务
	s.Serve(lis)
}
<commit_msg>add statsd pprof<commit_after>package main

import (
	log "github.com/GameGophers/nsq-logger"
	_ "github.com/GameGophers/statsd-pprof"
	"google.golang.org/grpc"
	"net"
	"os"
	pb "proto"
)

const (
	_port = ":50003"
)

func main() {
	log.SetPrefix(SERVICE)
	// 监听
	lis, err := net.Listen("tcp", _port)
	if err != nil {
		log.Critical(err)
		os.Exit(-1)
	}
	log.Info("listening on ", lis.Addr())

	// 注册服务
	s := grpc.NewServer()
	ins := &server{}
	ins.init()
	pb.RegisterSnowflakeServiceServer(s, ins)

	// 开始服务
	s.Serve(lis)
}
<|endoftext|>


<commit_before>package container

import (
	"fmt"
	"os"

	executorpkg "github.com/docker/docker/daemon/cluster/executor"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/events"
	"github.com/docker/swarmkit/agent/exec"
	"github.com/docker/swarmkit/api"
	"github.com/docker/swarmkit/log"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

// controller implements agent.Controller against docker's API.
//
// Most operations against docker's API are done through the container name,
// which is unique to the task.
type controller struct {
	backend executorpkg.Backend
	task    *api.Task
	adapter *containerAdapter
	closed  chan struct{}
	err     error
}

var _ exec.Controller = &controller{}

// NewController returns a dockerexec runner for the provided task.
func newController(b executorpkg.Backend, task *api.Task) (*controller, error) {
	adapter, err := newContainerAdapter(b, task)
	if err != nil {
		return nil, err
	}

	return &controller{
		backend: b,
		task:    task,
		adapter: adapter,
		closed:  make(chan struct{}),
	}, nil
}

func (r *controller) Task() (*api.Task, error) {
	return r.task, nil
}

// ContainerStatus returns the container-specific status for the task.
func (r *controller) ContainerStatus(ctx context.Context) (*api.ContainerStatus, error) {
	ctnr, err := r.adapter.inspect(ctx)
	if err != nil {
		if isUnknownContainer(err) {
			return nil, nil
		}
		return nil, err
	}
	return parseContainerStatus(ctnr)
}

// Update tasks a recent task update and applies it to the container.
func (r *controller) Update(ctx context.Context, t *api.Task) error {
	// TODO(stevvooe): While assignment of tasks is idempotent, we do allow
	// updates of metadata, such as labelling, as well as any other properties
	// that make sense.
	return nil
}

// Prepare creates a container and ensures the image is pulled.
//
// If the container has already be created, exec.ErrTaskPrepared is returned.
func (r *controller) Prepare(ctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	// Make sure all the networks that the task needs are created.
	if err := r.adapter.createNetworks(ctx); err != nil {
		return err
	}

	// Make sure all the volumes that the task needs are created.
	if err := r.adapter.createVolumes(ctx, r.backend); err != nil {
		return err
	}

	if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
		if err := r.adapter.pullImage(ctx); err != nil {
			cause := errors.Cause(err)
			if cause == context.Canceled || cause == context.DeadlineExceeded {
				return err
			}

			// NOTE(stevvooe): We always try to pull the image to make sure we have
			// the most up to date version. This will return an error, but we only
			// log it. If the image truly doesn't exist, the create below will
			// error out.
			//
			// This gives us some nice behavior where we use up to date versions of
			// mutable tags, but will still run if the old image is available but a
			// registry is down.
			//
			// If you don't want this behavior, lock down your image to an
			// immutable tag or digest.
			log.G(ctx).WithError(err).Error("pulling image failed")
		}
	}

	if err := r.adapter.create(ctx, r.backend); err != nil {
		if isContainerCreateNameConflict(err) {
			if _, err := r.adapter.inspect(ctx); err != nil {
				return err
			}

			// container is already created. success!
			return exec.ErrTaskPrepared
		}

		return err
	}

	return nil
}

// Start the container. An error will be returned if the container is already started.
func (r *controller) Start(ctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	ctnr, err := r.adapter.inspect(ctx)
	if err != nil {
		return err
	}

	// Detect whether the container has *ever* been started. If so, we don't
	// issue the start.
	//
	// TODO(stevvooe): This is very racy. While reading inspect, another could
	// start the process and we could end up starting it twice.
	if ctnr.State.Status != "created" {
		return exec.ErrTaskStarted
	}

	if err := r.adapter.start(ctx); err != nil {
		return errors.Wrap(err, "starting container failed")
	}

	// no health check
	if ctnr.Config == nil || ctnr.Config.Healthcheck == nil {
		return nil
	}

	healthCmd := ctnr.Config.Healthcheck.Test

	if len(healthCmd) == 0 || healthCmd[0] == "NONE" {
		return nil
	}

	// wait for container to be healthy
	eventq := r.adapter.events(ctx)

	var healthErr error
	for {
		select {
		case event := <-eventq:
			if !r.matchevent(event) {
				continue
			}

			switch event.Action {
			case "die": // exit on terminal events
				ctnr, err := r.adapter.inspect(ctx)
				if err != nil {
					return errors.Wrap(err, "die event received")
				} else if ctnr.State.ExitCode != 0 {
					return &exitError{code: ctnr.State.ExitCode, cause: healthErr}
				}

				return nil
			case "destroy":
				// If we get here, something has gone wrong but we want to exit
				// and report anyways.
				return ErrContainerDestroyed
			case "health_status: unhealthy":
				// in this case, we stop the container and report unhealthy status
				if err := r.Shutdown(ctx); err != nil {
					return errors.Wrap(err, "unhealthy container shutdown failed")
				}
				// set health check error, and wait for container to fully exit ("die" event)
				healthErr = ErrContainerUnhealthy
			case "health_status: healthy":
				return nil
			}
		case <-ctx.Done():
			return ctx.Err()
		case <-r.closed:
			return r.err
		}
	}
}

// Wait on the container to exit.
func (r *controller) Wait(pctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	ctx, cancel := context.WithCancel(pctx)
	defer cancel()

	healthErr := make(chan error, 1)
	go func() {
		ectx, cancel := context.WithCancel(ctx) // cancel event context on first event
		defer cancel()
		if err := r.checkHealth(ectx); err == ErrContainerUnhealthy {
			healthErr <- ErrContainerUnhealthy
			if err := r.Shutdown(ectx); err != nil {
				log.G(ectx).WithError(err).Debug("shutdown failed on unhealthy")
			}
		}
	}()

	err := r.adapter.wait(ctx)
	if ctx.Err() != nil {
		return ctx.Err()
	}

	if err != nil {
		ee := &exitError{}
		if ec, ok := err.(exec.ExitCoder); ok {
			ee.code = ec.ExitCode()
		}
		select {
		case e := <-healthErr:
			ee.cause = e
		default:
			if err.Error() != "" {
				ee.cause = err
			}
		}
		return ee
	}

	return nil
}

// Shutdown the container cleanly.
func (r *controller) Shutdown(ctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	if err := r.adapter.shutdown(ctx); err != nil {
		if isUnknownContainer(err) || isStoppedContainer(err) {
			return nil
		}

		return err
	}

	return nil
}

// Terminate the container, with force.
func (r *controller) Terminate(ctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	if err := r.adapter.terminate(ctx); err != nil {
		if isUnknownContainer(err) {
			return nil
		}

		return err
	}

	return nil
}

// Remove the container and its resources.
func (r *controller) Remove(ctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	// It may be necessary to shut down the task before removing it.
	if err := r.Shutdown(ctx); err != nil {
		if isUnknownContainer(err) {
			return nil
		}
		// This may fail if the task was already shut down.
		log.G(ctx).WithError(err).Debug("shutdown failed on removal")
	}

	// Try removing networks referenced in this task in case this
	// task is the last one referencing it
	if err := r.adapter.removeNetworks(ctx); err != nil {
		if isUnknownContainer(err) {
			return nil
		}
		return err
	}

	if err := r.adapter.remove(ctx); err != nil {
		if isUnknownContainer(err) {
			return nil
		}

		return err
	}
	return nil
}

// Close the runner and clean up any ephemeral resources.
func (r *controller) Close() error {
	select {
	case <-r.closed:
		return r.err
	default:
		r.err = exec.ErrControllerClosed
		close(r.closed)
	}
	return nil
}

func (r *controller) matchevent(event events.Message) bool {
	if event.Type != events.ContainerEventType {
		return false
	}

	// TODO(stevvooe): Filter based on ID matching, in addition to name.

	// Make sure the events are for this container.
	if event.Actor.Attributes["name"] != r.adapter.container.name() {
		return false
	}

	return true
}

func (r *controller) checkClosed() error {
	select {
	case <-r.closed:
		return r.err
	default:
		return nil
	}
}

func parseContainerStatus(ctnr types.ContainerJSON) (*api.ContainerStatus, error) {
	status := &api.ContainerStatus{
		ContainerID: ctnr.ID,
		PID:         int32(ctnr.State.Pid),
		ExitCode:    int32(ctnr.State.ExitCode),
	}

	return status, nil
}

type exitError struct {
	code  int
	cause error
}

func (e *exitError) Error() string {
	if e.cause != nil {
		return fmt.Sprintf("task: non-zero exit (%v): %v", e.code, e.cause)
	}

	return fmt.Sprintf("task: non-zero exit (%v)", e.code)
}

func (e *exitError) ExitCode() int {
	return int(e.code)
}

func (e *exitError) Cause() error {
	return e.cause
}

// checkHealth blocks until unhealthy container is detected or ctx exits
func (r *controller) checkHealth(ctx context.Context) error {
	eventq := r.adapter.events(ctx)

	for {
		select {
		case <-ctx.Done():
			return nil
		case <-r.closed:
			return nil
		case event := <-eventq:
			if !r.matchevent(event) {
				continue
			}

			switch event.Action {
			case "health_status: unhealthy":
				return ErrContainerUnhealthy
			}
		}
	}
}
<commit_msg>container/controller: avoid cancellation with forked pull context<commit_after>package container

import (
	"fmt"
	"os"

	executorpkg "github.com/docker/docker/daemon/cluster/executor"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/events"
	"github.com/docker/swarmkit/agent/exec"
	"github.com/docker/swarmkit/api"
	"github.com/docker/swarmkit/log"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

// controller implements agent.Controller against docker's API.
//
// Most operations against docker's API are done through the container name,
// which is unique to the task.
type controller struct {
	backend executorpkg.Backend
	task    *api.Task
	adapter *containerAdapter
	closed  chan struct{}
	err     error

	pulled     chan struct{} // closed after pull
	cancelPull func()        // cancels pull context if not nil
	pullErr    error         // pull error, only read after pulled closed
}

var _ exec.Controller = &controller{}

// NewController returns a dockerexec runner for the provided task.
func newController(b executorpkg.Backend, task *api.Task) (*controller, error) {
	adapter, err := newContainerAdapter(b, task)
	if err != nil {
		return nil, err
	}

	return &controller{
		backend: b,
		task:    task,
		adapter: adapter,
		closed:  make(chan struct{}),
	}, nil
}

func (r *controller) Task() (*api.Task, error) {
	return r.task, nil
}

// ContainerStatus returns the container-specific status for the task.
func (r *controller) ContainerStatus(ctx context.Context) (*api.ContainerStatus, error) {
	ctnr, err := r.adapter.inspect(ctx)
	if err != nil {
		if isUnknownContainer(err) {
			return nil, nil
		}
		return nil, err
	}
	return parseContainerStatus(ctnr)
}

// Update tasks a recent task update and applies it to the container.
func (r *controller) Update(ctx context.Context, t *api.Task) error {
	// TODO(stevvooe): While assignment of tasks is idempotent, we do allow
	// updates of metadata, such as labelling, as well as any other properties
	// that make sense.
	return nil
}

// Prepare creates a container and ensures the image is pulled.
//
// If the container has already be created, exec.ErrTaskPrepared is returned.
func (r *controller) Prepare(ctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	// Make sure all the networks that the task needs are created.
	if err := r.adapter.createNetworks(ctx); err != nil {
		return err
	}

	// Make sure all the volumes that the task needs are created.
	if err := r.adapter.createVolumes(ctx, r.backend); err != nil {
		return err
	}

	if os.Getenv("DOCKER_SERVICE_PREFER_OFFLINE_IMAGE") != "1" {
		if r.pulled == nil {
			// Fork the pull to a different context to allow pull to continue
			// on re-entrant calls to Prepare. This ensures that Prepare can be
			// idempotent and not incur the extra cost of pulling when
			// cancelled on updates.
			var pctx context.Context

			r.pulled = make(chan struct{})
			pctx, r.cancelPull = context.WithCancel(context.Background()) // TODO(stevvooe): Bind a context to the entire controller.

			go func() {
				defer close(r.pulled)
				r.pullErr = r.adapter.pullImage(pctx) // protected by closing r.pulled
			}()
		}

		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-r.pulled:
			if r.pullErr != nil {
				// NOTE(stevvooe): We always try to pull the image to make sure we have
				// the most up to date version. This will return an error, but we only
				// log it. If the image truly doesn't exist, the create below will
				// error out.
				//
				// This gives us some nice behavior where we use up to date versions of
				// mutable tags, but will still run if the old image is available but a
				// registry is down.
				//
				// If you don't want this behavior, lock down your image to an
				// immutable tag or digest.
				log.G(ctx).WithError(r.pullErr).Error("pulling image failed")
			}
		}
	}

	if err := r.adapter.create(ctx, r.backend); err != nil {
		if isContainerCreateNameConflict(err) {
			if _, err := r.adapter.inspect(ctx); err != nil {
				return err
			}

			// container is already created. success!
			return exec.ErrTaskPrepared
		}

		return err
	}

	return nil
}

// Start the container. An error will be returned if the container is already started.
func (r *controller) Start(ctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	ctnr, err := r.adapter.inspect(ctx)
	if err != nil {
		return err
	}

	// Detect whether the container has *ever* been started. If so, we don't
	// issue the start.
	//
	// TODO(stevvooe): This is very racy. While reading inspect, another could
	// start the process and we could end up starting it twice.
	if ctnr.State.Status != "created" {
		return exec.ErrTaskStarted
	}

	if err := r.adapter.start(ctx); err != nil {
		return errors.Wrap(err, "starting container failed")
	}

	// no health check
	if ctnr.Config == nil || ctnr.Config.Healthcheck == nil {
		return nil
	}

	healthCmd := ctnr.Config.Healthcheck.Test

	if len(healthCmd) == 0 || healthCmd[0] == "NONE" {
		return nil
	}

	// wait for container to be healthy
	eventq := r.adapter.events(ctx)

	var healthErr error
	for {
		select {
		case event := <-eventq:
			if !r.matchevent(event) {
				continue
			}

			switch event.Action {
			case "die": // exit on terminal events
				ctnr, err := r.adapter.inspect(ctx)
				if err != nil {
					return errors.Wrap(err, "die event received")
				} else if ctnr.State.ExitCode != 0 {
					return &exitError{code: ctnr.State.ExitCode, cause: healthErr}
				}

				return nil
			case "destroy":
				// If we get here, something has gone wrong but we want to exit
				// and report anyways.
				return ErrContainerDestroyed
			case "health_status: unhealthy":
				// in this case, we stop the container and report unhealthy status
				if err := r.Shutdown(ctx); err != nil {
					return errors.Wrap(err, "unhealthy container shutdown failed")
				}
				// set health check error, and wait for container to fully exit ("die" event)
				healthErr = ErrContainerUnhealthy
			case "health_status: healthy":
				return nil
			}
		case <-ctx.Done():
			return ctx.Err()
		case <-r.closed:
			return r.err
		}
	}
}

// Wait on the container to exit.
func (r *controller) Wait(pctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	ctx, cancel := context.WithCancel(pctx)
	defer cancel()

	healthErr := make(chan error, 1)
	go func() {
		ectx, cancel := context.WithCancel(ctx) // cancel event context on first event
		defer cancel()
		if err := r.checkHealth(ectx); err == ErrContainerUnhealthy {
			healthErr <- ErrContainerUnhealthy
			if err := r.Shutdown(ectx); err != nil {
				log.G(ectx).WithError(err).Debug("shutdown failed on unhealthy")
			}
		}
	}()

	err := r.adapter.wait(ctx)
	if ctx.Err() != nil {
		return ctx.Err()
	}

	if err != nil {
		ee := &exitError{}
		if ec, ok := err.(exec.ExitCoder); ok {
			ee.code = ec.ExitCode()
		}
		select {
		case e := <-healthErr:
			ee.cause = e
		default:
			if err.Error() != "" {
				ee.cause = err
			}
		}
		return ee
	}

	return nil
}

// Shutdown the container cleanly.
func (r *controller) Shutdown(ctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	if r.cancelPull != nil {
		r.cancelPull()
	}

	if err := r.adapter.shutdown(ctx); err != nil {
		if isUnknownContainer(err) || isStoppedContainer(err) {
			return nil
		}

		return err
	}

	return nil
}

// Terminate the container, with force.
func (r *controller) Terminate(ctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	if r.cancelPull != nil {
		r.cancelPull()
	}

	if err := r.adapter.terminate(ctx); err != nil {
		if isUnknownContainer(err) {
			return nil
		}

		return err
	}

	return nil
}

// Remove the container and its resources.
func (r *controller) Remove(ctx context.Context) error {
	if err := r.checkClosed(); err != nil {
		return err
	}

	if r.cancelPull != nil {
		r.cancelPull()
	}

	// It may be necessary to shut down the task before removing it.
	if err := r.Shutdown(ctx); err != nil {
		if isUnknownContainer(err) {
			return nil
		}
		// This may fail if the task was already shut down.
		log.G(ctx).WithError(err).Debug("shutdown failed on removal")
	}

	// Try removing networks referenced in this task in case this
	// task is the last one referencing it
	if err := r.adapter.removeNetworks(ctx); err != nil {
		if isUnknownContainer(err) {
			return nil
		}
		return err
	}

	if err := r.adapter.remove(ctx); err != nil {
		if isUnknownContainer(err) {
			return nil
		}

		return err
	}
	return nil
}

// Close the runner and clean up any ephemeral resources.
func (r *controller) Close() error {
	select {
	case <-r.closed:
		return r.err
	default:
		if r.cancelPull != nil {
			r.cancelPull()
		}

		r.err = exec.ErrControllerClosed
		close(r.closed)
	}
	return nil
}

func (r *controller) matchevent(event events.Message) bool {
	if event.Type != events.ContainerEventType {
		return false
	}

	// TODO(stevvooe): Filter based on ID matching, in addition to name.

	// Make sure the events are for this container.
	if event.Actor.Attributes["name"] != r.adapter.container.name() {
		return false
	}

	return true
}

func (r *controller) checkClosed() error {
	select {
	case <-r.closed:
		return r.err
	default:
		return nil
	}
}

func parseContainerStatus(ctnr types.ContainerJSON) (*api.ContainerStatus, error) {
	status := &api.ContainerStatus{
		ContainerID: ctnr.ID,
		PID:         int32(ctnr.State.Pid),
		ExitCode:    int32(ctnr.State.ExitCode),
	}

	return status, nil
}

type exitError struct {
	code  int
	cause error
}

func (e *exitError) Error() string {
	if e.cause != nil {
		return fmt.Sprintf("task: non-zero exit (%v): %v", e.code, e.cause)
	}

	return fmt.Sprintf("task: non-zero exit (%v)", e.code)
}

func (e *exitError) ExitCode() int {
	return int(e.code)
}

func (e *exitError) Cause() error {
	return e.cause
}

// checkHealth blocks until unhealthy container is detected or ctx exits
func (r *controller) checkHealth(ctx context.Context) error {
	eventq := r.adapter.events(ctx)

	for {
		select {
		case <-ctx.Done():
			return nil
		case <-r.closed:
			return nil
		case event := <-eventq:
			if !r.matchevent(event) {
				continue
			}

			switch event.Action {
			case "health_status: unhealthy":
				return ErrContainerUnhealthy
			}
		}
	}
}
<|endoftext|>


<commit_before>package quibit

import (
	"fmt"
)

var peerList map[string]*Peer
var quit chan bool

// Initialize the Quibit Service
// Frames from the network will be sent to recvChan, and includes the sending peer
// Frames for the network should be sent to sendChan, and include the receiving peer
// New Peers for connecting should be sent to peerChan.
// A local server will be started on the port specified by "port"
// If an error is returned, than neither the server or mux has been started.
func Initialize(log chan string, recvChan, sendChan chan Frame, peerChan chan Peer, port uint16) error {
	var err error

	err = initServer(recvChan, peerChan, fmt.Sprintf(":%d", port))
	if err != nil {
		return err
	}

	peerList = make(map[string]*Peer)
	quit = make(chan bool)

	go mux(recvChan, sendChan, peerChan, quit, log)
	return nil
}

// Cleanup the Quibit Service
// End the mux and server routines, and Disconnect from all peers.
func Cleanup() {
	quit <- true
}

func KillPeer(p string) {
	peer, ok := peerList[p]
	if ok {
		peer.Disconnect()
		delete(peerList, p)
	}
}

func GetPeer(p string) *Peer {
	peer, ok := peerList[p]
	if ok {
		return peer
	} else {
		return nil
	}
}

func mux(recvChan, sendChan chan Frame, peerChan chan Peer, quit chan bool, log chan string) {
	var frame Frame
	var peer Peer
	var err error

	for {
		select {
		case frame = <-sendChan:
			// Received frame to send to peer(s)
			if frame.Peer == "" {
				// Send to all peers
				for key, p := range peerList {
					err = p.sendFrame(frame)
					if err != nil {
						if err.Error() != QuibitError(eHEADER).Error() {
							// Disconnect from Peer
							p.Disconnect()
							delete(peerList, key)
						}
						// Malformed header, break out of for loop
						fmt.Println("Malformed header in frame!")
						break
					}
				}
			} else {
				// Send to one peer
				p, ok := peerList[frame.Peer]
				if ok {
					err = p.sendFrame(frame)
					if err != nil {
						if err.Error() != QuibitError(eHEADER).Error() {
							// Disconnect from Peer
							p.Disconnect()
							delete(peerList, frame.Peer)
						}
						// Malformed header
						fmt.Println("Malformed header in frame!")
					}
				}
			}

		case peer = <-peerChan:
			// Received a new peer to connect to...
			err = peer.connect()
			if err == nil {
				go peer.receive(recvChan, log)
				peerList[peer.String()] = &peer

			} else {
				fmt.Println("Error adding peer: ", err)
			}
		case <-quit:
			for _, p := range peerList {
				p.Disconnect()
			}
			return
		} // End select
	} // End for
} // End mux()
<commit_msg>Even more debugging<commit_after>package quibit

import (
	"fmt"
)

var peerList map[string]*Peer
var quit chan bool

// Initialize the Quibit Service
// Frames from the network will be sent to recvChan, and includes the sending peer
// Frames for the network should be sent to sendChan, and include the receiving peer
// New Peers for connecting should be sent to peerChan.
// A local server will be started on the port specified by "port"
// If an error is returned, than neither the server or mux has been started.
func Initialize(log chan string, recvChan, sendChan chan Frame, peerChan chan Peer, port uint16) error {
	var err error

	err = initServer(recvChan, peerChan, fmt.Sprintf(":%d", port))
	if err != nil {
		return err
	}

	peerList = make(map[string]*Peer)
	quit = make(chan bool)

	go mux(recvChan, sendChan, peerChan, quit, log)
	return nil
}

// Cleanup the Quibit Service
// End the mux and server routines, and Disconnect from all peers.
func Cleanup() {
	quit <- true
}

func KillPeer(p string) {
	peer, ok := peerList[p]
	if ok {
		peer.Disconnect()
		delete(peerList, p)
	}
}

func GetPeer(p string) *Peer {
	peer, ok := peerList[p]
	if ok {
		return peer
	} else {
		return nil
	}
}

func mux(recvChan, sendChan chan Frame, peerChan chan Peer, quit chan bool, log chan string) {
	var frame Frame
	var peer Peer
	var err error

	for {
		select {
		case frame = <-sendChan:
			// Received frame to send to peer(s)
			if frame.Peer == "" {
				fmt.Println("Broadcasting message!")
				// Send to all peers
				for key, p := range peerList {
					fmt.Println("Sending Message to Peer: ", key)
					err = p.sendFrame(frame)
					if err != nil {
						if err.Error() != QuibitError(eHEADER).Error() {
							// Disconnect from Peer
							p.Disconnect()
							delete(peerList, key)
						}
						// Malformed header, break out of for loop
						fmt.Println("Malformed header in frame!")
						break
					}
				}
			} else {
				// Send to one peer
				p, ok := peerList[frame.Peer]
				if ok {
					err = p.sendFrame(frame)
					if err != nil {
						if err.Error() != QuibitError(eHEADER).Error() {
							// Disconnect from Peer
							p.Disconnect()
							delete(peerList, frame.Peer)
						}
						// Malformed header
						fmt.Println("Malformed header in frame!")
					}
				}
			}

		case peer = <-peerChan:
			// Received a new peer to connect to...
			err = peer.connect()
			if err == nil {
				go peer.receive(recvChan, log)
				peerList[peer.String()] = &peer

			} else {
				fmt.Println("Error adding peer: ", err)
			}
		case <-quit:
			for _, p := range peerList {
				p.Disconnect()
			}
			return
		} // End select
	} // End for
} // End mux()
<|endoftext|>