<commit_before>package trafcacc

import (
	"encoding/gob"
	"encoding/hex"
	"errors"
	"net"
	"sync"

	log "github.com/Sirupsen/logrus"
)

type cmd uint8

const (
	data cmd = iota
	close
	connect
)

type packet struct {
	Connid uint32
	Seqid  uint32
	Buf    []byte
	Cmd    cmd
}

// sendRaw only happens in backend to remote upstream addr
func (t *trafcacc) sendRaw(p packet) {
	log.WithFields(log.Fields{
		"connid": p.Connid,
		"seqid":  p.Seqid,
		"len":    len(p.Buf),
		"zdata":  shrinkString(hex.EncodeToString(p.Buf)),
	}).Debugln(t.roleString(), "sendRaw() to remote addr")

	// use cpool here , conn by connid
	conn := t.cpool.get(p.Connid)
	u := t.upool.next()

	if conn == nil {
		var err error
		// dial
		switch u.proto {
		case "tcp":
			conn, err = net.Dial("tcp", u.addr)
			if err != nil {
				log.WithFields(log.Fields{
					"connid": p.Connid,
					"error":  err,
				}).Debugln(t.roleString(), "Dial error")
				// reply error and close connection
				t.replyPkt(packet{Connid: p.Connid, Cmd: close})
				return
			}
			t.cpool.add(p.Connid, conn)
			go func() {
				log.Debugln(t.roleString(), "connected to remote begin to read")
				rname := "sendRawRead"
				routineAdd(rname)
				defer routineDel(rname)

				seqid := uint32(1)
				defer func() {
					log.WithFields(log.Fields{
						"connid": p.Connid,
						"seqid":  seqid,
					}).Debugln(t.roleString(), "remote connection closed")
					conn.Close()
					t.cpool.del(p.Connid)
					t.replyPkt(packet{Connid: p.Connid, Seqid: seqid, Cmd: close})
				}()
				b := make([]byte, buffersize)
				for {
					//TODO: how to break this loop when conn is closed
					n, err := conn.Read(b)
					if err != nil {
						break
					}
					err = t.replyPkt(packet{Connid: p.Connid, Seqid: seqid, Buf: b[0:n]})
					if err != nil {
						break
					}
					seqid++
				}
			}()
		}
	}

	t.pushToQueue(p, conn)
}

// send packed data to backend, only used on front-end
func (t *trafcacc) sendPkt(p packet) {
	log.WithFields(log.Fields{
		"connid": p.Connid,
		"seqid":  p.Seqid,
		"len":    len(p.Buf),
		"cmd":    p.Cmd,
		"zdata":  shrinkString(hex.EncodeToString(p.Buf)),
	}).Debugln(t.roleString(), "sendPkt()")

	u := t.upool.next()

	func() {
		u.mux.Lock()
		defer u.mux.Unlock()
		if u.conn == nil {
			// dial
			switch u.proto {
			case "tcp":
				conn, err := net.Dial("tcp", u.addr)
				if err != nil {
					// reply error and close connection to client
					log.WithFields(log.Fields{
						"connid": p.Connid,
					}).Debugln(t.roleString(), "dial in sendpkt() failed:", err)
					t.replyRaw(packet{Connid: p.Connid, Cmd: close})
					return
				}
				u.conn = conn
				u.encoder = gob.NewEncoder(conn)
				u.decoder = gob.NewDecoder(conn)
				// build packet reading slaves
				go func() {
					rname := "sendpktDecode"
					routineAdd(rname)
					defer routineDel(rname)

					defer u.close()

					u.mux.RLock()
					dec := u.decoder
					u.mux.RUnlock()
					for {
						p := packet{}
						err := dec.Decode(&p)
						if err != nil {
							log.WithFields(log.Fields{
								"connid": p.Connid,
								"error":  err,
							}).Debugln(t.roleString(), "read packet from backend failed")
							return
						}
						t.replyRaw(p)
					}
				}()
			case "udp":
				// TODO: udp
			}
		}
	}()

	err := u.encoder.Encode(&p)
	if err != nil {
		u.close()
		log.WithFields(log.Fields{
			"connid": p.Connid,
			"error":  err,
		}).Debugln(t.roleString(), "encode in sendpkt() failed")
		// reply error and close connection to client
		t.replyRaw(packet{Connid: p.Connid, Cmd: close})
		return
	}
}

// reply Raw only happens in front-end to client
func (t *trafcacc) replyRaw(p packet) {
	log.WithFields(log.Fields{
		"connid": p.Connid,
		"seqid":  p.Seqid,
		"cmd":    p.Cmd,
		"len":    len(p.Buf),
		"zdata":  shrinkString(hex.EncodeToString(p.Buf)),
	}).Debugln(t.roleString(), "replyRaw()")
	conn := t.cpool.get(p.Connid)

	if conn == nil {
		log.WithFields(log.Fields{
			"connid": p.Connid,
			"seqid":  p.Seqid,
			"len":    len(p.Buf),
			"cmd":    p.Cmd,
			"zdata":  shrinkString(hex.EncodeToString(p.Buf)),
		}).Debugln(t.roleString(), "reply to no-exist client conn")
		t.cpool.del(p.Connid)
		// TODO: how do I know next seqid
		// do we really need send close command here?
		// t.sendPkt(packet{Connid: p.Connid, Cmd: close})
	}
	t.pushToQueue(p, conn)
}

// reply Packet only happens in backend to frontend
func (t *trafcacc) replyPkt(p packet) error {
	log.WithFields(log.Fields{
		"connid": p.Connid,
		"seqid":  p.Seqid,
		"len":    len(p.Buf),
		"zdata":  shrinkString(hex.EncodeToString(p.Buf)),
	}).Debugln(t.roleString(), "replyPkt() to frontend")

	conn := t.epool.next()
	if conn != nil {
		return conn.Encode(p)
	}
	return errors.New("connection not exist anymore")
}

type pktQueue struct {
	lastseq uint32
	cond    *sync.Cond
	queue   map[uint32]*packet
}

func exists(queue map[uint32]*packet, seqid uint32) bool {
	_, ok := queue[seqid]
	return ok
}

// remove by connid from packet queue pool
func (t *trafcacc) removeQueue(connid uint32) {
	t.mux.Lock()
	defer t.mux.Unlock()
	pq := t.pq[connid]
	if pq != nil {
		delete(t.pq, connid)
	}
}

// ensure Write to client and remote in sequence
func (t *trafcacc) pushToQueue(p packet, conn net.Conn) {
	// TODO: just write if it's udp?
	t.mux.Lock()
	pq := t.pq[p.Connid]

	if pq == nil {
		pq = &pktQueue{
			cond:  sync.NewCond(&sync.Mutex{}),
			queue: make(map[uint32]*packet),
		}
		t.pq[p.Connid] = pq
		log.WithFields(log.Fields{
			"connid": p.Connid,
		}).Debugln(t.roleString(), "add new packet queue")

		go t.orderedWrite(pq, p.Connid, conn)
	}
	t.mux.Unlock()

	cond := pq.cond

	cond.L.Lock()
	pq.queue[p.Seqid] = &p
	log.WithFields(log.Fields{
		"connid": p.Connid,
		"seqid":  p.Seqid,
		"Cmd":    p.Cmd,
		"queue":  keysOfmap(pq.queue),
	}).Debugln(t.roleString(), "add new seq to queue")
	cond.L.Unlock()
	cond.Signal()
}

// ensure write order for this connid
func (t *trafcacc) orderedWrite(pq *pktQueue, connid uint32, conn net.Conn) {
	rname := "orderedWrite"
	routineAdd(rname)
	defer routineDel(rname)

	defer func() {
		log.Debugln(t.roleString(), "packet queue of", connid, " exit")
		if conn != nil {
			conn.Close()
		}
		t.removeQueue(connid)
	}()

	cond := pq.cond
	for {
		cond.L.Lock()
		for !exists(pq.queue, pq.lastseq+1) {
			log.WithFields(log.Fields{
				"connid": connid,
				"seqid":  pq.lastseq + 1,
				"queue":  keysOfmap(pq.queue),
			}).Debugln(t.roleString(), "no new seq in the order")
			cond.Wait()
		}
		lastseq := pq.lastseq + 1

		log.WithFields(log.Fields{
			"connid":  connid,
			"lastseq": lastseq,
			"queue":   keysOfmap(pq.queue),
		}).Debugln(t.roleString(), "new seq packet is ready")

		cond.L.Unlock()

		for i := lastseq; ; i++ {
			cond.L.Lock()
			pkt, ok := pq.queue[i]
			cond.L.Unlock()
			if !ok {
				break
			} else {
				if pkt.Buf != nil {
					log.WithFields(log.Fields{
						"connid": pkt.Connid,
						"seqid":  pkt.Seqid,
						"len":    len(pkt.Buf),
						"zdata":  shrinkString(hex.EncodeToString(pkt.Buf)),
					}).Debugln(t.roleString(), "orderedWrite()")
					if conn == nil {
						log.Debugln(t.roleString(), "orderedWrite() connection already lost")
						return
					}
					_, err := conn.Write(pkt.Buf)
					if err != nil {
						// remove when connection closed
						log.Debugln(t.roleString(), "orderedWrite() err", err)
						return
					}
				}
				if pkt.Cmd == close {
					log.Debugln(t.roleString(), "orderedWrite() received close command")
					return
				}
				cond.L.Lock()
				pq.lastseq = i
				delete(pq.queue, i)
				cond.L.Unlock()
			}
		}

	}
}
<commit_msg>try to fix read hung<commit_after>package trafcacc

import (
	"encoding/gob"
	"encoding/hex"
	"errors"
	"net"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
)

type cmd uint8

const (
	data cmd = iota
	close
	connect
)

type packet struct {
	Connid uint32
	Seqid  uint32
	Buf    []byte
	Cmd    cmd
}

// sendRaw only happens in backend to remote upstream addr
func (t *trafcacc) sendRaw(p packet) {
	log.WithFields(log.Fields{
		"connid": p.Connid,
		"seqid":  p.Seqid,
		"len":    len(p.Buf),
		"zdata":  shrinkString(hex.EncodeToString(p.Buf)),
	}).Debugln(t.roleString(), "sendRaw() to remote addr")

	// use cpool here , conn by connid
	conn := t.cpool.get(p.Connid)
	u := t.upool.next()

	if conn == nil {
		var err error
		// dial
		switch u.proto {
		case "tcp":
			conn, err = net.Dial("tcp", u.addr)
			if err != nil {
				log.WithFields(log.Fields{
					"connid": p.Connid,
					"error":  err,
				}).Debugln(t.roleString(), "Dial error")
				// reply error and close connection
				t.replyPkt(packet{Connid: p.Connid, Cmd: close})
				return
			}
			t.cpool.add(p.Connid, conn)
			go func() {
				log.Debugln(t.roleString(), "connected to remote begin to read")
				rname := "sendRawRead"
				routineAdd(rname)
				defer routineDel(rname)

				seqid := uint32(1)
				defer func() {
					log.WithFields(log.Fields{
						"connid": p.Connid,
						"seqid":  seqid,
					}).Debugln(t.roleString(), "remote connection closed")
					conn.Close()
					t.cpool.del(p.Connid)
					t.replyPkt(packet{Connid: p.Connid, Seqid: seqid, Cmd: close})
				}()
				b := make([]byte, buffersize)
				for {
					//TODO: how to break this loop when conn is closed
					conn.SetReadDeadline(time.Now().Add(time.Second))
					n, err := conn.Read(b)
					if err != nil {
						if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
							continue
						}
						log.WithFields(log.Fields{
							"connid": p.Connid,
							"error":  err,
						}).Debugln(t.roleString(), "remote connection read failed")
						break
					}
					conn.SetReadDeadline(time.Time{})
					err = t.replyPkt(packet{Connid: p.Connid, Seqid: seqid, Buf: b[0:n]})
					if err != nil {
						break
					}
					seqid++
				}
			}()
		}
	}

	t.pushToQueue(p, conn)
}

