<commit_before>package util

import (
	"compress/gzip"
	"encoding/gob"
	"fmt"
	"io"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/TuftsBCB/fragbag"
	"github.com/TuftsBCB/fragbag/bow"
	"github.com/TuftsBCB/fragbag/bowdb"
	"github.com/TuftsBCB/hhfrag"
	"github.com/TuftsBCB/io/msa"
	"github.com/TuftsBCB/io/pdb"
	"github.com/TuftsBCB/seq"
)

func Library(path string) fragbag.Library {
	lib, err := fragbag.Open(OpenFile(path))
	Assert(err, "Could not open fragment library '%s'", path)
	return lib
}

func StructureLibrary(path string) fragbag.StructureLibrary {
	lib, ok := Library(path).(fragbag.StructureLibrary)
	if !ok {
		Fatalf("%s (%T) is not a structure library.", path, lib)
	}
	return lib
}

func SequenceLibrary(path string) fragbag.SequenceLibrary {
	lib, ok := Library(path).(fragbag.SequenceLibrary)
	if !ok {
		Fatalf("%s (%T) is not a sequence library.", path, lib)
	}
	return lib
}

func MSA(path string) seq.MSA {
	if strings.HasSuffix(path, "a2m") || strings.HasSuffix(path, "a3m") {
		aligned, err := msa.Read(OpenFile(path))
		Assert(err, "Could not read MSA (a2m/a3m) from '%s'", path)
		return aligned
	}
	aligned, err := msa.ReadFasta(OpenFile(path))
	Assert(err, "Could not read MSA (fasta) from '%s'", path)
	return aligned
}

func OpenBOWDB(path string) *bowdb.DB {
	db, err := bowdb.OpenDB(path)
	Assert(err, "Could not open BOW database '%s'", path)
	return db
}

func PDBRead(path string) *pdb.Entry {
	entry, err := pdb.ReadPDB(path)
	Assert(err, "Could not open PDB file '%s'", path)
	return entry
}

// PDBPath takes a PDB identifier (e.g., "1ctf" or "1ctfA") and returns
// the full path to the PDB file on the file system.
//
// The PDB_PATH environment variable must be set.
func PDBPath(pid string) string {
	if !IsPDBID(pid) && !IsChainID(pid) {
		Fatalf("PDB ids must contain 4 or 5 characters, but '%s' has %d.",
			pid, len(pid))
	}
	pdbPath := os.Getenv("PDB_PATH")
	if len(pdbPath) == 0 || !IsDir(pdbPath) {
		Fatalf("The PDB_PATH environment variable must be set to open " +
			"PDB chains by just their ID.\n" +
			"PDB_PATH should be set to the directory containing a full " +
			"copy of the PDB database.")
	}

	pdbid := strings.ToLower(pid[0:4])
	group := pdbid[1:3]
	basename := fmt.Sprintf("pdb%s.ent.gz", pdbid)
	return path.Join(pdbPath, group, basename)
}

func PDBReadId(pid string) (*pdb.Entry, *pdb.Chain) {
	e := PDBRead(PDBPath(pid))
	if IsChainID(pid) {
		chain := e.Chain(pid[4])
		if chain == nil {
			Fatalf("Could not find chain '%s' in PDB entry '%s'.", pid[4], pid)
		}
		return e, chain
	}
	return e, nil
}

func GetFmap(fpath string) *hhfrag.FragmentMap {
	var fmap *hhfrag.FragmentMap
	var err error

	switch {
	case IsFasta(fpath):
		fmap, err = HHfragConf.MapFromFasta(FlagPdbHhmDB, FlagSeqDB, fpath)
		Assert(err, "Could not generate map from '%s'", fpath)
	case IsFmap(fpath):
		fmap = FmapRead(fpath)
	default:
		Fatalf("File '%s' is not a fasta or fmap file.", fpath)
	}

	return fmap
}

func FmapRead(path string) *hhfrag.FragmentMap {
	var fmap *hhfrag.FragmentMap
	f := OpenFile(path)
	defer f.Close()

	r := gob.NewDecoder(f)
	Assert(r.Decode(&fmap), "Could not GOB decode fragment map '%s'", path)
	return fmap
}

func FmapWrite(w io.Writer, fmap *hhfrag.FragmentMap) {
	encoder := gob.NewEncoder(w)
	Assert(encoder.Encode(fmap), "Could not GOB encode fragment map")
}

func BOWRead(path string) bow.BOW {
	var bow bow.BOW
	f := OpenFile(path)
	defer f.Close()

	r := gob.NewDecoder(f)
	Assert(r.Decode(&bow), "Could not GOB decode BOW '%s'", path)
	return bow
}

func BOWWrite(w io.Writer, bow bow.BOW) {
	encoder := gob.NewEncoder(w)
	Assert(encoder.Encode(bow), "Could not GOB encode BOW")
}

func OpenFile(path string) *os.File {
	f, err := os.Open(path)
	Assert(err, "Could not open file '%s'", path)
	return f
}

func CreateFile(path string) *os.File {
	f, err := os.Create(path)
	Assert(err, "Could not create file '%s'", path)
	return f
}

func ParseInt(str string) int {
	num, err := strconv.ParseInt(str, 10, 32)
	Assert(err, "Could not parse '%s' as an integer", str)
	return int(num)
}

func IsFasta(fpath string) bool {
	suffix := func(ext string) bool {
		return strings.HasSuffix(fpath, ext)
	}
	return suffix(".fasta") || suffix(".fas") ||
		suffix(".fasta.gz") || suffix(".fas.gz")
}

func OpenFasta(fpath string) io.Reader {
	if strings.HasSuffix(fpath, ".gz") {
		r, err := gzip.NewReader(OpenFile(fpath))
		Assert(err, "Could not open '%s'", fpath)
		return r
	}
	return OpenFile(fpath)
}

func IsFmap(fpath string) bool {
	return strings.HasSuffix(fpath, ".fmap")
}

func IsPDB(fpath string) bool {
	suffix := func(ext string) bool {
		return strings.HasSuffix(fpath, ext)
	}
	return suffix(".ent.gz") || suffix(".pdb") || suffix(".ent")
}

func IsChainID(s string) bool {
	return len(s) == 5
}

func IsPDBID(s string) bool {
	return len(s) == 4
}
<commit_msg>Use path/filepath instead of path.<commit_after>package util

import (
	"compress/gzip"
	"encoding/gob"
	"fmt"
	"io"
	"os"
	path "path/filepath"
	"strconv"
	"strings"

	"github.com/TuftsBCB/fragbag"
	"github.com/TuftsBCB/fragbag/bow"
	"github.com/TuftsBCB/fragbag/bowdb"
	"github.com/TuftsBCB/hhfrag"
	"github.com/TuftsBCB/io/msa"
	"github.com/TuftsBCB/io/pdb"
	"github.com/TuftsBCB/seq"
)

func Library(path string) fragbag.Library {
	lib, err := fragbag.Open(OpenFile(path))
	Assert(err, "Could not open fragment library '%s'", path)
	return lib
}

func StructureLibrary(path string) fragbag.StructureLibrary {
	lib, ok := Library(path).(fragbag.StructureLibrary)
	if !ok {
		Fatalf("%s (%T) is not a structure library.", path, lib)
	}
	return lib
}

func SequenceLibrary(path string) fragbag.SequenceLibrary {
	lib, ok := Library(path).(fragbag.SequenceLibrary)
	if !ok {
		Fatalf("%s (%T) is not a sequence library.", path, lib)
	}
	return lib
}

func MSA(path string) seq.MSA {
	if strings.HasSuffix(path, "a2m") || strings.HasSuffix(path, "a3m") {
		aligned, err := msa.Read(OpenFile(path))
		Assert(err, "Could not read MSA (a2m/a3m) from '%s'", path)
		return aligned
	}
	aligned, err := msa.ReadFasta(OpenFile(path))
	Assert(err, "Could not read MSA (fasta) from '%s'", path)
	return aligned
}

func OpenBOWDB(path string) *bowdb.DB {
	db, err := bowdb.OpenDB(path)
	Assert(err, "Could not open BOW database '%s'", path)
	return db
}

func PDBRead(path string) *pdb.Entry {
	entry, err := pdb.ReadPDB(path)
	Assert(err, "Could not open PDB file '%s'", path)
	return entry
}

// PDBPath takes a PDB identifier (e.g., "1ctf" or "1ctfA") and returns
// the full path to the PDB file on the file system.
//
// The PDB_PATH environment variable must be set.
func PDBPath(pid string) string {
	if !IsPDBID(pid) && !IsChainID(pid) {
		Fatalf("PDB ids must contain 4 or 5 characters, but '%s' has %d.",
			pid, len(pid))
	}
	pdbPath := os.Getenv("PDB_PATH")
	if len(pdbPath) == 0 || !IsDir(pdbPath) {
		Fatalf("The PDB_PATH environment variable must be set to open " +
			"PDB chains by just their ID.\n" +
			"PDB_PATH should be set to the directory containing a full " +
			"copy of the PDB database.")
	}

	pdbid := strings.ToLower(pid[0:4])
	group := pdbid[1:3]
	basename := fmt.Sprintf("pdb%s.ent.gz", pdbid)
	return path.Join(pdbPath, group, basename)
}

func PDBReadId(pid string) (*pdb.Entry, *pdb.Chain) {
	e := PDBRead(PDBPath(pid))
	if IsChainID(pid) {
		chain := e.Chain(pid[4])
		if chain == nil {
			Fatalf("Could not find chain '%s' in PDB entry '%s'.", pid[4], pid)
		}
		return e, chain
	}
	return e, nil
}

func GetFmap(fpath string) *hhfrag.FragmentMap {
	var fmap *hhfrag.FragmentMap
	var err error

	switch {
	case IsFasta(fpath):
		fmap, err = HHfragConf.MapFromFasta(FlagPdbHhmDB, FlagSeqDB, fpath)
		Assert(err, "Could not generate map from '%s'", fpath)
	case IsFmap(fpath):
		fmap = FmapRead(fpath)
	default:
		Fatalf("File '%s' is not a fasta or fmap file.", fpath)
	}

	return fmap
}

func FmapRead(path string) *hhfrag.FragmentMap {
	var fmap *hhfrag.FragmentMap
	f := OpenFile(path)
	defer f.Close()

	r := gob.NewDecoder(f)
	Assert(r.Decode(&fmap), "Could not GOB decode fragment map '%s'", path)
	return fmap
}

func FmapWrite(w io.Writer, fmap *hhfrag.FragmentMap) {
	encoder := gob.NewEncoder(w)
	Assert(encoder.Encode(fmap), "Could not GOB encode fragment map")
}

func BOWRead(path string) bow.BOW {
	var bow bow.BOW
	f := OpenFile(path)
	defer f.Close()

	r := gob.NewDecoder(f)
	Assert(r.Decode(&bow), "Could not GOB decode BOW '%s'", path)
	return bow
}

func BOWWrite(w io.Writer, bow bow.BOW) {
	encoder := gob.NewEncoder(w)
	Assert(encoder.Encode(bow), "Could not GOB encode BOW")
}

