<commit_before>package golog

import (
	"flag"
	"fmt"
	"os"
)

type MultiLogOuter struct {
	// TODO Add mutex.
	outers map[string]LogOuter
}

func (l *MultiLogOuter) String() string {
	return fmt.Sprint("\"", l.outers, "\"")
}

func (l *MultiLogOuter) Set(name string) bool {
	if file, err := os.Create(name); err != nil {
		os.Stderr.WriteString(
			fmt.Sprint("Error opening file for logging", name,
				": ", err))
		return false
	} else {
		l.AddLogOuter(name, NewWriterLogOuter(file))
		return true
	}

	panic("Code never reaches here, this mollifies the compiler.")
}

func (l *MultiLogOuter) AddLogOuter(key string, outer LogOuter) {
	// TODO Grab mutex.
	l.outers[key] = outer
}

func (l *MultiLogOuter) RemoveLogOuter(key string) {
	// TODO Be Go1 compatible. :)
	l.outers[key] = nil, false
}

func (l *MultiLogOuter) Output(m *LogMessage) {
	// TODO Grab mutex.
	for _, outer := range l.outers {
		outer.Output(m)
	}
}

var defaultLogOuters *MultiLogOuter = &MultiLogOuter{make(map[string]LogOuter)}

func NewDefaultMultiLogOuter() *MultiLogOuter {
	return &MultiLogOuter{
		outers: map[string]LogOuter{"default": defaultLogOuters},
	}
}

func init() {
	flag.Var(defaultLogOuters, "golog.logfile", "Log to given file - can "+
		"be provided multiple times to log to multiple files")
}
<commit_msg>MultiLogOuter is an interface<commit_after>package golog

import (
	"flag"
	"fmt"
	"os"
)

type MultiLogOuter interface {
	LogOuter
	AddLogOuter(key string, outer LogOuter)
	RemoveLogOuter(key string)
}

type multiLogOuterImpl struct {
	// TODO Add mutex.
	outers map[string]LogOuter
}

func (l *multiLogOuterImpl) String() string {
	return fmt.Sprint("\"", l.outers, "\"")
}

func (l *multiLogOuterImpl) Set(name string) bool {
	if file, err := os.Create(name); err != nil {
		os.Stderr.WriteString(
			fmt.Sprint("Error opening file for logging", name,
				": ", err))
		return false
	} else {
		l.AddLogOuter(name, NewWriterLogOuter(file))
		return true
	}

	panic("Code never reaches here, this mollifies the compiler.")
}

func (l *multiLogOuterImpl) AddLogOuter(key string, outer LogOuter) {
	// TODO Grab mutex.
	l.outers[key] = outer
}

func (l *multiLogOuterImpl) RemoveLogOuter(key string) {
	// TODO Be Go1 compatible. :)
	l.outers[key] = nil, false
}

func (l *multiLogOuterImpl) Output(m *LogMessage) {
	// TODO Grab mutex.
	for _, outer := range l.outers {
		outer.Output(m)
	}
}

var defaultLogOuters MultiLogOuter = &multiLogOuterImpl{make(map[string]LogOuter)}

func NewDefaultMultiLogOuter() MultiLogOuter {
	return &multiLogOuterImpl{
		outers: map[string]LogOuter{"default": defaultLogOuters},
	}
}

func init() {
	// TODO Find a way to export this?
	flag.Var(defaultLogOuters.(*multiLogOuterImpl), "golog.logfile",
		"Log to given file - can be provided multiple times to log "+
			"to multiple files")
}
<|endoftext|>


<commit_before>package golog

import (
	"flag"
	"fmt"
	"os"
)

type MultiLogOuter interface {
	LogOuter
	AddLogOuter(key string, outer LogOuter)
	RemoveLogOuter(key string)
}

type MultiLogOuterFlag interface {
	MultiLogOuter
	flag.Value
}

type multiLogOuterImpl struct {
	// TODO Add mutex.
	outers map[string]LogOuter
}

func (l *multiLogOuterImpl) String() string {
	// TODO better string
	return fmt.Sprint("\"", l.outers, "\"")
}

func (l *multiLogOuterImpl) Set(name string) bool {
	if outer, err := NewFileLogOuter(name); err != nil {
		os.Stderr.WriteString(
			fmt.Sprint("Error opening file for logging", name,
				": ", err))
		return false
	} else {
		l.AddLogOuter(name, outer)
		return true
	}

	panic("Code never reaches here, this mollifies the compiler.")
}

func (l *multiLogOuterImpl) AddLogOuter(key string, outer LogOuter) {
	// TODO Grab mutex.
	l.outers[key] = outer
}

func (l *multiLogOuterImpl) RemoveLogOuter(key string) {
	// TODO Grab mutex.
	// TODO Be Go1 compatible. :)
	l.outers[key] = nil, false
}

func (l *multiLogOuterImpl) Output(m *LogMessage) {
	// TODO Grab mutex.
	for _, outer := range l.outers {
		outer.Output(m)
	}
}

var defaultLogOuters MultiLogOuterFlag = NewMultiLogOuter()

func NewDefaultMultiLogOuter() MultiLogOuterFlag {
	return &multiLogOuterImpl{
		outers: map[string]LogOuter{"default": defaultLogOuters},
	}
}

func NewMultiLogOuter() MultiLogOuterFlag {
	return &multiLogOuterImpl{make(map[string]LogOuter)}
}

func init() {
	flag.Var(defaultLogOuters, "golog.logfile",
		"Log to given file - can be provided multiple times to log "+
			"to multiple files")
}
<commit_msg>Comment MultiLogOuter<commit_after>package golog

import (
	"flag"
	"fmt"
	"os"
)

// A MultiLogOuter is a LogOuter with multiple keyed LogOuters. All functions
// should be safe to call in a multi-threaded environment.
type MultiLogOuter interface {
	LogOuter
	// Add the LogOuter, associating it with the key.
	AddLogOuter(key string, outer LogOuter)
	// Remove the LogOuter associated with the key.
	RemoveLogOuter(key string)
}

// A MultiLogOuter than can also be used as a flag for setting logfiles.
// For example, it is possible to use a logger other than default via:
// 	var myOuter MultiLogOuterFlag = NewMultiLogOuter()
//
// 	func init() {
// 		flag.Var(myOuter,
// 			"mypack.logfile",
// 			"Log to file - can be provided multiple times")
// 	}
type MultiLogOuterFlag interface {
	MultiLogOuter
	flag.Value
}

type multiLogOuterImpl struct {
	// TODO Add mutex.
	outers map[string]LogOuter
}

func (l *multiLogOuterImpl) String() string {
	// TODO better string
	return fmt.Sprint("\"", l.outers, "\"")
}

func (l *multiLogOuterImpl) Set(name string) bool {
	if outer, err := NewFileLogOuter(name); err != nil {
		os.Stderr.WriteString(
			fmt.Sprint("Error opening file for logging", name,
				": ", err))
		return false
	} else {
		l.AddLogOuter(name, outer)
		return true
	}

	panic("Code never reaches here, this mollifies the compiler.")
}

func (l *multiLogOuterImpl) AddLogOuter(key string, outer LogOuter) {
	// TODO Grab mutex.
	l.outers[key] = outer
}

func (l *multiLogOuterImpl) RemoveLogOuter(key string) {
	// TODO Grab mutex.
	// TODO Be Go1 compatible. :)
	l.outers[key] = nil, false
}

func (l *multiLogOuterImpl) Output(m *LogMessage) {
	// TODO Grab mutex.
	for _, outer := range l.outers {
		outer.Output(m)
	}
}

var defaultLogOuters MultiLogOuterFlag = NewMultiLogOuter()

func NewDefaultMultiLogOuter() MultiLogOuterFlag {
	return &multiLogOuterImpl{
		outers: map[string]LogOuter{"default": defaultLogOuters},
	}
}

func NewMultiLogOuter() MultiLogOuterFlag {
	return &multiLogOuterImpl{make(map[string]LogOuter)}
}

func init() {
	flag.Var(defaultLogOuters, "golog.logfile",
		"Log to given file - can be provided multiple times to log "+
			"to multiple files")
}
<|endoftext|>


<commit_before>//problem 11.3

package chapter11

func BinarySearchAiEqI(a []int) int {
	lo, hi := 0, len(a) - 1

	for lo <= hi {
		mid := (lo + hi) / 2

		if a[mid] == mid {
			return mid
		} else if a[mid] < mid {
			lo = mid + 1
		} else {
			hi = mid - 1
		}
	}

	return -1
}
<commit_msg>gofmt binarysearchaieqi.go<commit_after>//problem 11.3

package chapter11

func BinarySearchAiEqI(a []int) int {
	lo, hi := 0, len(a)-1

	for lo <= hi {
		mid := (lo + hi) / 2

		if a[mid] == mid {
			return mid
		} else if a[mid] < mid {
			lo = mid + 1
		} else {
			hi = mid - 1
		}
	}

	return -1
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"github.com/simonz05/godis"
)

const HELP string = `
<!DOCTYPE html>

        <title>cdrv.ws</title>

cdrvws(1)                          CDRV.WS                          cdrvws(1)

NAME
    cdrvws: command line url shortener:

SYNOPSIS
    <command> | curl -F 'rvw=<-' http://cdrv.ws

`

const CHARS string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const BASE uint64 = uint64(len(CHARS))

var redis *godis.Client

func main() {
	connectToRedis()
	http.HandleFunc("/", route)
	startServer()
}

func encode(id uint64) string {
	encoded := ""
	for id > 0 {
		encoded += string(CHARS[id%BASE])
		id = id / BASE
	}
	return encoded
}

func createShortUrl(longurl string) (error, string) {
	shortid, err := redis.Incr("urlId")
	if err != nil {
		return err, ""
	}
	shorturl := encode(uint64(shortid))
	if err := redis.Set(shorturl, longurl); err != nil {
		return err, ""
	}
	return nil, shorturl
}

func expand(shorturl string) (error, string) {
	longurl, err := redis.Get(shorturl)
	if err != nil && err.Error() == "Nonexisting key" {
		return nil, ""
	} else if err != nil {
		return err, ""
	}
	return nil, longurl.String()
}

func connectToRedis() {
	rawurl := os.Getenv("REDISTOGO_URL")
	redisurl := url.URL{
		User: url.UserPassword("", ""),
	}
	if rawurl != "" {
		var err error
		_, err = (&redisurl).Parse(rawurl)
		if err != nil {
			log.Fatal("Could not parse redis url", err)
		}
	}
	password, _ := redisurl.User.Password()
	redis = godis.New(redisurl.Host, 0, password)
}

func startServer() {
	err := http.ListenAndServe(":" + os.Getenv("PORT"), nil)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

func route(w http.ResponseWriter, req *http.Request) {
	if req.Method == "GET" {
		if req.URL.String() == "/" {
			handleHome(w, req)
		} else {
			handleExpand(w, req)
		}
	} else if req.Method == "POST" {
		handleShorten(w, req)
	}
}

func handleHome(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, HELP)
}