// send packed data to backend, only used on front-end
func (t *trafcacc) sendPkt(p packet) {
	log.WithFields(log.Fields{
		"connid": p.Connid,
		"seqid":  p.Seqid,
		"len":    len(p.Buf),
		"cmd":    p.Cmd,
		"zdata":  shrinkString(hex.EncodeToString(p.Buf)),
	}).Debugln(t.roleString(), "sendPkt()")

	u := t.upool.next()

	func() {
		u.mux.Lock()
		defer u.mux.Unlock()
		if u.conn == nil {
			// dial
			switch u.proto {
			case "tcp":
				conn, err := net.Dial("tcp", u.addr)
				if err != nil {
					// reply error and close connection to client
					log.WithFields(log.Fields{
						"connid": p.Connid,
					}).Debugln(t.roleString(), "dial in sendpkt() failed:", err)
					t.replyRaw(packet{Connid: p.Connid, Cmd: close})
					return
				}
				u.conn = conn
				u.encoder = gob.NewEncoder(conn)
				u.decoder = gob.NewDecoder(conn)
				// build packet reading slaves
				go func() {
					rname := "sendpktDecode"
					routineAdd(rname)
					defer routineDel(rname)

					defer u.close()

					u.mux.RLock()
					dec := u.decoder
					u.mux.RUnlock()
					for {
						p := packet{}
						err := dec.Decode(&p)
						if err != nil {
							log.WithFields(log.Fields{
								"connid": p.Connid,
								"error":  err,
							}).Debugln(t.roleString(), "read packet from backend failed")
							return
						}
						t.replyRaw(p)
					}
				}()
			case "udp":
				// TODO: udp
			}
		}
	}()

	err := u.encoder.Encode(&p)
	if err != nil {
		u.close()
		log.WithFields(log.Fields{
			"connid": p.Connid,
			"error":  err,
		}).Debugln(t.roleString(), "encode in sendpkt() failed")
		// reply error and close connection to client
		t.replyRaw(packet{Connid: p.Connid, Cmd: close})
		return
	}
}

// reply Raw only happens in front-end to client
func (t *trafcacc) replyRaw(p packet) {
	log.WithFields(log.Fields{
		"connid": p.Connid,
		"seqid":  p.Seqid,
		"cmd":    p.Cmd,
		"len":    len(p.Buf),
		"zdata":  shrinkString(hex.EncodeToString(p.Buf)),
	}).Debugln(t.roleString(), "replyRaw()")
	conn := t.cpool.get(p.Connid)

	if conn == nil {
		log.WithFields(log.Fields{
			"connid": p.Connid,
			"seqid":  p.Seqid,
			"len":    len(p.Buf),
			"cmd":    p.Cmd,
			"zdata":  shrinkString(hex.EncodeToString(p.Buf)),
		}).Debugln(t.roleString(), "reply to no-exist client conn")
		t.cpool.del(p.Connid)
		// TODO: how do I know next seqid
		// do we really need send close command here?
		// t.sendPkt(packet{Connid: p.Connid, Cmd: close})
	}
	t.pushToQueue(p, conn)
}

// reply Packet only happens in backend to frontend
func (t *trafcacc) replyPkt(p packet) error {
	log.WithFields(log.Fields{
		"connid": p.Connid,
		"seqid":  p.Seqid,
		"len":    len(p.Buf),
		"zdata":  shrinkString(hex.EncodeToString(p.Buf)),
	}).Debugln(t.roleString(), "replyPkt() to frontend")

	conn := t.epool.next()
	if conn != nil {
		return conn.Encode(p)
	}
	return errors.New("connection not exist anymore")
}

type pktQueue struct {
	lastseq uint32
	cond    *sync.Cond
	queue   map[uint32]*packet
}

func exists(queue map[uint32]*packet, seqid uint32) bool {
	_, ok := queue[seqid]
	return ok
}

// remove by connid from packet queue pool
func (t *trafcacc) removeQueue(connid uint32) {
	t.mux.Lock()
	defer t.mux.Unlock()
	pq := t.pq[connid]
	if pq != nil {
		delete(t.pq, connid)
	}
}

// ensure Write to client and remote in sequence
func (t *trafcacc) pushToQueue(p packet, conn net.Conn) {
	// TODO: just write if it's udp?
	t.mux.Lock()
	pq := t.pq[p.Connid]

	if pq == nil {
		pq = &pktQueue{
			cond:  sync.NewCond(&sync.Mutex{}),
			queue: make(map[uint32]*packet),
		}
		t.pq[p.Connid] = pq
		log.WithFields(log.Fields{
			"connid": p.Connid,
		}).Debugln(t.roleString(), "add new packet queue")

		go t.orderedWrite(pq, p.Connid, conn)
	}
	t.mux.Unlock()

	cond := pq.cond

	cond.L.Lock()
	pq.queue[p.Seqid] = &p
	log.WithFields(log.Fields{
		"connid": p.Connid,
		"seqid":  p.Seqid,
		"Cmd":    p.Cmd,
		"queue":  keysOfmap(pq.queue),
	}).Debugln(t.roleString(), "add new seq to queue")
	cond.L.Unlock()
	cond.Signal()
}

// ensure write order for this connid
func (t *trafcacc) orderedWrite(pq *pktQueue, connid uint32, conn net.Conn) {
	rname := "orderedWrite"
	routineAdd(rname)
	defer routineDel(rname)

	defer func() {
		log.WithFields(log.Fields{
			"connid": connid,
			"conn": conn,
		}).Debugln(t.roleString(), "packet queue exit")
		if conn != nil {
			conn.Close()
		}
		t.removeQueue(connid)
	}()

	cond := pq.cond
	for {
		cond.L.Lock()
		for !exists(pq.queue, pq.lastseq+1) {
			log.WithFields(log.Fields{
				"connid": connid,
				"seqid":  pq.lastseq + 1,
				"queue":  keysOfmap(pq.queue),
			}).Debugln(t.roleString(), "no new seq in the order")
			cond.Wait()
		}
		lastseq := pq.lastseq + 1

		log.WithFields(log.Fields{
			"connid":  connid,
			"lastseq": lastseq,
			"queue":   keysOfmap(pq.queue),
		}).Debugln(t.roleString(), "new seq packet is ready")

		cond.L.Unlock()

		for i := lastseq; ; i++ {
			cond.L.Lock()
			pkt, ok := pq.queue[i]
			cond.L.Unlock()
			if !ok {
				break
			} else {
				if pkt.Buf != nil {
					log.WithFields(log.Fields{
						"connid": pkt.Connid,
						"seqid":  pkt.Seqid,
						"len":    len(pkt.Buf),
						"zdata":  shrinkString(hex.EncodeToString(pkt.Buf)),
					}).Debugln(t.roleString(), "orderedWrite()")
					if conn == nil {
						log.Debugln(t.roleString(), "orderedWrite() connection already lost")
						return
					}
					_, err := conn.Write(pkt.Buf)
					if err != nil {
						// remove when connection closed
						log.Debugln(t.roleString(), "orderedWrite() err", err)
						return
					}
				}
				if pkt.Cmd == close {
					log.Debugln(t.roleString(), "orderedWrite() received close command")
					return
				}
				cond.L.Lock()
				pq.lastseq = i
				delete(pq.queue, i)
				cond.L.Unlock()
			}
		}

	}
}
<|endoftext|>


<commit_before>package libovsdb

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"testing"
)

func TestListDbs(t *testing.T) {
	if testing.Short() {
		t.Skip()
	}

	ovs, err := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if err != nil {
		panic(err)
	}
	reply, err := ovs.ListDbs()

	if err != nil {
		log.Fatal("ListDbs error:", err)
	}

	if reply[0] != "Open_vSwitch" {
		t.Error("Expected: 'Open_vSwitch', Got:", reply)
	}
	var b bytes.Buffer
	ovs.Schema[reply[0]].Print(&b)
	ovs.Disconnect()
}

func TestGetSchemas(t *testing.T) {
	if testing.Short() {
		t.Skip()
	}

	ovs, err := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if err != nil {
		panic(err)
	}

	var dbName string = "Open_vSwitch"
	reply, err := ovs.GetSchema(dbName)

	if err != nil {
		log.Fatal("GetSchemas error:", err)
		t.Error("Error Processing GetSchema for ", dbName, err)
	}

	if reply.Name != dbName {
		t.Error("Schema Name mismatch. Expected: ", dbName, "Got: ", reply.Name)
	}
	ovs.Disconnect()
}

var bridgeName string = "gopher-br"
var bridgeUuid string

func TestInsertTransact(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, err := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if err != nil {
		log.Fatal("Failed to Connect. error:", err)
		panic(err)
	}

	// NamedUuid is used to add multiple related Operations in a single Transact operation
	namedUuid := "gopher"

	// bridge row to insert
	bridge := make(map[string]interface{})
	bridge["name"] = bridgeName

	// simple insert operation
	insertOp := Operation{
		Op:       "insert",
		Table:    "Bridge",
		Row:      bridge,
		UUIDName: namedUuid,
	}

	// Inserting a Bridge row in Bridge table requires mutating the open_vswitch table.
	mutateUuid := []UUID{UUID{namedUuid}}
	mutateSet, _ := newOvsSet(mutateUuid)
	mutation := NewMutation("bridges", "insert", mutateSet)
	// hacked Condition till we get Monitor / Select working
	condition := NewCondition("_uuid", "!=", UUID{"2f77b348-9768-4866-b761-89d5177ecdab"})

	// simple mutate operation
	mutateOp := Operation{
		Op:        "mutate",
		Table:     "Open_vSwitch",
		Mutations: []interface{}{mutation},
		Where:     []interface{}{condition},
	}

	operations := []Operation{insertOp, mutateOp}
	reply, err := ovs.Transact("Open_vSwitch", operations...)

	if len(reply) < len(operations) {
		t.Error("Number of Replies should be atleast equal to number of Operations")
	}
	ok := true
	for i, o := range reply {
		if o.Error != "" && i < len(operations) {
			t.Error("Transaction Failed due to an error :", o.Error, " in ", operations[i])
			ok = false
		} else if o.Error != "" {
			t.Error("Transaction Failed due to an error :", o.Error)
			ok = false
		}
	}
	if ok {
		fmt.Println("Bridge Addition Successful : ", reply[0].UUID.uuid)
		bridgeUuid = reply[0].UUID.uuid
	}
	ovs.Disconnect()
}