func OpenFile(path string) *os.File {
	f, err := os.Open(path)
	Assert(err, "Could not open file '%s'", path)
	return f
}

func CreateFile(path string) *os.File {
	f, err := os.Create(path)
	Assert(err, "Could not create file '%s'", path)
	return f
}

func ParseInt(str string) int {
	num, err := strconv.ParseInt(str, 10, 32)
	Assert(err, "Could not parse '%s' as an integer", str)
	return int(num)
}

func IsFasta(fpath string) bool {
	suffix := func(ext string) bool {
		return strings.HasSuffix(fpath, ext)
	}
	return suffix(".fasta") || suffix(".fas") ||
		suffix(".fasta.gz") || suffix(".fas.gz")
}

func OpenFasta(fpath string) io.Reader {
	if strings.HasSuffix(fpath, ".gz") {
		r, err := gzip.NewReader(OpenFile(fpath))
		Assert(err, "Could not open '%s'", fpath)
		return r
	}
	return OpenFile(fpath)
}

func IsFmap(fpath string) bool {
	return strings.HasSuffix(fpath, ".fmap")
}

func IsPDB(fpath string) bool {
	suffix := func(ext string) bool {
		return strings.HasSuffix(fpath, ext)
	}
	return suffix(".ent.gz") || suffix(".pdb") || suffix(".ent")
}

func IsChainID(s string) bool {
	return len(s) == 5
}

func IsPDBID(s string) bool {
	return len(s) == 4
}
<|endoftext|>


<commit_before>package rest

import (
	"encoding/json"
	"fmt"
	"path"

	"github.com/bitfinexcom/bitfinex-api-go/v2"
)

// OrderService manages data flow for the Order API endpoint
type OrderService struct {
	requestFactory
	Synchronous
}

type OrderIDs []int
type GroupOrderIDs []int
type ClientOrderIDs [][]interface{}
type OrderOps [][]interface{}

// OrderMultiOpsRequest - data structure for constructing order multi ops request payload
type OrderMultiOpsRequest struct {
	Ops OrderOps `json:"ops"`
}

// CancelOrderMultiRequest - data structure for constructing cancel order multi request payload
type CancelOrderMultiRequest struct {
	OrderIDs       OrderIDs       `json:"id,omitempty"`
	GroupOrderIDs  GroupOrderIDs  `json:"gid,omitempty"`
	ClientOrderIDs ClientOrderIDs `json:"cid,omitempty"`
	All            int            `json:"all,omitempty"`
}

// Retrieves all of the active orders
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) All() (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getActiveOrders("")
}

// Retrieves all of the active orders with for the given symbol
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) GetBySymbol(symbol string) (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getActiveOrders(symbol)
}

// Retrieve an active order by the given ID
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) GetByOrderId(orderID int64) (o *bitfinex.Order, err error) {
	os, err := s.All()
	if err != nil {
		return nil, err
	}
	for _, order := range os.Snapshot {
		if order.ID == orderID {
			return order, nil
		}
	}
	return nil, bitfinex.ErrNotFound
}

// Retrieves all past orders
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) AllHistory() (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getHistoricalOrders("")
}

// Retrieves all past orders with the given symbol
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) GetHistoryBySymbol(symbol string) (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getHistoricalOrders(symbol)
}

// Retrieve a single order in history with the given id
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) GetHistoryByOrderId(orderID int64) (o *bitfinex.Order, err error) {
	os, err := s.AllHistory()
	if err != nil {
		return nil, err
	}
	for _, order := range os.Snapshot {
		if order.ID == orderID {
			return order, nil
		}
	}
	return nil, bitfinex.ErrNotFound
}

// Retrieves the trades generated by an order
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) OrderTrades(symbol string, orderID int64) (*bitfinex.TradeExecutionUpdateSnapshot, error) {
	key := fmt.Sprintf("%s:%d", symbol, orderID)
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("order", key, "trades"))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return bitfinex.NewTradeExecutionUpdateSnapshotFromRaw(raw)
}

func (s *OrderService) getActiveOrders(symbol string) (*bitfinex.OrderSnapshot, error) {
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("orders", symbol))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	os, err := bitfinex.NewOrderSnapshotFromRaw(raw)
	if err != nil {
		return nil, err
	}
	if os == nil {
		return &bitfinex.OrderSnapshot{}, nil
	}
	return os, nil
}

func (s *OrderService) getHistoricalOrders(symbol string) (*bitfinex.OrderSnapshot, error) {
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("orders", symbol, "hist"))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	os, err := bitfinex.NewOrderSnapshotFromRaw(raw)
	if err != nil {
		return nil, err
	}
	if os == nil {
		return &bitfinex.OrderSnapshot{}, nil
	}
	return os, nil
}

// Submit a request to create a new order
// see https://docs.bitfinex.com/reference#submit-order for more info
func (s *OrderService) SubmitOrder(order *bitfinex.OrderNewRequest) (*bitfinex.Notification, error) {
	bytes, err := order.ToJSON()
	if err != nil {
		return nil, err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "submit"), bytes)
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return bitfinex.NewNotificationFromRaw(raw)
}

// Submit a request to update an order with the given id with the given changes
// see https://docs.bitfinex.com/reference#order-update for more info
func (s *OrderService) SubmitUpdateOrder(order *bitfinex.OrderUpdateRequest) (*bitfinex.Notification, error) {
	bytes, err := order.ToJSON()
	if err != nil {
		return nil, err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "update"), bytes)
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return bitfinex.NewNotificationFromRaw(raw)
}

// Submit a request to cancel an order with the given Id
// see https://docs.bitfinex.com/reference#cancel-order for more info
func (s *OrderService) SubmitCancelOrder(oc *bitfinex.OrderCancelRequest) error {
	bytes, err := oc.ToJSON()
	if err != nil {
		return err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "cancel"), bytes)
	if err != nil {
		return err
	}
	_, err = s.Request(req)
	if err != nil {
		return err
	}
	return nil
}

// CancelOrderMulti cancels multiple orders simultaneously. Orders can be canceled based on the Order ID,
// the combination of Client Order ID and Client Order Date, or the Group Order ID. Alternatively, the body
// param 'all' can be used with a value of 1 to cancel all orders.
// see https://docs.bitfinex.com/reference#rest-auth-order-cancel-multi for more info
func (s *OrderService) CancelOrderMulti(args CancelOrderMultiRequest) (*bitfinex.Notification, error) {
	bytes, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "cancel", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}

// CancelOrdersMultiOp cancels multiple orders simultaneously. Accepts a slice of order ID's to be canceled.
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) CancelOrdersMultiOp(ids OrderIDs) (*bitfinex.Notification, error) {
	pld := OrderMultiOpsRequest{
		Ops: OrderOps{
			{
				"oc_multi",
				map[string][]int{"id": ids},
			},
		},
	}

	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}

// CancelOrderMultiOp cancels order. Accepts orderID to be canceled.
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) CancelOrderMultiOp(orderID int) (*bitfinex.Notification, error) {
	pld := OrderMultiOpsRequest{
		Ops: OrderOps{
			{
				"oc",
				map[string]int{"id": orderID},
			},
		},
	}

	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}

// OrderNewMultiOp creates new order. Accepts instance of bitfinex.OrderNewRequest
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) OrderNewMultiOp(order bitfinex.OrderNewRequest) (*bitfinex.Notification, error) {
	pld := OrderMultiOpsRequest{
		Ops: OrderOps{
			{
				"on",
				order.EnrichedPayload(),
			},
		},
	}

	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}

// OrderUpdateMultiOp updates order. Accepts instance of bitfinex.OrderUpdateRequest
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) OrderUpdateMultiOp(order bitfinex.OrderUpdateRequest) (*bitfinex.Notification, error) {
	pld := OrderMultiOpsRequest{
		Ops: OrderOps{
			{
				"ou",
				order.EnrichedPayload(),
			},
		},
	}

	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}

// OrderMultiOp - send Multiple order-related operations. Please note the sent object has
// only one property with a value of a slice of slices detailing each order operation.
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) OrderMultiOp(ops OrderOps) (*bitfinex.Notification, error) {
	enrichedOrderOps := OrderOps{}

	for _, v := range ops {
		if v[0] == "on" {
			o, ok := v[1].(bitfinex.OrderNewRequest)
			if !ok {
				return nil, fmt.Errorf("Invalid type for `on` operation. Expected: bitfinex.OrderNewRequest")
			}
			v[1] = o.EnrichedPayload()
		}

		if v[0] == "ou" {
			o, ok := v[1].(bitfinex.OrderUpdateRequest)
			if !ok {
				return nil, fmt.Errorf("Invalid type for `ou` operation. Expected: bitfinex.OrderUpdateRequest")
			}
			v[1] = o.EnrichedPayload()
		}

		enrichedOrderOps = append(enrichedOrderOps, v)
	}

	pld := OrderMultiOpsRequest{Ops: enrichedOrderOps}
	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}
<commit_msg>v2/rest/orders.go putting new order and tradeexecutionupdate packages to work<commit_after>package rest

import (
	"encoding/json"
	"fmt"
	"path"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/order"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/tradeexecutionupdate"
	"github.com/bitfinexcom/bitfinex-api-go/v2"
)

// OrderService manages data flow for the Order API endpoint
type OrderService struct {
	requestFactory
	Synchronous
}

type OrderIDs []int
type GroupOrderIDs []int
type ClientOrderIDs [][]interface{}
type OrderOps [][]interface{}

// OrderMultiOpsRequest - data structure for constructing order multi ops request payload
type OrderMultiOpsRequest struct {
	Ops OrderOps `json:"ops"`
}

// CancelOrderMultiRequest - data structure for constructing cancel order multi request payload
type CancelOrderMultiRequest struct {
	OrderIDs       OrderIDs       `json:"id,omitempty"`
	GroupOrderIDs  GroupOrderIDs  `json:"gid,omitempty"`
	ClientOrderIDs ClientOrderIDs `json:"cid,omitempty"`
	All            int            `json:"all,omitempty"`
}

// Retrieves all of the active orders
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) All() (*order.Snapshot, error) {
	// use no symbol, this will get all orders
	return s.getActiveOrders("")
}

// Retrieves all of the active orders with for the given symbol
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) GetBySymbol(symbol string) (*order.Snapshot, error) {
	// use no symbol, this will get all orders
	return s.getActiveOrders(symbol)
}

// Retrieve an active order by the given ID
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) GetByOrderId(orderID int64) (o *order.Order, err error) {
	os, err := s.All()
	if err != nil {
		return nil, err
	}
	for _, order := range os.Snapshot {
		if order.ID == orderID {
			return order, nil
		}
	}
	return nil, bitfinex.ErrNotFound
}

// Retrieves all past orders
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) AllHistory() (*order.Snapshot, error) {
	// use no symbol, this will get all orders
	return s.getHistoricalOrders("")
}

// Retrieves all past orders with the given symbol
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) GetHistoryBySymbol(symbol string) (*order.Snapshot, error) {
	// use no symbol, this will get all orders
	return s.getHistoricalOrders(symbol)
}