func handleShorten(w http.ResponseWriter, req *http.Request) {
	err, shorturl := createShortUrl(req.FormValue("rvw"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fullurl := url.URL{
		Scheme: "http",
		Host:   req.Host,
		Path:   "/" + shorturl,
	}
	fmt.Fprintln(w, fullurl.String())
}

func handleExpand(w http.ResponseWriter, req *http.Request) {
	shorturl := strings.Trim(req.URL.String(), "/")
	err, longurl := expand(shorturl)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else if longurl == "" {
		http.NotFound(w, req)
	} else {
		http.Redirect(w, req, longurl, http.StatusMovedPermanently)
	}
}
<commit_msg>a little logging<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"github.com/simonz05/godis"
)

const HELP string = `
<!DOCTYPE html>

        <title>cdrv.ws</title>

cdrvws(1)                          CDRV.WS                          cdrvws(1)

NAME
    cdrvws: command line url shortener:

SYNOPSIS
    <command> | curl -F 'rvw=<-' http://cdrv.ws

`

const CHARS string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const BASE uint64 = uint64(len(CHARS))

var redis *godis.Client

func main() {
	connectToRedis()
	http.HandleFunc("/", route)
	startServer()
}

func encode(id uint64) string {
	encoded := ""
	for id > 0 {
		encoded += string(CHARS[id%BASE])
		id = id / BASE
	}
	return encoded
}

func createShortUrl(longurl string) (error, string) {
	shortid, err := redis.Incr("urlId")
	if err != nil {
		return err, ""
	}
	shorturl := encode(uint64(shortid))
	if err := redis.Set(shorturl, longurl); err != nil {
		return err, ""
	}
	return nil, shorturl
}

func expand(shorturl string) (error, string) {
	longurl, err := redis.Get(shorturl)
	if err != nil && err.Error() == "Nonexisting key" {
		return nil, ""
	} else if err != nil {
		return err, ""
	}
	return nil, longurl.String()
}

func connectToRedis() {
	rawurl := os.Getenv("REDISTOGO_URL")
	log.Printf("Redis to go url: %s\n", rawurl)
	redisurl := url.URL{
		User: url.UserPassword("", ""),
	}
	if rawurl != "" {
		var err error
		_, err = (&redisurl).Parse(rawurl)
		if err != nil {
			log.Fatal("Could not parse redis url", err)
		}
	}
	password, _ := redisurl.User.Password()
	log.Printf("Connecting to redis: %s\n", redisurl.String())
	redis = godis.New(redisurl.Host, 0, password)
}

func startServer() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("Starting on %s\n", port)
	err := http.ListenAndServe(":" + port, nil)
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

func route(w http.ResponseWriter, req *http.Request) {
	if req.Method == "GET" {
		if req.URL.String() == "/" {
			handleHome(w, req)
		} else {
			handleExpand(w, req)
		}
	} else if req.Method == "POST" {
		handleShorten(w, req)
	}
}

func handleHome(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(w, HELP)
}

func handleShorten(w http.ResponseWriter, req *http.Request) {
	err, shorturl := createShortUrl(req.FormValue("rvw"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fullurl := url.URL{
		Scheme: "http",
		Host:   req.Host,
		Path:   "/" + shorturl,
	}
	fmt.Fprintln(w, fullurl.String())
}

func handleExpand(w http.ResponseWriter, req *http.Request) {
	shorturl := strings.Trim(req.URL.String(), "/")
	err, longurl := expand(shorturl)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else if longurl == "" {
		http.NotFound(w, req)
	} else {
		http.Redirect(w, req, longurl, http.StatusMovedPermanently)
	}
}
<|endoftext|>


<commit_before>package collector

import (
	"encoding/json"
	"strings"

	flattener "github.com/karimra/go-map-flattener"
	"github.com/openconfig/gnmi/proto/gnmi"
)

// EventMsg //
type EventMsg struct {
	Name      string                 `json:"name,omitempty"` // measurement name
	Timestamp int64                  `json:"timestamp,omitempty"`
	Tags      map[string]string      `json:"tags,omitempty"`
	Values    map[string]interface{} `json:"values,omitempty"`
	Deletes   []string               `json:"deletes,omitempty"`
}

// ResponseToEventMsgs //
func ResponseToEventMsgs(name string, rsp *gnmi.SubscribeResponse, meta map[string]string) ([]*EventMsg, error) {
	if rsp == nil {
		return nil, nil
	}
	var err error
	evs := make([]*EventMsg, 0)
	switch rsp := rsp.Response.(type) {
	case *gnmi.SubscribeResponse_Update:
		namePrefix, prefixTags := TagsFromGNMIPath(rsp.Update.Prefix)
		for _, upd := range rsp.Update.Update {
			e := &EventMsg{
				Tags:   make(map[string]string),
				Values: make(map[string]interface{}),
			}
			e.Timestamp = rsp.Update.Timestamp
			e.Name = name
			for k, v := range prefixTags {
				e.Tags[k] = v
			}
			pathName, pTags := TagsFromGNMIPath(upd.Path)
			pathName = strings.TrimRight(namePrefix, "/") + "/" + strings.TrimLeft(pathName, "/")
			for k, v := range pTags {
				if vv, ok := e.Tags[k]; ok {
					if v != vv {
						e.Tags[pathName+":::"+k] = v
					}
					continue
				}
				e.Tags[k] = v
			}
			e.Values, err = getValueFlat(pathName, upd.GetVal())
			if err != nil {
				return nil, err
			}
			for k, v := range meta {
				if k == "format" {
					continue
				}
				if _, ok := e.Tags[k]; ok {
					e.Tags["meta:"+k] = v
					continue
				}
				e.Tags[k] = v
			}
			evs = append(evs, e)
		}

		if len(rsp.Update.Delete) > 0 {
			e := &EventMsg{
				Deletes: make([]string, 0, len(rsp.Update.Delete)),
			}
			e.Timestamp = rsp.Update.Timestamp
			e.Name = name
			for k, v := range prefixTags {
				e.Tags[k] = v
			}
			for k, v := range meta {
				if k == "format" {
					continue
				}
				if _, ok := e.Tags[k]; ok {
					e.Tags["meta:"+k] = v
					continue
				}
				e.Tags[k] = v
			}
			for _, del := range rsp.Update.Delete {
				e.Deletes = append(e.Deletes, gnmiPathToXPath(del))
			}
			evs = append(evs, e)
		}
	}
	return evs, nil
}

// TagsFromGNMIPath //
func TagsFromGNMIPath(p *gnmi.Path) (string, map[string]string) {
	if p == nil {
		return "", nil
	}
	tags := make(map[string]string)
	sb := strings.Builder{}
	if p.Origin != "" {
		sb.WriteString(p.Origin)
		sb.Write([]byte(":"))
	}
	for _, e := range p.Elem {
		if e.Name != "" {
			sb.Write([]byte("/"))
			sb.WriteString(e.Name)
		}
		if e.Key != nil {
			for k, v := range e.Key {
				if e.Name != "" {
					elems := strings.Split(e.Name, ":")
					if len(elems) > 0 {
						tags[elems[len(elems)-1]+"_"+k] = v
					}
				} else {
					tags[k] = v
				}
			}
		}
	}
	if p.GetTarget() != "" {
		tags["target"] = p.GetTarget()
	}
	return sb.String(), tags
}

func getValueFlat(prefix string, updValue *gnmi.TypedValue) (map[string]interface{}, error) {
	if updValue == nil {
		return nil, nil
	}
	var jsondata []byte
	values := make(map[string]interface{})
	switch updValue.Value.(type) {
	case *gnmi.TypedValue_AsciiVal:
		values[prefix] = updValue.GetAsciiVal()
	case *gnmi.TypedValue_BoolVal:
		values[prefix] = updValue.GetBoolVal()
	case *gnmi.TypedValue_BytesVal:
		values[prefix] = updValue.GetBytesVal()
	case *gnmi.TypedValue_DecimalVal:
		values[prefix] = updValue.GetDecimalVal()
	case *gnmi.TypedValue_FloatVal:
		values[prefix] = updValue.GetFloatVal()
	case *gnmi.TypedValue_IntVal:
		values[prefix] = updValue.GetIntVal()
	case *gnmi.TypedValue_StringVal:
		values[prefix] = updValue.GetStringVal()
	case *gnmi.TypedValue_UintVal:
		values[prefix] = updValue.GetUintVal()
	case *gnmi.TypedValue_LeaflistVal:
		values[prefix] = updValue.GetLeaflistVal()
	case *gnmi.TypedValue_ProtoBytes:
		values[prefix] = updValue.GetProtoBytes()
	case *gnmi.TypedValue_AnyVal:
		values[prefix] = updValue.GetAnyVal()
	case *gnmi.TypedValue_JsonIetfVal:
		jsondata = updValue.GetJsonIetfVal()
	case *gnmi.TypedValue_JsonVal:
		jsondata = updValue.GetJsonVal()
	}
	if len(jsondata) != 0 {
		var value interface{}
		err := json.Unmarshal(jsondata, &value)
		if err != nil {
			return nil, err
		}
		switch value := value.(type) {
		case map[string]interface{}:
			f := flattener.NewFlattener()
			f.SetPrefix(prefix)
			values, err = f.Flatten(value)
		default:
			values[prefix] = value
		}
		if err != nil {
			return nil, err
		}
	}
	return values, nil
}
<commit_msg>initialize Tags map<commit_after>package collector

import (
	"encoding/json"
	"strings"

	flattener "github.com/karimra/go-map-flattener"
	"github.com/openconfig/gnmi/proto/gnmi"
)

// EventMsg //
type EventMsg struct {
	Name      string                 `json:"name,omitempty"` // measurement name
	Timestamp int64                  `json:"timestamp,omitempty"`
	Tags      map[string]string      `json:"tags,omitempty"`
	Values    map[string]interface{} `json:"values,omitempty"`
	Deletes   []string               `json:"deletes,omitempty"`
}

// ResponseToEventMsgs //
func ResponseToEventMsgs(name string, rsp *gnmi.SubscribeResponse, meta map[string]string) ([]*EventMsg, error) {
	if rsp == nil {
		return nil, nil
	}
	var err error
	evs := make([]*EventMsg, 0)
	switch rsp := rsp.Response.(type) {
	case *gnmi.SubscribeResponse_Update:
		namePrefix, prefixTags := TagsFromGNMIPath(rsp.Update.Prefix)
		for _, upd := range rsp.Update.Update {
			e := &EventMsg{
				Tags:   make(map[string]string),
				Values: make(map[string]interface{}),
			}
			e.Timestamp = rsp.Update.Timestamp
			e.Name = name
			for k, v := range prefixTags {
				e.Tags[k] = v
			}
			pathName, pTags := TagsFromGNMIPath(upd.Path)
			pathName = strings.TrimRight(namePrefix, "/") + "/" + strings.TrimLeft(pathName, "/")
			for k, v := range pTags {
				if vv, ok := e.Tags[k]; ok {
					if v != vv {
						e.Tags[pathName+":::"+k] = v
					}
					continue
				}
				e.Tags[k] = v
			}
			e.Values, err = getValueFlat(pathName, upd.GetVal())
			if err != nil {
				return nil, err
			}
			for k, v := range meta {
				if k == "format" {
					continue
				}
				if _, ok := e.Tags[k]; ok {
					e.Tags["meta:"+k] = v
					continue
				}
				e.Tags[k] = v
			}
			evs = append(evs, e)
		}

		if len(rsp.Update.Delete) > 0 {
			e := &EventMsg{
				Tags:    make(map[string]string),
				Deletes: make([]string, 0, len(rsp.Update.Delete)),
			}
			e.Timestamp = rsp.Update.Timestamp
			e.Name = name
			for k, v := range prefixTags {
				e.Tags[k] = v
			}
			for k, v := range meta {
				if k == "format" {
					continue
				}
				if _, ok := e.Tags[k]; ok {
					e.Tags["meta:"+k] = v
					continue
				}
				e.Tags[k] = v
			}
			for _, del := range rsp.Update.Delete {
				e.Deletes = append(e.Deletes, gnmiPathToXPath(del))
			}
			evs = append(evs, e)
		}
	}
	return evs, nil
}

// TagsFromGNMIPath //
func TagsFromGNMIPath(p *gnmi.Path) (string, map[string]string) {
	if p == nil {
		return "", nil
	}
	tags := make(map[string]string)
	sb := strings.Builder{}
	if p.Origin != "" {
		sb.WriteString(p.Origin)
		sb.Write([]byte(":"))
	}
	for _, e := range p.Elem {
		if e.Name != "" {
			sb.Write([]byte("/"))
			sb.WriteString(e.Name)
		}
		if e.Key != nil {
			for k, v := range e.Key {
				if e.Name != "" {
					elems := strings.Split(e.Name, ":")
					if len(elems) > 0 {
						tags[elems[len(elems)-1]+"_"+k] = v
					}
				} else {
					tags[k] = v
				}
			}
		}
	}
	if p.GetTarget() != "" {
		tags["target"] = p.GetTarget()
	}
	return sb.String(), tags
}

func getValueFlat(prefix string, updValue *gnmi.TypedValue) (map[string]interface{}, error) {
	if updValue == nil {
		return nil, nil
	}
	var jsondata []byte
	values := make(map[string]interface{})
	switch updValue.Value.(type) {
	case *gnmi.TypedValue_AsciiVal:
		values[prefix] = updValue.GetAsciiVal()
	case *gnmi.TypedValue_BoolVal:
		values[prefix] = updValue.GetBoolVal()
	case *gnmi.TypedValue_BytesVal:
		values[prefix] = updValue.GetBytesVal()
	case *gnmi.TypedValue_DecimalVal:
		values[prefix] = updValue.GetDecimalVal()
	case *gnmi.TypedValue_FloatVal:
		values[prefix] = updValue.GetFloatVal()
	case *gnmi.TypedValue_IntVal:
		values[prefix] = updValue.GetIntVal()
	case *gnmi.TypedValue_StringVal:
		values[prefix] = updValue.GetStringVal()
	case *gnmi.TypedValue_UintVal:
		values[prefix] = updValue.GetUintVal()
	case *gnmi.TypedValue_LeaflistVal:
		values[prefix] = updValue.GetLeaflistVal()
	case *gnmi.TypedValue_ProtoBytes:
		values[prefix] = updValue.GetProtoBytes()
	case *gnmi.TypedValue_AnyVal:
		values[prefix] = updValue.GetAnyVal()
	case *gnmi.TypedValue_JsonIetfVal:
		jsondata = updValue.GetJsonIetfVal()
	case *gnmi.TypedValue_JsonVal:
		jsondata = updValue.GetJsonVal()
	}
	if len(jsondata) != 0 {
		var value interface{}
		err := json.Unmarshal(jsondata, &value)
		if err != nil {
			return nil, err
		}
		switch value := value.(type) {
		case map[string]interface{}:
			f := flattener.NewFlattener()
			f.SetPrefix(prefix)
			values, err = f.Flatten(value)
		default:
			values[prefix] = value
		}
		if err != nil {
			return nil, err
		}
	}
	return values, nil
}
<|endoftext|>


<commit_before>//+build generate
package model

//go:generate codecgen -u=true -o=codec.go data.go

import (
	"errors"
	"time"

	"github.com/geotrace/geo"
	"gopkg.in/mgo.v2/bson"
)

// User описывает информацию о пользователе.
//
// Логин пользователя является глобальным уникальным идентификатором
// пользователя и не может повторяться для разных пользователей. Поэтому, скорее
// всего, удобнее использовать в качестве такого идентификатора e-mail, что
// избавит от головной боли с уникальностью. Или любой другой идентификатор,
// который будет действительно глобально уникальным.
//
// Пользователи объединяются в группы, которые разделяют общие ресурсы: имеют
// доступ к трекам устройств той же группы, общие описания мест и так далее.
// Пользователь может состоять только в одной группе, но может ее сменить.
// Идентификатор группы генерируется непосредственно сервером.
//
// Пароль пользователя не хранится в системе, а вместо этого хранится хеш от
// него: этого вполне достаточно, чтобы иметь возможность проверить правильность
// введенного пароля, но не позволит его восстановить в исходном виде. В
// качестве алгоритма хеширования выбран bcrypt (Provos and Mazières's bcrypt
// adaptive hashing algorithm).
type User struct {
	// логин пользователя
	Login string `bson:"_id" json:"id"`
	// уникальный идентификатор группы
	GroupID string `bson:"groupId,omitempty" json:"groupId,omitempty"`
	// отображаемое имя
	Name string `bson:"name,omitempty" json:"name,omitempty"`
	// хеш пароля пользователя
	Password Password `bson:"password" json:"-"`
}

// Device описывает информацию об устройстве.
//
// Каждое устройство имеет свой глобальный уникальный идентификатор, который не
// может повторяться. Плюс, устройство в каждый момент времени может быть
// привязано только к одной группе пользователей. Это позволяет устройству
// менять группу, блокируя доступ к старым данным, которые были собраны для
// другой группы.
//
// Устройству может быть назначен его тип. Это поле используется внутри сервиса
// для идентификации поддерживаемых устройством возможностей, формата данных и
// команд.
type Device struct {
	// глобальный уникальный идентификатор устройства
	ID string `bson:"_id" json:"id"`
	// уникальный идентификатор группы
	GroupID string `bson:"groupId,omitempty" json:"groupId,omitempty"`
	// отображаемое имя
	Name string `bson:"name,omitempty" json:"name,omitempty"`
	// идентификатор типа устройства
	Type string `bson:"type,omitempty" json:"type,omitempty"`
	// хеш пароля для авторизации
	Password Password `bson:"password,omitempty" json:"-"`
}

// String возвращает строку с отображаемым именем устройства. Если для данного
// устройства определено имя, то возвращается именно оно. В противном случае
// возвращается уникальный идентификатор устройства.
func (d *Device) String() string {
	if d.Name != "" {
		return d.Name
	}
	return d.ID
}

// Event обычно описывает место, время и событие, которое в нем случилось.
//
// Каждое событие получает свой уникальный идентификатор, назначаемый
// непосредственно системой. Кроме того, событие привязано к конкретному
// идентификатору устройства и группе пользователей. Группа пользователей здесь
// представлена отдельным свойством, не смотря на то, что ее можно достаточно
// легко получить и из связи с устройством. Это сделано намеренно, чтобы в тех
// случаях, когда устройство меняет владельца (группу), старые данные о событиях
// не становились автоматически доступны новым пользователям.
//
// Каждое событие в обязательном порядке характеризуется временем, когда оно
// произошло. Если при создании описания события время было опущено, то будет
// автоматически добавлено текущее время сервера.
//
// Тип события задает один из предопределенных типов события. Если не указано,
// то считается, что тип события не определен.
//
// Каждое событие обычно характеризуется координатами географической точки, в
// которой оно случилось и дополнительным параметром, указывающим возможный
// радиус погрешности вычисления данной точки.
//
// Дополнительно, каждое событие может иметь свое описание в текстовом виде и
// иконку, характеризующую его в некотором визуальном виде. Но с последним
// обычно тяжело: кто и сколько таких иконок нарисует? Поэтому было принято
// решения вместо иконки использовать пиктограмму из стандартного набора эмодзи.
//
// И, наконец, последний элемент: именованные поля с произвольным содержимым,
// позволяющим описать любую дополнительную информацию. В частности, думаю,
// значения датчиков и сенсоров хорошо и удобно сохранять именно в таком виде.
// Плюс, всегда можно добавить что-то дополнительно практически в любом удобном
// формате. Главное, чтобы приложение знало, что потом с этим делать.
type Event struct {
	// уникальный идентификатор записи
	ID bson.ObjectId `bson:"_id" json:"id"`
	// уникальный идентификатор устройства
	DeviceID string `bson:"deviceId" json:"deviceId"`
	// уникальный идентификатор группы
	GroupID string `bson:"groupId,omitempty" json:"groupId,omitempty"`

	// временная метка
	Time time.Time `bson:"time" json:"time"`
	// тип события: Arrive, Leave, Travel, Check-in, Happen
	Type string `bson:"type,omitempty" json:"type,omitempty"`
	// координаты точки
	Location *geo.Point `bson:"location,omitempty" json:"location,omitempty"`
	// погрешность координат в метрах
	Accuracy float64 `bson:"accuracy,omitempty" json:"accuracy,omitempty"`
	// идентификатор места
	PlaceID string `bson:"placeId,omitempty" json:"placeId,omitempty"`
	// уровень заряда устройства на тот момент
	Power uint8 `bson:"power,omitempty" json:"power,omitempty"`

	// иконка в виде эмодзи
	Emoji rune `bson:"emoji,omitempty" json:"emoji,omitempty"`
	// текстовый комментарий к событию
	Comment string `bson:"comment,omitempty" json:"comment,omitempty"`
	// дополнительная именованная информация
	Data map[string]interface{} `bson:"data,omitempty,inline" json:"data,omitempty"`
}

// Place описывает географическое место, задаваемое для группы пользователей.
// Такое место может быть описано либо в виде круга, задаваемого координатами
// центральной точки и радиусом в метрах, либо полигоном. Круг имеет более
// высокий приоритет, поэтому если задано и то, и другое, то используется именно
// описание круга.
//
// К сожалению, в формате GeoJSON, который использует для описание
// географических координат в MongoDB, нет возможности описать круг. Поэтому для
// работы с ним его приходится трансформировать его в некий многоугольник.
// Получившийся результат сохраняется в поле Geo и индексируется сервером баз
// данных. В том же случае, если задан полигон, то его описания просто
// копируется в это поле без каких-либо изменений.
type Place struct {
	// уникальный идентификатор описания места
	ID string `bson:"_id,omitempty" json:"id"`
	// уникальный идентификатор группы
	GroupID string `bson:"groupId,omitempty" json:"groupId,omitempty"`
	// отображаемое имя
	Name string `bson:"name,omitempty" json:"name,omitempty"`
	// географическое описание места как круга
	Circle *geo.Circle `bson:"circle,omitempty" json:"circle,omitempty"`
	// географическое описание места в виде полигона
	Polygon *geo.Polygon `bson:"polygon,omitempty" json:"polygon,omitempty"`
	// описание в формате GeoJSON для поиска
	Geo interface{} `bson:"geo" json:"-"`
}

// ErrBadPlaceData возвращается, если ни полигон, ни окружность не заданы в
// описании места.
var ErrBadPlaceData = errors.New("circle or polygon is require in place")

// String возвращает строку с отображаемым именем описания места. Если для
// данного места задано имя, то возвращается именно оно. В противном случае
// возвращается его уникальный идентификатор.
func (p *Place) String() string {
	if p.Name != "" {
		return p.Name
	}
	return p.ID
}

// prepare осуществляет предварительную подготовку данных, создавая специальный
// объект для индекса.
func (p *Place) prepare() (err error) {
	// анализируем описание места и формируем данные для индексации
	if p.Circle != nil {
		p.Polygon = nil
		p.Geo = p.Circle.Geo()
	} else if p.Polygon != nil {
		p.Circle = nil
		p.Geo = p.Polygon.Geo()
	} else {
		err = ErrBadPlaceData
	}
	return
}
<commit_msg>JSON names<commit_after>//+build generate
package model

//go:generate codecgen -u=true -o=codec.go data.go

import (
	"errors"
	"time"

	"github.com/geotrace/geo"
	"gopkg.in/mgo.v2/bson"
)

// User описывает информацию о пользователе.
//
// Логин пользователя является глобальным уникальным идентификатором
// пользователя и не может повторяться для разных пользователей. Поэтому, скорее
// всего, удобнее использовать в качестве такого идентификатора e-mail, что
// избавит от головной боли с уникальностью. Или любой другой идентификатор,
// который будет действительно глобально уникальным.
//
// Пользователи объединяются в группы, которые разделяют общие ресурсы: имеют
// доступ к трекам устройств той же группы, общие описания мест и так далее.
// Пользователь может состоять только в одной группе, но может ее сменить.
// Идентификатор группы генерируется непосредственно сервером.
//
// Пароль пользователя не хранится в системе, а вместо этого хранится хеш от
// него: этого вполне достаточно, чтобы иметь возможность проверить правильность
// введенного пароля, но не позволит его восстановить в исходном виде. В
// качестве алгоритма хеширования выбран bcrypt (Provos and Mazières's bcrypt
// adaptive hashing algorithm).
type User struct {
	// логин пользователя
	Login string `bson:"_id" json:"id"`
	// уникальный идентификатор группы
	GroupID string `bson:"group,omitempty" json:"group,omitempty"`
	// отображаемое имя
	Name string `bson:"name,omitempty" json:"name,omitempty"`
	// хеш пароля пользователя
	Password Password `bson:"password" json:"-"`
}

// Device описывает информацию об устройстве.
//
// Каждое устройство имеет свой глобальный уникальный идентификатор, который не
// может повторяться. Плюс, устройство в каждый момент времени может быть
// привязано только к одной группе пользователей. Это позволяет устройству
// менять группу, блокируя доступ к старым данным, которые были собраны для
// другой группы.
//
// Устройству может быть назначен его тип. Это поле используется внутри сервиса
// для идентификации поддерживаемых устройством возможностей, формата данных и
// команд.
type Device struct {
	// глобальный уникальный идентификатор устройства
	ID string `bson:"_id" json:"id"`
	// уникальный идентификатор группы
	GroupID string `bson:"group,omitempty" json:"group,omitempty"`
	// отображаемое имя
	Name string `bson:"name,omitempty" json:"name,omitempty"`
	// идентификатор типа устройства
	Type string `bson:"type,omitempty" json:"type,omitempty"`
	// хеш пароля для авторизации
	Password Password `bson:"password,omitempty" json:"-"`
}

// String возвращает строку с отображаемым именем устройства. Если для данного
// устройства определено имя, то возвращается именно оно. В противном случае
// возвращается уникальный идентификатор устройства.
func (d *Device) String() string {
	if d.Name != "" {
		return d.Name
	}
	return d.ID
}

// Event обычно описывает место, время и событие, которое в нем случилось.
//
// Каждое событие получает свой уникальный идентификатор, назначаемый
// непосредственно системой. Кроме того, событие привязано к конкретному
// идентификатору устройства и группе пользователей. Группа пользователей здесь
// представлена отдельным свойством, не смотря на то, что ее можно достаточно
// легко получить и из связи с устройством. Это сделано намеренно, чтобы в тех
// случаях, когда устройство меняет владельца (группу), старые данные о событиях
// не становились автоматически доступны новым пользователям.
//
// Каждое событие в обязательном порядке характеризуется временем, когда оно
// произошло. Если при создании описания события время было опущено, то будет
// автоматически добавлено текущее время сервера.
//
// Тип события задает один из предопределенных типов события. Если не указано,
// то считается, что тип события не определен.
//
// Каждое событие обычно характеризуется координатами географической точки, в
// которой оно случилось и дополнительным параметром, указывающим возможный
// радиус погрешности вычисления данной точки.
//
// Дополнительно, каждое событие может иметь свое описание в текстовом виде и
// иконку, характеризующую его в некотором визуальном виде. Но с последним
// обычно тяжело: кто и сколько таких иконок нарисует? Поэтому было принято
// решения вместо иконки использовать пиктограмму из стандартного набора эмодзи.
//
// И, наконец, последний элемент: именованные поля с произвольным содержимым,
// позволяющим описать любую дополнительную информацию. В частности, думаю,
// значения датчиков и сенсоров хорошо и удобно сохранять именно в таком виде.
// Плюс, всегда можно добавить что-то дополнительно практически в любом удобном
// формате. Главное, чтобы приложение знало, что потом с этим делать.
type Event struct {
	// уникальный идентификатор записи
	ID bson.ObjectId `bson:"_id" json:"id"`
	// уникальный идентификатор устройства
	DeviceID string `bson:"device" json:"device"`
	// уникальный идентификатор группы
	GroupID string `bson:"group,omitempty" json:"group,omitempty"`

	// временная метка
	Time time.Time `bson:"time" json:"time"`
	// тип события: Arrive, Leave, Travel, Check-in, Happen
	Type string `bson:"type,omitempty" json:"type,omitempty"`
	// координаты точки
	Location *geo.Point `bson:"location,omitempty" json:"location,omitempty"`
	// погрешность координат в метрах
	Accuracy float64 `bson:"accuracy,omitempty" json:"accuracy,omitempty"`
	// идентификатор места
	PlaceID string `bson:"place,omitempty" json:"place,omitempty"`
	// уровень заряда устройства на тот момент
	Power uint8 `bson:"power,omitempty" json:"power,omitempty"`

	// иконка в виде эмодзи
	Emoji rune `bson:"emoji,omitempty" json:"emoji,omitempty"`
	// текстовый комментарий к событию
	Comment string `bson:"comment,omitempty" json:"comment,omitempty"`
	// дополнительная именованная информация
	Data map[string]interface{} `bson:"data,omitempty,inline" json:"data,omitempty"`
}

// Place описывает географическое место, задаваемое для группы пользователей.
// Такое место может быть описано либо в виде круга, задаваемого координатами
// центральной точки и радиусом в метрах, либо полигоном. Круг имеет более
// высокий приоритет, поэтому если задано и то, и другое, то используется именно
// описание круга.
//
// К сожалению, в формате GeoJSON, который использует для описание
// географических координат в MongoDB, нет возможности описать круг. Поэтому для
// работы с ним его приходится трансформировать его в некий многоугольник.
// Получившийся результат сохраняется в поле Geo и индексируется сервером баз
// данных. В том же случае, если задан полигон, то его описания просто
// копируется в это поле без каких-либо изменений.
type Place struct {
	// уникальный идентификатор описания места
	ID string `bson:"_id,omitempty" json:"id"`
	// уникальный идентификатор группы
	GroupID string `bson:"group,omitempty" json:"group,omitempty"`
	// отображаемое имя
	Name string `bson:"name,omitempty" json:"name,omitempty"`
	// географическое описание места как круга
	Circle *geo.Circle `bson:"circle,omitempty" json:"circle,omitempty"`
	// географическое описание места в виде полигона
	Polygon *geo.Polygon `bson:"polygon,omitempty" json:"polygon,omitempty"`
	// описание в формате GeoJSON для поиска
	Geo interface{} `bson:"geo" json:"-"`
}

// ErrBadPlaceData возвращается, если ни полигон, ни окружность не заданы в
// описании места.
var ErrBadPlaceData = errors.New("circle or polygon is require in place")

// String возвращает строку с отображаемым именем описания места. Если для
// данного места задано имя, то возвращается именно оно. В противном случае
// возвращается его уникальный идентификатор.
func (p *Place) String() string {
	if p.Name != "" {
		return p.Name
	}
	return p.ID
}

// prepare осуществляет предварительную подготовку данных, создавая специальный
// объект для индекса.
func (p *Place) prepare() (err error) {
	// анализируем описание места и формируем данные для индексации
	if p.Circle != nil {
		p.Polygon = nil
		p.Geo = p.Circle.Geo()
	} else if p.Polygon != nil {
		p.Circle = nil
		p.Geo = p.Polygon.Geo()
	} else {
		err = ErrBadPlaceData
	}
	return
}
<|endoftext|>


<commit_before>package commands

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/evandbrown/dm/conf"
	"github.com/evandbrown/dm/googlecloud"
	"github.com/evandbrown/dm/template"
	"github.com/evandbrown/dm/util"
	"github.com/nu7hatch/gouuid"
	"github.com/spf13/cobra"
)

const (
	uidlen            = 5
	maxlen            = 63
	namere            = "[a-z]([-a-z0-9]*[a-z0-9])?"
	configpathDefault = "config.yaml"
	varspathDefault   = "vars.yaml"
)

var uid bool
var configpath, varspath string
var vars configVar
var deployCmd = &cobra.Command{
	Use:   "deploy",
	Short: "Deploy a configuration to Deployment Manager.",
}

func init() {
	deployCmd.Flags().StringVarP(&configpath, "config-file", "c", configpathDefault, "The name of the config to deploy.")
	deployCmd.Flags().VarP(&vars, "var", "v", "A variable value to provide to the vars.yaml file for use in a deployment. Define multiple with -v var1=foo -v var2=2")
	deployCmd.Flags().StringVarP(&varspath, "vars-file", "x", varspathDefault, "The name of the config to deploy.")
	deployCmd.Flags().BoolVarP(&uid, "uid", "u", true, "Should a 7 char UID be appended to deployment name. Defaults is yes")
	deployCmd.Run = func(cmd *cobra.Command, args []string) {
		util.Check(deploy(cmd, args))
	}
}

// Special flag type to accumulate vars
type configVar struct {
	vars map[string]string
}

// Implemenet the flag interface
func (v *configVar) String() string {
	return fmt.Sprint(*v)
}

// Implemenet the flag interface
func (v *configVar) Set(value string) error {
	if v.vars == nil {
		v.vars = make(map[string]string)
	}
	s := strings.Split(value, "=")
	if len(s) != 2 {
		return errors.New("value must be formatted as k=v")
	}
	v.vars[strings.TrimSpace(s[0])] = strings.TrimSpace(s[1])
	return nil
}

func (v *configVar) Type() string {
	return "Convig var"
}

func deploy(cmd *cobra.Command, args []string) error {
	log.Info(vars)
	//TODO this should be a validation method
	if Project == "" {
		log.Fatal("--project parameter is required to create a new deployment")
	}

	log.Debug("Creating deployment manager service")
	service, err := googlecloud.GetService()
	util.Check(err)

	Name, err = getName(uid)
	if err != nil {
		log.Warning(err)
		return err
	}

	log.Infof("Creating new deployment %s", Name)

	// Create a context builder
	ctxb := template.NewContextBuilder()

	// Add base template
	base, err := ioutil.ReadFile(configpath)
	if err != nil {
		return err
	}
	ctxb.Data = string(base)
	ctxb.Path = configpath

	// Create a var provider for vars.yaml
	varfile, err := os.Open(varspath)
	if err != nil {
		return err
	}
	vp := &template.VarsDotYAMLMapper{}
	err = vp.Parse(varfile)
	if err != nil {
		return err
	}

	ctxb.AddConstraints(vp.Keys())
	ctxb.AddUserVars(vp.Map())

	// Add CLI avrs
	ctxb.AddUserVars(vars.vars)

	// Validate and render
	err = ctxb.Validate()
	if err != nil {
		return err
	}

	// Check for errors
	if ctxb.Error != nil {
		return ctxb.Error
	}

	// Create a deployment object for the DM API
	config, err := ctxb.RenderConfig()
	if err != nil {
		return err
	}

	d, err := template.GenerateDeployment(Name, "", config)
	if err != nil {
		return err
	}
	d.Intent = "UPDATE"
	call := service.Deployments.Insert(Project, d)
	_, error := call.Do()
	util.Check(error)

	//TODO only set Vars if the varspath file actually exists
	dConfig := conf.Deployment{
		Id:      Name,
		Project: Project,
		Config:  configpath,
		Vars:    varspath,
	}

	_, err = conf.AppendDeployment(dConfig, true)
	if err != nil {
		log.Fatal(fmt.Sprintf("Config was deployed but there was an error writing the config file. You will not be able to use other `dm` commands, but the deployment will exist. Error was %s", err))
	}

	fmt.Printf("Created deployment %s.\n", Name)
	return nil
}

func getName(setUid bool) (string, error) {
	var name string
	var err error
	if len(Name) == 0 {
		name, err = os.Getwd()
		if err != nil {
			return "", err
		}
		dirs := strings.Split(name, "/")
		name = dirs[len(dirs)-1]
	} else {
		name = Name
	}

	// Replace underscores
	name = strings.Replace(name, "_", "-", -1)
	name = strings.ToLower(name)

	// Reduce name prefix to keep total to < 63 chars
	if setUid && len(name)+uidlen > maxlen {
		name = name[:maxlen-uidlen]
	}

	// Append a uid
	if setUid {
		u, err := uuid.NewV4()
		util.Check(err)
		name += "-" + u.String()[:uidlen-1]
	}

	// Validate name
	if match, err := regexp.MatchString(namere, name); match == false || err != nil {
		return "", errors.New(fmt.Sprintf("The provided or derived name for the deployment is invalid: %s. Must match regex %s", name, namere))
	}
	return name, nil
}
<commit_msg>Skip vars if vars.yaml doesn't exist<commit_after>package commands

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/evandbrown/dm/conf"
	"github.com/evandbrown/dm/googlecloud"
	"github.com/evandbrown/dm/template"
	"github.com/evandbrown/dm/util"
	"github.com/nu7hatch/gouuid"
	"github.com/spf13/cobra"
)

const (
	uidlen            = 5
	maxlen            = 63
	namere            = "[a-z]([-a-z0-9]*[a-z0-9])?"
	configpathDefault = "config.yaml"
	varspathDefault   = "vars.yaml"
)

var uid bool
var configpath, varspath string
var vars configVar
var deployCmd = &cobra.Command{
	Use:   "deploy",
	Short: "Deploy a configuration to Deployment Manager.",
}

func init() {
	deployCmd.Flags().StringVarP(&configpath, "config-file", "c", configpathDefault, "The name of the config to deploy.")
	deployCmd.Flags().VarP(&vars, "var", "v", "A variable value to provide to the vars.yaml file for use in a deployment. Define multiple with -v var1=foo -v var2=2")
	deployCmd.Flags().StringVarP(&varspath, "vars-file", "x", varspathDefault, "The name of the config to deploy.")
	deployCmd.Flags().BoolVarP(&uid, "uid", "u", true, "Should a 7 char UID be appended to deployment name. Defaults is yes")
	deployCmd.Run = func(cmd *cobra.Command, args []string) {
		util.Check(deploy(cmd, args))
	}
}

// Special flag type to accumulate vars
type configVar struct {
	vars map[string]string
}

// Implemenet the flag interface
func (v *configVar) String() string {
	return fmt.Sprint(*v)
}

// Implemenet the flag interface
func (v *configVar) Set(value string) error {
	if v.vars == nil {
		v.vars = make(map[string]string)
	}
	s := strings.Split(value, "=")
	if len(s) != 2 {
		return errors.New("value must be formatted as k=v")
	}
	v.vars[strings.TrimSpace(s[0])] = strings.TrimSpace(s[1])
	return nil
}

func (v *configVar) Type() string {
	return "Convig var"
}

func deploy(cmd *cobra.Command, args []string) error {
	log.Info(vars)
	//TODO this should be a validation method
	if Project == "" {
		log.Fatal("--project parameter is required to create a new deployment")
	}

	log.Debug("Creating deployment manager service")
	service, err := googlecloud.GetService()
	util.Check(err)

	Name, err = getName(uid)
	if err != nil {
		log.Warning(err)
		return err
	}

	log.Infof("Creating new deployment %s", Name)

	// Create a context builder
	ctxb := template.NewContextBuilder()

	// Add base template
	base, err := ioutil.ReadFile(configpath)
	if err != nil {
		return err
	}
	ctxb.Data = string(base)
	ctxb.Path = configpath

	// Create a var provider for vars.yaml
	varfile, err := os.Open(varspath)
	if err == nil {
		vp := &template.VarsDotYAMLMapper{}
		err = vp.Parse(varfile)
		if err != nil {
			return err
		}

		ctxb.AddConstraints(vp.Keys())
		ctxb.AddUserVars(vp.Map())

		// Add CLI avrs
		ctxb.AddUserVars(vars.vars)

	}
	// Validate and render
	err = ctxb.Validate()
	if err != nil {
		return err
	}

	// Check for errors
	if ctxb.Error != nil {
		return ctxb.Error
	}

	// Create a deployment object for the DM API
	config, err := ctxb.RenderConfig()
	if err != nil {
		return err
	}

	d, err := template.GenerateDeployment(Name, "", config)
	if err != nil {
		return err
	}
	d.Intent = "UPDATE"
	call := service.Deployments.Insert(Project, d)
	_, error := call.Do()
	util.Check(error)

	//TODO only set Vars if the varspath file actually exists
	dConfig := conf.Deployment{
		Id:      Name,
		Project: Project,
		Config:  configpath,
		Vars:    varspath,
	}

	_, err = conf.AppendDeployment(dConfig, true)
	if err != nil {
		log.Fatal(fmt.Sprintf("Config was deployed but there was an error writing the config file. You will not be able to use other `dm` commands, but the deployment will exist. Error was %s", err))
	}

	fmt.Printf("Created deployment %s.\n", Name)
	return nil
}

func getName(setUid bool) (string, error) {
	var name string
	var err error
	if len(Name) == 0 {
		name, err = os.Getwd()
		if err != nil {
			return "", err
		}
		dirs := strings.Split(name, "/")
		name = dirs[len(dirs)-1]
	} else {
		name = Name
	}

	// Replace underscores
	name = strings.Replace(name, "_", "-", -1)
	name = strings.ToLower(name)

	// Reduce name prefix to keep total to < 63 chars
	if setUid && len(name)+uidlen > maxlen {
		name = name[:maxlen-uidlen]
	}

	// Append a uid
	if setUid {
		u, err := uuid.NewV4()
		util.Check(err)
		name += "-" + u.String()[:uidlen-1]
	}

	// Validate name
	if match, err := regexp.MatchString(namere, name); match == false || err != nil {
		return "", errors.New(fmt.Sprintf("The provided or derived name for the deployment is invalid: %s. Must match regex %s", name, namere))
	}
	return name, nil
}
<|endoftext|>


<commit_before>package metadata

import (
	"fmt"
	"strings"

	"github.com/StackExchange/slog"
	"github.com/StackExchange/wmi"
	"github.com/bosun-monitor/scollector/opentsdb"
)

func init() {
	metafuncs = append(metafuncs, metaWindowsVersion, metaWindowsIfaces)
}

func metaWindowsVersion() {
	var dst []Win32_OperatingSystem
	q := wmi.CreateQuery(&dst, "")
	err := wmi.Query(q, &dst)
	if err != nil {
		slog.Error(err)
		return
	}

	var dstComputer []Win32_ComputerSystem
	q = wmi.CreateQuery(&dstComputer, "")
	err = wmi.Query(q, &dstComputer)
	if err != nil {
		slog.Error(err)
		return
	}

	var dstBIOS []Win32_BIOS
	q = wmi.CreateQuery(&dstBIOS, "")
	err = wmi.Query(q, &dstBIOS)
	if err != nil {
		slog.Error(err)
		return
	}

	for _, v := range dst {
		AddMeta("", nil, "version", v.Version, true)
		AddMeta("", nil, "versionCaption", v.Caption, true)
	}

	for _, v := range dstComputer {
		AddMeta("", nil, "manufacturer", v.Manufacturer, true)
		AddMeta("", nil, "model", v.Model, true)
		AddMeta("", nil, "memoryTotal", v.TotalPhysicalMemory, true)
	}

	for _, v := range dstBIOS {
		AddMeta("", nil, "serialNumber", v.SerialNumber, true)
	}
}

type Win32_OperatingSystem struct {
	Caption string
	Version string
}

type Win32_ComputerSystem struct {
	Manufacturer        string
	Model               string
	TotalPhysicalMemory uint64
}

type Win32_BIOS struct {
	SerialNumber string
}

func metaWindowsIfaces() {
	var dstConfigs []Win32_NetworkAdapterConfiguration
	q := wmi.CreateQuery(&dstConfigs, "WHERE MACAddress != null")
	err := wmi.Query(q, &dstConfigs)
	if err != nil {
		slog.Error(err)
		return
	}

	mNicConfigs := make(map[string]*Win32_NetworkAdapterConfiguration)
	for i, nic := range dstConfigs {
		mNicConfigs[nic.SettingID] = &dstConfigs[i]
	}

	var dstAdapters []MSFT_NetAdapter
	q = wmi.CreateQuery(&dstAdapters, "WHERE HardwareInterface = True") //Exclude virtual adapters
	err = wmi.QueryNamespace(q, &dstAdapters, "root\\StandardCimv2")
	if err != nil {
		slog.Error(err)
		return
	}

	for _, v := range dstAdapters {
		tag := opentsdb.TagSet{"iface": fmt.Sprint("Interface", v.InterfaceIndex)}
		AddMeta("", tag, "description", v.InterfaceDescription, true)
		AddMeta("", tag, "name", v.Name, true)
		AddMeta("", tag, "speed", v.Speed, true)

		nicConfig := mNicConfigs[v.InterfaceGuid]
		if nicConfig != nil {
			AddMeta("", tag, "mac", strings.Replace(nicConfig.MACAddress, ":", "", -1), true)
			for _, ip := range *nicConfig.IPAddress {
				AddMeta("", tag, "addr", ip, true) // blocked by array support in WMI See https://github.com/StackExchange/wmi/issues/5
			}
		}
	}
}

type MSFT_NetAdapter struct {
	Name                 string //NY-WEB09-PRI-NIC-A
	Speed                uint64 //Bits per Second
	InterfaceDescription string //Intel(R) Gigabit ET Quad Port Server Adapter #2
	InterfaceName        string //Ethernet_10
	InterfaceGuid        string //unique id
	InterfaceIndex       uint32
}

type Win32_NetworkAdapterConfiguration struct {
	IPAddress  *[]string //Both IPv4 and IPv6
	MACAddress string    //00:1B:21:93:00:00
	SettingID  string    //Matches InterfaceGuid
}
<commit_msg>cmd/scollector: change speed metadata to be a pointer and default to zero when not available<commit_after>package metadata

import (
	"fmt"
	"strings"

	"github.com/StackExchange/slog"
	"github.com/StackExchange/wmi"
	"github.com/bosun-monitor/scollector/opentsdb"
)

func init() {
	metafuncs = append(metafuncs, metaWindowsVersion, metaWindowsIfaces)
}

func metaWindowsVersion() {
	var dst []Win32_OperatingSystem
	q := wmi.CreateQuery(&dst, "")
	err := wmi.Query(q, &dst)
	if err != nil {
		slog.Error(err)
		return
	}

	var dstComputer []Win32_ComputerSystem
	q = wmi.CreateQuery(&dstComputer, "")
	err = wmi.Query(q, &dstComputer)
	if err != nil {
		slog.Error(err)
		return
	}

	var dstBIOS []Win32_BIOS
	q = wmi.CreateQuery(&dstBIOS, "")
	err = wmi.Query(q, &dstBIOS)
	if err != nil {
		slog.Error(err)
		return
	}

	for _, v := range dst {
		AddMeta("", nil, "version", v.Version, true)
		AddMeta("", nil, "versionCaption", v.Caption, true)
	}

	for _, v := range dstComputer {
		AddMeta("", nil, "manufacturer", v.Manufacturer, true)
		AddMeta("", nil, "model", v.Model, true)
		AddMeta("", nil, "memoryTotal", v.TotalPhysicalMemory, true)
	}

	for _, v := range dstBIOS {
		AddMeta("", nil, "serialNumber", v.SerialNumber, true)
	}
}

type Win32_OperatingSystem struct {
	Caption string
	Version string
}

type Win32_ComputerSystem struct {
	Manufacturer        string
	Model               string
	TotalPhysicalMemory uint64
}

type Win32_BIOS struct {
	SerialNumber string
}

func metaWindowsIfaces() {
	var dstConfigs []Win32_NetworkAdapterConfiguration
	q := wmi.CreateQuery(&dstConfigs, "WHERE MACAddress != null")
	err := wmi.Query(q, &dstConfigs)
	if err != nil {
		slog.Error(err)
		return
	}

	mNicConfigs := make(map[string]*Win32_NetworkAdapterConfiguration)
	for i, nic := range dstConfigs {
		mNicConfigs[nic.SettingID] = &dstConfigs[i]
	}

	var dstAdapters []MSFT_NetAdapter
	q = wmi.CreateQuery(&dstAdapters, "WHERE HardwareInterface = True") //Exclude virtual adapters
	err = wmi.QueryNamespace(q, &dstAdapters, "root\\StandardCimv2")
	if err != nil {
		slog.Error(err)
		return
	}

	for _, v := range dstAdapters {
		tag := opentsdb.TagSet{"iface": fmt.Sprint("Interface", v.InterfaceIndex)}
		AddMeta("", tag, "description", v.InterfaceDescription, true)
		AddMeta("", tag, "name", v.Name, true)
		if v.Speed != nil {
			AddMeta("", tag, "speed", v.Speed, true)
		} else {
			AddMeta("", tag, "speed", 0, true)
		}

		nicConfig := mNicConfigs[v.InterfaceGuid]
		if nicConfig != nil {
			AddMeta("", tag, "mac", strings.Replace(nicConfig.MACAddress, ":", "", -1), true)
			for _, ip := range *nicConfig.IPAddress {
				AddMeta("", tag, "addr", ip, true) // blocked by array support in WMI See https://github.com/StackExchange/wmi/issues/5
			}
		}
	}
}

type MSFT_NetAdapter struct {
	Name                 string  //NY-WEB09-PRI-NIC-A
	Speed                *uint64 //Bits per Second
	InterfaceDescription string  //Intel(R) Gigabit ET Quad Port Server Adapter #2
	InterfaceName        string  //Ethernet_10
	InterfaceGuid        string  //unique id
	InterfaceIndex       uint32
}

type Win32_NetworkAdapterConfiguration struct {
	IPAddress  *[]string //Both IPv4 and IPv6
	MACAddress string    //00:1B:21:93:00:00
	SettingID  string    //Matches InterfaceGuid
}
<|endoftext|>


<commit_before>/*
Package conekta implements a client that wraps the conekta API (v 0.3.0)

Create a new client to make requests to the resources exposed by the API.
For example, to create a charge to be paid in OXXO (more examples are included in
the examples directory):

	client := conekta.NewClient()
	charge := &conekta.Charge{
		Amount: 20000, //in cents
		Currency: "MXN",
		Description: "Some useless widgets",
		Cash: Oxxo
	}
    res, err := client.Charges.Create(charge)

Authenticating requests.

All requests to conekta must be authenticated. The client expects to find
the CONEKTA_API_KEY environment variable with your account's API key:

 	export CONEKTA_API_KEY=your_api_key

or, if you prefer:

 	os.Setenv("CONEKTA_API_KEY", your_api_key)

Handling responses

Handling errors

*/
package conekta

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strconv"
	"time"
)