func TestDBSchemaValidation(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, e := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if e != nil {
		log.Fatal("Failed to Connect. error:", e)
		panic(e)
	}

	bridge := make(map[string]interface{})
	bridge["name"] = "docker-ovs"

	operation := Operation{
		Op:    "insert",
		Table: "Bridge",
		Row:   bridge,
	}

	_, err := ovs.Transact("Invalid_DB", operation)
	if err == nil {
		t.Error("Invalid DB operation Validation failed")
	}

	ovs.Disconnect()
}

func TestTableSchemaValidation(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, e := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if e != nil {
		log.Fatal("Failed to Connect. error:", e)
		panic(e)
	}

	bridge := make(map[string]interface{})
	bridge["name"] = "docker-ovs"

	operation := Operation{
		Op:    "insert",
		Table: "InvalidTable",
		Row:   bridge,
	}
	_, err := ovs.Transact("Open_vSwitch", operation)

	if err == nil {
		t.Error("Invalid Table Name Validation failed")
	}

	ovs.Disconnect()
}

func TestColumnSchemaInRowValidation(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, e := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if e != nil {
		log.Fatal("Failed to Connect. error:", e)
		panic(e)
	}

	bridge := make(map[string]interface{})
	bridge["name"] = "docker-ovs"
	bridge["invalid_column"] = "invalid_column"

	operation := Operation{
		Op:    "insert",
		Table: "Bridge",
		Row:   bridge,
	}

	_, err := ovs.Transact("Open_vSwitch", operation)

	if err == nil {
		t.Error("Invalid Column Name Validation failed")
	}

	ovs.Disconnect()
}

func TestColumnSchemaInMultipleRowsValidation(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, e := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if e != nil {
		log.Fatal("Failed to Connect. error:", e)
		panic(e)
	}

	rows := make([]map[string]interface{}, 2)

	invalidBridge := make(map[string]interface{})
	invalidBridge["invalid_column"] = "invalid_column"

	bridge := make(map[string]interface{})
	bridge["name"] = "docker-ovs"

	rows[0] = invalidBridge
	rows[1] = bridge
	operation := Operation{
		Op:    "insert",
		Table: "Bridge",
		Rows:  rows,
	}
	_, err := ovs.Transact("Open_vSwitch", operation)

	if err == nil {
		t.Error("Invalid Column Name Validation failed")
	}

	ovs.Disconnect()
}

func TestColumnSchemaValidation(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, e := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if e != nil {
		log.Fatal("Failed to Connect. error:", e)
		panic(e)
	}

	operation := Operation{
		Op:      "select",
		Table:   "Bridge",
		Columns: []string{"name", "invalidColumn"},
	}
	_, err := ovs.Transact("Open_vSwitch", operation)

	if err == nil {
		t.Error("Invalid Column Name Validation failed")
	}

	ovs.Disconnect()
}
<commit_msg>Adding Delete Transaction tests and it also acts as a proper cleanup for the insert Bridge test<commit_after>package libovsdb

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"testing"
)

func TestListDbs(t *testing.T) {
	if testing.Short() {
		t.Skip()
	}

	ovs, err := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if err != nil {
		panic(err)
	}
	reply, err := ovs.ListDbs()

	if err != nil {
		log.Fatal("ListDbs error:", err)
	}

	if reply[0] != "Open_vSwitch" {
		t.Error("Expected: 'Open_vSwitch', Got:", reply)
	}
	var b bytes.Buffer
	ovs.Schema[reply[0]].Print(&b)
	ovs.Disconnect()
}

func TestGetSchemas(t *testing.T) {
	if testing.Short() {
		t.Skip()
	}

	ovs, err := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if err != nil {
		panic(err)
	}

	var dbName string = "Open_vSwitch"
	reply, err := ovs.GetSchema(dbName)

	if err != nil {
		log.Fatal("GetSchemas error:", err)
		t.Error("Error Processing GetSchema for ", dbName, err)
	}

	if reply.Name != dbName {
		t.Error("Schema Name mismatch. Expected: ", dbName, "Got: ", reply.Name)
	}
	ovs.Disconnect()
}

var bridgeName string = "gopher-br"
var bridgeUuid string

func TestInsertTransact(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, err := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if err != nil {
		log.Fatal("Failed to Connect. error:", err)
		panic(err)
	}

	// NamedUuid is used to add multiple related Operations in a single Transact operation
	namedUuid := "gopher"

	// bridge row to insert
	bridge := make(map[string]interface{})
	bridge["name"] = bridgeName

	// simple insert operation
	insertOp := Operation{
		Op:       "insert",
		Table:    "Bridge",
		Row:      bridge,
		UUIDName: namedUuid,
	}

	// Inserting a Bridge row in Bridge table requires mutating the open_vswitch table.
	mutateUuid := []UUID{UUID{namedUuid}}
	mutateSet, _ := newOvsSet(mutateUuid)
	mutation := NewMutation("bridges", "insert", mutateSet)
	// hacked Condition till we get Monitor / Select working
	condition := NewCondition("_uuid", "!=", UUID{"2f77b348-9768-4866-b761-89d5177ecdab"})

	// simple mutate operation
	mutateOp := Operation{
		Op:        "mutate",
		Table:     "Open_vSwitch",
		Mutations: []interface{}{mutation},
		Where:     []interface{}{condition},
	}

	operations := []Operation{insertOp, mutateOp}
	reply, err := ovs.Transact("Open_vSwitch", operations...)

	if len(reply) < len(operations) {
		t.Error("Number of Replies should be atleast equal to number of Operations")
	}
	ok := true
	for i, o := range reply {
		if o.Error != "" && i < len(operations) {
			t.Error("Transaction Failed due to an error :", o.Error, " in ", operations[i])
			ok = false
		} else if o.Error != "" {
			t.Error("Transaction Failed due to an error :", o.Error)
			ok = false
		}
	}
	if ok {
		fmt.Println("Bridge Addition Successful : ", reply[0].UUID.uuid)
		bridgeUuid = reply[0].UUID.uuid
	}
	ovs.Disconnect()
}

func TestDeleteTransact(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	if bridgeUuid == "" {
		t.Skip()
	}

	ovs, err := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if err != nil {
		log.Fatal("Failed to Connect. error:", err)
		panic(err)
	}

	// simple delete operation
	condition := NewCondition("name", "==", bridgeName)
	deleteOp := Operation{
		Op:    "delete",
		Table: "Bridge",
		Where: []interface{}{condition},
	}

	// Deleting a Bridge row in Bridge table requires mutating the open_vswitch table.
	mutateUuid := []UUID{UUID{bridgeUuid}}
	mutateSet, _ := newOvsSet(mutateUuid)
	mutation := NewMutation("bridges", "delete", mutateSet)
	// hacked Condition till we get Monitor / Select working
	condition = NewCondition("_uuid", "!=", UUID{"2f77b348-9768-4866-b761-89d5177ecdab"})

	// simple mutate operation
	mutateOp := Operation{
		Op:        "mutate",
		Table:     "Open_vSwitch",
		Mutations: []interface{}{mutation},
		Where:     []interface{}{condition},
	}

	operations := []Operation{deleteOp, mutateOp}
	reply, err := ovs.Transact("Open_vSwitch", operations...)

	if len(reply) < len(operations) {
		t.Error("Number of Replies should be atleast equal to number of Operations")
	}
	ok := true
	for i, o := range reply {
		if o.Error != "" && i < len(operations) {
			t.Error("Transaction Failed due to an error :", o.Error, " in ", operations[i])
			ok = false
		} else if o.Error != "" {
			t.Error("Transaction Failed due to an error :", o.Error)
			ok = false
		}
	}
	if ok {
		fmt.Println("Bridge Delete Successful", reply[0].Count)
	}
	ovs.Disconnect()
}

func TestDBSchemaValidation(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, e := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if e != nil {
		log.Fatal("Failed to Connect. error:", e)
		panic(e)
	}

	bridge := make(map[string]interface{})
	bridge["name"] = "docker-ovs"

	operation := Operation{
		Op:    "insert",
		Table: "Bridge",
		Row:   bridge,
	}

	_, err := ovs.Transact("Invalid_DB", operation)
	if err == nil {
		t.Error("Invalid DB operation Validation failed")
	}

	ovs.Disconnect()
}

func TestTableSchemaValidation(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, e := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if e != nil {
		log.Fatal("Failed to Connect. error:", e)
		panic(e)
	}

	bridge := make(map[string]interface{})
	bridge["name"] = "docker-ovs"

	operation := Operation{
		Op:    "insert",
		Table: "InvalidTable",
		Row:   bridge,
	}
	_, err := ovs.Transact("Open_vSwitch", operation)

	if err == nil {
		t.Error("Invalid Table Name Validation failed")
	}

	ovs.Disconnect()
}

func TestColumnSchemaInRowValidation(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, e := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if e != nil {
		log.Fatal("Failed to Connect. error:", e)
		panic(e)
	}

	bridge := make(map[string]interface{})
	bridge["name"] = "docker-ovs"
	bridge["invalid_column"] = "invalid_column"

	operation := Operation{
		Op:    "insert",
		Table: "Bridge",
		Row:   bridge,
	}

	_, err := ovs.Transact("Open_vSwitch", operation)

	if err == nil {
		t.Error("Invalid Column Name Validation failed")
	}

	ovs.Disconnect()
}

func TestColumnSchemaInMultipleRowsValidation(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, e := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if e != nil {
		log.Fatal("Failed to Connect. error:", e)
		panic(e)
	}

	rows := make([]map[string]interface{}, 2)

	invalidBridge := make(map[string]interface{})
	invalidBridge["invalid_column"] = "invalid_column"

	bridge := make(map[string]interface{})
	bridge["name"] = "docker-ovs"

	rows[0] = invalidBridge
	rows[1] = bridge
	operation := Operation{
		Op:    "insert",
		Table: "Bridge",
		Rows:  rows,
	}
	_, err := ovs.Transact("Open_vSwitch", operation)

	if err == nil {
		t.Error("Invalid Column Name Validation failed")
	}

	ovs.Disconnect()
}

func TestColumnSchemaValidation(t *testing.T) {

	if testing.Short() {
		t.Skip()
	}

	ovs, e := Connect(os.Getenv("DOCKER_IP"), int(6640))
	if e != nil {
		log.Fatal("Failed to Connect. error:", e)
		panic(e)
	}

	operation := Operation{
		Op:      "select",
		Table:   "Bridge",
		Columns: []string{"name", "invalidColumn"},
	}
	_, err := ovs.Transact("Open_vSwitch", operation)

	if err == nil {
		t.Error("Invalid Column Name Validation failed")
	}

	ovs.Disconnect()
}
<|endoftext|>


<commit_before>// this file contains a pre-processor to pull some stuff out of the markdown file before parsing it

package gnosis

import (
	"bufio"
	"bytes"
	"errors"
	"html/template"
	"os"
)

type PageMetadata struct {
	Keywords map[string]bool
	Tags     map[string]bool
	Loaded   bool
	Page     []byte
}