// Retrieve a single order in history with the given id
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) GetHistoryByOrderId(orderID int64) (o *order.Order, err error) {
	os, err := s.AllHistory()
	if err != nil {
		return nil, err
	}
	for _, order := range os.Snapshot {
		if order.ID == orderID {
			return order, nil
		}
	}
	return nil, bitfinex.ErrNotFound
}

// Retrieves the trades generated by an order
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) OrderTrades(symbol string, orderID int64) (*tradeexecutionupdate.Snapshot, error) {
	key := fmt.Sprintf("%s:%d", symbol, orderID)
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("order", key, "trades"))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return tradeexecutionupdate.SnapshotFromRaw(raw)
}

func (s *OrderService) getActiveOrders(symbol string) (*order.Snapshot, error) {
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("orders", symbol))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	os, err := order.SnapshotFromRaw(raw)
	if err != nil {
		return nil, err
	}
	if os == nil {
		return &order.Snapshot{}, nil
	}
	return os, nil
}

func (s *OrderService) getHistoricalOrders(symbol string) (*order.Snapshot, error) {
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("orders", symbol, "hist"))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	os, err := order.SnapshotFromRaw(raw)
	if err != nil {
		return nil, err
	}
	if os == nil {
		return &order.Snapshot{}, nil
	}
	return os, nil
}

// Submit a request to create a new order
// see https://docs.bitfinex.com/reference#submit-order for more info
func (s *OrderService) SubmitOrder(onr *order.NewRequest) (*bitfinex.Notification, error) {
	bytes, err := onr.ToJSON()
	if err != nil {
		return nil, err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "submit"), bytes)
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return bitfinex.NewNotificationFromRaw(raw)
}

// Submit a request to update an order with the given id with the given changes
// see https://docs.bitfinex.com/reference#order-update for more info
func (s *OrderService) SubmitUpdateOrder(our *order.UpdateRequest) (*bitfinex.Notification, error) {
	bytes, err := our.ToJSON()
	if err != nil {
		return nil, err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "update"), bytes)
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return bitfinex.NewNotificationFromRaw(raw)
}

// Submit a request to cancel an order with the given Id
// see https://docs.bitfinex.com/reference#cancel-order for more info
func (s *OrderService) SubmitCancelOrder(oc *order.CancelRequest) error {
	bytes, err := oc.ToJSON()
	if err != nil {
		return err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "cancel"), bytes)
	if err != nil {
		return err
	}
	_, err = s.Request(req)
	if err != nil {
		return err
	}
	return nil
}

// CancelOrderMulti cancels multiple orders simultaneously. Orders can be canceled based on the Order ID,
// the combination of Client Order ID and Client Order Date, or the Group Order ID. Alternatively, the body
// param 'all' can be used with a value of 1 to cancel all orders.
// see https://docs.bitfinex.com/reference#rest-auth-order-cancel-multi for more info
func (s *OrderService) CancelOrderMulti(args CancelOrderMultiRequest) (*bitfinex.Notification, error) {
	bytes, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "cancel", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}

// CancelOrdersMultiOp cancels multiple orders simultaneously. Accepts a slice of order ID's to be canceled.
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) CancelOrdersMultiOp(ids OrderIDs) (*bitfinex.Notification, error) {
	pld := OrderMultiOpsRequest{
		Ops: OrderOps{
			{
				"oc_multi",
				map[string][]int{"id": ids},
			},
		},
	}

	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}

// CancelOrderMultiOp cancels order. Accepts orderID to be canceled.
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) CancelOrderMultiOp(orderID int) (*bitfinex.Notification, error) {
	pld := OrderMultiOpsRequest{
		Ops: OrderOps{
			{
				"oc",
				map[string]int{"id": orderID},
			},
		},
	}

	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}

// OrderNewMultiOp creates new order. Accepts instance of order.NewRequest
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) OrderNewMultiOp(onr order.NewRequest) (*bitfinex.Notification, error) {
	pld := OrderMultiOpsRequest{
		Ops: OrderOps{
			{
				"on",
				onr.EnrichedPayload(),
			},
		},
	}

	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}

// OrderUpdateMultiOp updates order. Accepts instance of order.UpdateRequest
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) OrderUpdateMultiOp(our order.UpdateRequest) (*bitfinex.Notification, error) {
	pld := OrderMultiOpsRequest{
		Ops: OrderOps{
			{
				"ou",
				our.EnrichedPayload(),
			},
		},
	}

	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}

// OrderMultiOp - send Multiple order-related operations. Please note the sent object has
// only one property with a value of a slice of slices detailing each order operation.
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) OrderMultiOp(ops OrderOps) (*bitfinex.Notification, error) {
	enrichedOrderOps := OrderOps{}

	for _, v := range ops {
		if v[0] == "on" {
			o, ok := v[1].(order.NewRequest)
			if !ok {
				return nil, fmt.Errorf("Invalid type for `on` operation. Expected: order.NewRequest")
			}
			v[1] = o.EnrichedPayload()
		}

		if v[0] == "ou" {
			o, ok := v[1].(order.UpdateRequest)
			if !ok {
				return nil, fmt.Errorf("Invalid type for `ou` operation. Expected: order.UpdateRequest")
			}
			v[1] = o.EnrichedPayload()
		}

		enrichedOrderOps = append(enrichedOrderOps, v)
	}

	pld := OrderMultiOpsRequest{Ops: enrichedOrderOps}
	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}

	return bitfinex.NewNotificationFromRaw(raw)
}
<|endoftext|>


<commit_before>package voldemort

import (
	"errors"
	"fmt"
	"log"
	"net"
	"sync"
	"time"
)

type VoldemortPool struct {
	// Channel used to control access to multiple VoldemortConn objects
	pool chan *VoldemortConn

	failures chan *VoldemortConn

	// used to track how many connections we should have from each server
	// if a conn goes down, we should then be able to find the one with less and therefore retry
	servers map[string]int

	// keep a count of active servers - servers that are capable of being queried
	active      int
	active_lock *sync.Mutex

	// Track size of pool - the pool in the amount of servers not currently out on jobs
	size      int
	size_lock *sync.Mutex

	closed bool // state of the pool - false if open/true if closed
}

func NewPool(bserver *net.TCPAddr, proto string) (*VoldemortPool, error) {

	// we need to dial one server in the beginning to get all the details against the cluster
	vc, err := Dial(bserver, proto)

	if err != nil {
		return nil, err
	}

	// Find out how many servers there are so we can make a nice pool - only ony of each for now!
	poolSize := len(vc.cl.Servers)

	// This channel will be used to hold all the conns and distribute them to clients
	p := make(chan *VoldemortConn, poolSize)

	// The failure chan will be unbuffered
	f := make(chan *VoldemortConn)

	var (
		nvc   *VoldemortConn
		faddr string
	)

	// initialise the map - this creates the structure and all counters (int) will be 0
	servers := make(map[string]int)

	var activeCount int

	for j := 0; j < 1; j++ {

		for _, v := range vc.cl.Servers {

			faddr = fmt.Sprintf("%s:%d", v.Host, v.Socket)

			log.Printf("Adding server to pool - %s", faddr)

			addr, err := net.ResolveTCPAddr("tcp", faddr)
			if err != nil {
				log.Fatal(err)
			}

			nvc, err = Dial(addr, proto)
			if err != nil {
				log.Printf("server - %s - unavailable - cannot add to the pool", addr)
				continue
			}

			activeCount++
			// Update the connection counter for this server
			servers[faddr]++

			// Add the conn to the channel so it can be used
			p <- nvc

		}

	}

	// Initialise the pool with all the required variables
	vp := &VoldemortPool{pool: p, failures: f, size: poolSize, active: activeCount, servers: servers, closed: false}

	// start the watcher!
	go vp.watcher()

	return vp, nil

}

// Get a VoldemortConn struct from the channel and return it
func (vp *VoldemortPool) GetConn() (vc *VoldemortConn, err error) {

	if vp.active == 0 {
		return nil, errors.New("no active servers available")
	}

	// return after 250 milliseconds regardless of result - protect the app!
	select {
	case _ = <-time.After(time.Millisecond * 250):
		return nil, errors.New("timeout getting a connection to voldemort")
	case vc = <-vp.pool:
		// lock the pool count and decrease
		vp.size_lock.Lock()
		vp.size--
		vp.size_lock.Unlock()
		return vc, nil
	}

}

// watcher is run in a go routine and sits around just watching for failures
// when it spots one it throws it over the another reconnect() running in another go routine
func (vp *VoldemortPool) watcher() {

	var vc *VoldemortConn

	log.Println("conn watcher running")

	for {

		vc = <-vp.failures

		// decrease the count under lock
		vp.active_lock.Lock()
		vp.active--
		vp.active_lock.Unlock()

		log.Println("failure collected")

		go vp.reconnect(vc)

	}

}

// the client will try and reconnect forever but with incremental backoff to 1 minute {1,2,4,8,16,32,60}
func (vp *VoldemortPool) reconnect(vc *VoldemortConn) {

	log.Printf("trying to reconnect - %s", vc.s)

	var (
		retry int = 1
		d     time.Duration
	)

	for {

		vaddr, err := net.ResolveTCPAddr("tcp", vc.s)
		if err != nil {
			log.Printf("reconnecting to %s - address error - %s", vc.s, err)
		}

		newvc, err := Dial(vaddr, vc.proto)

		if err == nil {

			log.Printf("new connection found - %s", vc.s)

			// Wait 1 minute before actually doing queries to let the node catch up
			time.Sleep(1 * time.Minute)

			// increase the count under lock
			vp.active_lock.Lock()
			vp.active++
			vp.active_lock.Unlock()

			vp.ReleaseConn(newvc, true)
			return

		}

		log.Printf("error reconnecting to %s - %s - retrying in %d seconds", vc.s, err, retry)

		d, err = time.ParseDuration(fmt.Sprintf("%ds", retry))

		if err != nil {
			log.Fatal(err)
		}

		time.Sleep(d)

		if retry >= 60 {
			retry = 60
			continue
		}

		retry = retry * 2

	}

	return

}

func (vp *VoldemortPool) ReleaseConn(vc *VoldemortConn, state bool) {

	if !state {
		// OH dear - it looks like a conn has failed - time to sort that out!
		// we need a new conn here
		log.Println("server failure - %s", vc.s)
		vp.failures <- vc
		return
	}

	// make sure the pool isn't closed
	if !vp.closed {
		vp.pool <- vc
	}

	// up the count again
	vp.size_lock.Lock()
	vp.size++
	vp.size_lock.Unlock()

	return

}

func (vp *VoldemortPool) Empty() {

	var vc *VoldemortConn

	// close the pool
	vp.closed = true
	close(vp.pool)

	// now that we have closed the pool run through what's left on it and close all the conns
	select {
	case vc = <-vp.pool:
		log.Printf("closing conn - %s", vc.s)
		vc.Close()
	default:
		return
	}

	log.Println("all voldemort connections closed")
}
<commit_msg>mutex not a pointer<commit_after>package voldemort

import (
	"errors"
	"fmt"
	"log"
	"net"
	"sync"
	"time"
)