const (
	baseURLString       = "https://api.conekta.io"
	apiVersion          = "0.3.0"
	gonektaVersion      = "0.1"
	userAgent           = "gonekta-" + gonektaVersion
	mimeType            = "application/vnd.conekta." + apiVersion + "+json"
	jsonMimeType        = "application/json"
	headerUserAgent     = "User-Agent"
	headerContentType   = "Content-Type"
	headerAccept        = "Accept"
	headerConektaClient = "X-Conekta-Client-User-Agent"
	envConektaAPIKey    = "CONEKTA_API_KEY"
)

type Client struct {
	client    *http.Client
	userAgent string
	ApiKey    string
	BaseURL   *url.URL
	Charges   *chargesResource
	Customers *customersResource
	Plans     *plansResource
}

type ConektaError struct {
	Response *http.Response
	Type     string `json:"type"`
	Code     string `json:"code"`
	Param    string `json:"param"`
	Message  string `json:"message"`
}

type GonektaError struct {
	Message string `json:"message"`
}

type timestamp struct {
	time.Time
}

type Param map[string]interface{}

func (t timestamp) String() string {
	return t.Time.String()
}

func (ts *timestamp) UnmarshalJSON(b []byte) error {
	result, err := strconv.ParseInt(string(b), 10, 64)
	if err == nil {
		(*ts).Time = time.Unix(result, 0)
	} else {
		(*ts).Time, err = time.Parse(`"`+time.RFC3339+`"`, string(b))
	}
	return err
}