func (pdata *PageMetadata) LoadPage(pageName string) error {
	f, err := os.Open(pageName)
	reader := bufio.NewReader(f)
	upperLine, fullLine, err := reader.ReadLine()

	// inspect the first line you read
	if err != nil {
		return err
	} else if !fullLine {
		return errors.New("first line I read wasn't a full line")
	} else if pdata.lineIsTitle(upperLine) {
		return errors.New("first line looks an awful lot like the underside of the title o.O")
	}

	lowerLine, fullLine, err := reader.ReadLine()

	// inspect the lower line
	if err != nil {
		return err
	} else if !fullLine {
		return errors.New("second line I read wasn't a full line")
	} else if pdata.lineIsTitle(lowerLine) {

		// read the rest of the page
		var restOfPage []byte
		_, err = reader.Read(restOfPage)
		if err != nil {
			return err
		}
		// if the second line is a title, read the rest of the page in
		// you don't have any metadata to work with here, move on
		pdata.Page = bytes.Join([][]byte{upperLine, lowerLine, restOfPage}, []byte("\n"))

		// you've successfully loaded the page - so return nothing
		pdata.Loaded = true
		return nil
	}

	// if you're at this point, the first line is metadata
	// you gotta process it and work with the next line
	// so let's just read through the file until we hit the title
	for !pdata.lineIsTitle(lowerLine) {
		// process the line
		pdata.processMetadata(upperLine)
		// shift the lower line up
		upperLine = lowerLine
		// read in a new lower line
		lowerLine, fullLine, err := reader.ReadLine()
		if err != nil {
			return err
		} else if !fullLine {
			return errors.New("I filled my buffer with a line")
		}
	}

	// by this point, I should have read everything in - let's read the rest and just return it
	var restOfPage []byte
	_, err = reader.Read(restOfPage)
	pdata.Page = bytes.Join([][]byte{upperLine, lowerLine, restOfPage}, []byte("\n"))

	return err
}

// takes a single line of input and determines if it's a top level markdown header
func (pdata *PageMetadata) lineIsTitle(line []byte) bool {
	finalLength := len(line)
	i := 0

	// if the row doesn't start with tabs, spaces, or ='s
	if (line[i] != ' ' && line[i] != '=') && line[i] != '\t' {
		return false
	}

	// skip any spaces or tabs at the start
	for line[i] == ' ' || line[i] == '\t' {
		i++
	}

	// if the next item's not a =, bail out
	if line[i] != '=' {
		return false
	}

	// run through all of the ='s
	for line[i] == '=' {
		i++
	}

	if line[i] != ' ' && line[i] != '\t' && line[i] != '\n' {
		return false
	}

	//ditch all spaces after any ='s
	for line[i] == ' ' || line[i] == '\t' {
		i++
	}

	if finalLength == i+1 {
		return true
	} else {
		return false
	}
}

func (pdata *PageMetadata) checkMatch(input []byte, looking []byte, tracker map[string]bool) {
	// trim off any blank spaces at the start of the line
	value := bytes.Trim(input, " \t")

	// should be a substring match based on the start of the array
	if bytes.Equal(input[:len(looking)], looking) {
		// trim off the target from the []byte
		value = input[len(looking):]

		// trim spaces at the start and at the end
		value = bytes.Trim(value, " \t\n")

		if value[0] == ':' || value[0] == '=' {
			value = bytes.Trim(value, " \t\n=:")
		}

		// replace any spaces in the middle with -'s
		bytes.Replace(value, []byte(" "), []byte("-"), -1)

		// suppress any double dashes
		for i := 0; i < len(value)-1; i++ {
			if value[i] == '-' && value[i+1] == '-' {
				value = append(value[:i], value[i+1:]...)
			}
		}

		// now just add the value to the array that you're tracking
		tracker[string(value)] = true
	}
}

// returns all the tags within a list as an array of strings
func (pdata *PageMetadata) ListMeta() ([]string, []string) {
	topics := []string{}
	for oneTag, _ := range pdata.Tags {
		topics = append(topics[:], oneTag)
	}

	keywords := []string{}
	for oneKeyword, _ := range pdata.Keywords {
		keywords = append(keywords[:], oneKeyword)
	}

	return topics, keywords
}