type VoldemortPool struct {
	// Channel used to control access to multiple VoldemortConn objects
	pool chan *VoldemortConn

	failures chan *VoldemortConn

	// used to track how many connections we should have from each server
	// if a conn goes down, we should then be able to find the one with less and therefore retry
	servers map[string]int

	// keep a count of active servers - servers that are capable of being queried
	active      int
	active_lock sync.Mutex

	// Track size of pool - the pool in the amount of servers not currently out on jobs
	size      int
	size_lock sync.Mutex

	closed bool // state of the pool - false if open/true if closed
}

func NewPool(bserver *net.TCPAddr, proto string) (*VoldemortPool, error) {

	// we need to dial one server in the beginning to get all the details against the cluster
	vc, err := Dial(bserver, proto)

	if err != nil {
		return nil, err
	}

	// Find out how many servers there are so we can make a nice pool - only ony of each for now!
	poolSize := len(vc.cl.Servers)

	// This channel will be used to hold all the conns and distribute them to clients
	p := make(chan *VoldemortConn, poolSize)

	// The failure chan will be unbuffered
	f := make(chan *VoldemortConn)

	var (
		nvc   *VoldemortConn
		faddr string
	)

	// initialise the map - this creates the structure and all counters (int) will be 0
	servers := make(map[string]int)

	var activeCount int

	for j := 0; j < 1; j++ {

		for _, v := range vc.cl.Servers {

			faddr = fmt.Sprintf("%s:%d", v.Host, v.Socket)

			log.Printf("Adding server to pool - %s", faddr)

			addr, err := net.ResolveTCPAddr("tcp", faddr)
			if err != nil {
				log.Fatal(err)
			}

			nvc, err = Dial(addr, proto)
			if err != nil {
				log.Printf("server - %s - unavailable - cannot add to the pool", addr)
				continue
			}

			activeCount++
			// Update the connection counter for this server
			servers[faddr]++

			// Add the conn to the channel so it can be used
			p <- nvc

		}

	}

	// Initialise the pool with all the required variables
	vp := &VoldemortPool{pool: p, failures: f, size: poolSize, active: activeCount, servers: servers, closed: false}

	// start the watcher!
	go vp.watcher()

	return vp, nil

}

// Get a VoldemortConn struct from the channel and return it
func (vp *VoldemortPool) GetConn() (vc *VoldemortConn, err error) {

	if vp.active == 0 {
		return nil, errors.New("no active servers available")
	}

	// return after 250 milliseconds regardless of result - protect the app!
	select {
	case _ = <-time.After(time.Millisecond * 250):
		return nil, errors.New("timeout getting a connection to voldemort")
	case vc = <-vp.pool:
		// lock the pool count and decrease
		vp.size_lock.Lock()
		vp.size--
		vp.size_lock.Unlock()
		return vc, nil
	}

}

// watcher is run in a go routine and sits around just watching for failures
// when it spots one it throws it over the another reconnect() running in another go routine
func (vp *VoldemortPool) watcher() {

	var vc *VoldemortConn

	log.Println("conn watcher running")

	for {

		vc = <-vp.failures

		// decrease the count under lock
		vp.active_lock.Lock()
		vp.active--
		vp.active_lock.Unlock()

		log.Println("failure collected")

		go vp.reconnect(vc)

	}

}

// the client will try and reconnect forever but with incremental backoff to 1 minute {1,2,4,8,16,32,60}
func (vp *VoldemortPool) reconnect(vc *VoldemortConn) {

	log.Printf("trying to reconnect - %s", vc.s)

	var (
		retry int = 1
		d     time.Duration
	)

	for {

		vaddr, err := net.ResolveTCPAddr("tcp", vc.s)
		if err != nil {
			log.Printf("reconnecting to %s - address error - %s", vc.s, err)
		}

		newvc, err := Dial(vaddr, vc.proto)

		if err == nil {

			log.Printf("new connection found - %s", vc.s)

			// Wait 1 minute before actually doing queries to let the node catch up
			time.Sleep(1 * time.Minute)

			// increase the count under lock
			vp.active_lock.Lock()
			vp.active++
			vp.active_lock.Unlock()

			vp.ReleaseConn(newvc, true)
			return

		}

		log.Printf("error reconnecting to %s - %s - retrying in %d seconds", vc.s, err, retry)

		d, err = time.ParseDuration(fmt.Sprintf("%ds", retry))

		if err != nil {
			log.Fatal(err)
		}

		time.Sleep(d)

		if retry >= 60 {
			retry = 60
			continue
		}

		retry = retry * 2

	}

	return

}

func (vp *VoldemortPool) ReleaseConn(vc *VoldemortConn, state bool) {

	if !state {
		// OH dear - it looks like a conn has failed - time to sort that out!
		// we need a new conn here
		log.Println("server failure - %s", vc.s)
		vp.failures <- vc
		return
	}

	// make sure the pool isn't closed
	if !vp.closed {
		vp.pool <- vc
	}

	// up the count again
	vp.size_lock.Lock()
	vp.size++
	vp.size_lock.Unlock()

	return

}