// NewClient returns a configured conekta client. All requests to the API
// go through this value.
func NewClient() *Client {
	baseUrl, _ := url.Parse(baseURLString)
	cli := &Client{
		client:    http.DefaultClient,
		BaseURL:   baseUrl,
		userAgent: userAgent,
	}
	cli.Charges = newChargesResource(cli)
	cli.Customers = newCustomersResource(cli)
	cli.Plans = newPlansResource(cli)

	return cli
}

func (c *Client) execute(method, path string, resBody, reqBody interface{}) error {
	req, err := c.prepareRequest(method, path, reqBody)
	if err != nil {
		return err
	}
	err = c.executeRequest(req, resBody)
	return err
}

func (c *Client) prepareRequest(method, path string, body interface{}) (*http.Request, error) {
	relative, err := url.Parse(path)
	if err != nil {
		return nil, err
	}

	baseUrl := c.BaseURL.ResolveReference(relative)
	buf := new(bytes.Buffer)

	if body != nil {
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, baseUrl.String(), buf)
	if err != nil {
		return nil, err
	}

	req.Header.Add(headerContentType, jsonMimeType)
	req.Header.Add(headerAccept, mimeType)
	req.Header.Add(headerUserAgent, userAgent)
	req.Header.Add(headerConektaClient, func() string {
		j, _ := json.Marshal(map[string]string{
			"lang":         "Go",
			"lang_version": runtime.Version(),
			"uname":        runtime.GOOS,
		})
		return string(j)
	}())

	if len(c.ApiKey) == 0 {
		c.ApiKey = os.Getenv(envConektaAPIKey)
	}

	if len(c.ApiKey) == 0 {
		return nil, GonektaError{"Missing CONEKTA_API_KEY"}
	}
	req.SetBasicAuth(c.ApiKey, "")
	return req, nil
}