// return the bytes to display the tags on the page
// takes the prefix for the tags
func (pdata *PageMetadata) PrintTags(tagPrefix string) template.HTML {
	response := []byte{}
	openingTag := []byte("
")
	closingTag := []byte("
")
	for oneTag, _ := range pdata.Tags {
		response = bytes.Join([][]byte{openingTag, []byte(tagPrefix), []byte(oneTag), closingTag}, []byte(""))
	}
	return template.HTML(response)
}

// returns the bytes to add the keywrods to the html output
func (pdata *PageMetadata) PrintKeywords() template.HTML {
	response := []byte("<meta name='keywords' content='")
	for oneKeyword, _ := range pdata.Keywords {
		response = bytes.Join([][]byte{response, []byte(oneKeyword)}, []byte(","))
	}
	// clean up the end of the string and add the ending tag
	response = bytes.TrimSuffix(response, []byte{','})
	response = append(response, []byte("'>")...)

	return template.HTML(response)
}

// runs through all restricted tags, and looks for a match
// if matched, returns true, otherwise false
func (pdata *PageMetadata) MatchedTag(checkTags []string) bool {
	for _, tag := range checkTags {
		if pdata.Tags[tag] == true {
			return true
		}
	}
	return false
}

func (pdata *PageMetadata) processMetadata(line []byte) error {
	pdata.checkMatch(line, "tag", pdata.Tags)
	pdata.checkMatch(line, "topic", pdata.Tags)
	pdata.checkMatch(line, "category", pdata.Tags)

	pdata.checkMatch(line, "keyword", pdata.Keywords)
	pdata.checkMatch(line, "keywords", pdata.Keywords)
	pdata.checkMatch(line, "meta", pdata.Keywords)
}
<commit_msg>changed Tags to Topics, and hit almost all syntax errors<commit_after>// this file contains a pre-processor to pull some stuff out of the markdown file before parsing it

package gnosis

import (
	"bufio"
	"bytes"
	"errors"
	"html/template"
	"os"
)

type PageMetadata struct {
	Keywords map[string]bool
	Topics   map[string]bool
	Loaded   bool
	Page     []byte
}

func (pdata *PageMetadata) LoadPage(pageName string) error {
	f, err := os.Open(pageName)
	reader := bufio.NewReader(f)
	upperLine, fullLine, err := reader.ReadLine()

	// inspect the first line you read
	if err != nil {
		return err
	} else if !fullLine {
		return errors.New("first line I read wasn't a full line")
	} else if pdata.lineIsTitle(upperLine) {
		return errors.New("first line looks an awful lot like the underside of the title o.O")
	}

	lowerLine, fullLine, err := reader.ReadLine()

	// inspect the lower line
	if err != nil {
		return err
	} else if !fullLine {
		return errors.New("second line I read wasn't a full line")
	} else if pdata.lineIsTitle(lowerLine) {

		// read the rest of the page
		var restOfPage []byte
		_, err = reader.Read(restOfPage)
		if err != nil {
			return err
		}
		// if the second line is a title, read the rest of the page in
		// you don't have any metadata to work with here, move on
		pdata.Page = bytes.Join([][]byte{upperLine, lowerLine, restOfPage}, []byte("\n"))

		// you've successfully loaded the page - so return nothing
		pdata.Loaded = true
		return nil
	}

	// if you're at this point, the first line is metadata
	// you gotta process it and work with the next line
	// so let's just read through the file until we hit the title
	for !pdata.lineIsTitle(lowerLine) {
		// process the line
		pdata.processMetadata(upperLine)
		// shift the lower line up
		upperLine = lowerLine
		// read in a new lower line
		lowerLine, fullLine, err := reader.ReadLine()
		if err != nil {
			return err
		} else if !fullLine {
			return errors.New("I filled my buffer with a line")
		}
	}

	// by this point, I should have read everything in - let's read the rest and just return it
	var restOfPage []byte
	_, err = reader.Read(restOfPage)
	pdata.Page = bytes.Join([][]byte{upperLine, lowerLine, restOfPage}, []byte("\n"))

	return err
}

// takes a single line of input and determines if it's a top level markdown header
func (pdata *PageMetadata) lineIsTitle(line []byte) bool {
	finalLength := len(line)
	i := 0

	// if the row doesn't start with tabs, spaces, or ='s
	if (line[i] != ' ' && line[i] != '=') && line[i] != '\t' {
		return false
	}

	// skip any spaces or tabs at the start
	for line[i] == ' ' || line[i] == '\t' {
		i++
	}

	// if the next item's not a =, bail out
	if line[i] != '=' {
		return false
	}

	// run through all of the ='s
	for line[i] == '=' {
		i++
	}

	if line[i] != ' ' && line[i] != '\t' && line[i] != '\n' {
		return false
	}

	//ditch all spaces after any ='s
	for line[i] == ' ' || line[i] == '\t' {
		i++
	}

	if finalLength == i+1 {
		return true
	} else {
		return false
	}
}

func (pdata *PageMetadata) checkMatch(input []byte, looking []byte, tracker map[string]bool) {
	// trim off any blank spaces at the start of the line
	value := bytes.Trim(input, " \t")

	// should be a substring match based on the start of the array
	if bytes.Equal(input[:len(looking)], looking) {
		// trim off the target from the []byte
		value = input[len(looking):]

		// trim spaces at the start and at the end
		value = bytes.Trim(value, " \t\n")

		if value[0] == ':' || value[0] == '=' {
			value = bytes.Trim(value, " \t\n=:")
		}

		// replace any spaces in the middle with -'s
		bytes.Replace(value, []byte(" "), []byte("-"), -1)

		// suppress any double dashes
		for i := 0; i < len(value)-1; i++ {
			if value[i] == '-' && value[i+1] == '-' {
				value = append(value[:i], value[i+1:]...)
			}
		}

		// now just add the value to the array that you're tracking
		tracker[string(value)] = true
	}
}

// returns all the tags within a list as an array of strings
func (pdata *PageMetadata) ListMeta() ([]string, []string) {
	topics := []string{}
	for oneTag, _ := range pdata.Topics {
		topics = append(topics[:], oneTag)
	}

	keywords := []string{}
	for oneKeyword, _ := range pdata.Keywords {
		keywords = append(keywords[:], oneKeyword)
	}

	return topics, keywords
}

// return the bytes to display the tags on the page
// takes the prefix for the tags
func (pdata *PageMetadata) PrintTopics(tagPrefix string) template.HTML {
	response := []byte{}
	openingTag := []byte("
")
	closingTag := []byte("
")
	for oneTag, _ := range pdata.Topics {
		response = bytes.Join([][]byte{openingTag, []byte(tagPrefix), []byte(oneTag), closingTag}, []byte(""))
	}
	return template.HTML(response)
}

// returns the bytes to add the keywrods to the html output
func (pdata *PageMetadata) PrintKeywords() template.HTML {
	response := []byte("<meta name='keywords' content='")
	for oneKeyword, _ := range pdata.Keywords {
		response = bytes.Join([][]byte{response, []byte(oneKeyword)}, []byte(","))
	}
	// clean up the end of the string and add the ending tag
	response = bytes.TrimSuffix(response, []byte{','})
	response = append(response, []byte("'>")...)

	return template.HTML(response)
}

// runs through all restricted tags, and looks for a match
// if matched, returns true, otherwise false
func (pdata *PageMetadata) MatchedTag(checkTags []string) bool {
	for _, tag := range checkTags {
		if pdata.Topics[tag] == true {
			return true
		}
	}
	return false
}

func (pdata *PageMetadata) processMetadata(line []byte) error {
	pdata.checkMatch(line, []byte("tag"), pdata.Topics)
	pdata.checkMatch(line, []byte("topic"), pdata.Topics)
	pdata.checkMatch(line, []byte("category"), pdata.Topics)

	pdata.checkMatch(line, []byte("keyword"), pdata.Keywords)
	pdata.checkMatch(line, []byte("keywords"), pdata.Keywords)
	pdata.checkMatch(line, []byte("meta"), pdata.Keywords)

	// need a better return value than error?
	return nil
}
<|endoftext|>


<commit_before>package agro

import (
	"fmt"
	"strings"

	"github.com/barakmich/agro/types"
)

type Path struct {
	Volume string
	Path   string
}

func (p Path) GetDepth() int {
	if p.Path == "/" {
		return 0
	}
	a := strings.TrimSuffix(p.Path, "/")
	return len(strings.Split(a, "/")) - 1
}

func (p Path) Key() string {
	return fmt.Sprintf("%s:%4x:%s", p.Volume, p.GetDepth(), p.Path)
}

type Metadata interface {
	Mkfs() error
	CreateVolume(volume string) error // TODO(barakmich): Volume and FS options
	GetVolumes() ([]string, error)

	CommitInodeIndex() (uint64, error)

	Mkdir(path Path, dir *types.Directory) error
	Getdir(path Path) (*types.Directory, []Path, error)
	// TODO(barakmich): Get ring, get other nodes, look up nodes for keys, etc.
	// TODO(barakmich): Extend with GC interaction, et al
}

type CreateMetadataFunc func(address string) Metadata

var metadata map[string]CreateMetadataFunc

func RegisterMetadataProvider(name string, newFunc CreateMetadataFunc) {
	if metadata == nil {
		metadata = make(map[string]CreateMetadataFunc)
	}
	metadata[name] = newFunc
}

func CreateMetadata(name, address string) Metadata {
	return metadata[name](address)
}
<commit_msg>Speed up GetDepth()<commit_after>package agro

import (
	"fmt"
	"strings"

	"github.com/barakmich/agro/types"
)

type Path struct {
	Volume string
	Path   string
}

func (p Path) GetDepth() int {
	if p.Path == "/" {
		return 0
	}
	return strings.Count(strings.TrimSuffix(p.Path, "/"), "/")
}

func (p Path) Key() string {
	return fmt.Sprintf("%s:%4x:%s", p.Volume, p.GetDepth(), p.Path)
}

type Metadata interface {
	Mkfs() error
	CreateVolume(volume string) error // TODO(barakmich): Volume and FS options
	GetVolumes() ([]string, error)

	CommitInodeIndex() (uint64, error)

	Mkdir(path Path, dir *types.Directory) error
	Getdir(path Path) (*types.Directory, []Path, error)
	// TODO(barakmich): Get ring, get other nodes, look up nodes for keys, etc.
	// TODO(barakmich): Extend with GC interaction, et al
}

type CreateMetadataFunc func(address string) Metadata

var metadata map[string]CreateMetadataFunc

func RegisterMetadataProvider(name string, newFunc CreateMetadataFunc) {
	if metadata == nil {
		metadata = make(map[string]CreateMetadataFunc)
	}
	metadata[name] = newFunc
}

func CreateMetadata(name, address string) Metadata {
	return metadata[name](address)
}
<|endoftext|>


<commit_before>package dynamodb

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
	"github.com/ohsu-comp-bio/funnel/proto/tes"
	"golang.org/x/net/context"
)

// GetTask gets a task, which describes a running task
func (db *DynamoDB) GetTask(ctx context.Context, req *tes.GetTaskRequest) (*tes.Task, error) {
	var task *tes.Task
	var response *dynamodb.GetItemOutput
	var err error

	switch req.View {
	case tes.TaskView_MINIMAL:
		response, err = db.getMinimalView(ctx, req.Id)
	case tes.TaskView_BASIC:
		response, err = db.getBasicView(ctx, req.Id)
	case tes.TaskView_FULL:
		response, err = db.getFullView(ctx, req.Id)
	}
	if err != nil {
		return nil, err
	}

	if response.Item == nil {
		return nil, tes.ErrNotFound
	}

	err = dynamodbattribute.UnmarshalMap(response.Item, &task)
	if err != nil {
		return nil, fmt.Errorf("failed to DynamoDB unmarshal Task, %v", err)
	}

	return task, nil
}

// ListTasks returns a list of taskIDs
func (db *DynamoDB) ListTasks(ctx context.Context, req *tes.ListTasksRequest) (*tes.ListTasksResponse, error) {

	var tasks []*tes.Task
	var query *dynamodb.QueryInput
	pageSize := int64(tes.GetPageSize(req.GetPageSize()))

	query = &dynamodb.QueryInput{
		TableName:              aws.String(db.taskTable),
		Limit:                  aws.Int64(pageSize),
		ScanIndexForward:       aws.Bool(false),
		ConsistentRead:         aws.Bool(true),
		KeyConditionExpression: aws.String(fmt.Sprintf("%s = :v1", db.partitionKey)),
		ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
			":v1": {
				S: aws.String(db.partitionValue),
			},
		},
	}

	if req.State != tes.Unknown {
		query.ExpressionAttributeNames = map[string]*string{
			"#state": aws.String("state"),
		}
		query.ExpressionAttributeValues[":stateFilter"] = &dynamodb.AttributeValue{
			N: aws.String(strconv.Itoa(int(req.State))),
		}
		query.FilterExpression = aws.String("#state = :stateFilter")
	}

	if req.Tags != nil {
		tmpl := "tags.%s = :%sFilter"
		filterParts := []string{}
		for k, v := range req.Tags {
			filterParts = append(filterParts, fmt.Sprintf(tmpl, k, k))
			query.ExpressionAttributeValues[fmt.Sprintf(":%sFilter", k)] = &dynamodb.AttributeValue{
				S: aws.String(v),
			}
		}
		query.FilterExpression = aws.String(strings.Join(filterParts, " AND "))
	}

	if req.View == tes.TaskView_MINIMAL {
		query.ExpressionAttributeNames = map[string]*string{
			"#state": aws.String("state"),
		}
		query.ProjectionExpression = aws.String("id, #state")
	}

	if req.PageToken != "" {
		query.ExclusiveStartKey = map[string]*dynamodb.AttributeValue{
			db.partitionKey: {
				S: aws.String(db.partitionValue),
			},
			"id": {
				S: aws.String(req.PageToken),
			},
		}
	}

	response, err := db.client.QueryWithContext(ctx, query)

	if err != nil {
		return nil, err
	}

	if req.View == tes.TaskView_FULL {
		for _, item := range response.Items {
			// TODO handle errors
			_ = db.getContent(ctx, item)
			_ = db.getExecutorOutput(ctx, item, "stdout", db.stdoutTable)
			_ = db.getExecutorOutput(ctx, item, "stderr", db.stderrTable)
		}
	}

	err = dynamodbattribute.UnmarshalListOfMaps(response.Items, &tasks)
	if err != nil {
		return nil, fmt.Errorf("failed to DynamoDB unmarshal Tasks, %v", err)
	}

	out := tes.ListTasksResponse{
		Tasks: tasks,
	}

	if response.LastEvaluatedKey != nil {
		out.NextPageToken = *response.LastEvaluatedKey["id"].S
	}

	return &out, nil
}
<commit_msg>api/tes: ListTasks tag filter fix for empty string as value of tag in dynamodb<commit_after>package dynamodb

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
	"github.com/ohsu-comp-bio/funnel/proto/tes"
	"golang.org/x/net/context"
)

// GetTask gets a task, which describes a running task
func (db *DynamoDB) GetTask(ctx context.Context, req *tes.GetTaskRequest) (*tes.Task, error) {
	var task *tes.Task
	var response *dynamodb.GetItemOutput
	var err error

	switch req.View {
	case tes.TaskView_MINIMAL:
		response, err = db.getMinimalView(ctx, req.Id)
	case tes.TaskView_BASIC:
		response, err = db.getBasicView(ctx, req.Id)
	case tes.TaskView_FULL:
		response, err = db.getFullView(ctx, req.Id)
	}
	if err != nil {
		return nil, err
	}

	if response.Item == nil {
		return nil, tes.ErrNotFound
	}

	err = dynamodbattribute.UnmarshalMap(response.Item, &task)
	if err != nil {
		return nil, fmt.Errorf("failed to DynamoDB unmarshal Task, %v", err)
	}

	return task, nil
}

// ListTasks returns a list of taskIDs
func (db *DynamoDB) ListTasks(ctx context.Context, req *tes.ListTasksRequest) (*tes.ListTasksResponse, error) {

	var tasks []*tes.Task
	var query *dynamodb.QueryInput
	pageSize := int64(tes.GetPageSize(req.GetPageSize()))

	query = &dynamodb.QueryInput{
		TableName:              aws.String(db.taskTable),
		Limit:                  aws.Int64(pageSize),
		ScanIndexForward:       aws.Bool(false),
		ConsistentRead:         aws.Bool(true),
		KeyConditionExpression: aws.String(fmt.Sprintf("%s = :v1", db.partitionKey)),
		ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
			":v1": {
				S: aws.String(db.partitionValue),
			},
		},
	}

	if req.State != tes.Unknown {
		query.ExpressionAttributeNames = map[string]*string{
			"#state": aws.String("state"),
		}
		query.ExpressionAttributeValues[":stateFilter"] = &dynamodb.AttributeValue{
			N: aws.String(strconv.Itoa(int(req.State))),
		}
		query.FilterExpression = aws.String("#state = :stateFilter")
	}

	if req.Tags != nil {
		filterParts := []string{}
		for k, v := range req.Tags {
			tmpl := "tags.%s = :%sFilter"
			filterParts = append(filterParts, fmt.Sprintf(tmpl, k, k))
			if v == "" {
				query.ExpressionAttributeValues[fmt.Sprintf(":%sFilter", k)] = &dynamodb.AttributeValue{
					NULL: aws.Bool(true),
				}
			} else {
				query.ExpressionAttributeValues[fmt.Sprintf(":%sFilter", k)] = &dynamodb.AttributeValue{
					S: aws.String(v),
				}
			}
		}
		query.FilterExpression = aws.String(strings.Join(filterParts, " AND "))
	}

	if req.View == tes.TaskView_MINIMAL {
		query.ExpressionAttributeNames = map[string]*string{
			"#state": aws.String("state"),
		}
		query.ProjectionExpression = aws.String("id, #state")
	}

	if req.PageToken != "" {
		query.ExclusiveStartKey = map[string]*dynamodb.AttributeValue{
			db.partitionKey: {
				S: aws.String(db.partitionValue),
			},
			"id": {
				S: aws.String(req.PageToken),
			},
		}
	}

	response, err := db.client.QueryWithContext(ctx, query)

	if err != nil {
		return nil, err
	}

	if req.View == tes.TaskView_FULL {
		for _, item := range response.Items {
			// TODO handle errors
			_ = db.getContent(ctx, item)
			_ = db.getExecutorOutput(ctx, item, "stdout", db.stdoutTable)
			_ = db.getExecutorOutput(ctx, item, "stderr", db.stderrTable)
		}
	}

	err = dynamodbattribute.UnmarshalListOfMaps(response.Items, &tasks)
	if err != nil {
		return nil, fmt.Errorf("failed to DynamoDB unmarshal Tasks, %v", err)
	}

	out := tes.ListTasksResponse{
		Tasks: tasks,
	}

	if response.LastEvaluatedKey != nil {
		out.NextPageToken = *response.LastEvaluatedKey["id"].S
	}

	return &out, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testing

import (
	"encoding/json"
	"fmt"
	"github.com/dotcloud/docker"
	"math/rand"
	"net"
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestNewServer(t *testing.T) {
	server, err := NewServer()
	if err != nil {
		t.Fatal(err)
	}
	defer server.listener.Close()
	conn, err := net.Dial("tcp", server.listener.Addr().String())
	if err != nil {
		t.Fatal(err)
	}
	conn.Close()
}

func TestServerStop(t *testing.T) {
	server, err := NewServer()
	if err != nil {
		t.Fatal(err)
	}
	server.Stop()
	_, err = net.Dial("tcp", server.listener.Addr().String())
	if err == nil {
		t.Error("Unexpected <nil> error when dialing to stopped server")
	}
}

func TestServerStopNoListener(t *testing.T) {
	server := DockerServer{}
	server.Stop()
}

func TestServerURL(t *testing.T) {
	server, err := NewServer()
	if err != nil {
		t.Fatal(err)
	}
	defer server.Stop()
	url := server.URL()
	if expected := "http://" + server.listener.Addr().String() + "/"; url != expected {
		t.Errorf("DockerServer.URL(): Want %q. Got %q.", expected, url)
	}
}

func TestServerURLNoListener(t *testing.T) {
	server := DockerServer{}
	url := server.URL()
	if url != "" {
		t.Errorf("DockerServer.URL(): Expected empty URL on handler mode, got %q.", url)
	}
}

func TestListContainers(t *testing.T) {
	server := DockerServer{}
	addContainers(&server, 2)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/v1.1/containers/json?all=1", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Errorf("ListContainers: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	expected := make([]docker.APIContainers, 2)
	for i, container := range server.containers {
		expected[i] = docker.APIContainers{
			ID:      container.ID,
			Image:   container.Image,
			Command: strings.Join(container.Config.Cmd, " "),
			Created: container.Created.Unix(),
			Status:  container.State.String(),
			Ports:   container.NetworkSettings.PortMappingHuman(),
		}
	}
	var got []docker.APIContainers
	err := json.NewDecoder(recorder.Body).Decode(&got)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("ListContainers. Want %#v. Got %#v.", expected, got)
	}
}

func TestCreateContainer(t *testing.T) {
	server := DockerServer{}
	server.imgIDs = map[string]string{"base": "a1234"}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	body := `{"Hostname":"", "User":"", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true,
"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"],
"Dns":null, "Image":"base", "Volumes":{}, "VolumesFrom":""}`
	request, _ := http.NewRequest("POST", "/v1.1/containers/create", strings.NewReader(body))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusCreated {
		t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code)
	}
	var returned docker.Container
	err := json.NewDecoder(recorder.Body).Decode(&returned)
	if err != nil {
		t.Fatal(err)
	}
	stored := server.containers[0]
	if returned.ID != stored.ID {
		t.Errorf("CreateContainer: ID mismatch. Stored: %q. Returned: %q.", stored.ID, returned.ID)
	}
	if stored.State.Running {
		t.Errorf("CreateContainer should not set container to running state.")
	}
}