func (vp *VoldemortPool) Empty() {

	var vc *VoldemortConn

	// close the pool
	vp.closed = true
	close(vp.pool)

	// now that we have closed the pool run through what's left on it and close all the conns
	select {
	case vc = <-vp.pool:
		log.Printf("closing conn - %s", vc.s)
		vc.Close()
	default:
		return
	}

	log.Println("all voldemort connections closed")
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package govet implements a Shipshape analyzer that runs go vet over all Go
// files in the given ShipshapeContext.
package govet

import (
	"errors"
	"fmt"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/golang/protobuf/proto"
	notepb "shipshape/proto/note_proto"
	ctxpb "shipshape/proto/shipshape_context_proto"
	rangepb "shipshape/proto/textrange_proto"
)

const (
	exitStatus = "exit status 1"
)

var (
	issueRE = regexp.MustCompile(`([^:]*):([0-9]+): (.*)`)
)

// GoVetAnalyzer is a wrapper around the go vet command line tool.
// This assumes it runs in a location where go is on the path.
type GoVetAnalyzer struct{}

func (GoVetAnalyzer) Category() string { return "go vet" }

func isGoFile(path string) bool {
	return filepath.Ext(path) == ".go"
}

func (gva *GoVetAnalyzer) analyzeOneFile(ctx *ctxpb.ShipshapeContext, path string) ([]*notepb.Note, error) {
	var notes []*notepb.Note
	cmd := exec.Command("go", "vet", path)
	buf, err := cmd.CombinedOutput()

	switch err := err.(type) {
	case nil:
		// No issues reported, do nothing.
	case *exec.ExitError:
		// go vet exits with an error when there are findings to report.
		if err.Error() != exitStatus {
			return notes, err
		}

		// go vet gives one issue per line, with the penultimate line indicating
		// the exit code and the last line being empty.
		var issues = strings.Split(string(buf), "\n")
		if len(issues) < 3 {
			// TODO(ciera): We should be able to keep going here
			// and try the next file. However, our API doesn't allow for
			// returning multiple errors. We need to reconsider the API.
			return notes, errors.New("did not get correct output from `go vet`")
		}
		for _, issue := range issues[:len(issues)-2] {
			parts := issueRE.FindStringSubmatch(issue)
			if len(parts) != 4 {
				return notes, fmt.Errorf("`go vet` gave incorrectly formatted issue: %q", issue)
			}

			filename := parts[1]
			description := parts[3]

			// Convert the line number into a base-10 32-bit int.
			line, err := strconv.ParseInt(parts[2], 10, 32)
			if err != nil {
				return notes, err
			}

			notes = append(notes, &notepb.Note{
				// TODO(collinwinter): we should synthesize subcategories here.
				Category:    proto.String(gva.Category()),
				Description: proto.String(description),
				Location: &notepb.Location{
					SourceContext: ctx.SourceContext,
					Path:          proto.String(filename),
					Range: &rangepb.TextRange{
						StartLine: proto.Int32(int32(line)),
					},
				},
			})
		}
	default:
		return notes, err
	}
	return notes, nil
}

func (gva *GoVetAnalyzer) Analyze(ctx *ctxpb.ShipshapeContext) ([]*notepb.Note, error) {
	var notes []*notepb.Note

	// Call go vet on each go file individually. go vet requires that all files
	// given be in the same directory, and this is an easy way of achieving that.
	for _, path := range ctx.FilePath {
		if !isGoFile(path) {
			continue
		}

		ourNotes, err := gva.analyzeOneFile(ctx, path)
		// TODO(collinwinter): figure out whether analyzers should return an
		// error XOR notes and impose that everywhere.
		notes = append(notes, ourNotes...)
		if err != nil {
			return notes, err
		}
	}
	return notes, nil
}
<commit_msg>Add info aobut go vet failure<commit_after>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package govet implements a Shipshape analyzer that runs go vet over all Go
// files in the given ShipshapeContext.
package govet

import (
	"fmt"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/golang/protobuf/proto"
	notepb "shipshape/proto/note_proto"
	ctxpb "shipshape/proto/shipshape_context_proto"
	rangepb "shipshape/proto/textrange_proto"
)

const (
	exitStatus = "exit status 1"
)

var (
	issueRE = regexp.MustCompile(`([^:]*):([0-9]+): (.*)`)
)

// GoVetAnalyzer is a wrapper around the go vet command line tool.
// This assumes it runs in a location where go is on the path.
type GoVetAnalyzer struct{}

func (GoVetAnalyzer) Category() string { return "go vet" }

func isGoFile(path string) bool {
	return filepath.Ext(path) == ".go"
}

func (gva *GoVetAnalyzer) analyzeOneFile(ctx *ctxpb.ShipshapeContext, path string) ([]*notepb.Note, error) {
	var notes []*notepb.Note
	cmd := exec.Command("go", "vet", path)
	buf, err := cmd.CombinedOutput()

	switch err := err.(type) {
	case nil:
		// No issues reported, do nothing.
	case *exec.ExitError:
		// go vet exits with an error when there are findings to report.
		if err.Error() != exitStatus {
			return notes, err
		}

		// go vet gives one issue per line, with the penultimate line indicating
		// the exit code and the last line being empty.
		var issues = strings.Split(string(buf), "\n")
		if len(issues) < 3 {
			// TODO(ciera): We should be able to keep going here
			// and try the next file. However, our API doesn't allow for
			// returning multiple errors. We need to reconsider the API.
			return notes, fmt.Errorf("did not get correct output from `go vet`, output was: %v", string(buf))
		}
		for _, issue := range issues[:len(issues)-2] {
			parts := issueRE.FindStringSubmatch(issue)
			if len(parts) != 4 {
				return notes, fmt.Errorf("`go vet` gave incorrectly formatted issue: %q", issue)
			}

			filename := parts[1]
			description := parts[3]

			// Convert the line number into a base-10 32-bit int.
			line, err := strconv.ParseInt(parts[2], 10, 32)
			if err != nil {
				return notes, err
			}

			notes = append(notes, &notepb.Note{
				// TODO(collinwinter): we should synthesize subcategories here.
				Category:    proto.String(gva.Category()),
				Description: proto.String(description),
				Location: &notepb.Location{
					SourceContext: ctx.SourceContext,
					Path:          proto.String(filename),
					Range: &rangepb.TextRange{
						StartLine: proto.Int32(int32(line)),
					},
				},
			})
		}
	default:
		return notes, err
	}
	return notes, nil
}

func (gva *GoVetAnalyzer) Analyze(ctx *ctxpb.ShipshapeContext) ([]*notepb.Note, error) {
	var notes []*notepb.Note

	// Call go vet on each go file individually. go vet requires that all files
	// given be in the same directory, and this is an easy way of achieving that.
	for _, path := range ctx.FilePath {
		if !isGoFile(path) {
			continue
		}

		ourNotes, err := gva.analyzeOneFile(ctx, path)
		// TODO(collinwinter): figure out whether analyzers should return an
		// error XOR notes and impose that everywhere.
		notes = append(notes, ourNotes...)
		if err != nil {
			return notes, err
		}
	}
	return notes, nil
}
<|endoftext|>


<commit_before>package main

import (

	"fmt"

)

func parse(files []string) {

	for _, file := range files {
		fmt.Println(colorize("going to parse...", file))
		filterDistribution(file)
	}
	fmt.Println("all done")

}
<commit_msg>clean up ; more work to be done with color handling, just a test<commit_after>package main

import (

	"fmt"

)

func parse(files []string) {

	for _, file := range files {
		d := "going to parse..." + file
		fmt.Println(colorize(d))
		filterDistribution(file)
	}
	fmt.Println("all done")

}
<|endoftext|>


<commit_before>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This LevelDB Go implementation is based on LevelDB C++ implementation.
// Which contains the following header:
//   Copyright (c) 2011 The LevelDB Authors. All rights reserved.
//   Use of this source code is governed by a BSD-style license that can be
//   found in the LEVELDBCPP_LICENSE file. See the LEVELDBCPP_AUTHORS file
//   for names of contributors.

package memdb

import (
	"bytes"
	"leveldb"
	"math/rand"
	"unsafe"
)

const tMaxHeight = 12

var (
	mPtrSize  int
	mNodeSize int
)

func init() {
	node := new(mNode)
	mPtrSize = int(unsafe.Sizeof(node))
	mNodeSize = int(unsafe.Sizeof(*node))
}

type mNode struct {
	key   []byte
	value []byte
	next  []*mNode
}

func (p *mNode) Next(n int) *mNode {
	return p.next[n]
}

func (p *mNode) SetNext(n int, x *mNode) {
	p.next[n] = x
}

type DB struct {
	cmp       leveldb.BasicComparator
	rnd       *rand.Rand
	head      *mNode
	maxHeight int
	memSize   int
}

func New(cmp leveldb.BasicComparator) *DB {
	p := &DB{
		cmp:       cmp,
		rnd:       rand.New(rand.NewSource(0xdeadbeef)),
		maxHeight: 1,
	}
	p.head = p.newNode(nil, nil, tMaxHeight)
	return p
}

func (p *DB) Put(key []byte, value []byte) {
	prev := make([]*mNode, tMaxHeight)
	x := p.findGreaterOrEqual(key, prev)
	n := p.randHeight()
	if n > p.maxHeight {
		for i := p.maxHeight; i < n; i++ {
			prev[i] = p.head
		}
		p.maxHeight = n
	}

	x = p.newNode(key, value, n)
	for i := 0; i < n; i++ {
		x.SetNext(i, prev[i].Next(i))
		prev[i].SetNext(i, x)
	}
}

func (p *DB) Contains(key []byte) bool {
	x := p.findGreaterOrEqual(key, nil)
	if x != nil && bytes.Equal(x.key, key) {
		return true
	}
	return false
}

func (p *DB) Get(key []byte) (rkey, value []byte, err error) {
	i := p.NewIterator()
	if !i.Seek(key) {
		return nil, nil, leveldb.ErrNotFound
	}
	return i.Key(), i.Value(), nil
}

func (p *DB) NewIterator() *Iterator {
	return &Iterator{p: p}
}

func (p *DB) Size() int {
	return p.memSize
}

func (p *DB) newNode(key, value []byte, height int) *mNode {
	p.memSize += mNodeSize + (mPtrSize * height)
	p.memSize += len(key) + len(value)
	return &mNode{key, value, make([]*mNode, height)}
}

func (p *DB) findGreaterOrEqual(key []byte, prev []*mNode) *mNode {
	x := p.head
	n := p.maxHeight - 1
	for {
		next := x.Next(n)
		if next != nil && p.cmp.Compare(next.key, key) < 0 {
			// Keep searching in this list
			x = next
		} else {
			if prev != nil {
				prev[n] = x
			}
			if n == 0 {
				return next
			}
			n--
		}
	}
	return nil
}

func (p *DB) findLessThan(key []byte) *mNode {
	x := p.head
	n := p.maxHeight - 1
	for {
		next := x.Next(n)
		if next == nil || p.cmp.Compare(next.key, key) >= 0 {
			if n == 0 {
				return x
			}
			n--
		} else {
			x = next
		}
	}
	return nil
}

func (p *DB) findLast() *mNode {
	x := p.head
	n := p.maxHeight - 1
	for {
		next := x.Next(n)
		if next == nil {
			if n == 0 {
				return x
			}
			n--
		} else {
			x = next
		}
	}
	return nil
}

func (p *DB) randHeight() int {
	const branching = 4
	n := 1
	for n < tMaxHeight && p.rnd.Int()%branching == 0 {
		n++
	}
	return n
}

type Iterator struct {
	p      *DB
	node   *mNode
	onLast bool
}

func (i *Iterator) Valid() bool {
	return i.node != nil
}

func (i *Iterator) First() bool {
	i.node = i.p.head.Next(0)
	return i.node != nil
}

func (i *Iterator) Last() bool {
	i.node = i.p.findLast()
	return i.node != nil
}

func (i *Iterator) Seek(key []byte) (r bool) {
	i.node = i.p.findGreaterOrEqual(key, nil)
	return i.node != nil
}

func (i *Iterator) Next() bool {
	if i.node == nil {
		return i.First()
	}
	i.node = i.node.Next(0)
	res := i.node != nil
	if !res {
		i.onLast = true
	}
	return res
}

func (i *Iterator) Prev() bool {
	if i.node == nil {
		if i.onLast {
			return i.Last()
		}
		return false
	}
	i.node = i.p.findLessThan(i.node.key)
	if i.node == i.p.head {
		i.node = nil
	}
	return i.node != nil
}

func (i *Iterator) Key() []byte {
	if i.node == nil {
		return nil
	}
	return i.node.key
}

func (i *Iterator) Value() []byte {
	if i.node == nil {
		return nil
	}
	return i.node.value
}

func (i *Iterator) Error() error { return nil }
<commit_msg>memdb: fix bug which Seek() return true on empty db<commit_after>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This LevelDB Go implementation is based on LevelDB C++ implementation.
// Which contains the following header:
//   Copyright (c) 2011 The LevelDB Authors. All rights reserved.
//   Use of this source code is governed by a BSD-style license that can be
//   found in the LEVELDBCPP_LICENSE file. See the LEVELDBCPP_AUTHORS file
//   for names of contributors.

package memdb

import (
	"bytes"
	"leveldb"
	"math/rand"
	"unsafe"
)

const tMaxHeight = 12

var (
	mPtrSize  int
	mNodeSize int
)

func init() {
	node := new(mNode)
	mPtrSize = int(unsafe.Sizeof(node))
	mNodeSize = int(unsafe.Sizeof(*node))
}

type mNode struct {
	key   []byte
	value []byte
	next  []*mNode
}

func (p *mNode) Next(n int) *mNode {
	return p.next[n]
}

func (p *mNode) SetNext(n int, x *mNode) {
	p.next[n] = x
}

type DB struct {
	cmp       leveldb.BasicComparator
	rnd       *rand.Rand
	head      *mNode
	maxHeight int
	memSize   int
}

func New(cmp leveldb.BasicComparator) *DB {
	p := &DB{
		cmp:       cmp,
		rnd:       rand.New(rand.NewSource(0xdeadbeef)),
		maxHeight: 1,
	}
	p.head = p.newNode(nil, nil, tMaxHeight)
	return p
}

func (p *DB) Put(key []byte, value []byte) {
	prev := make([]*mNode, tMaxHeight)
	x := p.findGreaterOrEqual(key, prev)
	n := p.randHeight()
	if n > p.maxHeight {
		for i := p.maxHeight; i < n; i++ {
			prev[i] = p.head
		}
		p.maxHeight = n
	}

	x = p.newNode(key, value, n)
	for i := 0; i < n; i++ {
		x.SetNext(i, prev[i].Next(i))
		prev[i].SetNext(i, x)
	}
}

func (p *DB) Contains(key []byte) bool {
	x := p.findGreaterOrEqual(key, nil)
	if x != nil && bytes.Equal(x.key, key) {
		return true
	}
	return false
}

func (p *DB) Get(key []byte) (rkey, value []byte, err error) {
	i := p.NewIterator()
	if !i.Seek(key) {
		return nil, nil, leveldb.ErrNotFound
	}
	return i.Key(), i.Value(), nil
}

func (p *DB) NewIterator() *Iterator {
	return &Iterator{p: p}
}

func (p *DB) Size() int {
	return p.memSize
}

func (p *DB) newNode(key, value []byte, height int) *mNode {
	p.memSize += mNodeSize + (mPtrSize * height)
	p.memSize += len(key) + len(value)
	return &mNode{key, value, make([]*mNode, height)}
}

func (p *DB) findGreaterOrEqual(key []byte, prev []*mNode) *mNode {
	x := p.head
	n := p.maxHeight - 1
	for {
		next := x.Next(n)
		if next != nil && p.cmp.Compare(next.key, key) < 0 {
			// Keep searching in this list
			x = next
		} else {
			if prev != nil {
				prev[n] = x
			}
			if n == 0 {
				return next
			}
			n--
		}
	}
	return nil
}

func (p *DB) findLessThan(key []byte) *mNode {
	x := p.head
	n := p.maxHeight - 1
	for {
		next := x.Next(n)
		if next == nil || p.cmp.Compare(next.key, key) >= 0 {
			if n == 0 {
				return x
			}
			n--
		} else {
			x = next
		}
	}
	return nil
}

func (p *DB) findLast() *mNode {
	x := p.head
	n := p.maxHeight - 1
	for {
		next := x.Next(n)
		if next == nil {
			if n == 0 {
				return x
			}
			n--
		} else {
			x = next
		}
	}
	return nil
}

func (p *DB) randHeight() int {
	const branching = 4
	n := 1
	for n < tMaxHeight && p.rnd.Int()%branching == 0 {
		n++
	}
	return n
}

type Iterator struct {
	p      *DB
	node   *mNode
	onLast bool
}

func (i *Iterator) Valid() bool {
	return i.node != nil && i.node != i.p.head
}

func (i *Iterator) First() bool {
	i.node = i.p.head.Next(0)
	return i.Valid()
}

func (i *Iterator) Last() bool {
	i.node = i.p.findLast()
	return i.Valid()
}

func (i *Iterator) Seek(key []byte) (r bool) {
	i.node = i.p.findGreaterOrEqual(key, nil)
	return i.Valid()
}

func (i *Iterator) Next() bool {
	if i.node == nil {
		return i.First()
	}
	i.node = i.node.Next(0)
	res := i.Valid()
	if !res {
		i.onLast = true
	}
	return res
}

func (i *Iterator) Prev() bool {
	if i.node == nil {
		if i.onLast {
			return i.Last()
		}
		return false
	}
	i.node = i.p.findLessThan(i.node.key)
	if i.node == i.p.head {
		i.node = nil
	}
	return i.Valid()
}

func (i *Iterator) Key() []byte {
	if !i.Valid() {
		return nil
	}
	return i.node.key
}

func (i *Iterator) Value() []byte {
	if !i.Valid() {
		return nil
	}
	return i.node.value
}

func (i *Iterator) Error() error { return nil }
<|endoftext|>


<commit_before>// Copyright 2014 The Serpepiced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package web

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"sort"
	"sync"
	"time"

	"github.com/zenoss/glog"

	"github.com/control-center/serviced/coordinator/client"
	"github.com/control-center/serviced/utils"
	"github.com/control-center/serviced/zzk"
	"github.com/control-center/serviced/zzk/registry"
)