func (c *Client) executeRequest(req *http.Request, val interface{}) error {

	res, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	err = handleConektaError(res)

	if err != nil {
		return err
	}

	if val != nil {
		err = json.NewDecoder(res.Body).Decode(val)
	}
	return err
}

func (e *ConektaError) Error() string {
	return fmt.Sprintf("[%d] %s %s %s %s",
		e.Response.StatusCode,
		e.Type,
		e.Code,
		e.Param,
		e.Message,
	)
}

func (e GonektaError) Error() string {
	return e.Message
}

func handleConektaError(r *http.Response) error {
	if code := r.StatusCode; 200 <= code && code <= 299 {
		return nil
	}
	e := &ConektaError{Response: r}
	body, err := ioutil.ReadAll(r.Body)
	if err == nil && body != nil {
		json.Unmarshal(body, e)
	}
	return e
}
<commit_msg>Fix apiKey error test<commit_after>/*
Package conekta implements a client that wraps the conekta API (v 0.3.0)

Create a new client to make requests to the resources exposed by the API.
For example, to create a charge to be paid in OXXO (more examples are included in
the examples directory):

	client := conekta.NewClient()
	charge := &conekta.Charge{
		Amount: 20000, //in cents
		Currency: "MXN",
		Description: "Some useless widgets",
		Cash: Oxxo
	}
    res, err := client.Charges.Create(charge)

Authenticating requests.

All requests to conekta must be authenticated. The client expects to find
the CONEKTA_API_KEY environment variable with your account's API key:

 	export CONEKTA_API_KEY=your_api_key

or, if you prefer:

 	os.Setenv("CONEKTA_API_KEY", your_api_key)

Handling responses

Handling errors

*/
package conekta

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strconv"
	"time"
)