func TestCreateContainerInvalidBody(t *testing.T) {
	server := DockerServer{}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("POST", "/v1.1/containers/create", strings.NewReader("whaaaaaat---"))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusBadRequest {
		t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
	}
}

func TestCreateContainerImageNotFound(t *testing.T) {
	server := DockerServer{}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	body := `{"Hostname":"", "User":"", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true,
"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"],
"Dns":null, "Image":"base", "Volumes":{}, "VolumesFrom":""}`
	request, _ := http.NewRequest("POST", "/v1.1/containers/create", strings.NewReader(body))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func TestCommitContainer(t *testing.T) {
	server := DockerServer{}
	addContainers(&server, 2)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("POST", "/v1.1/commit?container="+server.containers[0].ID, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Errorf("CommitContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	expected := fmt.Sprintf(`{"ID":"%s"}`, server.images[0].ID)
	if got := recorder.Body.String(); got != expected {
		t.Errorf("CommitContainer: wrong response body. Want %q. Got %q.", expected, got)
	}
}

func TestCommitContainerComplete(t *testing.T) {
	server := DockerServer{}
	server.imgIDs = make(map[string]string)
	addContainers(&server, 2)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	queryString := "container=" + server.containers[0].ID + "&repo=tsuru/python&m=saving&author=developers"
	queryString += `&run={"Cmd": ["cat", "/world"],"PortSpecs":["22"]}`
	request, _ := http.NewRequest("POST", "/v1.1/commit?"+queryString, nil)
	server.ServeHTTP(recorder, request)
	image := server.images[0]
	if image.Parent != server.containers[0].Image {
		t.Errorf("CommitContainer: wrong parent image. Want %q. Got %q.", server.containers[0].Image, image.Parent)
	}
	if image.Container != server.containers[0].ID {
		t.Errorf("CommitContainer: wrong container. Want %q. Got %q.", server.containers[0].ID, image.Container)
	}
	message := "saving"
	if image.Comment != message {
		t.Errorf("CommitContainer: wrong comment (commit message). Want %q. Got %q.", message, image.Comment)
	}
	author := "developers"
	if image.Author != author {
		t.Errorf("CommitContainer: wrong author. Want %q. Got %q.", author, image.Author)
	}
	if id := server.imgIDs["tsuru/python"]; id != image.ID {
		t.Errorf("CommitContainer: wrong ID saved for repository. Want %q. Got %q.", image.ID, id)
	}
	portSpecs := []string{"22"}
	if !reflect.DeepEqual(image.Config.PortSpecs, portSpecs) {
		t.Errorf("CommitContainer: wrong port spec in config. Want %#v. Got %#v.", portSpecs, image.Config.PortSpecs)
	}
	cmd := []string{"cat", "/world"}
	if !reflect.DeepEqual(image.Config.Cmd, cmd) {
		t.Errorf("CommitContainer: wrong cmd in config. Want %#v. Got %#v.", cmd, image.Config.Cmd)
	}
}

func TestCommitContainerInvalidRun(t *testing.T) {
	server := DockerServer{}
	addContainers(&server, 1)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("POST", "/v1.1/commit?container="+server.containers[0].ID+"&run=abc---", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusBadRequest {
		t.Errorf("CommitContainer. Wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
	}
}

func TestCommitContainerNotFound(t *testing.T) {
	server := DockerServer{}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("POST", "/v1.1/commit?container=abc123", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Errorf("CommitContainer. Wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func TestInspectContainer(t *testing.T) {
	server := DockerServer{}
	addContainers(&server, 2)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	path := fmt.Sprintf("/v1.1/containers/%s/json", server.containers[0].ID)
	request, _ := http.NewRequest("GET", path, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Errorf("InspectContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	expected := server.containers[0]
	var got docker.Container
	err := json.NewDecoder(recorder.Body).Decode(&got)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(got, *expected) {
		t.Errorf("InspectContainer: wrong value. Want %#v. Got %#v.", expected, got)
	}
}

func TestInspectContainerNotFound(t *testing.T) {
	server := DockerServer{}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/v1.1/containers/abc123/json", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Errorf("InspectContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func TestStartContainer(t *testing.T) {
	server := DockerServer{}
	addContainers(&server, 1)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	path := fmt.Sprintf("/v1.1/containers/%s/start", server.containers[0].ID)
	request, _ := http.NewRequest("POST", path, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if !server.containers[0].State.Running {
		t.Error("StartContainer: did not set the container to running state")
	}
}

func TestStartContainerNotFound(t *testing.T) {
	server := DockerServer{}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	path := "/v1.1/containers/abc123/start"
	request, _ := http.NewRequest("POST", path, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func addContainers(server *DockerServer, n int) {
	server.cMut.Lock()
	defer server.cMut.Unlock()
	for i := 0; i < n; i++ {
		date := time.Now().Add(time.Duration((rand.Int() % (i + 1))) * time.Hour)
		container := docker.Container{
			ID:      fmt.Sprintf("%x", rand.Int()%10000),
			Created: date,
			Path:    "ls",
			Args:    []string{"-la", ".."},
			Config: &docker.Config{
				Hostname:     fmt.Sprintf("docker-%d", i),
				AttachStdout: true,
				AttachStderr: true,
				Env:          []string{"ME=you", fmt.Sprintf("NUMBER=%d", i)},
				Cmd:          []string{"ls", "-la", ".."},
				Dns:          nil,
				Image:        "base",
			},
			State: docker.State{
				Running:   false,
				Pid:       400 + i,
				ExitCode:  0,
				StartedAt: date,
			},
			Image: "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
			NetworkSettings: &docker.NetworkSettings{
				IPAddress:   fmt.Sprintf("10.10.10.%d", i+2),
				IPPrefixLen: 24,
				Gateway:     "10.10.10.1",
				Bridge:      "docker0",
				PortMapping: map[string]string{"8888": fmt.Sprintf("%d", 49600+i)},
			},
			ResolvConfPath: "/etc/resolv.conf",
		}
		server.containers = append(server.containers, &container)
	}
}
<commit_msg>testing: improve error reporting<commit_after>// Copyright 2013 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testing

import (
	"encoding/json"
	"fmt"
	"github.com/dotcloud/docker"
	"math/rand"
	"net"
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestNewServer(t *testing.T) {
	server, err := NewServer()
	if err != nil {
		t.Fatal(err)
	}
	defer server.listener.Close()
	conn, err := net.Dial("tcp", server.listener.Addr().String())
	if err != nil {
		t.Fatal(err)
	}
	conn.Close()
}

func TestServerStop(t *testing.T) {
	server, err := NewServer()
	if err != nil {
		t.Fatal(err)
	}
	server.Stop()
	_, err = net.Dial("tcp", server.listener.Addr().String())
	if err == nil {
		t.Error("Unexpected <nil> error when dialing to stopped server")
	}
}

func TestServerStopNoListener(t *testing.T) {
	server := DockerServer{}
	server.Stop()
}

func TestServerURL(t *testing.T) {
	server, err := NewServer()
	if err != nil {
		t.Fatal(err)
	}
	defer server.Stop()
	url := server.URL()
	if expected := "http://" + server.listener.Addr().String() + "/"; url != expected {
		t.Errorf("DockerServer.URL(): Want %q. Got %q.", expected, url)
	}
}

func TestServerURLNoListener(t *testing.T) {
	server := DockerServer{}
	url := server.URL()
	if url != "" {
		t.Errorf("DockerServer.URL(): Expected empty URL on handler mode, got %q.", url)
	}
}

func TestListContainers(t *testing.T) {
	server := DockerServer{}
	addContainers(&server, 2)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/v1.1/containers/json?all=1", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Errorf("ListContainers: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	expected := make([]docker.APIContainers, 2)
	for i, container := range server.containers {
		expected[i] = docker.APIContainers{
			ID:      container.ID,
			Image:   container.Image,
			Command: strings.Join(container.Config.Cmd, " "),
			Created: container.Created.Unix(),
			Status:  container.State.String(),
			Ports:   container.NetworkSettings.PortMappingHuman(),
		}
	}
	var got []docker.APIContainers
	err := json.NewDecoder(recorder.Body).Decode(&got)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("ListContainers. Want %#v. Got %#v.", expected, got)
	}
}

func TestCreateContainer(t *testing.T) {
	server := DockerServer{}
	server.imgIDs = map[string]string{"base": "a1234"}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	body := `{"Hostname":"", "User":"", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true,
"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"],
"Dns":null, "Image":"base", "Volumes":{}, "VolumesFrom":""}`
	request, _ := http.NewRequest("POST", "/v1.1/containers/create", strings.NewReader(body))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusCreated {
		t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code)
	}
	var returned docker.Container
	err := json.NewDecoder(recorder.Body).Decode(&returned)
	if err != nil {
		t.Fatal(err)
	}
	stored := server.containers[0]
	if returned.ID != stored.ID {
		t.Errorf("CreateContainer: ID mismatch. Stored: %q. Returned: %q.", stored.ID, returned.ID)
	}
	if stored.State.Running {
		t.Errorf("CreateContainer should not set container to running state.")
	}
}

func TestCreateContainerInvalidBody(t *testing.T) {
	server := DockerServer{}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("POST", "/v1.1/containers/create", strings.NewReader("whaaaaaat---"))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusBadRequest {
		t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
	}
}

func TestCreateContainerImageNotFound(t *testing.T) {
	server := DockerServer{}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	body := `{"Hostname":"", "User":"", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true,
"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"],
"Dns":null, "Image":"base", "Volumes":{}, "VolumesFrom":""}`
	request, _ := http.NewRequest("POST", "/v1.1/containers/create", strings.NewReader(body))
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func TestCommitContainer(t *testing.T) {
	server := DockerServer{}
	addContainers(&server, 2)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("POST", "/v1.1/commit?container="+server.containers[0].ID, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Errorf("CommitContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	expected := fmt.Sprintf(`{"ID":"%s"}`, server.images[0].ID)
	if got := recorder.Body.String(); got != expected {
		t.Errorf("CommitContainer: wrong response body. Want %q. Got %q.", expected, got)
	}
}

func TestCommitContainerComplete(t *testing.T) {
	server := DockerServer{}
	server.imgIDs = make(map[string]string)
	addContainers(&server, 2)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	queryString := "container=" + server.containers[0].ID + "&repo=tsuru/python&m=saving&author=developers"
	queryString += `&run={"Cmd": ["cat", "/world"],"PortSpecs":["22"]}`
	request, _ := http.NewRequest("POST", "/v1.1/commit?"+queryString, nil)
	server.ServeHTTP(recorder, request)
	image := server.images[0]
	if image.Parent != server.containers[0].Image {
		t.Errorf("CommitContainer: wrong parent image. Want %q. Got %q.", server.containers[0].Image, image.Parent)
	}
	if image.Container != server.containers[0].ID {
		t.Errorf("CommitContainer: wrong container. Want %q. Got %q.", server.containers[0].ID, image.Container)
	}
	message := "saving"
	if image.Comment != message {
		t.Errorf("CommitContainer: wrong comment (commit message). Want %q. Got %q.", message, image.Comment)
	}
	author := "developers"
	if image.Author != author {
		t.Errorf("CommitContainer: wrong author. Want %q. Got %q.", author, image.Author)
	}
	if id := server.imgIDs["tsuru/python"]; id != image.ID {
		t.Errorf("CommitContainer: wrong ID saved for repository. Want %q. Got %q.", image.ID, id)
	}
	portSpecs := []string{"22"}
	if !reflect.DeepEqual(image.Config.PortSpecs, portSpecs) {
		t.Errorf("CommitContainer: wrong port spec in config. Want %#v. Got %#v.", portSpecs, image.Config.PortSpecs)
	}
	cmd := []string{"cat", "/world"}
	if !reflect.DeepEqual(image.Config.Cmd, cmd) {
		t.Errorf("CommitContainer: wrong cmd in config. Want %#v. Got %#v.", cmd, image.Config.Cmd)
	}
}

func TestCommitContainerInvalidRun(t *testing.T) {
	server := DockerServer{}
	addContainers(&server, 1)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("POST", "/v1.1/commit?container="+server.containers[0].ID+"&run=abc---", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusBadRequest {
		t.Errorf("CommitContainer. Wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
	}
}

func TestCommitContainerNotFound(t *testing.T) {
	server := DockerServer{}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("POST", "/v1.1/commit?container=abc123", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Errorf("CommitContainer. Wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func TestInspectContainer(t *testing.T) {
	server := DockerServer{}
	addContainers(&server, 2)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	path := fmt.Sprintf("/v1.1/containers/%s/json", server.containers[0].ID)
	request, _ := http.NewRequest("GET", path, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Errorf("InspectContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	expected := server.containers[0]
	var got docker.Container
	err := json.NewDecoder(recorder.Body).Decode(&got)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(got, *expected) {
		t.Errorf("InspectContainer: wrong value. Want %#v. Got %#v.", *expected, got)
	}
}

func TestInspectContainerNotFound(t *testing.T) {
	server := DockerServer{}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/v1.1/containers/abc123/json", nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Errorf("InspectContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func TestStartContainer(t *testing.T) {
	server := DockerServer{}
	addContainers(&server, 1)
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	path := fmt.Sprintf("/v1.1/containers/%s/start", server.containers[0].ID)
	request, _ := http.NewRequest("POST", path, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK {
		t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code)
	}
	if !server.containers[0].State.Running {
		t.Error("StartContainer: did not set the container to running state")
	}
}

func TestStartContainerNotFound(t *testing.T) {
	server := DockerServer{}
	server.buildMuxer()
	recorder := httptest.NewRecorder()
	path := "/v1.1/containers/abc123/start"
	request, _ := http.NewRequest("POST", path, nil)
	server.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNotFound {
		t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
	}
}

func addContainers(server *DockerServer, n int) {
	server.cMut.Lock()
	defer server.cMut.Unlock()
	for i := 0; i < n; i++ {
		date := time.Now().Add(time.Duration((rand.Int() % (i + 1))) * time.Hour)
		container := docker.Container{
			ID:      fmt.Sprintf("%x", rand.Int()%10000),
			Created: date,
			Path:    "ls",
			Args:    []string{"-la", ".."},
			Config: &docker.Config{
				Hostname:     fmt.Sprintf("docker-%d", i),
				AttachStdout: true,
				AttachStderr: true,
				Env:          []string{"ME=you", fmt.Sprintf("NUMBER=%d", i)},
				Cmd:          []string{"ls", "-la", ".."},
				Dns:          nil,
				Image:        "base",
			},
			State: docker.State{
				Running:   false,
				Pid:       400 + i,
				ExitCode:  0,
				StartedAt: date,
			},
			Image: "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
			NetworkSettings: &docker.NetworkSettings{
				IPAddress:   fmt.Sprintf("10.10.10.%d", i+2),
				IPPrefixLen: 24,
				Gateway:     "10.10.10.1",
				Bridge:      "docker0",
				PortMapping: map[string]string{"8888": fmt.Sprintf("%d", 49600+i)},
			},
			ResolvConfPath: "/etc/resolv.conf",
		}
		server.containers = append(server.containers, &container)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"sync"
)

type MemoryAdapter struct {
	clientsMu *sync.RWMutex
	clients   map[string]ClientWriter
	room      string
}

func NewMemoryAdapter(room string) *MemoryAdapter {
	var clientsMu sync.RWMutex
	return &MemoryAdapter{
		clientsMu: &clientsMu,
		clients:   map[string]ClientWriter{},
		room:      room,
	}
}

// Add a client to the room
func (m *MemoryAdapter) Add(client ClientWriter) (err error) {
	m.clientsMu.Lock()
	clientID := client.ID()
	m.clients[clientID] = client
	err = m.broadcast(NewMessageRoomJoin(m.room, clientID, client.Metadata()))
	m.clientsMu.Unlock()
	return
}

func (m *MemoryAdapter) Close() error {
	return nil
}

// Remove a client from the room
func (m *MemoryAdapter) Remove(clientID string) (err error) {
	m.clientsMu.Lock()
	delete(m.clients, clientID)
	err = m.broadcast(NewMessageRoomLeave(m.room, clientID))
	m.clientsMu.Unlock()
	return
}

func (m *MemoryAdapter) Metadata(clientID string) (metadata string, ok bool) {
	client, ok := m.clients[clientID]
	if ok {
		metadata = client.Metadata()
	}
	return
}

func (m *MemoryAdapter) SetMetadata(clientID string, metadata string) (ok bool) {
	client, ok := m.clients[clientID]
	if ok {
		client.SetMetadata(metadata)
	}
	return ok
}

// Returns clients with metadata
func (m *MemoryAdapter) Clients() (clientIDs map[string]string, err error) {
	m.clientsMu.RLock()
	clientIDs = map[string]string{}
	for clientID, client := range m.clients {
		clientIDs[clientID] = client.Metadata()
	}
	m.clientsMu.RUnlock()
	return
}

func (m *MemoryAdapter) Size() (value int, err error) {
	m.clientsMu.RLock()
	value = len(m.clients)
	m.clientsMu.RUnlock()
	return
}

// Send a message to all sockets
func (m *MemoryAdapter) Broadcast(msg Message) error {
	m.clientsMu.RLock()
	err := m.broadcast(msg)
	m.clientsMu.RUnlock()
	return err
}

func (m *MemoryAdapter) broadcast(msg Message) (err error) {
	for clientID := range m.clients {
		if emitErr := m.emit(clientID, msg); emitErr != nil && err == nil {
			err = emitErr
		}
	}
	return
}

// Sends a message to specific socket.
func (m *MemoryAdapter) Emit(clientID string, msg Message) error {
	m.clientsMu.RLock()
	m.emit(clientID, msg)
	m.clientsMu.RUnlock()
	return nil
}

func (m *MemoryAdapter) emit(clientID string, msg Message) error {
	client, ok := m.clients[clientID]
	if !ok {
		return fmt.Errorf("Client not found, clientID: %s", clientID)
	}
	err := client.Write(msg)
	if err != nil {
		return fmt.Errorf("MemoryAdapter.emit error: %w", err)
	}
	return nil
}
<commit_msg>Add lock to metadata in memoryadapter.go<commit_after>package server

import (
	"fmt"
	"sync"
)

type MemoryAdapter struct {
	clientsMu *sync.RWMutex
	clients   map[string]ClientWriter
	room      string
}

func NewMemoryAdapter(room string) *MemoryAdapter {
	var clientsMu sync.RWMutex
	return &MemoryAdapter{
		clientsMu: &clientsMu,
		clients:   map[string]ClientWriter{},
		room:      room,
	}
}

// Add a client to the room
func (m *MemoryAdapter) Add(client ClientWriter) (err error) {
	m.clientsMu.Lock()
	clientID := client.ID()
	m.clients[clientID] = client
	err = m.broadcast(NewMessageRoomJoin(m.room, clientID, client.Metadata()))
	m.clientsMu.Unlock()
	return
}

func (m *MemoryAdapter) Close() error {
	return nil
}

// Remove a client from the room
func (m *MemoryAdapter) Remove(clientID string) (err error) {
	m.clientsMu.Lock()
	delete(m.clients, clientID)
	err = m.broadcast(NewMessageRoomLeave(m.room, clientID))
	m.clientsMu.Unlock()
	return
}

func (m *MemoryAdapter) Metadata(clientID string) (metadata string, ok bool) {
	m.clientsMu.RLock()
	defer m.clientsMu.RUnlock()
	client, ok := m.clients[clientID]
	if ok {
		metadata = client.Metadata()
	}
	return
}

func (m *MemoryAdapter) SetMetadata(clientID string, metadata string) (ok bool) {
	m.clientsMu.Lock()
	defer m.clientsMu.Unlock()
	client, ok := m.clients[clientID]
	if ok {
		client.SetMetadata(metadata)
	}
	return ok
}

// Returns clients with metadata
func (m *MemoryAdapter) Clients() (clientIDs map[string]string, err error) {
	m.clientsMu.RLock()
	clientIDs = map[string]string{}
	for clientID, client := range m.clients {
		clientIDs[clientID] = client.Metadata()
	}
	m.clientsMu.RUnlock()
	return
}

func (m *MemoryAdapter) Size() (value int, err error) {
	m.clientsMu.RLock()
	value = len(m.clients)
	m.clientsMu.RUnlock()
	return
}

// Send a message to all sockets
func (m *MemoryAdapter) Broadcast(msg Message) error {
	m.clientsMu.RLock()
	err := m.broadcast(msg)
	m.clientsMu.RUnlock()
	return err
}

func (m *MemoryAdapter) broadcast(msg Message) (err error) {
	for clientID := range m.clients {
		if emitErr := m.emit(clientID, msg); emitErr != nil && err == nil {
			err = emitErr
		}
	}
	return
}

// Sends a message to specific socket.
func (m *MemoryAdapter) Emit(clientID string, msg Message) error {
	m.clientsMu.RLock()
	err := m.emit(clientID, msg)
	m.clientsMu.RUnlock()
	return err
}

func (m *MemoryAdapter) emit(clientID string, msg Message) error {
	client, ok := m.clients[clientID]
	if !ok {
		return fmt.Errorf("Client not found, clientID: %s", clientID)
	}
	err := client.Write(msg)
	if err != nil {
		return fmt.Errorf("MemoryAdapter.emit error: %w", err)
	}
	return nil
}
<|endoftext|>


<commit_before>// The plugin package provides the functionality to both expose a Packer
// plugin binary and to connect to an existing Packer plugin binary.
//
// Packer supports plugins in the form of self-contained external static
// Go binaries. These binaries behave in a certain way (enforced by this
// package) and are connected to in a certain way (also enforced by this
// package).
package plugin

import (
	"errors"
	"fmt"
	"github.com/mitchellh/packer/packer"
	packrpc "github.com/mitchellh/packer/packer/rpc"
	"log"
	"net"
	"net/rpc"
	"os"
	"os/signal"
	"runtime"
	"strconv"
)

const MagicCookieKey = "PACKER_PLUGIN_MAGIC_COOKIE"
const MagicCookieValue = "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2"

// This serves a single RPC connection on the given RPC server on
// a random port.
func serve(server *rpc.Server) (err error) {
	if os.Getenv(MagicCookieKey) != MagicCookieValue {
		return errors.New("Please do not execute plugins directly. Packer will execute these for you.")
	}

	// If there is no explicit number of Go threads to use, then set it
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	minPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MIN_PORT"), 10, 32)
	if err != nil {
		return
	}

	maxPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MAX_PORT"), 10, 32)
	if err != nil {
		return
	}

	log.Printf("Plugin minimum port: %d\n", minPort)
	log.Printf("Plugin maximum port: %d\n", maxPort)

	// Set the RPC port range
	packrpc.PortRange(int(minPort), int(maxPort))

	var address string
	var listener net.Listener
	for port := minPort; port <= maxPort; port++ {
		address = fmt.Sprintf("127.0.0.1:%d", port)
		listener, err = net.Listen("tcp", address)
		if err != nil {
			err = nil
			continue
		}

		break
	}

	defer listener.Close()

	// Output the address to stdout
	log.Printf("Plugin address: %s\n", address)
	fmt.Println(address)
	os.Stdout.Sync()

	// Accept a connection
	log.Println("Waiting for connection...")
	conn, err := listener.Accept()
	if err != nil {
		log.Printf("Error accepting connection: %s\n", err.Error())
		return
	}

	// Serve a single connection
	log.Println("Serving a plugin connection...")
	server.ServeConn(conn)
	return
}

// Registers a signal handler to "swallow" interrupts so that the
// plugin isn't killed. The main host Packer process is responsible
// for killing the plugins when interrupted.
func swallowInterrupts() {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)

	go func() {
		<-ch
		log.Println("Received interrupt signal. Ignoring.")
	}()
}

// Serves a builder from a plugin.
func ServeBuilder(builder packer.Builder) {
	log.Println("Preparing to serve a builder plugin...")

	server := rpc.NewServer()
	packrpc.RegisterBuilder(server, builder)

	swallowInterrupts()
	if err := serve(server); err != nil {
		log.Printf("ERROR: %s", err)
		os.Exit(1)
	}
}

// Serves a command from a plugin.
func ServeCommand(command packer.Command) {
	log.Println("Preparing to serve a command plugin...")

	server := rpc.NewServer()
	packrpc.RegisterCommand(server, command)

	swallowInterrupts()
	if err := serve(server); err != nil {
		log.Printf("ERROR: %s", err)
		os.Exit(1)
	}
}

// Serves a hook from a plugin.
func ServeHook(hook packer.Hook) {
	log.Println("Preparing to serve a hook plugin...")

	server := rpc.NewServer()
	packrpc.RegisterHook(server, hook)

	swallowInterrupts()
	if err := serve(server); err != nil {
		log.Printf("ERROR: %s", err)
		os.Exit(1)
	}
}

// Serves a post-processor from a plugin.
func ServePostProcessor(p packer.PostProcessor) {
	log.Println("Preparing to serve a post-processor plugin...")

	server := rpc.NewServer()
	packrpc.RegisterPostProcessor(server, p)

	swallowInterrupts()
	if err := serve(server); err != nil {
		log.Printf("ERROR: %s", err)
		os.Exit(1)
	}
}

// Serves a provisioner from a plugin.
func ServeProvisioner(p packer.Provisioner) {
	log.Println("Preparing to serve a provisioner plugin...")

	server := rpc.NewServer()
	packrpc.RegisterProvisioner(server, p)

	swallowInterrupts()
	if err := serve(server); err != nil {
		log.Printf("ERROR: %s", err)
		os.Exit(1)
	}
}
<commit_msg>packer/plugin: swallowInterrupts in plugin should swallow all<commit_after>// The plugin package provides the functionality to both expose a Packer
// plugin binary and to connect to an existing Packer plugin binary.
//
// Packer supports plugins in the form of self-contained external static
// Go binaries. These binaries behave in a certain way (enforced by this
// package) and are connected to in a certain way (also enforced by this
// package).
package plugin

import (
	"errors"
	"fmt"
	"github.com/mitchellh/packer/packer"
	packrpc "github.com/mitchellh/packer/packer/rpc"
	"log"
	"net"
	"net/rpc"
	"os"
	"os/signal"
	"runtime"
	"strconv"
)

const MagicCookieKey = "PACKER_PLUGIN_MAGIC_COOKIE"
const MagicCookieValue = "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2"

// This serves a single RPC connection on the given RPC server on
// a random port.
func serve(server *rpc.Server) (err error) {
	if os.Getenv(MagicCookieKey) != MagicCookieValue {
		return errors.New("Please do not execute plugins directly. Packer will execute these for you.")
	}

	// If there is no explicit number of Go threads to use, then set it
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	minPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MIN_PORT"), 10, 32)
	if err != nil {
		return
	}

	maxPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MAX_PORT"), 10, 32)
	if err != nil {
		return
	}

	log.Printf("Plugin minimum port: %d\n", minPort)
	log.Printf("Plugin maximum port: %d\n", maxPort)

	// Set the RPC port range
	packrpc.PortRange(int(minPort), int(maxPort))

	var address string
	var listener net.Listener
	for port := minPort; port <= maxPort; port++ {
		address = fmt.Sprintf("127.0.0.1:%d", port)
		listener, err = net.Listen("tcp", address)
		if err != nil {
			err = nil
			continue
		}

		break
	}

	defer listener.Close()

	// Output the address to stdout
	log.Printf("Plugin address: %s\n", address)
	fmt.Println(address)
	os.Stdout.Sync()

	// Accept a connection
	log.Println("Waiting for connection...")
	conn, err := listener.Accept()
	if err != nil {
		log.Printf("Error accepting connection: %s\n", err.Error())
		return
	}

	// Serve a single connection
	log.Println("Serving a plugin connection...")
	server.ServeConn(conn)
	return
}

// Registers a signal handler to "swallow" interrupts so that the
// plugin isn't killed. The main host Packer process is responsible
// for killing the plugins when interrupted.
func swallowInterrupts() {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)

	go func() {
		for {
			<-ch
			log.Println("Received interrupt signal. Ignoring.")
		}
	}()
}

// Serves a builder from a plugin.
func ServeBuilder(builder packer.Builder) {
	log.Println("Preparing to serve a builder plugin...")

	server := rpc.NewServer()
	packrpc.RegisterBuilder(server, builder)

	swallowInterrupts()
	if err := serve(server); err != nil {
		log.Printf("ERROR: %s", err)
		os.Exit(1)
	}
}

// Serves a command from a plugin.
func ServeCommand(command packer.Command) {
	log.Println("Preparing to serve a command plugin...")

	server := rpc.NewServer()
	packrpc.RegisterCommand(server, command)

	swallowInterrupts()
	if err := serve(server); err != nil {
		log.Printf("ERROR: %s", err)
		os.Exit(1)
	}
}

// Serves a hook from a plugin.
func ServeHook(hook packer.Hook) {
	log.Println("Preparing to serve a hook plugin...")

	server := rpc.NewServer()
	packrpc.RegisterHook(server, hook)

	swallowInterrupts()
	if err := serve(server); err != nil {
		log.Printf("ERROR: %s", err)
		os.Exit(1)
	}
}

// Serves a post-processor from a plugin.
func ServePostProcessor(p packer.PostProcessor) {
	log.Println("Preparing to serve a post-processor plugin...")

	server := rpc.NewServer()
	packrpc.RegisterPostProcessor(server, p)

	swallowInterrupts()
	if err := serve(server); err != nil {
		log.Printf("ERROR: %s", err)
		os.Exit(1)
	}
}

// Serves a provisioner from a plugin.
func ServeProvisioner(p packer.Provisioner) {
	log.Println("Preparing to serve a provisioner plugin...")

	server := rpc.NewServer()
	packrpc.RegisterProvisioner(server, p)

	swallowInterrupts()
	if err := serve(server); err != nil {
		log.Printf("ERROR: %s", err)
		os.Exit(1)
	}
}
<|endoftext|>