var (
	localpepregistry = pepRegistry{lookup: make(map[string]*pepInfo), pepWatch: make(map[string]chan<- interface{})}
)

type pepInfo struct {
	sync.RWMutex
	endpoints []pepEndpointInfo
	counter   int
}

func newPepInfo() *pepInfo {
	return &pepInfo{endpoints: make([]pepEndpointInfo, 0)}
}

func (pepi *pepInfo) GetNext() (pepEndpointInfo, error) {
	pepi.Lock()
	defer pepi.Unlock()
	if len(pepi.endpoints) == 0 {
		return pepEndpointInfo{}, errors.New("no public endpoint endpoints available")
	}
	pep := pepi.endpoints[pepi.counter%len(pepi.endpoints)]
	pepi.counter++
	return pep, nil
}

type pepEndpointInfo struct {
	hostIP    string
	epPort    uint16
	privateIP string
	serviceID string
}

func createpepEndpointInfo(pep *registry.PublicEndpoint) pepEndpointInfo {
	return pepEndpointInfo{
		hostIP:    pep.HostIP,
		epPort:    pep.ContainerPort,
		privateIP: pep.ContainerIP,
		serviceID: pep.ServiceID,
	}
}

//pepRegistry keeps track of all current known public endpoints and their corresponding target endpoints
type pepRegistry struct {
	sync.RWMutex
	lookup   map[string]*pepInfo           //maps pep key (name-type) to all availabe target endpoints
	pepWatch map[string]chan<- interface{} //watches to ZK public endpoint dir Channel is to cancel watch
}

func (pr *pepRegistry) getWatch(path string) (chan<- interface{}, bool) {
	pr.RLock()
	defer pr.RUnlock()
	channel, found := pr.pepWatch[path]
	return channel, found
}

func (pr *pepRegistry) setWatch(path string, cancel chan<- interface{}) {
	pr.RLock()
	defer pr.RUnlock()
	pr.pepWatch[path] = cancel
}

func (pr *pepRegistry) deleteWatch(path string) {
	pr.Lock()
	defer pr.Unlock()
	delete(pr.pepWatch, path)
}

//get returns a pepInfo, bool is true or false if path is found
func (pr *pepRegistry) get(path string) (*pepInfo, bool) {
	pr.RLock()
	defer pr.RUnlock()
	pepInfo, found := pr.lookup[path]
	if !found {
		glog.V(4).Infof("path %v not found in map %v", path, pr.lookup)
	}
	return pepInfo, found
}

//setPublicEndpointInfo sets/replaces all the endpoints available for a public endpoint
func (pr *pepRegistry) setPublicEndpointInfo(path string, pepInfo *pepInfo) {
	pr.Lock()
	defer pr.Unlock()
	pr.lookup[path] = pepInfo
	glog.Infof("setPublicEndpointInfo adding Public Endpoint %v with backend: %#v", path, pepInfo)
}

func areEqual(s1, s2 []string) bool {

	if s1 == nil || s2 == nil {
		return false
	}
	if len(s1) != len(s2) {
		return false
	}
	for i, v := range s1 {
		if v != s2[i] {
			return false
		}
	}
	return true
}

func (sc *ServiceConfig) syncPublicEndpoints(shutdown <-chan interface{}) error {
	glog.Info("syncPublicEndpoints starting")

	glog.V(2).Infof("getting pool based connection")
	// public endpoints are at the root level (not pool aware)
	poolBasedConn, err := zzk.GetLocalConnection("/")
	if err != nil {
		glog.Errorf("syncPublicEndpoints - Error getting pool based zk connection: %v", err)
		return err
	}

	glog.V(2).Infof("creating zkPepRegistry")
	zkPepRegistry, err := registry.PublicEndpointRegistry(poolBasedConn)
	if err != nil {
		glog.Errorf("syncPublicEndpoints - Error getting public endpoint registry: %v", err)
		return err
	}

	processPublicEndpoints := func(conn client.Connection, parentPath string, childIDs ...string) {
		glog.V(1).Infof("processPublicEndpoints STARTING for parentPath:%s childIDs:%v", parentPath, childIDs)

		currentPEPs := make(map[string]struct{})
		//watch any new public endpoint nodes
		for _, pepID := range childIDs {
			pepPath := fmt.Sprintf("%s/%s", parentPath, pepID)
			currentPEPs[pepPath] = struct{}{}
			if _, found := localpepregistry.getWatch(pepPath); !found {
				glog.Infof("processing public endpoint watch: %s", pepPath)
				cancelChan := make(chan interface{})
				localpepregistry.setWatch(pepPath, cancelChan)
				go func(pepID string) {
					defer localpepregistry.deleteWatch(pepPath)
					glog.Infof("starting public endpoint watch: %s", pepPath)
					var lastChildIDs []string
					processPublicEndpoint := func(conn client.Connection, parentPath string, childIDs ...string) {

						glog.V(1).Infof("watching:%s %+v", parentPath, childIDs)
						if !sort.StringsAreSorted(childIDs) {
							sort.Strings(childIDs)
						}
						if areEqual(lastChildIDs, childIDs) {
							glog.V(1).Infof("not processing children because they are the same as last ones: %v = %v ", lastChildIDs, childIDs)
							return
						}
						glog.V(1).Infof("processing public endpoint parent %v; children %v", parentPath, childIDs)
						pr, err := registry.PublicEndpointRegistry(conn)
						if err != nil {
							glog.Errorf("processPublicEndpoint - Error getting public endpoint registry: %v", err)
							return
						}

						errors := false
						pepEndpoints := newPepInfo()
						for _, child := range childIDs {
							pepEndpoint, err := pr.GetItem(conn, parentPath+"/"+child)
							if err != nil {
								errors = true
								glog.Errorf("processPublicEndpoint - Error getting public endpoint for %v/%v: %v", parentPath, child, err)
								continue
							}
							glog.V(1).Infof("Processing public endpoint %s/%s: %#v", parentPath, child, pepEndpoint)
							pepInfo := createpepEndpointInfo(pepEndpoint)
							pepEndpoints.endpoints = append(pepEndpoints.endpoints, pepInfo)
						}
						localpepregistry.setPublicEndpointInfo(pepID, pepEndpoints)
						if !errors {
							lastChildIDs = childIDs
						}
					}
					// loop if error. If watch is cancelled will not return error. Blocking call
					for {
						err := zkPepRegistry.WatchKey(conn, pepID, cancelChan, processPublicEndpoint, pepWatchError)
						if err == nil {
							glog.Infof("Public Endpoint Registry Watch %s Stopped", pepID)
							return
						}
						glog.Infof("Public Endpoint Registry Watch %s Restarting due to %v", pepID, err)
						time.Sleep(500 * time.Millisecond)
					}

				}(pepID)
			} else {
				glog.V(2).Infof("public endpoint %s already being watched", pepPath)
			}
		}

		//cancel watching any public endpoint nodes that are no longer
		for previousPEP, cancel := range localpepregistry.pepWatch {
			if _, found := currentPEPs[previousPEP]; !found {
				glog.Infof("Cancelling public endpoint watch for %s}", previousPEP)
				delete(localpepregistry.pepWatch, previousPEP)
				cancel <- true
				close(cancel)
			}
		}
	}
	cancelChan := make(chan interface{})
	for {
		glog.Info("Running zkPepRegistry.WatchRegistry")

		watchStopped := make(chan error)

		go func() {
			watchStopped <- zkPepRegistry.WatchRegistry(poolBasedConn, cancelChan, processPublicEndpoints, pepWatchError)
		}()
		select {
		case <-shutdown:
			close(cancelChan)
			for pep, ch := range localpepregistry.pepWatch {
				glog.V(1).Infof("Shutdown closing watch for %v", pep)
				close(ch)
			}
			return nil
		case err := <-watchStopped:
			if err != nil {
				glog.Infof("Public Endpoint Registry Watch Restarting due to %v", err)
				time.Sleep(500 * time.Millisecond)
			}

		}
	}
}

func pepWatchError(path string, err error) {
	glog.Warningf("processing pepWatchError on %s: %v", path, err)
}

// Lookup the appropriate public endpoint and forward the request to it.
// serviceIDs is the list of services on which the public endpoint is enabled
func (sc *ServiceConfig) publicendpointhandler(w http.ResponseWriter, r *http.Request, pepKey registry.PublicEndpointKey, serviceIDs map[string]struct{}) {
	start := time.Now()
	glog.V(1).Infof("publicendpointhandler handling: %+v", r)

	defer func() {
		glog.V(1).Infof("Time to process %s public endpoint request %v: %v", pepKey, r.URL, time.Since(start))
	}()

	pepEP, err := sc.getPublicEndpoint(string(pepKey))
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	// check that the endpoint's service id is in the list of public endpoints that are enabled.
	// This happens if more than one tenant has the same public endpoint. One tenant is off and the one that is running has
	// has disabled this public endpoint.
	if _, found := serviceIDs[pepEP.serviceID]; !found {
		http.Error(w, fmt.Sprintf("public endpoint %s not available", pepKey), http.StatusNotFound)
		return
	}

	rp := getReverseProxy(pepEP.hostIP, sc.muxPort, pepEP.privateIP, pepEP.epPort, sc.muxTLS && (sc.muxPort > 0))
	glog.V(1).Infof("Time to set up %s public endpoint proxy for %v: %v", pepKey, r.URL, time.Since(start))

	// Set up the X-Forwarded-Proto header so that downstream servers know
	// the request originated as HTTPS.
	if _, ok := r.Header["X-Forwarded-Proto"]; !ok {
		r.Header.Set("X-Forwarded-Proto", "https")
	}

	rp.ServeHTTP(w, r)
	return
}

func (sc *ServiceConfig) getPublicEndpoint(pepKey string) (pepEndpointInfo, error) {
	pepInfo, found := localpepregistry.get(pepKey)
	if !found {
		glog.V(4).Infof("public endpoint not enabled %s: %v", pepKey)
		return pepEndpointInfo{}, fmt.Errorf("service associated with public endpoint %v is not running", pepKey)
	}

	// round robin through available endpoints
	pepEP, err := pepInfo.GetNext()
	if err != nil {
		glog.V(4).Infof("no endpoint found for public endpoint %s: %v", pepKey, err)
		return pepEndpointInfo{}, err
	}

	return pepEP, nil
}