const (
	baseURLString       = "https://api.conekta.io"
	apiVersion          = "0.3.0"
	gonektaVersion      = "0.1"
	userAgent           = "gonekta-" + gonektaVersion
	mimeType            = "application/vnd.conekta." + apiVersion + "+json"
	jsonMimeType        = "application/json"
	headerUserAgent     = "User-Agent"
	headerContentType   = "Content-Type"
	headerAccept        = "Accept"
	headerConektaClient = "X-Conekta-Client-User-Agent"
	envConektaAPIKey    = "CONEKTA_API_KEY"
)

type Client struct {
	client    *http.Client
	userAgent string
	ApiKey    string
	BaseURL   *url.URL
	Charges   *chargesResource
	Customers *customersResource
	Plans     *plansResource
}

type ConektaError struct {
	Response *http.Response
	Type     string `json:"type"`
	Code     string `json:"code"`
	Param    string `json:"param"`
	Message  string `json:"message"`
}

type GonektaError struct {
	Message string `json:"message"`
}

type timestamp struct {
	time.Time
}

type Param map[string]interface{}

func (t timestamp) String() string {
	return t.Time.String()
}

func (ts *timestamp) UnmarshalJSON(b []byte) error {
	result, err := strconv.ParseInt(string(b), 10, 64)
	if err == nil {
		(*ts).Time = time.Unix(result, 0)
	} else {
		(*ts).Time, err = time.Parse(`"`+time.RFC3339+`"`, string(b))
	}
	return err
}

// NewClient returns a configured conekta client. All requests to the API
// go through this value.
func NewClient() *Client {
	baseUrl, _ := url.Parse(baseURLString)
	cli := &Client{
		client:    http.DefaultClient,
		BaseURL:   baseUrl,
		userAgent: userAgent,
	}
	cli.Charges = newChargesResource(cli)
	cli.Customers = newCustomersResource(cli)
	cli.Plans = newPlansResource(cli)

	return cli
}

func (c *Client) execute(method, path string, resBody, reqBody interface{}) error {
	req, err := c.prepareRequest(method, path, reqBody)
	if err != nil {
		return err
	}
	err = c.executeRequest(req, resBody)
	return err
}

func (c *Client) prepareRequest(method, path string, body interface{}) (*http.Request, error) {
	relative, err := url.Parse(path)
	if err != nil {
		return nil, err
	}

	baseUrl := c.BaseURL.ResolveReference(relative)
	buf := new(bytes.Buffer)

	if body != nil {
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, baseUrl.String(), buf)
	if err != nil {
		return nil, err
	}

	req.Header.Add(headerContentType, jsonMimeType)
	req.Header.Add(headerAccept, mimeType)
	req.Header.Add(headerUserAgent, userAgent)
	req.Header.Add(headerConektaClient, func() string {
		j, _ := json.Marshal(map[string]string{
			"lang":         "Go",
			"lang_version": runtime.Version(),
			"uname":        runtime.GOOS,
		})
		return string(j)
	}())

	apiKey := c.ApiKey

	if len(apiKey) == 0 {
		apiKey = os.Getenv(envConektaAPIKey)
	}
	if len(apiKey) == 0 {
		return nil, GonektaError{"Missing CONEKTA_API_KEY"}
	}
	req.SetBasicAuth(apiKey, "")
	return req, nil
}

func (c *Client) executeRequest(req *http.Request, val interface{}) error {

	res, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	err = handleConektaError(res)

	if err != nil {
		return err
	}

	if val != nil {
		err = json.NewDecoder(res.Body).Decode(val)
	}
	return err
}

func (e *ConektaError) Error() string {
	return fmt.Sprintf("[%d] %s %s %s %s",
		e.Response.StatusCode,
		e.Type,
		e.Code,
		e.Param,
		e.Message,
	)
}

func (e GonektaError) Error() string {
	return e.Message
}

func handleConektaError(r *http.Response) error {
	if code := r.StatusCode; 200 <= code && code <= 299 {
		return nil
	}
	e := &ConektaError{Response: r}
	body, err := ioutil.ReadAll(r.Body)
	if err == nil && body != nil {
		json.Unmarshal(body, e)
	}
	return e
}
<|endoftext|>


<commit_before>package tvdbapi

import (
	"fmt"
	"net/http"
	"io/ioutil"
	"encoding/json"
	"log"
	"net/url"
)

type searchData struct {
	Series []Series  `json:"data"`
}

type SearchQuery struct {
	Name string
	ImdbId string
	Zap2itId string
	AcceptLanguage string
}

type Series struct {
	Id int `json:"id"`
	SeriesName string `json:"seriesName"`
	Aliases []string `json:"aliases"`
	Banner string `json:"banner"`
	SeriesId string `json:"seriesId"`
	Status string `json:"status"`
	FirstAired string `json:"firstAired"`
	Network string `json:"network"`
	NetworkId string `json:"networkId"`
	Runtime string `json:"runtime"`
	Genre []string `json:"genre"`
	Overview string `json:"overview"`
	LastUpdated int `json:"lastUpdated"`
	AirsDayOfWeek string `json:"airsDayOfWeek"`
	AirsTime string `json:"airsTime"`
	Rating string `json:"rating"`
	ImdbId string `json:"imdbId"`
	Zap2itId string `json:"zap2itId"`
	Added string `json:"added"`
	SiteRating float32 `json:"siteRating"`
	SiteRatingCount int `json:"siteRatingCount"`
}

type seriesInfoData struct {
	SeriesInfo Series `json:"data"`
}

func (client Client) Search(query SearchQuery) ([]Series) {
	result := searchData{}
	values := url.Values{}

	if query.Name != "" {
		values.Add("name", query.Name)
	}

	if query.ImdbId != "" {
		values.Add("imdbId", query.ImdbId)
	}

	if query.Zap2itId != "" {
		values.Add("zap2itId", query.Zap2itId)
	}

	url := fmt.Sprintf("https://api.thetvdb.com/search/series?%s", values.Encode())

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("authorization", "Bearer " + client.ApiToken)
	if query.AcceptLanguage != "" {
		req.Header.Add("Accept-Language", query.AcceptLanguage)
	}

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()

	body, _ := ioutil.ReadAll(res.Body)

	err := json.Unmarshal(body, &result)

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

	log.Println("search completed successfully")
	log.Println(fmt.Sprintf("Total found: %v", len(result.Series)))

	return result.Series
}

func (client Client) GetSeriesInfo(series Series) Series {
	return client.GetSeriesInfoById(series.Id)
}

func (client Client) GetSeriesInfoById(seriesId int) Series {
	result := seriesInfoData{}

	url := fmt.Sprintf("https://api.thetvdb.com/series/%v", seriesId)

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("authorization", "Bearer " + client.ApiToken)

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()

	body, _ := ioutil.ReadAll(res.Body)

	err := json.Unmarshal(body, &result)

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

	log.Println("get series info completed successfully")
	log.Println(fmt.Sprintf("Series: %s; ImdbId: %s; LastUpdated: %s; Zip2itid: %s",
		result.SeriesInfo.SeriesName,
		result.SeriesInfo.ImdbId,
		result.SeriesInfo.LastUpdated,
		result.SeriesInfo.Zap2itId))

	return result.SeriesInfo
}<commit_msg>rename SeriesInfo to Series<commit_after>package tvdbapi

import (
	"fmt"
	"net/http"
	"io/ioutil"
	"encoding/json"
	"log"
	"net/url"
)

type searchData struct {
	Series []Series  `json:"data"`
}

type SearchQuery struct {
	Name string
	ImdbId string
	Zap2itId string
	AcceptLanguage string
}

type Series struct {
	Id int `json:"id"`
	SeriesName string `json:"seriesName"`
	Aliases []string `json:"aliases"`
	Banner string `json:"banner"`
	SeriesId string `json:"seriesId"`
	Status string `json:"status"`
	FirstAired string `json:"firstAired"`
	Network string `json:"network"`
	NetworkId string `json:"networkId"`
	Runtime string `json:"runtime"`
	Genre []string `json:"genre"`
	Overview string `json:"overview"`
	LastUpdated int `json:"lastUpdated"`
	AirsDayOfWeek string `json:"airsDayOfWeek"`
	AirsTime string `json:"airsTime"`
	Rating string `json:"rating"`
	ImdbId string `json:"imdbId"`
	Zap2itId string `json:"zap2itId"`
	Added string `json:"added"`
	SiteRating float32 `json:"siteRating"`
	SiteRatingCount int `json:"siteRatingCount"`
}

type seriesInfoData struct {
	Series Series `json:"data"`
}

func (client Client) Search(query SearchQuery) ([]Series) {
	result := searchData{}
	values := url.Values{}

	if query.Name != "" {
		values.Add("name", query.Name)
	}

	if query.ImdbId != "" {
		values.Add("imdbId", query.ImdbId)
	}

	if query.Zap2itId != "" {
		values.Add("zap2itId", query.Zap2itId)
	}

	url := fmt.Sprintf("https://api.thetvdb.com/search/series?%s", values.Encode())

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("authorization", "Bearer " + client.ApiToken)
	if query.AcceptLanguage != "" {
		req.Header.Add("Accept-Language", query.AcceptLanguage)
	}

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()

	body, _ := ioutil.ReadAll(res.Body)

	err := json.Unmarshal(body, &result)

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

	log.Println("search completed successfully")
	log.Println(fmt.Sprintf("Total found: %v", len(result.Series)))

	return result.Series
}

func (client Client) GetSeriesInfo(series Series) Series {
	return client.GetSeriesInfoById(series.Id)
}

func (client Client) GetSeriesInfoById(seriesId int) Series {
	result := seriesInfoData{}

	url := fmt.Sprintf("https://api.thetvdb.com/series/%v", seriesId)

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("authorization", "Bearer " + client.ApiToken)

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()

	body, _ := ioutil.ReadAll(res.Body)

	err := json.Unmarshal(body, &result)

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

	log.Println("get series info completed successfully")
	log.Println(fmt.Sprintf("Series: %s; ImdbId: %s; LastUpdated: %s; Zip2itid: %s",
		result.Series.SeriesName,
		result.Series.ImdbId,
		result.Series.LastUpdated,
		result.Series.Zap2itId))

	return result.Series
}<|endoftext|>


<commit_before>//this example starts discovery on adapter
//after discovery process GetDevices method
//returns list of discovered devices
//then with the help of mac address
//connectivity starts
//once sensors are connected it will
//fetch sensor name,manufacturer detail,
//firmware version, hardware version, model
//and sensor data...

package main

import (
	"errors"
	"os"
	"time"

	"github.com/muka/go-bluetooth/api"
	"github.com/muka/go-bluetooth/devices"
	"github.com/muka/go-bluetooth/emitter"
	log "github.com/sirupsen/logrus"
)

var adapterID = "hci0"

func main() {

	manager, err := api.NewManager()
	if err != nil {
		log.Error(err)
		os.Exit(1)
	}

	err = manager.RefreshState()
	if err != nil {
		log.Error(err)
		os.Exit(1)
	}

	err = ShowSensorTagInfo(adapterID)
	if err != nil {
		log.Error(err)
		os.Exit(1)
	}
}

//ShowSensorTagInfo show info from a sensor tag
func ShowSensorTagInfo(adapterID string) error {

	boo, err := api.AdapterExists(adapterID)
	if err != nil {
		return err
	}
	log.Debugf("AdapterExists: %t", boo)

	err = api.StartDiscoveryOn(adapterID)
	if err != nil {
		return err
	}
	// wait a moment for the device to be spawn
	time.Sleep(time.Second)

	devarr, err := api.GetDevices()
	if err != nil {
		return err
	}
	//log.Debug("devarr",devarr[0])
	len := len(devarr)
	log.Debugf("length: %d", len)

	for i := 0; i < len; i++ {
		prop1, err := devarr[i].GetProperties()
		if err != nil {
			log.Fatalf("Cannot load properties of %s: %s", devarr[i].Path, err.Error())
			continue
		}
		log.Debugf("DeviceProperties - ADDRESS: %s", prop1.Address)

		err = ConnectAndFetchSensorDetailAndData(prop1.Address)
		if err != nil {
			return err
		}
	}

	return nil
}

// ConnectAndFetchSensorDetailAndData load an show sensor data
func ConnectAndFetchSensorDetailAndData(tagAddress string) error {

	dev, err := api.GetDeviceByAddress(tagAddress)
	if err != nil {
		return err
	}
	log.Debugf("device (dev): %v", dev)

	if dev == nil {
		return errors.New("device not found")
	}

	if !dev.IsConnected() {
		log.Debug("not connected")
		err = dev.Connect()
		if err != nil {
			return err
		}
	} else {
		log.Debug("already connected")
	}

	sensorTag, err := devices.NewSensorTag(dev)
	if err != nil {
		return err
	}

	name := sensorTag.Temperature.GetName()
	log.Debugf("sensor name: %s", name)

	name1 := sensorTag.Humidity.GetName()
	log.Debugf("sensor name: %s", name1)

	mpu := sensorTag.Mpu.GetName()
	log.Debugf("sensor name: %s", mpu)

	barometric := sensorTag.Barometric.GetName()
	log.Debugf("sensor name: %s", barometric)

	luxometer := sensorTag.Luxometer.GetName()
	log.Debugf("sensor name: %s", luxometer)

	devInfo, err := sensorTag.DeviceInfo.Read()
	if err != nil {
		return err
	}

	log.Debug("FirmwareVersion: ", devInfo.FirmwareVersion)
	log.Debug("HardwareVersion: ", devInfo.HardwareVersion)
	log.Debug("Manufacturer: ", devInfo.Manufacturer)
	log.Debug("Model: ", devInfo.Model)

	err = sensorTag.Temperature.StartNotify()
	if err != nil {
		return err
	}

	err = sensorTag.Humidity.StartNotify()
	if err != nil {
		return err
	}

	err = sensorTag.Mpu.StartNotify(tagAddress)
	if err != nil {
		return err
	}

	err = sensorTag.Barometric.StartNotify(tagAddress)
	if err != nil {
		return err
	}

	err = sensorTag.Luxometer.StartNotify(tagAddress)
	if err != nil {
		return err
	}

	err = dev.On("data", emitter.NewCallback(func(ev emitter.Event) {
		x := ev.GetData().(devices.SensorTagDataEvent)
		log.Debugf("%++v", x)
	}))

	return err
}
<commit_msg>Set debug level to output info<commit_after>//this example starts discovery on adapter
//after discovery process GetDevices method
//returns list of discovered devices
//then with the help of mac address
//connectivity starts
//once sensors are connected it will
//fetch sensor name,manufacturer detail,
//firmware version, hardware version, model
//and sensor data...

package main

import (
	"errors"
	"os"
	"time"

	"github.com/muka/go-bluetooth/api"
	"github.com/muka/go-bluetooth/devices"
	"github.com/muka/go-bluetooth/emitter"
	log "github.com/sirupsen/logrus"
)

var adapterID = "hci0"

func main() {
	log.SetLevel(log.DebugLevel)

	manager, err := api.NewManager()
	if err != nil {
		log.Error(err)
		os.Exit(1)
	}

	err = manager.RefreshState()
	if err != nil {
		log.Error(err)
		os.Exit(1)
	}

	err = ShowSensorTagInfo(adapterID)
	if err != nil {
		log.Error(err)
		os.Exit(1)
	}
}

//ShowSensorTagInfo show info from a sensor tag
func ShowSensorTagInfo(adapterID string) error {

	boo, err := api.AdapterExists(adapterID)
	if err != nil {
		return err
	}
	log.Debugf("AdapterExists: %t", boo)

	err = api.StartDiscoveryOn(adapterID)
	if err != nil {
		return err
	}
	// wait a moment for the device to be spawn
	time.Sleep(time.Second)

	devarr, err := api.GetDevices()
	if err != nil {
		return err
	}
	//log.Debug("devarr",devarr[0])
	len := len(devarr)
	log.Debugf("length: %d", len)

	for i := 0; i < len; i++ {
		prop1, err := devarr[i].GetProperties()
		if err != nil {
			log.Fatalf("Cannot load properties of %s: %s", devarr[i].Path, err.Error())
			continue
		}
		log.Debugf("DeviceProperties - ADDRESS: %s", prop1.Address)

		err = ConnectAndFetchSensorDetailAndData(prop1.Address)
		if err != nil {
			return err
		}
	}

	return nil
}

// ConnectAndFetchSensorDetailAndData load an show sensor data
func ConnectAndFetchSensorDetailAndData(tagAddress string) error {

	dev, err := api.GetDeviceByAddress(tagAddress)
	if err != nil {
		return err
	}
	log.Debugf("device (dev): %v", dev)

	if dev == nil {
		return errors.New("device not found")
	}

	if !dev.IsConnected() {
		log.Debug("not connected")
		err = dev.Connect()
		if err != nil {
			return err
		}
	} else {
		log.Debug("already connected")
	}

	sensorTag, err := devices.NewSensorTag(dev)
	if err != nil {
		return err
	}

	name := sensorTag.Temperature.GetName()
	log.Debugf("sensor name: %s", name)

	name1 := sensorTag.Humidity.GetName()
	log.Debugf("sensor name: %s", name1)

	mpu := sensorTag.Mpu.GetName()
	log.Debugf("sensor name: %s", mpu)

	barometric := sensorTag.Barometric.GetName()
	log.Debugf("sensor name: %s", barometric)

	luxometer := sensorTag.Luxometer.GetName()
	log.Debugf("sensor name: %s", luxometer)

	devInfo, err := sensorTag.DeviceInfo.Read()
	if err != nil {
		return err
	}

	log.Debug("FirmwareVersion: ", devInfo.FirmwareVersion)
	log.Debug("HardwareVersion: ", devInfo.HardwareVersion)
	log.Debug("Manufacturer: ", devInfo.Manufacturer)
	log.Debug("Model: ", devInfo.Model)

	err = sensorTag.Temperature.StartNotify()
	if err != nil {
		return err
	}

	err = sensorTag.Humidity.StartNotify()
	if err != nil {
		return err
	}

	err = sensorTag.Mpu.StartNotify(tagAddress)
	if err != nil {
		return err
	}

	err = sensorTag.Barometric.StartNotify(tagAddress)
	if err != nil {
		return err
	}

	err = sensorTag.Luxometer.StartNotify(tagAddress)
	if err != nil {
		return err
	}

	err = dev.On("data", emitter.NewCallback(func(ev emitter.Event) {
		x := ev.GetData().(devices.SensorTagDataEvent)
		log.Debugf("%++v", x)
	}))

	return err
}
<|endoftext|>


<commit_before>package bitswap

import (
	"sync"
	"time"

	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
	key "github.com/ipfs/go-ipfs/blocks/key"
	engine "github.com/ipfs/go-ipfs/exchange/bitswap/decision"
	bsmsg "github.com/ipfs/go-ipfs/exchange/bitswap/message"
	bsnet "github.com/ipfs/go-ipfs/exchange/bitswap/network"
	wantlist "github.com/ipfs/go-ipfs/exchange/bitswap/wantlist"
	peer "github.com/ipfs/go-ipfs/p2p/peer"
)

type WantManager struct {
	// sync channels for Run loop
	incoming   chan []*bsmsg.Entry
	connect    chan peer.ID // notification channel for new peers connecting
	disconnect chan peer.ID // notification channel for peers disconnecting

	// synchronized by Run loop, only touch inside there
	peers map[peer.ID]*msgQueue
	wl    *wantlist.ThreadSafe

	network bsnet.BitSwapNetwork
	ctx     context.Context
}

func NewWantManager(ctx context.Context, network bsnet.BitSwapNetwork) *WantManager {
	return &WantManager{
		incoming:   make(chan []*bsmsg.Entry, 10),
		connect:    make(chan peer.ID, 10),
		disconnect: make(chan peer.ID, 10),
		peers:      make(map[peer.ID]*msgQueue),
		wl:         wantlist.NewThreadSafe(),
		network:    network,
		ctx:        ctx,
	}
}

type msgPair struct {
	to  peer.ID
	msg bsmsg.BitSwapMessage
}

type cancellation struct {
	who peer.ID
	blk key.Key
}