var reverseProxies map[string]*httputil.ReverseProxy
var reverseProxiesLock sync.Mutex
var localAddrs map[string]struct{}

func init() {
	var err error
	reverseProxies = make(map[string]*httputil.ReverseProxy)
	hostAddrs, err := utils.GetIPv4Addresses()
	if err != nil {
		panic(err)
	}
	localAddrs = make(map[string]struct{})
	for _, host := range hostAddrs {
		localAddrs[host] = struct{}{}
	}
}

func getReverseProxy(hostIP string, muxPort int, privateIP string, privatePort uint16, useTLS bool) *httputil.ReverseProxy {

	var remoteAddr string

	reverseProxiesLock.Lock()
	defer reverseProxiesLock.Unlock()

	_, isLocalContainer := localAddrs[hostIP]
	if isLocalContainer {
		remoteAddr = fmt.Sprintf("%s:%d", privateIP, privatePort)
	} else {
		remoteAddr = fmt.Sprintf("%s:%d", hostIP, muxPort)
	}

	key := fmt.Sprintf("%s,%d,%s,%d,%v", remoteAddr, muxPort, privateIP, privatePort, useTLS)
	proxy, ok := reverseProxies[key]
	if ok {
		return proxy
	}

	rpurl := url.URL{Scheme: "http", Host: remoteAddr}

	glog.V(1).Infof("publicendpointhandler reverse proxy to: %v", rpurl)

	transport := &http.Transport{Proxy: http.ProxyFromEnvironment}
	transport.Dial = func(network, addr string) (remote net.Conn, err error) {
		if useTLS && !isLocalContainer { // Only do TLS if connecting to a TCPMux
			config := tls.Config{InsecureSkipVerify: true}
			glog.V(1).Infof("public endpoint about to dial %s", remoteAddr)
			remote, err = tls.Dial("tcp4", remoteAddr, &config)
		} else {
			glog.V(1).Info("public endpoint about to dial %s", remoteAddr)
			remote, err = net.Dial("tcp4", remoteAddr)
		}
		if err != nil {
			return nil, err
		}

		if muxPort > 0 && !isLocalContainer {
			//TODO: move this check to happen sooner
			if len(privateIP) == 0 {
				return nil, fmt.Errorf("missing endpoint")
			}
			muxAddr := fmt.Sprintf("%s:%d\n", privateIP, privatePort)
			glog.V(1).Infof("public endpoint muxing to %s", muxAddr)
			io.WriteString(remote, muxAddr)

		}
		return remote, nil
	}
	rp := httputil.NewSingleHostReverseProxy(&rpurl)
	rp.Transport = transport
	rp.FlushInterval = time.Millisecond * 10

	reverseProxies[key] = rp
	return rp

}
<commit_msg>CC-1818: Fail nicer when unable to det. IPv4 addrs<commit_after>// Copyright 2014 The Serpepiced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package web

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"sort"
	"sync"
	"time"

	"github.com/zenoss/glog"

	"github.com/control-center/serviced/coordinator/client"
	"github.com/control-center/serviced/utils"
	"github.com/control-center/serviced/zzk"
	"github.com/control-center/serviced/zzk/registry"
)

var (
	localpepregistry = pepRegistry{lookup: make(map[string]*pepInfo), pepWatch: make(map[string]chan<- interface{})}
)

type pepInfo struct {
	sync.RWMutex
	endpoints []pepEndpointInfo
	counter   int
}

func newPepInfo() *pepInfo {
	return &pepInfo{endpoints: make([]pepEndpointInfo, 0)}
}

func (pepi *pepInfo) GetNext() (pepEndpointInfo, error) {
	pepi.Lock()
	defer pepi.Unlock()
	if len(pepi.endpoints) == 0 {
		return pepEndpointInfo{}, errors.New("no public endpoint endpoints available")
	}
	pep := pepi.endpoints[pepi.counter%len(pepi.endpoints)]
	pepi.counter++
	return pep, nil
}

type pepEndpointInfo struct {
	hostIP    string
	epPort    uint16
	privateIP string
	serviceID string
}

func createpepEndpointInfo(pep *registry.PublicEndpoint) pepEndpointInfo {
	return pepEndpointInfo{
		hostIP:    pep.HostIP,
		epPort:    pep.ContainerPort,
		privateIP: pep.ContainerIP,
		serviceID: pep.ServiceID,
	}
}

//pepRegistry keeps track of all current known public endpoints and their corresponding target endpoints
type pepRegistry struct {
	sync.RWMutex
	lookup   map[string]*pepInfo           //maps pep key (name-type) to all availabe target endpoints
	pepWatch map[string]chan<- interface{} //watches to ZK public endpoint dir Channel is to cancel watch
}

func (pr *pepRegistry) getWatch(path string) (chan<- interface{}, bool) {
	pr.RLock()
	defer pr.RUnlock()
	channel, found := pr.pepWatch[path]
	return channel, found
}

func (pr *pepRegistry) setWatch(path string, cancel chan<- interface{}) {
	pr.RLock()
	defer pr.RUnlock()
	pr.pepWatch[path] = cancel
}

func (pr *pepRegistry) deleteWatch(path string) {
	pr.Lock()
	defer pr.Unlock()
	delete(pr.pepWatch, path)
}

//get returns a pepInfo, bool is true or false if path is found
func (pr *pepRegistry) get(path string) (*pepInfo, bool) {
	pr.RLock()
	defer pr.RUnlock()
	pepInfo, found := pr.lookup[path]
	if !found {
		glog.V(4).Infof("path %v not found in map %v", path, pr.lookup)
	}
	return pepInfo, found
}

//setPublicEndpointInfo sets/replaces all the endpoints available for a public endpoint
func (pr *pepRegistry) setPublicEndpointInfo(path string, pepInfo *pepInfo) {
	pr.Lock()
	defer pr.Unlock()
	pr.lookup[path] = pepInfo
	glog.Infof("setPublicEndpointInfo adding Public Endpoint %v with backend: %#v", path, pepInfo)
}

func areEqual(s1, s2 []string) bool {

	if s1 == nil || s2 == nil {
		return false
	}
	if len(s1) != len(s2) {
		return false
	}
	for i, v := range s1 {
		if v != s2[i] {
			return false
		}
	}
	return true
}

func (sc *ServiceConfig) syncPublicEndpoints(shutdown <-chan interface{}) error {
	glog.Info("syncPublicEndpoints starting")

	glog.V(2).Infof("getting pool based connection")
	// public endpoints are at the root level (not pool aware)
	poolBasedConn, err := zzk.GetLocalConnection("/")
	if err != nil {
		glog.Errorf("syncPublicEndpoints - Error getting pool based zk connection: %v", err)
		return err
	}

	glog.V(2).Infof("creating zkPepRegistry")
	zkPepRegistry, err := registry.PublicEndpointRegistry(poolBasedConn)
	if err != nil {
		glog.Errorf("syncPublicEndpoints - Error getting public endpoint registry: %v", err)
		return err
	}

	processPublicEndpoints := func(conn client.Connection, parentPath string, childIDs ...string) {
		glog.V(1).Infof("processPublicEndpoints STARTING for parentPath:%s childIDs:%v", parentPath, childIDs)

		currentPEPs := make(map[string]struct{})
		//watch any new public endpoint nodes
		for _, pepID := range childIDs {
			pepPath := fmt.Sprintf("%s/%s", parentPath, pepID)
			currentPEPs[pepPath] = struct{}{}
			if _, found := localpepregistry.getWatch(pepPath); !found {
				glog.Infof("processing public endpoint watch: %s", pepPath)
				cancelChan := make(chan interface{})
				localpepregistry.setWatch(pepPath, cancelChan)
				go func(pepID string) {
					defer localpepregistry.deleteWatch(pepPath)
					glog.Infof("starting public endpoint watch: %s", pepPath)
					var lastChildIDs []string
					processPublicEndpoint := func(conn client.Connection, parentPath string, childIDs ...string) {

						glog.V(1).Infof("watching:%s %+v", parentPath, childIDs)
						if !sort.StringsAreSorted(childIDs) {
							sort.Strings(childIDs)
						}
						if areEqual(lastChildIDs, childIDs) {
							glog.V(1).Infof("not processing children because they are the same as last ones: %v = %v ", lastChildIDs, childIDs)
							return
						}
						glog.V(1).Infof("processing public endpoint parent %v; children %v", parentPath, childIDs)
						pr, err := registry.PublicEndpointRegistry(conn)
						if err != nil {
							glog.Errorf("processPublicEndpoint - Error getting public endpoint registry: %v", err)
							return
						}

						errors := false
						pepEndpoints := newPepInfo()
						for _, child := range childIDs {
							pepEndpoint, err := pr.GetItem(conn, parentPath+"/"+child)
							if err != nil {
								errors = true
								glog.Errorf("processPublicEndpoint - Error getting public endpoint for %v/%v: %v", parentPath, child, err)
								continue
							}
							glog.V(1).Infof("Processing public endpoint %s/%s: %#v", parentPath, child, pepEndpoint)
							pepInfo := createpepEndpointInfo(pepEndpoint)
							pepEndpoints.endpoints = append(pepEndpoints.endpoints, pepInfo)
						}
						localpepregistry.setPublicEndpointInfo(pepID, pepEndpoints)
						if !errors {
							lastChildIDs = childIDs
						}
					}
					// loop if error. If watch is cancelled will not return error. Blocking call
					for {
						err := zkPepRegistry.WatchKey(conn, pepID, cancelChan, processPublicEndpoint, pepWatchError)
						if err == nil {
							glog.Infof("Public Endpoint Registry Watch %s Stopped", pepID)
							return
						}
						glog.Infof("Public Endpoint Registry Watch %s Restarting due to %v", pepID, err)
						time.Sleep(500 * time.Millisecond)
					}

				}(pepID)
			} else {
				glog.V(2).Infof("public endpoint %s already being watched", pepPath)
			}
		}

		//cancel watching any public endpoint nodes that are no longer
		for previousPEP, cancel := range localpepregistry.pepWatch {
			if _, found := currentPEPs[previousPEP]; !found {
				glog.Infof("Cancelling public endpoint watch for %s}", previousPEP)
				delete(localpepregistry.pepWatch, previousPEP)
				cancel <- true
				close(cancel)
			}
		}
	}
	cancelChan := make(chan interface{})
	for {
		glog.Info("Running zkPepRegistry.WatchRegistry")

		watchStopped := make(chan error)

		go func() {
			watchStopped <- zkPepRegistry.WatchRegistry(poolBasedConn, cancelChan, processPublicEndpoints, pepWatchError)
		}()
		select {
		case <-shutdown:
			close(cancelChan)
			for pep, ch := range localpepregistry.pepWatch {
				glog.V(1).Infof("Shutdown closing watch for %v", pep)
				close(ch)
			}
			return nil
		case err := <-watchStopped:
			if err != nil {
				glog.Infof("Public Endpoint Registry Watch Restarting due to %v", err)
				time.Sleep(500 * time.Millisecond)
			}

		}
	}
}