type msgQueue struct {
	p peer.ID

	outlk   sync.Mutex
	out     bsmsg.BitSwapMessage
	network bsnet.BitSwapNetwork

	work chan struct{}
	done chan struct{}
}

func (pm *WantManager) WantBlocks(ks []key.Key) {
	log.Infof("want blocks: %s", ks)
	pm.addEntries(ks, false)
}

func (pm *WantManager) CancelWants(ks []key.Key) {
	pm.addEntries(ks, true)
}

func (pm *WantManager) addEntries(ks []key.Key, cancel bool) {
	var entries []*bsmsg.Entry
	for i, k := range ks {
		entries = append(entries, &bsmsg.Entry{
			Cancel: cancel,
			Entry: wantlist.Entry{
				Key:      k,
				Priority: kMaxPriority - i,
			},
		})
	}
	select {
	case pm.incoming <- entries:
	case <-pm.ctx.Done():
	}
}

func (pm *WantManager) SendBlock(ctx context.Context, env *engine.Envelope) {
	// Blocks need to be sent synchronously to maintain proper backpressure
	// throughout the network stack
	defer env.Sent()

	msg := bsmsg.New(false)
	msg.AddBlock(env.Block)
	log.Infof("Sending block %s to %s", env.Peer, env.Block)
	err := pm.network.SendMessage(ctx, env.Peer, msg)
	if err != nil {
		log.Noticef("sendblock error: %s", err)
	}
}

func (pm *WantManager) startPeerHandler(p peer.ID) *msgQueue {
	_, ok := pm.peers[p]
	if ok {
		// TODO: log an error?
		return nil
	}

	mq := pm.newMsgQueue(p)

	// new peer, we will want to give them our full wantlist
	fullwantlist := bsmsg.New(true)
	for _, e := range pm.wl.Entries() {
		fullwantlist.AddEntry(e.Key, e.Priority)
	}
	mq.out = fullwantlist
	mq.work <- struct{}{}

	pm.peers[p] = mq
	go mq.runQueue(pm.ctx)
	return mq
}

func (pm *WantManager) stopPeerHandler(p peer.ID) {
	pq, ok := pm.peers[p]
	if !ok {
		// TODO: log error?
		return
	}

	close(pq.done)
	delete(pm.peers, p)
}

func (mq *msgQueue) runQueue(ctx context.Context) {
	for {
		select {
		case <-mq.work: // there is work to be done

			err := mq.network.ConnectTo(ctx, mq.p)
			if err != nil {
				log.Noticef("cant connect to peer %s: %s", mq.p, err)
				// TODO: cant connect, what now?
				continue
			}

			// grab outgoing message
			mq.outlk.Lock()
			wlm := mq.out
			if wlm == nil || wlm.Empty() {
				mq.outlk.Unlock()
				continue
			}
			mq.out = nil
			mq.outlk.Unlock()

			// send wantlist updates
			err = mq.network.SendMessage(ctx, mq.p, wlm)
			if err != nil {
				log.Noticef("bitswap send error: %s", err)
				// TODO: what do we do if this fails?
			}
		case <-mq.done:
			return
		}
	}
}

func (pm *WantManager) Connected(p peer.ID) {
	pm.connect <- p
}

func (pm *WantManager) Disconnected(p peer.ID) {
	pm.disconnect <- p
}

// TODO: use goprocess here once i trust it
func (pm *WantManager) Run() {
	tock := time.NewTicker(rebroadcastDelay.Get())
	defer tock.Stop()
	for {
		select {
		case entries := <-pm.incoming:

			// add changes to our wantlist
			for _, e := range entries {
				if e.Cancel {
					pm.wl.Remove(e.Key)
				} else {
					pm.wl.Add(e.Key, e.Priority)
				}
			}

			// broadcast those wantlist changes
			for _, p := range pm.peers {
				p.addMessage(entries)
			}

		case <-tock.C:
			// resend entire wantlist every so often (REALLY SHOULDNT BE NECESSARY)
			var es []*bsmsg.Entry
			for _, e := range pm.wl.Entries() {
				es = append(es, &bsmsg.Entry{Entry: e})
			}
			for _, p := range pm.peers {
				p.outlk.Lock()
				p.out = bsmsg.New(true)
				p.outlk.Unlock()

				p.addMessage(es)
			}
		case p := <-pm.connect:
			pm.startPeerHandler(p)
		case p := <-pm.disconnect:
			pm.stopPeerHandler(p)
		case <-pm.ctx.Done():
			return
		}
	}
}

func (wm *WantManager) newMsgQueue(p peer.ID) *msgQueue {
	mq := new(msgQueue)
	mq.done = make(chan struct{})
	mq.work = make(chan struct{}, 1)
	mq.network = wm.network
	mq.p = p

	return mq
}

func (mq *msgQueue) addMessage(entries []*bsmsg.Entry) {
	mq.outlk.Lock()
	defer func() {
		mq.outlk.Unlock()
		select {
		case mq.work <- struct{}{}:
		default:
		}
	}()

	// if we have no message held, or the one we are given is full
	// overwrite the one we are holding
	if mq.out == nil {
		mq.out = bsmsg.New(false)
	}

	// TODO: add a msg.Combine(...) method
	// otherwise, combine the one we are holding with the
	// one passed in
	for _, e := range entries {
		if e.Cancel {
			mq.out.Cancel(e.Key)
		} else {
			mq.out.AddEntry(e.Key, e.Priority)
		}
	}
}
<commit_msg>select with context when sending on channels<commit_after>package bitswap

import (
	"sync"
	"time"

	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
	key "github.com/ipfs/go-ipfs/blocks/key"
	engine "github.com/ipfs/go-ipfs/exchange/bitswap/decision"
	bsmsg "github.com/ipfs/go-ipfs/exchange/bitswap/message"
	bsnet "github.com/ipfs/go-ipfs/exchange/bitswap/network"
	wantlist "github.com/ipfs/go-ipfs/exchange/bitswap/wantlist"
	peer "github.com/ipfs/go-ipfs/p2p/peer"
)

type WantManager struct {
	// sync channels for Run loop
	incoming   chan []*bsmsg.Entry
	connect    chan peer.ID // notification channel for new peers connecting
	disconnect chan peer.ID // notification channel for peers disconnecting

	// synchronized by Run loop, only touch inside there
	peers map[peer.ID]*msgQueue
	wl    *wantlist.ThreadSafe

	network bsnet.BitSwapNetwork
	ctx     context.Context
}

func NewWantManager(ctx context.Context, network bsnet.BitSwapNetwork) *WantManager {
	return &WantManager{
		incoming:   make(chan []*bsmsg.Entry, 10),
		connect:    make(chan peer.ID, 10),
		disconnect: make(chan peer.ID, 10),
		peers:      make(map[peer.ID]*msgQueue),
		wl:         wantlist.NewThreadSafe(),
		network:    network,
		ctx:        ctx,
	}
}

type msgPair struct {
	to  peer.ID
	msg bsmsg.BitSwapMessage
}

type cancellation struct {
	who peer.ID
	blk key.Key
}

type msgQueue struct {
	p peer.ID

	outlk   sync.Mutex
	out     bsmsg.BitSwapMessage
	network bsnet.BitSwapNetwork

	work chan struct{}
	done chan struct{}
}

func (pm *WantManager) WantBlocks(ks []key.Key) {
	log.Infof("want blocks: %s", ks)
	pm.addEntries(ks, false)
}

func (pm *WantManager) CancelWants(ks []key.Key) {
	pm.addEntries(ks, true)
}

func (pm *WantManager) addEntries(ks []key.Key, cancel bool) {
	var entries []*bsmsg.Entry
	for i, k := range ks {
		entries = append(entries, &bsmsg.Entry{
			Cancel: cancel,
			Entry: wantlist.Entry{
				Key:      k,
				Priority: kMaxPriority - i,
			},
		})
	}
	select {
	case pm.incoming <- entries:
	case <-pm.ctx.Done():
	}
}

func (pm *WantManager) SendBlock(ctx context.Context, env *engine.Envelope) {
	// Blocks need to be sent synchronously to maintain proper backpressure
	// throughout the network stack
	defer env.Sent()

	msg := bsmsg.New(false)
	msg.AddBlock(env.Block)
	log.Infof("Sending block %s to %s", env.Peer, env.Block)
	err := pm.network.SendMessage(ctx, env.Peer, msg)
	if err != nil {
		log.Noticef("sendblock error: %s", err)
	}
}

func (pm *WantManager) startPeerHandler(p peer.ID) *msgQueue {
	_, ok := pm.peers[p]
	if ok {
		// TODO: log an error?
		return nil
	}

	mq := pm.newMsgQueue(p)

	// new peer, we will want to give them our full wantlist
	fullwantlist := bsmsg.New(true)
	for _, e := range pm.wl.Entries() {
		fullwantlist.AddEntry(e.Key, e.Priority)
	}
	mq.out = fullwantlist
	mq.work <- struct{}{}

	pm.peers[p] = mq
	go mq.runQueue(pm.ctx)
	return mq
}

func (pm *WantManager) stopPeerHandler(p peer.ID) {
	pq, ok := pm.peers[p]
	if !ok {
		// TODO: log error?
		return
	}

	close(pq.done)
	delete(pm.peers, p)
}

func (mq *msgQueue) runQueue(ctx context.Context) {
	for {
		select {
		case <-mq.work: // there is work to be done

			err := mq.network.ConnectTo(ctx, mq.p)
			if err != nil {
				log.Noticef("cant connect to peer %s: %s", mq.p, err)
				// TODO: cant connect, what now?
				continue
			}

			// grab outgoing message
			mq.outlk.Lock()
			wlm := mq.out
			if wlm == nil || wlm.Empty() {
				mq.outlk.Unlock()
				continue
			}
			mq.out = nil
			mq.outlk.Unlock()

			// send wantlist updates
			err = mq.network.SendMessage(ctx, mq.p, wlm)
			if err != nil {
				log.Noticef("bitswap send error: %s", err)
				// TODO: what do we do if this fails?
			}
		case <-mq.done:
			return
		}
	}
}

func (pm *WantManager) Connected(p peer.ID) {
	select {
	case pm.connect <- p:
	case <-pm.ctx.Done():
	}
}

func (pm *WantManager) Disconnected(p peer.ID) {
	select {
	case pm.disconnect <- p:
	case <-pm.ctx.Done():
	}
}

// TODO: use goprocess here once i trust it
func (pm *WantManager) Run() {
	tock := time.NewTicker(rebroadcastDelay.Get())
	defer tock.Stop()
	for {
		select {
		case entries := <-pm.incoming:

			// add changes to our wantlist
			for _, e := range entries {
				if e.Cancel {
					pm.wl.Remove(e.Key)
				} else {
					pm.wl.Add(e.Key, e.Priority)
				}
			}

			// broadcast those wantlist changes
			for _, p := range pm.peers {
				p.addMessage(entries)
			}

		case <-tock.C:
			// resend entire wantlist every so often (REALLY SHOULDNT BE NECESSARY)
			var es []*bsmsg.Entry
			for _, e := range pm.wl.Entries() {
				es = append(es, &bsmsg.Entry{Entry: e})
			}
			for _, p := range pm.peers {
				p.outlk.Lock()
				p.out = bsmsg.New(true)
				p.outlk.Unlock()

				p.addMessage(es)
			}
		case p := <-pm.connect:
			pm.startPeerHandler(p)
		case p := <-pm.disconnect:
			pm.stopPeerHandler(p)
		case <-pm.ctx.Done():
			return
		}
	}
}

func (wm *WantManager) newMsgQueue(p peer.ID) *msgQueue {
	mq := new(msgQueue)
	mq.done = make(chan struct{})
	mq.work = make(chan struct{}, 1)
	mq.network = wm.network
	mq.p = p

	return mq
}

func (mq *msgQueue) addMessage(entries []*bsmsg.Entry) {
	mq.outlk.Lock()
	defer func() {
		mq.outlk.Unlock()
		select {
		case mq.work <- struct{}{}:
		default:
		}
	}()

	// if we have no message held, or the one we are given is full
	// overwrite the one we are holding
	if mq.out == nil {
		mq.out = bsmsg.New(false)
	}

	// TODO: add a msg.Combine(...) method
	// otherwise, combine the one we are holding with the
	// one passed in
	for _, e := range entries {
		if e.Cancel {
			mq.out.Cancel(e.Key)
		} else {
			mq.out.AddEntry(e.Key, e.Priority)
		}
	}
}
<|endoftext|>