func pepWatchError(path string, err error) {
	glog.Warningf("processing pepWatchError on %s: %v", path, err)
}

// Lookup the appropriate public endpoint and forward the request to it.
// serviceIDs is the list of services on which the public endpoint is enabled
func (sc *ServiceConfig) publicendpointhandler(w http.ResponseWriter, r *http.Request, pepKey registry.PublicEndpointKey, serviceIDs map[string]struct{}) {
	start := time.Now()
	glog.V(1).Infof("publicendpointhandler handling: %+v", r)

	defer func() {
		glog.V(1).Infof("Time to process %s public endpoint request %v: %v", pepKey, r.URL, time.Since(start))
	}()

	pepEP, err := sc.getPublicEndpoint(string(pepKey))
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	// check that the endpoint's service id is in the list of public endpoints that are enabled.
	// This happens if more than one tenant has the same public endpoint. One tenant is off and the one that is running has
	// has disabled this public endpoint.
	if _, found := serviceIDs[pepEP.serviceID]; !found {
		http.Error(w, fmt.Sprintf("public endpoint %s not available", pepKey), http.StatusNotFound)
		return
	}

	rp := getReverseProxy(pepEP.hostIP, sc.muxPort, pepEP.privateIP, pepEP.epPort, sc.muxTLS && (sc.muxPort > 0))
	glog.V(1).Infof("Time to set up %s public endpoint proxy for %v: %v", pepKey, r.URL, time.Since(start))

	// Set up the X-Forwarded-Proto header so that downstream servers know
	// the request originated as HTTPS.
	if _, ok := r.Header["X-Forwarded-Proto"]; !ok {
		r.Header.Set("X-Forwarded-Proto", "https")
	}

	rp.ServeHTTP(w, r)
	return
}

func (sc *ServiceConfig) getPublicEndpoint(pepKey string) (pepEndpointInfo, error) {
	pepInfo, found := localpepregistry.get(pepKey)
	if !found {
		glog.V(4).Infof("public endpoint not enabled %s: %v", pepKey)
		return pepEndpointInfo{}, fmt.Errorf("service associated with public endpoint %v is not running", pepKey)
	}

	// round robin through available endpoints
	pepEP, err := pepInfo.GetNext()
	if err != nil {
		glog.V(4).Infof("no endpoint found for public endpoint %s: %v", pepKey, err)
		return pepEndpointInfo{}, err
	}

	return pepEP, nil
}

var reverseProxies map[string]*httputil.ReverseProxy
var reverseProxiesLock sync.Mutex
var localAddrs map[string]struct{}

func init() {
	var err error
	reverseProxies = make(map[string]*httputil.ReverseProxy)
	hostAddrs, err := utils.GetIPv4Addresses()
	if err != nil {
		glog.Fatal(err)
	}
	localAddrs = make(map[string]struct{})
	for _, host := range hostAddrs {
		localAddrs[host] = struct{}{}
	}
}

func getReverseProxy(hostIP string, muxPort int, privateIP string, privatePort uint16, useTLS bool) *httputil.ReverseProxy {

	var remoteAddr string

	reverseProxiesLock.Lock()
	defer reverseProxiesLock.Unlock()

	_, isLocalContainer := localAddrs[hostIP]
	if isLocalContainer {
		remoteAddr = fmt.Sprintf("%s:%d", privateIP, privatePort)
	} else {
		remoteAddr = fmt.Sprintf("%s:%d", hostIP, muxPort)
	}

	key := fmt.Sprintf("%s,%d,%s,%d,%v", remoteAddr, muxPort, privateIP, privatePort, useTLS)
	proxy, ok := reverseProxies[key]
	if ok {
		return proxy
	}

	rpurl := url.URL{Scheme: "http", Host: remoteAddr}

	glog.V(1).Infof("publicendpointhandler reverse proxy to: %v", rpurl)

	transport := &http.Transport{Proxy: http.ProxyFromEnvironment}
	transport.Dial = func(network, addr string) (remote net.Conn, err error) {
		if useTLS && !isLocalContainer { // Only do TLS if connecting to a TCPMux
			config := tls.Config{InsecureSkipVerify: true}
			glog.V(1).Infof("public endpoint about to dial %s", remoteAddr)
			remote, err = tls.Dial("tcp4", remoteAddr, &config)
		} else {
			glog.V(1).Info("public endpoint about to dial %s", remoteAddr)
			remote, err = net.Dial("tcp4", remoteAddr)
		}
		if err != nil {
			return nil, err
		}

		if muxPort > 0 && !isLocalContainer {
			//TODO: move this check to happen sooner
			if len(privateIP) == 0 {
				return nil, fmt.Errorf("missing endpoint")
			}
			muxAddr := fmt.Sprintf("%s:%d\n", privateIP, privatePort)
			glog.V(1).Infof("public endpoint muxing to %s", muxAddr)
			io.WriteString(remote, muxAddr)

		}
		return remote, nil
	}
	rp := httputil.NewSingleHostReverseProxy(&rpurl)
	rp.Transport = transport
	rp.FlushInterval = time.Millisecond * 10

	reverseProxies[key] = rp
	return rp

}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

func updateWatch(root string) {
	addWatches(root)
	removeWatches()
}
func addWatches(root string) {
	if rootWatch != root {
		// TODO: set gopath...
		adjustRoot(root)
	}

	watchNestedPaths(root)
}
func adjustRoot(root string) {
	fmt.Println("Watching new root:", root)
	for path, _ := range watched {
		removeWatch(path)
	}
	rootWatch = root
	watch(root)
}
func watchNestedPaths(root string) {
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if matches, _ := filepath.Glob(filepath.Join(path, "*test.go")); len(matches) > 0 {
			watch(path)
		}
		return nil
	})
}
func watch(path string) {
	if !watching(path) {
		fmt.Println("Watching:", path)
		watched[path] = true
		watcher.Watch(path)
	}
}

func watching(path string) bool {
	for w, _ := range watched {
		if w == path {
			return true
		}
	}
	return false
}

func removeWatches() {
	for path, _ := range watched {
		if !exists(path) {
			removeWatch(path)
		}
	}
}
func removeWatch(path string) {
	delete(watched, path)
	watcher.RemoveWatch(path)
	fmt.Println("No longer watching:", path)
}

func exists(directory string) bool {
	info, err := os.Stat(directory)
	return err == nil && info.IsDir()
}

func reactToChanges() {
	busy := false
	done := make(chan bool)
	ready := make(chan bool)

	for {
		select {
		case ev := <-watcher.Event:
			updateWatch(rootWatch)
			if strings.HasSuffix(ev.Name, ".go") && !busy {
				busy = true
				go runTests(done)
			}

		case err := <-watcher.Error:
			fmt.Println(err)

		case <-done:
			// TODO: rethink this delay?
			time.AfterFunc(1500*time.Millisecond, func() {
				ready <- true
			})

		case <-ready:
			busy = false
		}
	}
}

func runTests(done chan bool) {
	results := []*PackageResult{}
	revision := md5.New()

	fmt.Println("")
	for path, _ := range watched {
		fmt.Printf("Running tests for: %s ...", path)
		if err := os.Chdir(path); err != nil {
			fmt.Println("Could not chdir to:", path)
			continue
		}

		exec.Command("go", "test", "-i").Run()
		output, _ := exec.Command("go", "test", "-v", "-timeout=-42s").CombinedOutput()
		stringOutput := string(output)
		io.WriteString(revision, stringOutput)
		packageIndex := strings.Index(path, "/src/")
		packageName := path[packageIndex+len("/src/"):]
		result := parsePackageResults(packageName, stringOutput)
		fmt.Printf("[%s]\n", result.Outcome)
		results = append(results, result)
	}

	output := CompleteOutput{
		Revision: hex.EncodeToString(revision.Sum(nil)),
		Packages: results,
	}
	serialized, err := json.Marshal(output)
	if err != nil {
		fmt.Println("Problem serializing json test results!", err) // panic?
	} else {
		latestOutput = string(serialized)
	}
	done <- true
}

type CompleteOutput struct {
	Packages []*PackageResult
	Revision string
}
<commit_msg>Brought back tests running at startup...<commit_after>package main

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

func updateWatch(root string) {
	addWatches(root)
	removeWatches()
}
func addWatches(root string) {
	if rootWatch != root {
		// TODO: set gopath...
		adjustRoot(root)
	}

	watchNestedPaths(root)
}
func adjustRoot(root string) {
	fmt.Println("Watching new root:", root)
	for path, _ := range watched {
		removeWatch(path)
	}
	rootWatch = root
	watch(root)
}
func watchNestedPaths(root string) {
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if matches, _ := filepath.Glob(filepath.Join(path, "*test.go")); len(matches) > 0 {
			watch(path)
		}
		return nil
	})
}
func watch(path string) {
	if !watching(path) {
		fmt.Println("Watching:", path)
		watched[path] = true
		watcher.Watch(path)
	}
}

func watching(path string) bool {
	for w, _ := range watched {
		if w == path {
			return true
		}
	}
	return false
}

func removeWatches() {
	for path, _ := range watched {
		if !exists(path) {
			removeWatch(path)
		}
	}
}
func removeWatch(path string) {
	delete(watched, path)
	watcher.RemoveWatch(path)
	fmt.Println("No longer watching:", path)
}

func exists(directory string) bool {
	info, err := os.Stat(directory)
	return err == nil && info.IsDir()
}

func reactToChanges() {
	done, ready := make(chan bool), make(chan bool)
	busy := true

	go runTests(done)

	for {
		select {
		case ev := <-watcher.Event:
			updateWatch(rootWatch)
			if strings.HasSuffix(ev.Name, ".go") && !busy {
				busy = true
				go runTests(done)
			}

		case err := <-watcher.Error:
			fmt.Println(err)

		case <-done:
			// TODO: rethink this delay?
			time.AfterFunc(1500*time.Millisecond, func() {
				ready <- true
			})

		case <-ready:
			busy = false
		}
	}
}

func runTests(done chan bool) {
	results := []*PackageResult{}
	revision := md5.New()

	fmt.Println("")
	for path, _ := range watched {
		fmt.Printf("Running tests for: %s ...", path)
		if err := os.Chdir(path); err != nil {
			fmt.Println("Could not chdir to:", path)
			continue
		}

		exec.Command("go", "test", "-i").Run()
		output, _ := exec.Command("go", "test", "-v", "-timeout=-42s").CombinedOutput()
		stringOutput := string(output)
		io.WriteString(revision, stringOutput)
		packageIndex := strings.Index(path, "/src/")
		packageName := path[packageIndex+len("/src/"):]
		result := parsePackageResults(packageName, stringOutput)
		fmt.Printf("[%s]\n", result.Outcome)
		results = append(results, result)
	}

	output := CompleteOutput{
		Revision: hex.EncodeToString(revision.Sum(nil)),
		Packages: results,
	}
	serialized, err := json.Marshal(output)
	if err != nil {
		fmt.Println("Problem serializing json test results!", err) // panic?
	} else {
		latestOutput = string(serialized)
	}
	done <- true
}

type CompleteOutput struct {
	Packages []*PackageResult
	Revision string
}
<|endoftext|>