<commit_before>package main

import (
	"bufio"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"github.com/boltdb/bolt"
	"github.com/gonium/gosdm630"
	"io"
	"log"
	"os"
	"sync"
	"time"
)

const (
	DEFAULT_BUCKET = "records"
)

type SnipDB struct {
	sync.Mutex
	databaseFile string
	snips        []sdm630.QuerySnip
}

func NewSnipDB(
	dbfile string,
) *SnipDB {
	return &SnipDB{
		databaseFile: dbfile,
		snips:        []sdm630.QuerySnip{},
	}
}

func (r *SnipDB) AddSnip(snip sdm630.QuerySnip) {
	r.Lock()
	defer r.Unlock()
	r.snips = append(r.snips, snip)
}

func (r *SnipDB) RunRecorder(sleepSeconds int) {
	// open database
	db, err := bolt.Open(r.databaseFile,
		0600,
		&bolt.Options{Timeout: 1 * time.Second},
	)
	if err != nil {
		log.Fatal("Cannot open database, exiting. Error was: ", err.Error())
	}
	defer db.Close()

	// now: sleep, then store recorded snips.
	for {
		time.Sleep(time.Duration(sleepSeconds) * time.Second)
		r.Lock()
		log.Printf("Cached %d measurements, storing.", len(r.snips))
		// taken from https://github.com/boltdb/bolt#autoincrementing-integer-for-the-bucket
		err := db.Update(func(tx *bolt.Tx) error {
			// Retrieve the users bucket.
			// This should be created when the DB is first opened.
			b, err := tx.CreateBucketIfNotExists([]byte(DEFAULT_BUCKET))
			if err != nil {
				return fmt.Errorf("Failed to create storage bucket, error was: %s",
					err.Error())
			}
			for _, snip := range r.snips {
				// Generate ID for the reading.
				// This returns an error only if the Tx is closed or not writeable.
				// That can't happen in an Update() call so I ignore the error check.
				id, _ := b.NextSequence()
				buf, err := json.Marshal(snip)
				if err != nil {
					return fmt.Errorf("Failed to marshal data %s, error was: %s",
						snip.String(), err.Error())
				}
				err = b.Put(itob(id), buf)
				if err != nil {
					return fmt.Errorf("Failed to add snip %s to bucket, error was: %s",
						snip.String(), err.Error())
				}
			}
			return nil
		})
		if err != nil {
			log.Fatal("Failed to store records: %s", err.Error())
		} else {
			// clear the cache
			r.snips = []sdm630.QuerySnip{}
		}
		r.Unlock()
	}
}

// itob returns an 8-byte big endian representation of v.
func itob(v uint64) []byte {
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, uint64(v))
	return b
}

func (db *SnipDB) Inspect(w io.Writer) error {
	// open database
	blt, err := bolt.Open(db.databaseFile,
		0600,
		&bolt.Options{
			Timeout:  1 * time.Second,
			ReadOnly: true,
		},
	)
	if err != nil {
		return fmt.Errorf("Cannot open database, exiting. Error was: %s."+
			" Is the database file in use by another process?", err.Error())
	}
	defer blt.Close()

	firstSnipTime := time.Now()
	var lastSnipTime time.Time
	numSnips := 0

	err = blt.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(DEFAULT_BUCKET))
		c := b.Cursor()

		for k, v := c.First(); k != nil; k, v = c.Next() {
			numSnips += 1
			var snip sdm630.QuerySnip
			err := json.Unmarshal(v, &snip)
			if err != nil {
				return fmt.Errorf("Failed to unmarshal snip %s, error was: %s", v,
					err.Error())
			}
			if snip.ReadTimestamp.Before(firstSnipTime) {
				firstSnipTime = snip.ReadTimestamp
			}
			if snip.ReadTimestamp.After(lastSnipTime) {
				lastSnipTime = snip.ReadTimestamp
			}
		}
		return nil
	})

	fmt.Fprintf(w, "Found %d records:\n", numSnips)
	fmt.Fprintf(w, "* First recorded on %s\n", firstSnipTime)
	fmt.Fprintf(w, "* Last recorded on %s\n", lastSnipTime)
	return err
}

func (db *SnipDB) ExportCSV(csvfile string) error {
	// open database
	blt, err := bolt.Open(db.databaseFile,
		0600,
		&bolt.Options{
			Timeout:  1 * time.Second,
			ReadOnly: true,
		},
	)
	if err != nil {
		return fmt.Errorf("Cannot open database, exiting. Error was: %s."+
			" Is the database file in use by another process?", err.Error())
	}
	defer blt.Close()

	f, err := os.Create(csvfile)
	if err != nil {
		return fmt.Errorf("Cannot write to csv file, exiting. Error was: "+
			"%s.", err.Error())
	}
	defer f.Close()
	w := bufio.NewWriter(f)

	fmt.Fprintf(w, "ID\tTime\tL1\tL2\tL3\n")
	numSnips := 0
	err = blt.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(DEFAULT_BUCKET))
		c := b.Cursor()

		for k, v := c.First(); k != nil; k, v = c.Next() {
			numSnips += 1
			var snip sdm630.QuerySnip
			err := json.Unmarshal(v, &snip)
			if err != nil {
				return fmt.Errorf("Failed to unmarshal snip %s, error was: %s", v,
					err.Error())
			}

			// TODO: Export all snips
			switch snip.IEC61850 {
			case "WLocPhsA":
				fmt.Fprintf(w, "%d\t%s\t%.2f\t\t\n", snip.DeviceId,
					snip.ReadTimestamp, snip.Value)
			case "WLocPhsB":
				fmt.Fprintf(w, "%d\t%s\t\t%.2f\t\n", snip.DeviceId,
					snip.ReadTimestamp, snip.Value)
			case "WLocPhsC":
				fmt.Fprintf(w, "%d\t%s\t\t\t%.2f\n", snip.DeviceId,
					snip.ReadTimestamp, snip.Value)
			default:
				continue
			}

		}
		return nil
	})
	log.Printf("Exported %d records.", numSnips)
	w.Flush()
	return err
}
<commit_msg>more go vet fixes!<commit_after>package main

import (
	"bufio"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"github.com/boltdb/bolt"
	"github.com/gonium/gosdm630"
	"io"
	"log"
	"os"
	"sync"
	"time"
)

const (
	DEFAULT_BUCKET = "records"
)

type SnipDB struct {
	sync.Mutex
	databaseFile string
	snips        []sdm630.QuerySnip
}

func NewSnipDB(
	dbfile string,
) *SnipDB {
	return &SnipDB{
		databaseFile: dbfile,
		snips:        []sdm630.QuerySnip{},
	}
}

func (r *SnipDB) AddSnip(snip sdm630.QuerySnip) {
	r.Lock()
	defer r.Unlock()
	r.snips = append(r.snips, snip)
}

func (r *SnipDB) RunRecorder(sleepSeconds int) {
	// open database
	db, err := bolt.Open(r.databaseFile,
		0600,
		&bolt.Options{Timeout: 1 * time.Second},
	)
	if err != nil {
		log.Fatal("Cannot open database, exiting. Error was: ", err.Error())
	}
	defer db.Close()

	// now: sleep, then store recorded snips.
	for {
		time.Sleep(time.Duration(sleepSeconds) * time.Second)
		r.Lock()
		log.Printf("Cached %d measurements, storing.", len(r.snips))
		// taken from https://github.com/boltdb/bolt#autoincrementing-integer-for-the-bucket
		err := db.Update(func(tx *bolt.Tx) error {
			// Retrieve the users bucket.
			// This should be created when the DB is first opened.
			b, err := tx.CreateBucketIfNotExists([]byte(DEFAULT_BUCKET))
			if err != nil {
				return fmt.Errorf("Failed to create storage bucket, error was: %s",
					err.Error())
			}
			for _, snip := range r.snips {
				// Generate ID for the reading.
				// This returns an error only if the Tx is closed or not writeable.
				// That can't happen in an Update() call so I ignore the error check.
				id, _ := b.NextSequence()
				buf, err := json.Marshal(snip)
				if err != nil {
					return fmt.Errorf("Failed to marshal data %s, error was: %s",
						snip.String(), err.Error())
				}
				err = b.Put(itob(id), buf)
				if err != nil {
					return fmt.Errorf("Failed to add snip %s to bucket, error was: %s",
						snip.String(), err.Error())
				}
			}
			return nil
		})
		if err != nil {
			log.Fatalf("Failed to store records: %s", err.Error())
		} else {
			// clear the cache
			r.snips = []sdm630.QuerySnip{}
		}
		r.Unlock()
	}
}

// itob returns an 8-byte big endian representation of v.
func itob(v uint64) []byte {
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, uint64(v))
	return b
}

func (db *SnipDB) Inspect(w io.Writer) error {
	// open database
	blt, err := bolt.Open(db.databaseFile,
		0600,
		&bolt.Options{
			Timeout:  1 * time.Second,
			ReadOnly: true,
		},
	)
	if err != nil {
		return fmt.Errorf("Cannot open database, exiting. Error was: %s."+
			" Is the database file in use by another process?", err.Error())
	}
	defer blt.Close()

	firstSnipTime := time.Now()
	var lastSnipTime time.Time
	numSnips := 0

	err = blt.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(DEFAULT_BUCKET))
		c := b.Cursor()

		for k, v := c.First(); k != nil; k, v = c.Next() {
			numSnips += 1
			var snip sdm630.QuerySnip
			err := json.Unmarshal(v, &snip)
			if err != nil {
				return fmt.Errorf("Failed to unmarshal snip %s, error was: %s", v,
					err.Error())
			}
			if snip.ReadTimestamp.Before(firstSnipTime) {
				firstSnipTime = snip.ReadTimestamp
			}
			if snip.ReadTimestamp.After(lastSnipTime) {
				lastSnipTime = snip.ReadTimestamp
			}
		}
		return nil
	})

	fmt.Fprintf(w, "Found %d records:\n", numSnips)
	fmt.Fprintf(w, "* First recorded on %s\n", firstSnipTime)
	fmt.Fprintf(w, "* Last recorded on %s\n", lastSnipTime)
	return err
}

func (db *SnipDB) ExportCSV(csvfile string) error {
	// open database
	blt, err := bolt.Open(db.databaseFile,
		0600,
		&bolt.Options{
			Timeout:  1 * time.Second,
			ReadOnly: true,
		},
	)
	if err != nil {
		return fmt.Errorf("Cannot open database, exiting. Error was: %s."+
			" Is the database file in use by another process?", err.Error())
	}
	defer blt.Close()

	f, err := os.Create(csvfile)
	if err != nil {
		return fmt.Errorf("Cannot write to csv file, exiting. Error was: "+
			"%s.", err.Error())
	}
	defer f.Close()
	w := bufio.NewWriter(f)

	fmt.Fprintf(w, "ID\tTime\tL1\tL2\tL3\n")
	numSnips := 0
	err = blt.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(DEFAULT_BUCKET))
		c := b.Cursor()

		for k, v := c.First(); k != nil; k, v = c.Next() {
			numSnips += 1
			var snip sdm630.QuerySnip
			err := json.Unmarshal(v, &snip)
			if err != nil {
				return fmt.Errorf("Failed to unmarshal snip %s, error was: %s", v,
					err.Error())
			}

			// TODO: Export all snips
			switch snip.IEC61850 {
			case "WLocPhsA":
				fmt.Fprintf(w, "%d\t%s\t%.2f\t\t\n", snip.DeviceId,
					snip.ReadTimestamp, snip.Value)
			case "WLocPhsB":
				fmt.Fprintf(w, "%d\t%s\t\t%.2f\t\n", snip.DeviceId,
					snip.ReadTimestamp, snip.Value)
			case "WLocPhsC":
				fmt.Fprintf(w, "%d\t%s\t\t\t%.2f\n", snip.DeviceId,
					snip.ReadTimestamp, snip.Value)
			default:
				continue
			}

		}
		return nil
	})
	log.Printf("Exported %d records.", numSnips)
	w.Flush()
	return err
}
<|endoftext|>


<commit_before>package types

import (
	"fmt"
	"testing"
	"time"
)

var durationStrings = []struct {
	value    Duration
	expected string
}{
	{Duration(0), "0"},
	{Duration(-1), "-1ns"},
	{Duration(10 * time.Second), "10s"},
	{Duration(-7 * time.Minute), "-7m0s"},
	{Duration(1500 * time.Millisecond), "1.5s"},
	{Duration(1 * time.Hour), "1h0m0s"},
}

func TestDurationStringer(t *testing.T) {
	for _, record := range durationStrings {
		actual := record.value.String()
		if record.expected != actual {
			t.Errorf("Expected %s, but got %s", record.expected, actual)
		}
	}
}

func TestDurationMarshalJSON(t *testing.T) {
	for _, record := range durationStrings {
		actual, err := record.value.MarshalJSON()
		if err != nil {
			t.Fatalf("Failed to marshal duration: %v", err)
		}

		expected := fmt.Sprintf(`"%s"`, record.expected)
		if expected != string(actual) {
			t.Errorf("Expected %s, but got %s", expected, actual)
		}
	}
}

func TestDurationUnmarshalJSON(t *testing.T) {
	for _, record := range durationStrings {
		// perform the reverse conversion
		jsonValue := fmt.Sprintf(`"%s"`, record.expected)
		var actual Duration
		if err := actual.UnmarshalJSON([]byte(jsonValue)); err != nil {
			t.Fatalf("Failed to unmarshal duration: %v", err)
		}

		if record.value != actual {
			t.Errorf("Expected %s, but got %s", record.value, actual)
		}
	}

	var integralValues = []struct {
		input    int
		expected Duration
	}{
		{1000, Duration(1000)},
		{1245798273, Duration(1245798273)},
		{0, Duration(0)},
		{-45, Duration(-45)},
	}

	for _, record := range integralValues {
		jsonValue := fmt.Sprintf("%d", record.input)
		var actual Duration
		if err := actual.UnmarshalJSON([]byte(jsonValue)); err != nil {
			t.Fatalf("Failed to unmarshal duration: %v", err)
		}

		if record.expected != actual {
			t.Errorf("Expected %s, but got %s", record.expected, actual)
		}
	}
}
<commit_msg>Updated the test data<commit_after>package types

import (
	"fmt"
	"testing"
	"time"
)

var durationStrings = []struct {
	value    Duration
	expected string
}{
	{Duration(0), "0s"},
	{Duration(-1), "-1ns"},
	{Duration(10 * time.Second), "10s"},
	{Duration(-7 * time.Minute), "-7m0s"},
	{Duration(1500 * time.Millisecond), "1.5s"},
	{Duration(1 * time.Hour), "1h0m0s"},
}

func TestDurationStringer(t *testing.T) {
	for _, record := range durationStrings {
		actual := record.value.String()
		if record.expected != actual {
			t.Errorf("Expected %s, but got %s", record.expected, actual)
		}
	}
}

func TestDurationMarshalJSON(t *testing.T) {
	for _, record := range durationStrings {
		actual, err := record.value.MarshalJSON()
		if err != nil {
			t.Fatalf("Failed to marshal duration: %v", err)
		}

		expected := fmt.Sprintf(`"%s"`, record.expected)
		if expected != string(actual) {
			t.Errorf("Expected %s, but got %s", expected, actual)
		}
	}
}

func TestDurationUnmarshalJSON(t *testing.T) {
	for _, record := range durationStrings {
		// perform the reverse conversion
		jsonValue := fmt.Sprintf(`"%s"`, record.expected)
		var actual Duration
		if err := actual.UnmarshalJSON([]byte(jsonValue)); err != nil {
			t.Fatalf("Failed to unmarshal duration: %v", err)
		}

		if record.value != actual {
			t.Errorf("Expected %s, but got %s", record.value, actual)
		}
	}

	var integralValues = []struct {
		input    int
		expected Duration
	}{
		{1000, Duration(1000)},
		{1245798273, Duration(1245798273)},
		{0, Duration(0)},
		{-45, Duration(-45)},
	}

	for _, record := range integralValues {
		jsonValue := fmt.Sprintf("%d", record.input)
		var actual Duration
		if err := actual.UnmarshalJSON([]byte(jsonValue)); err != nil {
			t.Fatalf("Failed to unmarshal duration: %v", err)
		}

		if record.expected != actual {
			t.Errorf("Expected %s, but got %s", record.expected, actual)
		}
	}
}
<|endoftext|>


<commit_before>package todo

import "fmt"

// Todo represents a TODO.
type Todo struct {
	ID    int
	Title string
	State State
	Body  string
	Todos []*Todo
}

// State represents the state of TODO.
type State string

const (
	// Undone represents state of undone TODO.
	Undone State = "UNDONE"

	// Done represents state of done TODO.
	Done State = "DONE"

	// Waiting represents state of TODO which I'm waiting for someone to finish.
	Waiting State = "WAITING"
)

func (td *Todo) String() string {
	return fmt.Sprintf("%#v", td)
}
<commit_msg>Add (*Todo).Equal() to compare two TODOs<commit_after>package todo

import "fmt"

// Todo represents a TODO.
type Todo struct {
	ID    int
	Title string
	State State
	Body  string
	Todos []*Todo
}

// State represents the state of TODO.
type State string

const (
	// Undone represents state of undone TODO.
	Undone State = "UNDONE"

	// Done represents state of done TODO.
	Done State = "DONE"

	// Waiting represents state of TODO which I'm waiting for someone to finish.
	Waiting State = "WAITING"
)

func (td *Todo) String() string {
	return fmt.Sprintf("%#v", td)
}

// Equal compares self and another TODO deeply.
func (td *Todo) Equal(another *Todo) bool {
	if another == nil {
		return false
	}

	if td.ID != another.ID {
		return false
	}

	if td.Title != another.Title {
		return false
	}

	if td.State != another.State {
		return false
	}

	if td.Body != another.Body {
		return false
	}

	if len(td.Todos) != len(another.Todos) {
		return false
	}

	for i, sub := range td.Todos {
		equal := sub.Equal(another.Todos[i])
		if !equal {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>package veneur

import (
	"container/ring"
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	"github.com/Sirupsen/logrus"
	lightstep "github.com/lightstep/lightstep-tracer-go"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	"github.com/stripe/veneur/ssf"
	"github.com/stripe/veneur/trace"
)

const datadogResourceKey = "resource"
const datadogNameKey = "name"

const lightStepOperationKey = "name"

const totalSpansFlushedMetricKey = "worker.spans_flushed_total"

// spanSink is a receiver of spans that handles sending those spans to some
// downstream sink. Calls to `Ingest(span)` are meant to give the sink control
// of the span, with periodic calls to flush as a signal for sinks that don't
// handle their own flushing in a separate goroutine, etc.
type spanSink interface {
	Name() string
	Ingest(ssf.SSFSpan) error
	Flush()
}

// DatadogSpanSink is a sink for sending spans to a Datadog trace agent.
type datadogSpanSink struct {
	HTTPClient   *http.Client
	buffer       *ring.Ring
	bufferSize   int
	mutex        *sync.Mutex
	stats        *statsd.Client
	commonTags   map[string]string
	traceAddress string
}

// NewDatadogSpanSink creates a new Datadog sink for trace spans.
func NewDatadogSpanSink(config *Config, stats *statsd.Client, httpClient *http.Client, commonTags map[string]string) (*datadogSpanSink, error) {
	return &datadogSpanSink{
		HTTPClient:   httpClient,
		bufferSize:   config.SsfBufferSize,
		buffer:       ring.New(config.SsfBufferSize),
		mutex:        &sync.Mutex{},
		stats:        stats,
		commonTags:   commonTags,
		traceAddress: config.DatadogTraceAPIAddress,
	}, nil
}

// Name returns the name of this sink.
func (dd *datadogSpanSink) Name() string {
	return "datadog"
}

// Ingest takes the span and adds it to the ringbuffer.
func (dd *datadogSpanSink) Ingest(span ssf.SSFSpan) error {
	dd.mutex.Lock()
	defer dd.mutex.Unlock()

	dd.buffer.Value = span
	dd.buffer = dd.buffer.Next()
	return nil
}

// Flush signals the sink to send it's spans to their destination. For this
// sync it means we'll be making an HTTP request to send them along. We assume
// it's beneficial to performance to defer these until the normal 10s flush.
func (dd *datadogSpanSink) Flush() {
	dd.mutex.Lock()

	ssfSpans := make([]ssf.SSFSpan, 0, dd.buffer.Len())

	dd.buffer.Do(func(t interface{}) {
		const tooEarly = 1497
		const tooLate = 1497629343000000

		if t != nil {
			ssfSpan, ok := t.(ssf.SSFSpan)
			if !ok {
				log.Error("Got an unknown object in tracing ring!")
				// We'll just skip this one so we don't poison pill or anything.
				return
			}

			var timeErr string
			if ssfSpan.StartTimestamp < tooEarly {
				timeErr = "type:tooEarly"
			}
			if ssfSpan.StartTimestamp > tooLate {
				timeErr = "type:tooLate"
			}
			if timeErr != "" {
				dd.stats.Incr("worker.trace.sink.timestamp_error", []string{timeErr}, 1) // TODO tag as dd?
			}

			if ssfSpan.Tags == nil {
				ssfSpan.Tags = make(map[string]string)
			}

			// Add common tags from veneur's config
			// this will overwrite tags already present on the span
			for k, v := range dd.commonTags {
				ssfSpan.Tags[k] = v
			}
			ssfSpans = append(ssfSpans, ssfSpan)
		}
	})

	// Reset the ring.
	dd.buffer = ring.New(dd.bufferSize)

	// We're done manipulating stuff, let Ingest loose again.
	dd.mutex.Unlock()

	serviceCount := make(map[string]int64)
	var finalTraces []*DatadogTraceSpan
	// Conver the SSFSpans into Datadog Spans
	for _, span := range ssfSpans {
		// -1 is a canonical way of passing in invalid info in Go
		// so we should support that too
		parentID := span.ParentId

		// check if this is the root span
		if parentID <= 0 {
			// we need parentId to be zero for json:omitempty to work
			parentID = 0
		}

		resource := span.Tags[datadogResourceKey]
		name := span.Tags[datadogNameKey]

		tags := map[string]string{}
		// Get the span's existing tags
		for k, v := range span.Tags {
			tags[k] = v
		}

		delete(tags, datadogNameKey)
		delete(tags, datadogResourceKey)

		// TODO implement additional metrics
		var metrics map[string]float64

		var errorCode int64
		if span.Error {
			errorCode = 2
		}

		ddspan := &DatadogTraceSpan{
			TraceID:  span.TraceId,
			SpanID:   span.Id,
			ParentID: parentID,
			Service:  span.Service,
			Name:     name,
			Resource: resource,
			Start:    span.StartTimestamp,
			Duration: span.EndTimestamp - span.StartTimestamp,
			// TODO don't hardcode
			Type:    "http",
			Error:   errorCode,
			Metrics: metrics,
			Meta:    tags,
		}
		serviceCount[span.Service]++
		finalTraces = append(finalTraces, ddspan)
	}

	if len(finalTraces) != 0 {
		// this endpoint is not documented to take an array... but it does
		// another curious constraint of this endpoint is that it does not
		// support "Content-Encoding: deflate"

		err := postHelper(context.TODO(), dd.HTTPClient, dd.stats, fmt.Sprintf("%s/spans", dd.traceAddress), finalTraces, "flush_traces", false)

		if err == nil {
			log.WithField("traces", len(finalTraces)).Info("Completed flushing traces to Datadog")
			dd.stats.Count(totalSpansFlushedMetricKey, int64(len(ssfSpans)), []string{"sink:datadog"}, 1)
			// TODO: Per service counters?
		} else {
			log.WithFields(logrus.Fields{
				"traces":        len(finalTraces),
				logrus.ErrorKey: err}).Warn("Error flushing traces to Datadog")
		}
		for service, count := range serviceCount {
			dd.stats.Count(totalSpansFlushedMetricKey, count, []string{"sink:datadog", fmt.Sprintf("service:%s", service)}, 1)
		}
	} else {
		log.Info("No traces to flush to Datadog, skipping.")
	}
}

// lightStepSpanSink is a sink for spans to be sent to the LightStep client.
type lightStepSpanSink struct {
	tracer       opentracing.Tracer
	stats        *statsd.Client
	commonTags   map[string]string
	mutex        *sync.Mutex
	serviceCount map[string]int64
}

// NewLightStepSpanSink creates a new instance of a LightStepSpanSink.
func NewLightStepSpanSink(config *Config, stats *statsd.Client, commonTags map[string]string) (*lightStepSpanSink, error) {

	var host *url.URL
	host, err := url.Parse(config.TraceLightstepCollectorHost)
	if err != nil {
		log.WithError(err).WithField(
			"host", config.TraceLightstepCollectorHost,
		).Error("Error parsing LightStep collector URL")
		return &lightStepSpanSink{}, err
	}

	port, err := strconv.Atoi(host.Port())
	if err != nil {
		port = lightstepDefaultPort
	} else {
		log.WithError(err).WithFields(logrus.Fields{
			"port":         port,
			"default_port": lightstepDefaultPort,
		}).Warn("Error parsing LightStep port, using default")
	}

	reconPeriod, err := time.ParseDuration(config.TraceLightstepReconnectPeriod)
	if err != nil {
		log.WithError(err).WithFields(logrus.Fields{
			"interval":         config.TraceLightstepReconnectPeriod,
			"default_interval": lightstepDefaultInterval,
		}).Warn("Failed to parse reconnect duration, using default.")
		reconPeriod = lightstepDefaultInterval
	}

	log.WithFields(logrus.Fields{
		"Host": host.Hostname(),
		"Port": port,
	}).Info("Dialing lightstep host")

	maxSpans := config.TraceLightstepMaximumSpans
	if maxSpans == 0 {
		maxSpans = config.SsfBufferSize
		log.WithField("max spans", maxSpans).Info("Using default maximum spans — ssf_buffer_size — for LightStep")
	}

	lightstepTracer := lightstep.NewTracer(lightstep.Options{
		AccessToken:     config.TraceLightstepAccessToken,
		ReconnectPeriod: reconPeriod,
		Collector: lightstep.Endpoint{
			Host:      host.Hostname(),
			Port:      port,
			Plaintext: true,
		},
		UseGRPC:          true,
		MaxBufferedSpans: maxSpans,
	})

	return &lightStepSpanSink{
		tracer:       lightstepTracer,
		stats:        stats,
		serviceCount: make(map[string]int64),
		mutex:        &sync.Mutex{},
	}, nil
}

// Name returns this sink's name.
func (ls *lightStepSpanSink) Name() string {
	return "lightstep"
}

// Ingest takes in a span and passed it along to the LS client after
// some sanity checks and improvements are made.
func (ls *lightStepSpanSink) Ingest(ssfSpan ssf.SSFSpan) error {
	parentID := ssfSpan.ParentId
	if parentID <= 0 {
		parentID = 0
	}

	var errorCode int64
	if ssfSpan.Error {
		errorCode = 1
	}

	timestamp := time.Unix(ssfSpan.StartTimestamp/1e9, ssfSpan.StartTimestamp%1e9)
	sp := ls.tracer.StartSpan(
		ssfSpan.Tags[lightStepOperationKey],
		opentracing.StartTime(timestamp),
		lightstep.SetTraceID(uint64(ssfSpan.TraceId)),
		lightstep.SetSpanID(uint64(ssfSpan.Id)),
		lightstep.SetParentSpanID(uint64(parentID)))

	sp.SetTag(trace.ResourceKey, ssfSpan.Tags[trace.ResourceKey]) // TODO Why is this here?
	sp.SetTag(lightstep.ComponentNameKey, ssfSpan.Service)
	// TODO don't hardcode
	sp.SetTag("type", "http")
	sp.SetTag("error-code", errorCode)
	for k, v := range ssfSpan.Tags {
		sp.SetTag(k, v)
	}
	// And now set any veneur common tags
	for k, v := range ls.commonTags {
		sp.SetTag(k, v)
	}
	// TODO add metrics as tags to the span as well?

	if errorCode > 0 {
		// Note: this sets the OT-standard "error" tag, which
		// LightStep uses to flag error spans.
		ext.Error.Set(sp, true)
	}

	endTime := time.Unix(ssfSpan.EndTimestamp/1e9, ssfSpan.EndTimestamp%1e9)
	sp.FinishWithOptions(opentracing.FinishOptions{
		FinishTime: endTime,
	})

	service := ssfSpan.Service
	if service == "" {
		service = "unknown"
	}

	// Protect mutating the service count with a mutex
	ls.mutex.Lock()
	defer ls.mutex.Unlock()
	ls.serviceCount[service]++

	return nil
}

// Flush doesn't need to do anything to the LS tracer, so we emit metrics
// instead.
func (ls *lightStepSpanSink) Flush() {
	ls.mutex.Lock()
	defer ls.mutex.Unlock()

	totalCount := int64(0)
	for service, count := range ls.serviceCount {
		totalCount += count
		ls.stats.Count(totalSpansFlushedMetricKey, count, []string{"sink:lightstep", fmt.Sprintf("service:%s", service)}, 1)
	}
	ls.serviceCount = make(map[string]int64)
	log.WithField("total_spans", totalCount).Debug("Checkpointing flushed spans for Lightstep")
}
<commit_msg>Add some bits from the rebase<commit_after>package veneur

import (
	"container/ring"
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	"github.com/Sirupsen/logrus"
	lightstep "github.com/lightstep/lightstep-tracer-go"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	"github.com/stripe/veneur/ssf"
	"github.com/stripe/veneur/trace"
)

const datadogResourceKey = "resource"
const datadogNameKey = "name"

const lightStepOperationKey = "name"

const totalSpansFlushedMetricKey = "worker.spans_flushed_total"

// spanSink is a receiver of spans that handles sending those spans to some
// downstream sink. Calls to `Ingest(span)` are meant to give the sink control
// of the span, with periodic calls to flush as a signal for sinks that don't
// handle their own flushing in a separate goroutine, etc.
type spanSink interface {
	Name() string
	Ingest(ssf.SSFSpan) error
	Flush()
}

// DatadogSpanSink is a sink for sending spans to a Datadog trace agent.
type datadogSpanSink struct {
	HTTPClient   *http.Client
	buffer       *ring.Ring
	bufferSize   int
	mutex        *sync.Mutex
	stats        *statsd.Client
	commonTags   map[string]string
	traceAddress string
}

// NewDatadogSpanSink creates a new Datadog sink for trace spans.
func NewDatadogSpanSink(config *Config, stats *statsd.Client, httpClient *http.Client, commonTags map[string]string) (*datadogSpanSink, error) {
	return &datadogSpanSink{
		HTTPClient:   httpClient,
		bufferSize:   config.SsfBufferSize,
		buffer:       ring.New(config.SsfBufferSize),
		mutex:        &sync.Mutex{},
		stats:        stats,
		commonTags:   commonTags,
		traceAddress: config.DatadogTraceAPIAddress,
	}, nil
}

// Name returns the name of this sink.
func (dd *datadogSpanSink) Name() string {
	return "datadog"
}

// Ingest takes the span and adds it to the ringbuffer.
func (dd *datadogSpanSink) Ingest(span ssf.SSFSpan) error {
	dd.mutex.Lock()
	defer dd.mutex.Unlock()

	dd.buffer.Value = span
	dd.buffer = dd.buffer.Next()
	return nil
}

// Flush signals the sink to send it's spans to their destination. For this
// sync it means we'll be making an HTTP request to send them along. We assume
// it's beneficial to performance to defer these until the normal 10s flush.
func (dd *datadogSpanSink) Flush() {
	dd.mutex.Lock()

	ssfSpans := make([]ssf.SSFSpan, 0, dd.buffer.Len())

	dd.buffer.Do(func(t interface{}) {
		const tooEarly = 1497
		const tooLate = 1497629343000000

		if t != nil {
			ssfSpan, ok := t.(ssf.SSFSpan)
			if !ok {
				log.Error("Got an unknown object in tracing ring!")
				// We'll just skip this one so we don't poison pill or anything.
				return
			}

			var timeErr string
			if ssfSpan.StartTimestamp < tooEarly {
				timeErr = "type:tooEarly"
			}
			if ssfSpan.StartTimestamp > tooLate {
				timeErr = "type:tooLate"
			}
			if timeErr != "" {
				dd.stats.Incr("worker.trace.sink.timestamp_error", []string{timeErr}, 1) // TODO tag as dd?
			}

			if ssfSpan.Tags == nil {
				ssfSpan.Tags = make(map[string]string)
			}

			// Add common tags from veneur's config
			// this will overwrite tags already present on the span
			for k, v := range dd.commonTags {
				ssfSpan.Tags[k] = v
			}
			ssfSpans = append(ssfSpans, ssfSpan)
		}
	})

	// Reset the ring.
	dd.buffer = ring.New(dd.bufferSize)

	// We're done manipulating stuff, let Ingest loose again.
	dd.mutex.Unlock()

	serviceCount := make(map[string]int64)
	var finalTraces []*DatadogTraceSpan
	// Conver the SSFSpans into Datadog Spans
	for _, span := range ssfSpans {
		// -1 is a canonical way of passing in invalid info in Go
		// so we should support that too
		parentID := span.ParentId

		// check if this is the root span
		if parentID <= 0 {
			// we need parentId to be zero for json:omitempty to work
			parentID = 0
		}

		resource := span.Tags[datadogResourceKey]
		name := span.Name

		tags := map[string]string{}
		// Get the span's existing tags
		for k, v := range span.Tags {
			tags[k] = v
		}

		delete(tags, datadogResourceKey)

		// TODO implement additional metrics
		var metrics map[string]float64

		var errorCode int64
		if span.Error {
			errorCode = 2
		}

		ddspan := &DatadogTraceSpan{
			TraceID:  span.TraceId,
			SpanID:   span.Id,
			ParentID: parentID,
			Service:  span.Service,
			Name:     name,
			Resource: resource,
			Start:    span.StartTimestamp,
			Duration: span.EndTimestamp - span.StartTimestamp,
			// TODO don't hardcode
			Type:    "http",
			Error:   errorCode,
			Metrics: metrics,
			Meta:    tags,
		}
		serviceCount[span.Service]++
		finalTraces = append(finalTraces, ddspan)
	}

	if len(finalTraces) != 0 {
		// this endpoint is not documented to take an array... but it does
		// another curious constraint of this endpoint is that it does not
		// support "Content-Encoding: deflate"

		err := postHelper(context.TODO(), dd.HTTPClient, dd.stats, fmt.Sprintf("%s/spans", dd.traceAddress), finalTraces, "flush_traces", false)

		if err == nil {
			log.WithField("traces", len(finalTraces)).Info("Completed flushing traces to Datadog")
			dd.stats.Count(totalSpansFlushedMetricKey, int64(len(ssfSpans)), []string{"sink:datadog"}, 1)
			// TODO: Per service counters?
		} else {
			log.WithFields(logrus.Fields{
				"traces":        len(finalTraces),
				logrus.ErrorKey: err}).Warn("Error flushing traces to Datadog")
		}
		for service, count := range serviceCount {
			dd.stats.Count(totalSpansFlushedMetricKey, count, []string{"sink:datadog", fmt.Sprintf("service:%s", service)}, 1)
		}
	} else {
		log.Info("No traces to flush to Datadog, skipping.")
	}
}

// lightStepSpanSink is a sink for spans to be sent to the LightStep client.
type lightStepSpanSink struct {
	tracer       opentracing.Tracer
	stats        *statsd.Client
	commonTags   map[string]string
	mutex        *sync.Mutex
	serviceCount map[string]int64
}

// NewLightStepSpanSink creates a new instance of a LightStepSpanSink.
func NewLightStepSpanSink(config *Config, stats *statsd.Client, commonTags map[string]string) (*lightStepSpanSink, error) {

	var host *url.URL
	host, err := url.Parse(config.TraceLightstepCollectorHost)
	if err != nil {
		log.WithError(err).WithField(
			"host", config.TraceLightstepCollectorHost,
		).Error("Error parsing LightStep collector URL")
		return &lightStepSpanSink{}, err
	}

	port, err := strconv.Atoi(host.Port())
	if err != nil {
		port = lightstepDefaultPort
	} else {
		log.WithError(err).WithFields(logrus.Fields{
			"port":         port,
			"default_port": lightstepDefaultPort,
		}).Warn("Error parsing LightStep port, using default")
	}

	reconPeriod, err := time.ParseDuration(config.TraceLightstepReconnectPeriod)
	if err != nil {
		log.WithError(err).WithFields(logrus.Fields{
			"interval":         config.TraceLightstepReconnectPeriod,
			"default_interval": lightstepDefaultInterval,
		}).Warn("Failed to parse reconnect duration, using default.")
		reconPeriod = lightstepDefaultInterval
	}

	log.WithFields(logrus.Fields{
		"Host": host.Hostname(),
		"Port": port,
	}).Info("Dialing lightstep host")

	maxSpans := config.TraceLightstepMaximumSpans
	if maxSpans == 0 {
		maxSpans = config.SsfBufferSize
		log.WithField("max spans", maxSpans).Info("Using default maximum spans — ssf_buffer_size — for LightStep")
	}

	lightstepTracer := lightstep.NewTracer(lightstep.Options{
		AccessToken:     config.TraceLightstepAccessToken,
		ReconnectPeriod: reconPeriod,
		Collector: lightstep.Endpoint{
			Host:      host.Hostname(),
			Port:      port,
			Plaintext: true,
		},
		UseGRPC:          true,
		MaxBufferedSpans: maxSpans,
	})

	return &lightStepSpanSink{
		tracer:       lightstepTracer,
		stats:        stats,
		serviceCount: make(map[string]int64),
		mutex:        &sync.Mutex{},
	}, nil
}

// Name returns this sink's name.
func (ls *lightStepSpanSink) Name() string {
	return "lightstep"
}

// Ingest takes in a span and passed it along to the LS client after
// some sanity checks and improvements are made.
func (ls *lightStepSpanSink) Ingest(ssfSpan ssf.SSFSpan) error {
	parentID := ssfSpan.ParentId
	if parentID <= 0 {
		parentID = 0
	}

	var errorCode int64
	if ssfSpan.Error {
		errorCode = 1
	}

	timestamp := time.Unix(ssfSpan.StartTimestamp/1e9, ssfSpan.StartTimestamp%1e9)
	sp := ls.tracer.StartSpan(
		ssfSpan.Name,
		opentracing.StartTime(timestamp),
		lightstep.SetTraceID(uint64(ssfSpan.TraceId)),
		lightstep.SetSpanID(uint64(ssfSpan.Id)),
		lightstep.SetParentSpanID(uint64(parentID)))

	sp.SetTag(trace.ResourceKey, ssfSpan.Tags[trace.ResourceKey]) // TODO Why is this here?
	sp.SetTag(lightstep.ComponentNameKey, ssfSpan.Service)
	// TODO don't hardcode
	sp.SetTag("type", "http")
	sp.SetTag("error-code", errorCode)
	for k, v := range ssfSpan.Tags {
		sp.SetTag(k, v)
	}
	// And now set any veneur common tags
	for k, v := range ls.commonTags {
		sp.SetTag(k, v)
	}
	// TODO add metrics as tags to the span as well?

	if errorCode > 0 {
		// Note: this sets the OT-standard "error" tag, which
		// LightStep uses to flag error spans.
		ext.Error.Set(sp, true)
	}

	endTime := time.Unix(ssfSpan.EndTimestamp/1e9, ssfSpan.EndTimestamp%1e9)
	sp.FinishWithOptions(opentracing.FinishOptions{
		FinishTime: endTime,
	})

	service := ssfSpan.Service
	if service == "" {
		service = "unknown"
	}

	// Protect mutating the service count with a mutex
	ls.mutex.Lock()
	defer ls.mutex.Unlock()
	ls.serviceCount[service]++

	return nil
}

// Flush doesn't need to do anything to the LS tracer, so we emit metrics
// instead.
func (ls *lightStepSpanSink) Flush() {
	ls.mutex.Lock()
	defer ls.mutex.Unlock()

	totalCount := int64(0)
	for service, count := range ls.serviceCount {
		totalCount += count
		ls.stats.Count(totalSpansFlushedMetricKey, count, []string{"sink:lightstep", fmt.Sprintf("service:%s", service)}, 1)
	}
	ls.serviceCount = make(map[string]int64)
	log.WithField("total_spans", totalCount).Debug("Checkpointing flushed spans for Lightstep")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"

	"os"

	"fmt"

	"github.com/kyokomi/go-docomo/docomo"
)

var apiKey, keyword string

func init() {
	flag.StringVar(&apiKey, "APIKEY", os.Getenv("DOCOMO_APIKEY"), "docomo developerで登録したAPIKEY")
	flag.StringVar(&keyword, "keyword", "", "記事検索キーワード")
	flag.Parse()
}

func main() {
	if apiKey == "" {
		log.Fatalln("APIKEYを指定して下さい")
	}

	d := docomo.NewClient(apiKey)

	fmt.Println()
	fmt.Println("---ジャンル取得---")
	fmt.Println()

	gRes, err := d.Trend.GetGenre(docomo.TrendGenreRequest{})
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(gRes)

	fmt.Println()
	fmt.Println("---記事取得---")
	fmt.Println()

	var contentsReq docomo.TrendContentsRequest
	contentsReq.GenreID = &gRes.Genre[0].GenreID

	cRes, err := d.Trend.GetContents(contentsReq)
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(cRes)

	fmt.Println()
	fmt.Println("---キーワード検索---")
	fmt.Println()

	var searchReq docomo.TrendSearchRequest
	searchReq.Keyword = &keyword

	sRes, err := d.Trend.GetSearch(searchReq)
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(sRes)

	fmt.Println()
	fmt.Println("---関連記事検索---")
	fmt.Println()

	var relatedReq docomo.TrendRelatedRequest

	// TODO: なかなかヒットしないからループして全部チェックしてる
	for _, cont := range sRes.ArticleContents {
		relatedReq.ContentID = &cont.ContentID
		rRes, err := d.Trend.GetRelated(relatedReq)
		if err != nil {
			log.Fatalln(err)
		}

		if rRes.TotalResults > 0 {
			fmt.Println(rRes)
			break
		}
	}
}
<commit_msg>fix gofmt -s<commit_after>package main

import (
	"flag"
	"log"

	"os"

	"fmt"

	"github.com/kyokomi/go-docomo/docomo"
)

var apiKey, keyword string

func init() {
	flag.StringVar(&apiKey, "APIKEY", os.Getenv("DOCOMO_APIKEY"), "docomo developerで登録したAPIKEY")
	flag.StringVar(&keyword, "keyword", "", "記事検索キーワード")
	flag.Parse()
}

func main() {
	if apiKey == "" {
		log.Fatalln("APIKEYを指定して下さい")
	}

	d := docomo.NewClient(apiKey)

	fmt.Println()
	fmt.Println("---ジャンル取得---")
	fmt.Println()

	gRes, err := d.Trend.GetGenre(docomo.TrendGenreRequest{})
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(gRes)

	fmt.Println()
	fmt.Println("---記事取得---")
	fmt.Println()

	var contentsReq docomo.TrendContentsRequest
	contentsReq.GenreID = &gRes.Genre[0].GenreID

	cRes, err := d.Trend.GetContents(contentsReq)
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(cRes)

	fmt.Println()
	fmt.Println("---キーワード検索---")
	fmt.Println()

	var searchReq docomo.TrendSearchRequest
	searchReq.Keyword = &keyword

	sRes, err := d.Trend.GetSearch(searchReq)
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(sRes)

	fmt.Println()
	fmt.Println("---関連記事検索---")
	fmt.Println()

	var relatedReq docomo.TrendRelatedRequest

	// TODO: なかなかヒットしないからループして全部チェックしてる
	for _, cont := range sRes.ArticleContents {
		relatedReq.ContentID = &cont.ContentID
		rRes, err := d.Trend.GetRelated(relatedReq)
		if err != nil {
			log.Fatalln(err)
		}

		if rRes.TotalResults > 0 {
			fmt.Println(rRes)
			break
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"spherogo"
	"strconv"
	"strings"
)

var (
	devideId string
)

const (
	lf      = '\n'
	cr      = '\r'
	newline = "\r\n"
)

func main() {

	flag.Parse()

	Stdin := bufio.NewReader(os.Stdin)

	for {
		fmt.Print("sphero > ")
		line, err := Stdin.ReadString(lf)
		printError(err)
		line = strings.TrimRight(line, newline)

		commands := strings.Split(line, " ")
		switch strings.ToLower(commands[0]) {
		case "rgb":
			fmt.Println(commands[1:])
			err := commandRgb(commands[1:])
			printError(err)
		case "roll":
			err := commandRoll(commands[1:])
			printError(err)
		case "version":
		case "exit", "quit":
			fmt.Println("bye...")
			os.Exit(0)
		default:
		}
	}
}

func init() {
	flag.StringVar(&devideId, "d", "", "sphero device file")
}

func commandRgb(commands []string) error {
	sphero := spherogo.NewSphero(devideId)
	switch strings.ToLower(commands[0]) {
	case "get":
		sphero.GetRgb()
	case "set":
		red, green, blue, err := string2color(commands[1:])
		if err != nil {
			return err
		}
		persistent, err := strconv.ParseBool(commands[len(commands)-1])
		if err != nil {
			return err
		}
		sphero.SetRgb(red, green, blue, persistent)
	default:
		return fmt.Errorf("not support parameter")
	}
	return nil
}

func commandRoll(commands []string) error {
	if len(commands) < 3 {
		return fmt.Errorf("roll <speed> <heading> <state>")
	}
	speed, err := parse2Byte(commands[0])
	if err != nil {
		return err
	}
	heading, err := parse2Uint16(commands[1])
	if err != nil {
		return err
	}
	state, err := strconv.ParseBool(commands[2])
	if err != nil {
		return err
	}
	sphero := spherogo.NewSphero(devideId)
	sphero.Roll(speed, heading, state)
	return nil
}

func string2color(param []string) (byte, byte, byte, error) {
	switch param[0] {
	case "red":
		return 0xFF, 0x00, 0x00, nil
	case "green":
		return 0x00, 0xFF, 0x00, nil
	case "blue":
		return 0x00, 0x00, 0xFF, nil
	default:
		if len(param) < 3 {
			return 0xFF, 0xFF, 0xFF, fmt.Errorf("not support param count")
		}
		red, err := strconv.ParseUint(param[0], 10, 8)
		if err != nil {
			return 0xFF, 0xFF, 0xFF, err
		}
		green, err := strconv.ParseUint(param[1], 10, 8)
		if err != nil {
			return 0xFF, 0xFF, 0xFF, err
		}
		blue, err := strconv.ParseUint(param[2], 10, 8)
		if err != nil {
			return 0xFF, 0xFF, 0xFF, err
		}
		return uint8(red), uint8(green), uint8(blue), nil
	}
}

func printError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

func parse2Byte(s string) (byte, error) {
	ret, err := strconv.ParseUint(s, 10, 8)
	return byte(ret), err
}

func parse2Uint16(s string) (uint16, error) {
	ret, err := strconv.ParseUint(s, 10, 16)
	return uint16(ret), err
}
<commit_msg>終了するときにSpheroをSleepできるように処理を追加<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"spherogo"
	"strconv"
	"strings"
)

var (
	devideId string
)

const (
	lf      = '\n'
	cr      = '\r'
	newline = "\r\n"
)

func main() {

	flag.Parse()

	Stdin := bufio.NewReader(os.Stdin)

	for {
		fmt.Print("sphero > ")
		line, err := Stdin.ReadString(lf)
		printError(err)
		line = strings.TrimRight(line, newline)

		commands := strings.Split(line, " ")
		switch strings.ToLower(commands[0]) {
		case "rgb":
			fmt.Println(commands[1:])
			err := commandRgb(commands[1:])
			printError(err)
		case "roll":
			err := commandRoll(commands[1:])
			printError(err)
		case "version":
		case "exit", "quit":
			fmt.Println("bye...")
			commandShutdown()
			os.Exit(0)
		default:
		}
	}
}

func init() {
	flag.StringVar(&devideId, "d", "", "sphero device file")
}

func commandRgb(commands []string) error {
	sphero := spherogo.NewSphero(devideId)
	switch strings.ToLower(commands[0]) {
	case "get":
		sphero.GetRgb()
	case "set":
		red, green, blue, err := string2color(commands[1:])
		if err != nil {
			return err
		}
		persistent, err := strconv.ParseBool(commands[len(commands)-1])
		if err != nil {
			return err
		}
		sphero.SetRgb(red, green, blue, persistent)
	default:
		return fmt.Errorf("not support parameter")
	}
	return nil
}

func commandRoll(commands []string) error {
	if len(commands) < 3 {
		return fmt.Errorf("roll <speed> <heading> <state>")
	}
	speed, err := parse2Byte(commands[0])
	if err != nil {
		return err
	}
	heading, err := parse2Uint16(commands[1])
	if err != nil {
		return err
	}
	state, err := strconv.ParseBool(commands[2])
	if err != nil {
		return err
	}
	sphero := spherogo.NewSphero(devideId)
	sphero.Roll(speed, heading, state)
	return nil
}

func commandShutdown() {
	sphero := spherogo.NewSphero(devideId)
	sphero.Sleep()
}

func string2color(param []string) (byte, byte, byte, error) {
	switch param[0] {
	case "red":
		return 0xFF, 0x00, 0x00, nil
	case "green":
		return 0x00, 0xFF, 0x00, nil
	case "blue":
		return 0x00, 0x00, 0xFF, nil
	default:
		if len(param) < 3 {
			return 0xFF, 0xFF, 0xFF, fmt.Errorf("not support param count")
		}
		red, err := strconv.ParseUint(param[0], 10, 8)
		if err != nil {
			return 0xFF, 0xFF, 0xFF, err
		}
		green, err := strconv.ParseUint(param[1], 10, 8)
		if err != nil {
			return 0xFF, 0xFF, 0xFF, err
		}
		blue, err := strconv.ParseUint(param[2], 10, 8)
		if err != nil {
			return 0xFF, 0xFF, 0xFF, err
		}
		return uint8(red), uint8(green), uint8(blue), nil
	}
}

func printError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

func parse2Byte(s string) (byte, error) {
	ret, err := strconv.ParseUint(s, 10, 8)
	return byte(ret), err
}

func parse2Uint16(s string) (uint16, error) {
	ret, err := strconv.ParseUint(s, 10, 16)
	return uint16(ret), err
}
<|endoftext|>


<commit_before>package uaa

import (
	"errors"
	"fmt"
	"sync"

	"github.com/dgrijalva/jwt-go"
	"github.com/pivotal-cf-experimental/warrant"
	"github.com/pivotal-golang/lager"
)

type keysFetcher interface {
	GetSigningKeys() ([]warrant.SigningKey, error)
}

type TokenValidator struct {
	keysFetcher keysFetcher
	keyMap      map[string]warrant.SigningKey
	keyMutex    sync.RWMutex
	logger      lager.Logger
}

func NewTokenValidator(logger lager.Logger, keysFetcher keysFetcher) *TokenValidator {
	logger = logger.Session("token.validator")
	return &TokenValidator{
		logger:      logger,
		keysFetcher: keysFetcher,
		keyMap:      make(map[string]warrant.SigningKey),
	}
}

func (v *TokenValidator) LoadSigningKeys() error {
	v.logger.Info("loading signing keys")
	keys, err := v.keysFetcher.GetSigningKeys()

	if err != nil {
		v.logger.Error("loading signing keys failed", err)
		return err
	}

	keyMap := make(map[string]warrant.SigningKey, len(keys))

	for _, key := range keys {
		v.logger.Info("loaded signing key " + key.KeyId)
		keyMap[key.KeyId] = key
	}

	v.keyMutex.Lock()
	defer v.keyMutex.Unlock()
	v.keyMap = keyMap

	return nil
}

func (v *TokenValidator) findKey(id string) (warrant.SigningKey, bool) {
	v.keyMutex.RLock()
	defer v.keyMutex.RUnlock()

	key, ok := v.keyMap[id]
	return key, ok
}

func (v *TokenValidator) lookUp(keyId string) (string, error) {
	key, ok := v.findKey(keyId)

	if ok {
		return key.Value, nil
	}

	if err := v.LoadSigningKeys(); err != nil {
		return "", err
	}

	key, ok = v.findKey(keyId)

	if !ok {
		return "", fmt.Errorf("unknown key with id %s", keyId)
	}

	return key.Value, nil
}

func (v *TokenValidator) Parse(rawToken string) (*jwt.Token, error) {
	return jwt.Parse(rawToken, func(t *jwt.Token) (interface{}, error) {
		switch t.Method {
		case
			jwt.SigningMethodRS256,
			jwt.SigningMethodRS384,
			jwt.SigningMethodRS512:
			break
		default:
			return nil, fmt.Errorf("Unsupported signing method %v", t.Method.Alg())
		}

		keyId, ok := t.Header["kid"].(string)

		if !ok {
			return nil, errors.New("Unable to lookup key id for the token")
		}

		key, err := v.lookUp(keyId)

		if err != nil {
			return nil, err
		}

		return []byte(key), nil
	})
}
<commit_msg>tweak log output<commit_after>package uaa

import (
	"errors"
	"fmt"
	"sync"

	"github.com/dgrijalva/jwt-go"
	"github.com/pivotal-cf-experimental/warrant"
	"github.com/pivotal-golang/lager"
)

type keysFetcher interface {
	GetSigningKeys() ([]warrant.SigningKey, error)
}

type TokenValidator struct {
	keysFetcher keysFetcher
	keyMap      map[string]warrant.SigningKey
	keyMutex    sync.RWMutex
	logger      lager.Logger
}

func NewTokenValidator(logger lager.Logger, keysFetcher keysFetcher) *TokenValidator {
	logger = logger.Session("uaa.token.validator")
	return &TokenValidator{
		logger:      logger,
		keysFetcher: keysFetcher,
		keyMap:      make(map[string]warrant.SigningKey),
	}
}

func (v *TokenValidator) LoadSigningKeys() error {
	v.logger.Info("loading.keys")
	keys, err := v.keysFetcher.GetSigningKeys()

	if err != nil {
		v.logger.Error("loading.keys.failed", err)
		return err
	}

	keyMap := make(map[string]warrant.SigningKey, len(keys))
	keyNames := make([]string, 0, len(keys))

	for _, key := range keys {
		keyMap[key.KeyId] = key
		keyNames = append(keyNames, key.KeyId)
	}

	v.logger.Info("loaded.keys", lager.Data{
		"keys": keyNames,
	})

	v.keyMutex.Lock()
	defer v.keyMutex.Unlock()
	v.keyMap = keyMap

	return nil
}

func (v *TokenValidator) findKey(id string) (warrant.SigningKey, bool) {
	v.keyMutex.RLock()
	defer v.keyMutex.RUnlock()

	key, ok := v.keyMap[id]
	return key, ok
}

func (v *TokenValidator) lookUp(keyId string) (string, error) {
	key, ok := v.findKey(keyId)

	if ok {
		return key.Value, nil
	}

	if err := v.LoadSigningKeys(); err != nil {
		return "", err
	}

	key, ok = v.findKey(keyId)

	if !ok {
		return "", fmt.Errorf("unknown key with id %s", keyId)
	}

	return key.Value, nil
}

func (v *TokenValidator) Parse(rawToken string) (*jwt.Token, error) {
	return jwt.Parse(rawToken, func(t *jwt.Token) (interface{}, error) {
		switch t.Method {
		case
			jwt.SigningMethodRS256,
			jwt.SigningMethodRS384,
			jwt.SigningMethodRS512:
			break
		default:
			return nil, fmt.Errorf("Unsupported signing method %v", t.Method.Alg())
		}

		keyId, ok := t.Header["kid"].(string)

		if !ok {
			return nil, errors.New("Unable to lookup key id for the token")
		}

		key, err := v.lookUp(keyId)

		if err != nil {
			return nil, err
		}

		return []byte(key), nil
	})
}
<|endoftext|>


<commit_before>package authkit

import (
	"html/template"
	"strings"
)

var funcMap = template.FuncMap{
	"join": strings.Join,
}

var loginTemplate = template.Must(template.New("login").Funcs(funcMap).Parse(loginJS))
var loginJS = `
var __authkit__state__ = {
  authCallback : "__authkit__"+parseInt(Math.random()*1e12,10).toString(36),
  providers : {}
};

{{ $base := .Base }}
{{ range $key, $value := .Providers }}
__authkit__state__.providers["{{ $value.Network }}"] = {
  network : "{{ $value.Network }}",
  authurl : "{{ $value.AuthURL }}",
  client_id : "{{ $value.ClientID }}",
  scopes : "{{ join $value.Scopes " " }}",
  redirect: "{{ $base }}redirect"
};
{{ end }}

var authkit = {
  providers : function () {
    return __authkit__state__.providers;
  },
  provider : function (p) {
    return __authkit__state__.providers[p];
  },
  login : function (provider,base) {
    var p = authkit.provider(provider);
    if (!p) return;
    if (!base) base = window.location.origin;
    var scopes = encodeURIComponent(p.scopes);
    var redir = encodeURIComponent(base+p.redirect);
    var state = encodeURIComponent(JSON.stringify({network:p.network,redirect_uri:redir,cbid:__authkit__state__.authCallback}));
    var authU = p.authurl+"?redirect_uri="+redir+"&response_type=code&client_id="+p.client_id+"&state="+state;
    if (p.scopes) {
      authU = authU + "&scope="+scopes;
    }
    var self = this;
    self.user = function (cb) { this.authCB = cb; };
    window[__authkit__state__.authCallback] = function (token, usr) {
      console.log(usr);
      self.authCB(usr, token)
    }
    popup(authU, 500, 600);
    return self;
  }
};

function popup (u, w, h) {
  var documentElement = document.documentElement;

	// Multi Screen Popup Positioning (http://stackoverflow.com/a/16861050)
	//   Credit: http://www.xtf.dk/2011/08/center-new-popup-window-even-on.html
	// Fixes dual-screen position                         Most browsers      Firefox
	var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
	var dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;

	var width = window.innerWidth || documentElement.clientWidth || screen.width;
	var height = window.innerHeight || documentElement.clientHeight || screen.height;

	var left = ((width - w) / 2) + dualScreenLeft;
	var top  = ((height - h) / 2) + dualScreenTop;
	var feat = "resizeable=true,height=" + h + ",width=" + w + ",left=" + left + ",top="  + top
	window.open(u, "_blank", feat);
}
`
var redirectTemplate = template.Must(template.New("redirect").Funcs(funcMap).Parse(redirect))
var redirect = `
<html>
<body>
  <script>
	  // First, parse the query string
    var params = {}, queryString = location.search.substring(1),
        regex = /([^&=]+)=([^&]*)/g, m;
    while (m = regex.exec(queryString)) {
      params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
    }

    var req = new XMLHttpRequest();
    var cbid = JSON.parse(params.state).cbid;
    req.open('GET', '{{ .Base }}auth?code='+params.code+"&state="+params.state, true);

    req.onreadystatechange = function (e) {
      if (req.readyState == 4) {
        if(req.status == 200){
           var tok = req.getResponseHeader("Authorization");
           var usr = JSON.parse(req.responseText);
           console.log("window:", window);
           console.log("opener:", window.opener);
           console.log("cbid:", cbid);
           console.log("cb: ", window.opener[cbid]);
           window.opener[cbid](tok, usr);
           window.close();
        }
        else if(req.status == 400) {
            console.log('There was an error processing the access code:',req.responseText)
            window.opener[cbid](null, null, req);
            window.close();
        }
        else {
          console.log('something other than 200 was returned:',req.responseText)
          window.opener[cbid](null, null, req);
          window.close();
        }
      }
    };
    req.send(null);
	</script>
</body>
</html>
`
<commit_msg>logging output<commit_after>package authkit

import (
	"html/template"
	"strings"
)

var funcMap = template.FuncMap{
	"join": strings.Join,
}

var loginTemplate = template.Must(template.New("login").Funcs(funcMap).Parse(loginJS))
var loginJS = `
var __authkit__state__ = {
  authCallback : "__authkit__"+parseInt(Math.random()*1e12,10).toString(36),
  providers : {}
};

{{ $base := .Base }}
{{ range $key, $value := .Providers }}
__authkit__state__.providers["{{ $value.Network }}"] = {
  network : "{{ $value.Network }}",
  authurl : "{{ $value.AuthURL }}",
  client_id : "{{ $value.ClientID }}",
  scopes : "{{ join $value.Scopes " " }}",
  redirect: "{{ $base }}redirect"
};
{{ end }}

var authkit = {
  providers : function () {
    return __authkit__state__.providers;
  },
  provider : function (p) {
    return __authkit__state__.providers[p];
  },
  login : function (provider,base) {
    var p = authkit.provider(provider);
    if (!p) return;
    if (!base) base = window.location.origin;
    var scopes = encodeURIComponent(p.scopes);
    var redir = encodeURIComponent(base+p.redirect);
    var state = encodeURIComponent(JSON.stringify({network:p.network,redirect_uri:redir,cbid:__authkit__state__.authCallback}));
    var authU = p.authurl+"?redirect_uri="+redir+"&response_type=code&client_id="+p.client_id+"&state="+state;
    if (p.scopes) {
      authU = authU + "&scope="+scopes;
    }
    var self = this;
    self.user = function (cb) { this.authCB = cb; };
    window[__authkit__state__.authCallback] = function (token, usr) {
      console.log(usr);
      self.authCB(usr, token)
    }
    popup(authU, 500, 600);
    return self;
  }
};

function popup (u, w, h) {
  var documentElement = document.documentElement;

	// Multi Screen Popup Positioning (http://stackoverflow.com/a/16861050)
	//   Credit: http://www.xtf.dk/2011/08/center-new-popup-window-even-on.html
	// Fixes dual-screen position                         Most browsers      Firefox
	var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
	var dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;

	var width = window.innerWidth || documentElement.clientWidth || screen.width;
	var height = window.innerHeight || documentElement.clientHeight || screen.height;

	var left = ((width - w) / 2) + dualScreenLeft;
	var top  = ((height - h) / 2) + dualScreenTop;
	var feat = "resizeable=true,height=" + h + ",width=" + w + ",left=" + left + ",top="  + top
	window.open(u, "_blank", feat);
}
`
var redirectTemplate = template.Must(template.New("redirect").Funcs(funcMap).Parse(redirect))
var redirect = `
<html>
<body>
  <script>
	  // First, parse the query string
    var params = {}, queryString = location.search.substring(1),
        regex = /([^&=]+)=([^&]*)/g, m;
    while (m = regex.exec(queryString)) {
      params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
    }

    var req = new XMLHttpRequest();
    var cbid = JSON.parse(params.state).cbid;
    req.open('GET', '{{ .Base }}auth?code='+params.code+"&state="+params.state, true);

    req.onreadystatechange = function (e) {
      if (req.readyState == 4) {
        if(req.status == 200){
           var tok = req.getResponseHeader("Authorization");
           var usr = JSON.parse(req.responseText);
           console.log("window:", window);
           console.log("opener:", window.opener);
           console.log("cbid:", cbid);
           console.log("direct: ", window[cbid]);
           console.log("cb: ", window.opener[cbid]);
           window.opener[cbid](tok, usr);
           window.close();
        }
        else if(req.status == 400) {
            console.log('There was an error processing the access code:',req.responseText)
            window.opener[cbid](null, null, req);
            window.close();
        }
        else {
          console.log('something other than 200 was returned:',req.responseText)
          window.opener[cbid](null, null, req);
          window.close();
        }
      }
    };
    req.send(null);
	</script>
</body>
</html>
`
<|endoftext|>


<commit_before>package resources

import (
	"encoding/json"
	"fmt"
	"net/http"
)

const (
	SPECTRUM_SCALE     Backend = "spectrum-scale"
	SPECTRUM_SCALE_NFS Backend = "spectrum-scale-nfs"
	SOFTLAYER_NFS      Backend = "softlayer-nfs"
	SCBE               Backend = "scbe"
)

type Backend string

type UbiquityServerConfig struct {
	Port                int
	LogPath             string
	SpectrumScaleConfig SpectrumScaleConfig
	ScbeConfig          ScbeConfig
	BrokerConfig        BrokerConfig
}

// TODO we should consider to move dedicated backend structs to the backend resource file instead of this one.
type SpectrumScaleConfig struct {
	DefaultFilesystem string
	ConfigPath        string
	NfsServerAddr     string
	SshConfig         SshConfig
	RestConfig        RestConfig
	ForceDelete       bool
}

type CredentialInfo struct {
	UserName string `json:"username"`
	Password string `json:"password"`
	Group    string `json:"group"`
}

type ConnectionInfo struct {
	CredentialInfo CredentialInfo
	Port           int
	ManagementIP   string
	SkipVerifySSL  bool
}

type ScbeConfig struct {
	ConfigPath        string // TODO consider to remove later
	ConnectionInfo    ConnectionInfo
	DefaultService    string // SCBE storage service to be used by default if not mentioned by plugin
	DefaultVolumeSize string // The default volume size in case not specified by user
	DefaultFilesystem string // The default filesystem to create on new volumes
}

type SshConfig struct {
	User string
	Host string
	Port string
}

type RestConfig struct {
	Endpoint string
}

type SpectrumNfsRemoteConfig struct {
	ClientConfig string
}

type BrokerConfig struct {
	ConfigPath string
	Port       int //for CF Service broker
}

type UbiquityPluginConfig struct {
	DockerPlugin            UbiquityDockerPluginConfig
	LogPath                 string
	UbiquityServer          UbiquityServerConnectionInfo
	SpectrumNfsRemoteConfig SpectrumNfsRemoteConfig
}
type UbiquityDockerPluginConfig struct {
	//Address          string
	Port             int
	PluginsDirectory string
}

type UbiquityServerConnectionInfo struct {
	Address string
	Port    int
}

//go:generate counterfeiter -o ../fakes/fake_storage_client.go . StorageClient

type StorageClient interface {
	Activate() error
	CreateVolume(name string, opts map[string]interface{}) error
	RemoveVolume(name string) error
	ListVolumes() ([]VolumeMetadata, error)
	GetVolume(name string) (Volume, error)
	GetVolumeConfig(name string) (map[string]interface{}, error)
	Attach(name string) (string, error)
	Detach(name string) error
}

type CreateRequest struct {
	Name string
	Opts map[string]interface{}
}

type RemoveRequest struct {
	Name string
}

type AttachRequest struct {
	Name string
}

type DetachRequest struct {
	Name string
}

type ActivateResponse struct {
	Implements []string
}

func (r *ActivateResponse) WriteResponse(w http.ResponseWriter) {
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling response: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}

type GenericResponse struct {
	Err string
}

func (r *GenericResponse) WriteResponse(w http.ResponseWriter) {
	if r.Err != "" {
		w.WriteHeader(http.StatusBadRequest)
	}
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling response: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}

type GenericRequest struct {
	Name string
}

type MountResponse struct {
	Mountpoint string
	Err        string
}

func (r *MountResponse) WriteResponse(w http.ResponseWriter) {
	if r.Err != "" {
		w.WriteHeader(http.StatusBadRequest)
	}
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling Get response: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}

type VolumeMetadata struct {
	Name       string
	Mountpoint string
}

type GetResponse struct {
	Volume Volume
	Err    string
}
type DockerGetResponse struct {
	Volume VolumeMetadata
	Err    string
}

type Volume struct {
	Name    string
	Backend Backend
}

type GetConfigResponse struct {
	VolumeConfig map[string]interface{}
	Err          string
}

func (r *GetResponse) WriteResponse(w http.ResponseWriter) {
	if r.Err != "" {
		w.WriteHeader(http.StatusBadRequest)
	}
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling Get response: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}
func (r *DockerGetResponse) WriteResponse(w http.ResponseWriter) {
	if r.Err != "" {
		w.WriteHeader(http.StatusBadRequest)
	}
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling DockerGetResponse: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}

type ListResponse struct {
	Volumes []VolumeMetadata
	Err     string
}

func (r *ListResponse) WriteResponse(w http.ResponseWriter) {
	if r.Err != "" {
		w.WriteHeader(http.StatusBadRequest)
	}
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling Get response: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}

type FlexVolumeResponse struct {
	Status  string `json:"status"`
	Message string `json:"message"`
	Device  string `json:"device"`
}

type FlexVolumeMountRequest struct {
	MountPath   string                 `json:"mountPath"`
	MountDevice string                 `json:"name"`
	Opts        map[string]interface{} `json:"opts"`
}

type FlexVolumeUnmountRequest struct {
	MountPath string `json:"mountPath"`
}

type FlexVolumeDetachRequest struct {
	Name string `json:"name"`
}
<commit_msg>#28 : by mistake I removed DefaultBackend from UbiquityServerConfig (reverting)<commit_after>package resources

import (
	"encoding/json"
	"fmt"
	"net/http"
)

const (
	SPECTRUM_SCALE     Backend = "spectrum-scale"
	SPECTRUM_SCALE_NFS Backend = "spectrum-scale-nfs"
	SOFTLAYER_NFS      Backend = "softlayer-nfs"
	SCBE               Backend = "scbe"
)

type Backend string

type UbiquityServerConfig struct {
	Port                int
	LogPath             string
	SpectrumScaleConfig SpectrumScaleConfig
	ScbeConfig          ScbeConfig
	BrokerConfig        BrokerConfig
	DefaultBackend      string
}

// TODO we should consider to move dedicated backend structs to the backend resource file instead of this one.
type SpectrumScaleConfig struct {
	DefaultFilesystem string
	ConfigPath        string
	NfsServerAddr     string
	SshConfig         SshConfig
	RestConfig        RestConfig
	ForceDelete       bool
}

type CredentialInfo struct {
	UserName string `json:"username"`
	Password string `json:"password"`
	Group    string `json:"group"`
}

type ConnectionInfo struct {
	CredentialInfo CredentialInfo
	Port           int
	ManagementIP   string
	SkipVerifySSL  bool
}

type ScbeConfig struct {
	ConfigPath        string // TODO consider to remove later
	ConnectionInfo    ConnectionInfo
	DefaultService    string // SCBE storage service to be used by default if not mentioned by plugin
	DefaultVolumeSize string // The default volume size in case not specified by user
	DefaultFilesystem string // The default filesystem to create on new volumes
}

type SshConfig struct {
	User string
	Host string
	Port string
}

type RestConfig struct {
	Endpoint string
}

type SpectrumNfsRemoteConfig struct {
	ClientConfig string
}

type BrokerConfig struct {
	ConfigPath string
	Port       int //for CF Service broker
}

type UbiquityPluginConfig struct {
	DockerPlugin            UbiquityDockerPluginConfig
	LogPath                 string
	UbiquityServer          UbiquityServerConnectionInfo
	SpectrumNfsRemoteConfig SpectrumNfsRemoteConfig
}
type UbiquityDockerPluginConfig struct {
	//Address          string
	Port             int
	PluginsDirectory string
}

type UbiquityServerConnectionInfo struct {
	Address string
	Port    int
}

//go:generate counterfeiter -o ../fakes/fake_storage_client.go . StorageClient

type StorageClient interface {
	Activate() error
	CreateVolume(name string, opts map[string]interface{}) error
	RemoveVolume(name string) error
	ListVolumes() ([]VolumeMetadata, error)
	GetVolume(name string) (Volume, error)
	GetVolumeConfig(name string) (map[string]interface{}, error)
	Attach(name string) (string, error)
	Detach(name string) error
}

type CreateRequest struct {
	Name string
	Opts map[string]interface{}
}

type RemoveRequest struct {
	Name string
}

type AttachRequest struct {
	Name string
}

type DetachRequest struct {
	Name string
}

type ActivateResponse struct {
	Implements []string
}

func (r *ActivateResponse) WriteResponse(w http.ResponseWriter) {
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling response: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}

type GenericResponse struct {
	Err string
}

func (r *GenericResponse) WriteResponse(w http.ResponseWriter) {
	if r.Err != "" {
		w.WriteHeader(http.StatusBadRequest)
	}
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling response: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}

type GenericRequest struct {
	Name string
}

type MountResponse struct {
	Mountpoint string
	Err        string
}

func (r *MountResponse) WriteResponse(w http.ResponseWriter) {
	if r.Err != "" {
		w.WriteHeader(http.StatusBadRequest)
	}
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling Get response: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}

type VolumeMetadata struct {
	Name       string
	Mountpoint string
}

type GetResponse struct {
	Volume Volume
	Err    string
}
type DockerGetResponse struct {
	Volume VolumeMetadata
	Err    string
}

type Volume struct {
	Name    string
	Backend Backend
}

type GetConfigResponse struct {
	VolumeConfig map[string]interface{}
	Err          string
}

func (r *GetResponse) WriteResponse(w http.ResponseWriter) {
	if r.Err != "" {
		w.WriteHeader(http.StatusBadRequest)
	}
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling Get response: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}
func (r *DockerGetResponse) WriteResponse(w http.ResponseWriter) {
	if r.Err != "" {
		w.WriteHeader(http.StatusBadRequest)
	}
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling DockerGetResponse: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}

type ListResponse struct {
	Volumes []VolumeMetadata
	Err     string
}

func (r *ListResponse) WriteResponse(w http.ResponseWriter) {
	if r.Err != "" {
		w.WriteHeader(http.StatusBadRequest)
	}
	data, err := json.Marshal(r)
	if err != nil {
		fmt.Errorf("Error marshalling Get response: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, string(data))
}

type FlexVolumeResponse struct {
	Status  string `json:"status"`
	Message string `json:"message"`
	Device  string `json:"device"`
}

type FlexVolumeMountRequest struct {
	MountPath   string                 `json:"mountPath"`
	MountDevice string                 `json:"name"`
	Opts        map[string]interface{} `json:"opts"`
}

type FlexVolumeUnmountRequest struct {
	MountPath string `json:"mountPath"`
}

type FlexVolumeDetachRequest struct {
	Name string `json:"name"`
}
<|endoftext|>


<commit_before>package main

func OptOffset(p Program) Program {
	var o Program
	var lastmove Command
	// [>>>?<<<] for Add, Print, Scan, Clear, Mult
	// not for Move, BNZ, Search
	for i := 0; i < len(p); i++ {
		switch b := p[i:]; {
		case len(b) >= 3 &&
			b[0].Op == Move &&
			(b[1].Op == Add || b[1].Op == Print || b[1].Op == Scan || b[1].Op == Clear) &&
			b[2].Op == Move:
			o = append(o, Command{
				Op:  b[1].Op,
				Arg: b[1].Arg,
				Off: b[0].Arg,
			})
			lastmove = Command{
				Op:  Move,
				Arg: b[0].Arg + b[2].Arg,
			}
			p[i+2] = lastmove
			i += 1
		case len(b) >= 4 &&
			b[0].Op == Move &&
			b[1].Op == Mult &&
			b[2].Op == Clear &&
			b[3].Op == Move:
			o = append(o, Command{
				Op:  b[1].Op,
				Dst: b[1].Dst,
				Arg: b[1].Arg,
				Off: b[0].Arg,
			})
			o = append(o, Command{
				Op:  b[2].Op,
				Arg: b[2].Arg,
				Off: b[0].Arg,
			})
			lastmove = Command{
				Op:  Move,
				Arg: b[0].Arg + b[3].Arg,
			}
			p[i+3] = lastmove
			i += 2
		default:
			p[i].Branch = OptOffset(p[i].Branch)
			o = append(o, p[i])
		}
	}
	return o
}
<commit_msg>Remove leftovers<commit_after>package main

func OptOffset(p Program) Program {
	var o Program
	// [>>>?<<<] for Add, Print, Scan, Clear, Mult
	// not for Move, BNZ, Search
	for i := 0; i < len(p); i++ {
		switch b := p[i:]; {
		case len(b) >= 3 &&
			b[0].Op == Move &&
			(b[1].Op == Add || b[1].Op == Print || b[1].Op == Scan || b[1].Op == Clear) &&
			b[2].Op == Move:
			o = append(o, Command{
				Op:  b[1].Op,
				Arg: b[1].Arg,
				Off: b[0].Arg,
			})
			// push back combined move
			p[i+2] = Command{
				Op:  Move,
				Arg: b[0].Arg + b[2].Arg,
			}
			i += 1
		case len(b) >= 4 &&
			b[0].Op == Move &&
			b[1].Op == Mult &&
			b[2].Op == Clear &&
			b[3].Op == Move:
			o = append(o, Command{
				Op:  b[1].Op,
				Dst: b[1].Dst,
				Arg: b[1].Arg,
				Off: b[0].Arg,
			})
			// push back combined move
			o = append(o, Command{
				Op:  b[2].Op,
				Arg: b[2].Arg,
				Off: b[0].Arg,
			})
			p[i+3] = Command{
				Op:  Move,
				Arg: b[0].Arg + b[3].Arg,
			}
			i += 2
		default:
			p[i].Branch = OptOffset(p[i].Branch)
			o = append(o, p[i])
		}
	}
	return o
}
<|endoftext|>


<commit_before>// Copyright 2020 The LUCI 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 internal

import (
	"context"
	"fmt"
	"time"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/gae/service/datastore"
	"go.chromium.org/luci/server/tq"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/timestamppb"
)

// PokeInterval is target frequency of executions of PokePmTask.
//
// See Dispatch() for details.
const PokeInterval = time.Second

// PokePMTaskRef is used by PM implementation to add its handler.
var PokePMTaskRef tq.TaskClassRef

func init() {
	PokePMTaskRef = tq.RegisterTaskClass(tq.TaskClass{
		ID:        "poke-pm-task",
		Prototype: &PokePMTask{},
		Queue:     "manage-project",
	})

	tq.RegisterTaskClass(tq.TaskClass{
		ID:        "kick-poke-pm-task",
		Prototype: &KickPokePMTask{},
		Queue:     "manage-project",
		Quiet:     true,
		Handler: func(ctx context.Context, payload proto.Message) error {
			task := payload.(*KickPokePMTask)
			var eta time.Time
			if t := task.GetEta(); t != nil {
				eta = t.AsTime()
			}
			return Dispatch(ctx, task.GetLuciProject(), eta)
		},
	})
}

// Dispatch ensures invocation of ProjectManager via PokePMTask.
//
// ProjectManager will be invoced at approximately no earlier than both:
// * eta time (if given)
// * next possible.
func Dispatch(ctx context.Context, luciProject string, eta time.Time) error {
	if datastore.CurrentTransaction(ctx) != nil {
		payload := &KickPokePMTask{LuciProject: luciProject}
		if !eta.IsZero() {
			payload.Eta = timestamppb.New(eta)
		}
		return tq.AddTask(ctx, &tq.Task{
			Title:            luciProject,
			DeduplicationKey: "", // not allowed in a transaction
			Payload:          payload,
		})
	}

	// If actual local clock is more than `clockDrift` behind, the "next" computed
	// PokePMTask moment might be already executing, meaning task dedup will
	// ensure no new task will be scheduled AND the already executing run
	// might not have read the Event that was just written.
	// Thus, for safety, this should be large, however, will also leads to higher
	// latency of event processing of non-busy ProjectManagers.
	// TODO(tandrii): this can be reduced significantly once safety "ping" events
	// are originated from Config import cron tasks.
	const clockDrift = 100 * time.Millisecond
	now := clock.Now(ctx).Add(clockDrift) // Use the worst possible time.
	if eta.IsZero() || eta.Before(now) {
		eta = now
	}
	eta = eta.Truncate(PokeInterval).Add(PokeInterval)
	return tq.AddTask(ctx, &tq.Task{
		Title:            luciProject,
		DeduplicationKey: fmt.Sprintf("%s\n%d", luciProject, eta.UnixNano()),
		ETA:              eta,
		Payload:          &PokePMTask{LuciProject: luciProject},
	})
}
<commit_msg>cv: record idea as a TODO.<commit_after>// Copyright 2020 The LUCI 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 internal

import (
	"context"
	"fmt"
	"time"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/gae/service/datastore"
	"go.chromium.org/luci/server/tq"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/timestamppb"
)

// PokeInterval is target frequency of executions of PokePmTask.
//
// See Dispatch() for details.
const PokeInterval = time.Second

// PokePMTaskRef is used by PM implementation to add its handler.
var PokePMTaskRef tq.TaskClassRef

func init() {
	PokePMTaskRef = tq.RegisterTaskClass(tq.TaskClass{
		ID:        "poke-pm-task",
		Prototype: &PokePMTask{},
		Queue:     "manage-project",
	})

	tq.RegisterTaskClass(tq.TaskClass{
		ID:        "kick-poke-pm-task",
		Prototype: &KickPokePMTask{},
		Queue:     "manage-project",
		Quiet:     true,
		Handler: func(ctx context.Context, payload proto.Message) error {
			task := payload.(*KickPokePMTask)
			var eta time.Time
			if t := task.GetEta(); t != nil {
				eta = t.AsTime()
			}
			return Dispatch(ctx, task.GetLuciProject(), eta)
		},
	})
}

// Dispatch ensures invocation of ProjectManager via PokePMTask.
//
// ProjectManager will be invoced at approximately no earlier than both:
// * eta time (if given)
// * next possible.
func Dispatch(ctx context.Context, luciProject string, eta time.Time) error {
	if datastore.CurrentTransaction(ctx) != nil {
		// TODO(tandrii): use txndefer to immediately trigger a PokePMTask after
		// transaction completes to reduce latency in *most* circumstances.
		// The KickPokePMTask is still required for correctness.
		payload := &KickPokePMTask{LuciProject: luciProject}
		if !eta.IsZero() {
			payload.Eta = timestamppb.New(eta)
		}
		return tq.AddTask(ctx, &tq.Task{
			Title:            luciProject,
			DeduplicationKey: "", // not allowed in a transaction
			Payload:          payload,
		})
	}

	// If actual local clock is more than `clockDrift` behind, the "next" computed
	// PokePMTask moment might be already executing, meaning task dedup will
	// ensure no new task will be scheduled AND the already executing run
	// might not have read the Event that was just written.
	// Thus, for safety, this should be large, however, will also leads to higher
	// latency of event processing of non-busy ProjectManagers.
	// TODO(tandrii): this can be reduced significantly once safety "ping" events
	// are originated from Config import cron tasks.
	const clockDrift = 100 * time.Millisecond
	now := clock.Now(ctx).Add(clockDrift) // Use the worst possible time.
	if eta.IsZero() || eta.Before(now) {
		eta = now
	}
	eta = eta.Truncate(PokeInterval).Add(PokeInterval)
	return tq.AddTask(ctx, &tq.Task{
		Title:            luciProject,
		DeduplicationKey: fmt.Sprintf("%s\n%d", luciProject, eta.UnixNano()),
		ETA:              eta,
		Payload:          &PokePMTask{LuciProject: luciProject},
	})
}
<|endoftext|>


<commit_before>package main

import (
	//"fmt"
	//"github.com/tarm/goserial"
	"log"
	"os"
	"strings"
	//"encoding/binary"
	//"strconv"
	//"syscall"
	//"fmt"
	"io/ioutil"
)

func getList() ([]OsSerialPort, os.SyscallError) {
	//return getListViaWmiPnpEntity()
	return getListViaTtyList()
}

func getListViaTtyList() ([]OsSerialPort, os.SyscallError) {
	var err os.SyscallError

	log.Println("getting serial list on darwin")

	// make buffer of 100 max serial ports
	// return a slice
	list := make([]OsSerialPort, 100)

	files, _ := ioutil.ReadDir("/dev/")
	ctr := 0
	for _, f := range files {
		if strings.HasPrefix(f.Name(), "tty") {
			// it is a legitimate serial port
			list[ctr].Name = "/dev/" + f.Name()
			list[ctr].FriendlyName = f.Name()
			log.Println("Added serial port to list: ", list[ctr])
			ctr++
		}
		// stop-gap in case going beyond 100 (which should never happen)
		// i mean, really, who has more than 100 serial ports?
		if ctr > 99 {
			ctr = 99
		}
		//fmt.Println(f.Name())
		//fmt.Println(f.)
	}
	/*
		list := make([]OsSerialPort, 3)
		list[0].Name = "tty.serial1"
		list[0].FriendlyName = "tty.serial1"
		list[1].Name = "tty.serial2"
		list[1].FriendlyName = "tty.serial2"
		list[2].Name = "tty.Bluetooth-Modem"
		list[2].FriendlyName = "tty.Bluetooth-Modem"
	*/

	return list[0:ctr], err
}
<commit_msg>Enabled up to 1000 serial ports on Linux<commit_after>package main

import (
	//"fmt"
	//"github.com/tarm/goserial"
	"log"
	"os"
	"strings"
	//"encoding/binary"
	//"strconv"
	//"syscall"
	//"fmt"
	"io/ioutil"
)

func getList() ([]OsSerialPort, os.SyscallError) {
	//return getListViaWmiPnpEntity()
	return getListViaTtyList()
}

func getListViaTtyList() ([]OsSerialPort, os.SyscallError) {
	var err os.SyscallError

	log.Println("getting serial list on darwin")

	// make buffer of 1000 max serial ports
	// return a slice
	list := make([]OsSerialPort, 1000)

	files, _ := ioutil.ReadDir("/dev/")
	ctr := 0
	for _, f := range files {
		if strings.HasPrefix(f.Name(), "tty") {
			// it is a legitimate serial port
			list[ctr].Name = "/dev/" + f.Name()
			list[ctr].FriendlyName = f.Name()
			log.Println("Added serial port to list: ", list[ctr])
			ctr++
		}
		// stop-gap in case going beyond 1000 (which should never happen)
		// i mean, really, who has more than 1000 serial ports?
		if ctr > 999 {
			ctr = 999
		}
		//fmt.Println(f.Name())
		//fmt.Println(f.)
	}
	/*
		list := make([]OsSerialPort, 3)
		list[0].Name = "tty.serial1"
		list[0].FriendlyName = "tty.serial1"
		list[1].Name = "tty.serial2"
		list[1].FriendlyName = "tty.serial2"
		list[2].Name = "tty.Bluetooth-Modem"
		list[2].FriendlyName = "tty.Bluetooth-Modem"
	*/

	return list[0:ctr], err
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"net"
	"net/http"

	"github.com/apihub/apihub"

	"code.cloudfoundry.org/lager"
)

type ApihubServer struct {
	net.Listener
	logger lager.Logger

	listenAddr    string
	listenNetwork string
	router        *Router
	server        *http.Server
	storage       apihub.Storage
}

func New(log lager.Logger, listenNetwork, listenAddr string, storage apihub.Storage) *ApihubServer {
	s := &ApihubServer{
		logger:        log,
		listenAddr:    listenAddr,
		listenNetwork: listenNetwork,
		router:        NewRouter(),
		storage:       storage,
	}

	var handlers = map[Route]http.HandlerFunc{
		Home:          http.HandlerFunc(homeHandler),
		Ping:          http.HandlerFunc(pingHandler),
		AddService:    http.HandlerFunc(s.addService),
		ListServices:  http.HandlerFunc(s.listServices),
		RemoveService: http.HandlerFunc(s.removeService),
		FindService:   http.HandlerFunc(s.findService),
		UpdateService: http.HandlerFunc(s.updateService),
	}
	for route, handler := range handlers {
		s.router.AddHandler(RouterArguments{Path: Routes[route].Path, Method: Routes[route].Method, Handler: handler})
	}

	s.server = &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			s.router.r.ServeHTTP(w, r)
		}),
	}

	return s
}

func (a *ApihubServer) Start(keep bool) error {
	var err error

	a.logger.Info("apihub-start-server", lager.Data{"listenAddr": a.listenAddr})
	a.Listener, err = net.Listen(a.listenNetwork, a.listenAddr)
	if err != nil {
		fmt.Println(err)
		a.logger.Error("apihub-failed-starting-server", err)
		return err
	}

	if keep {
		a.logger.Info("started")
		a.server.Serve(a.Listener)
		return nil
	}

	go a.server.Serve(a.Listener)
	a.logger.Info("started")

	return nil
}

func (a *ApihubServer) Handler() http.Handler {
	return a.router.Handler()
}

func (a *ApihubServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.Handler()
}

func (a *ApihubServer) Stop() error {
	return a.Listener.Close()
}
<commit_msg>Change the way we log things in the server<commit_after>package api

import (
	"fmt"
	"net"
	"net/http"

	"github.com/apihub/apihub"

	"code.cloudfoundry.org/lager"
)

type ApihubServer struct {
	net.Listener
	logger lager.Logger

	listenAddr    string
	listenNetwork string
	router        *Router
	server        *http.Server
	storage       apihub.Storage
}

func New(log lager.Logger, listenNetwork, listenAddr string, storage apihub.Storage) *ApihubServer {
	s := &ApihubServer{
		logger:        log,
		listenAddr:    listenAddr,
		listenNetwork: listenNetwork,
		router:        NewRouter(),
		storage:       storage,
	}

	var handlers = map[Route]http.HandlerFunc{
		Home:          http.HandlerFunc(homeHandler),
		Ping:          http.HandlerFunc(pingHandler),
		AddService:    http.HandlerFunc(s.addService),
		ListServices:  http.HandlerFunc(s.listServices),
		RemoveService: http.HandlerFunc(s.removeService),
		FindService:   http.HandlerFunc(s.findService),
		UpdateService: http.HandlerFunc(s.updateService),
	}
	for route, handler := range handlers {
		s.router.AddHandler(RouterArguments{Path: Routes[route].Path, Method: Routes[route].Method, Handler: handler})
	}

	s.server = &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			s.router.r.ServeHTTP(w, r)
		}),
	}

	return s
}

func (a *ApihubServer) Start(keep bool) error {
	var err error

	log := a.logger.Session("start")

	log.Info("listening", lager.Data{"listenAddr": a.listenAddr})
	a.Listener, err = net.Listen(a.listenNetwork, a.listenAddr)
	if err != nil {
		fmt.Println(err)
		log.Error("failed-to-start", err)
		return err
	}

	if keep {
		log.Info("started")
		a.server.Serve(a.Listener)
		return nil
	}

	go a.server.Serve(a.Listener)
	log.Info("started")

	return nil
}

func (a *ApihubServer) Handler() http.Handler {
	return a.router.Handler()
}

func (a *ApihubServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.Handler()
}

func (a *ApihubServer) Stop() error {
	return a.Listener.Close()
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"

	"github.com/dolaterio/dolaterio/core"
)

var (
	Runner *dolaterio.Runner
	Engine *dolaterio.ContainerEngine
)

// Initializes a new API
func Initialize() error {
	docker := &dolaterio.ContainerEngine{}
	err := docker.Connect()
	if err != nil {
		return err
	}
	Engine = docker

	runner, err := dolaterio.NewRunner(&dolaterio.RunnerOptions{
		Concurrency: 10,
		Engine:      Engine,
	})
	if err != nil {
		return err
	}
	Runner = runner

	go func() {
		for {
			job, _ := Runner.Response()
			dbJob, _ := GetJob(job.ID)
			dbJob.Stdout = string(job.Stdout)
			dbJob.Stderr = string(job.Stderr)
			SaveJob(dbJob)
			fmt.Println("Finished Job " + job.ID)
		}
	}()

	return nil
}
<commit_msg>Added required docs<commit_after>package api

import (
	"fmt"

	"github.com/dolaterio/dolaterio/core"
)

var (
	// Runner is the dolater job runner
	Runner *dolaterio.Runner
)

// Initialize a new API
func Initialize() error {
	docker := &dolaterio.ContainerEngine{}
	err := docker.Connect()
	if err != nil {
		return err
	}

	runner, err := dolaterio.NewRunner(&dolaterio.RunnerOptions{
		Concurrency: 10,
		Engine:      docker,
	})
	if err != nil {
		return err
	}
	Runner = runner

	go func() {
		for {
			job, _ := Runner.Response()
			dbJob, _ := GetJob(job.ID)
			dbJob.Stdout = string(job.Stdout)
			dbJob.Stderr = string(job.Stderr)
			SaveJob(dbJob)
			fmt.Println("Finished Job " + job.ID)
		}
	}()

	return nil
}
<|endoftext|>


<commit_before>package sentinel

import (
	"time"

	"github.com/mdevilliers/redishappy/configuration"
	"github.com/mdevilliers/redishappy/services/logger"
	"github.com/mdevilliers/redishappy/services/redis"
	"github.com/mdevilliers/redishappy/types"
	"github.com/mdevilliers/redishappy/util"
)

const (
	SentinelMarkedUp    = 1
	SentinelMarkedDown  = 2
	SentinelMarkedAlive = 3
)

type Manager interface {
	Notify(event SentinelEvent)
	GetState(request TopologyRequest)
}

type SentinelManager struct {
	eventsChannel          chan SentinelEvent
	topologyRequestChannel chan TopologyRequest
	switchmasterchannel    chan types.MasterSwitchedEvent
	redisConnection        redis.RedisConnection
	configurationManager   *configuration.ConfigurationManager
}

var topologyState = SentinelTopology{Sentinels: map[string]*SentinelInfo{}}

func NewManager(switchmasterchannel chan types.MasterSwitchedEvent, cm *configuration.ConfigurationManager) *SentinelManager {

	events := make(chan SentinelEvent)
	requests := make(chan TopologyRequest)

	manager := &SentinelManager{eventsChannel: events,
		topologyRequestChannel: requests,
		switchmasterchannel:    switchmasterchannel,
		redisConnection:        redis.RadixRedisConnection{},
		configurationManager:   cm,
	}

	go loopEvents(events, requests, manager)
	go manager.bootstrap()
	return manager
}

func (m *SentinelManager) Notify(event SentinelEvent) {
	m.eventsChannel <- event
}

func (m *SentinelManager) GetState(request TopologyRequest) {
	m.topologyRequestChannel <- request
}

func (m *SentinelManager) ClearState() {
	topologyState = SentinelTopology{Sentinels: map[string]*SentinelInfo{}}
}

func (m *SentinelManager) GetCurrentTopology() types.MasterDetailsCollection {
	stateChannel := make(chan types.MasterDetailsCollection)
	go m.getTopology(stateChannel)
	return <-stateChannel
}

func (m *SentinelManager) getTopology(stateChannel chan types.MasterDetailsCollection) {

	topology := types.NewMasterDetailsCollection()
	configuration := m.configurationManager.GetCurrentConfiguration()

	for _, sentinel := range configuration.Sentinels {
		client, err := NewSentinelClient(sentinel, m.redisConnection)

		if err != nil {
			logger.Info.Printf("Error starting sentinel (%s) client : %s", sentinel.GetLocation(), err.Error())
			continue
		}
		defer client.Close()

		for _, clusterDetails := range configuration.Clusters {

			details, err := client.DiscoverMasterForCluster(clusterDetails.Name)
			if err != nil {
				continue
			}

			details.ExternalPort = clusterDetails.ExternalPort
			// TODO : last one wins?
			topology.AddOrReplace(details)
		}
	}
	stateChannel <- topology
}

func (m *SentinelManager) bootstrap() {

	configuration := m.configurationManager.GetCurrentConfiguration()

	for _, sentinel := range configuration.Sentinels {
		m.Notify(&SentinelAdded{Sentinel: sentinel})
	}

	util.Schedule(func() { m.bootstrap() }, time.Second*60)
}

func (m *SentinelManager) exploreSentinelTopology(sentinel types.Sentinel) {

	client, err := NewSentinelClient(sentinel, m.redisConnection)

	if err != nil {
		logger.Info.Printf("Error starting sentinel (%s) client : %s", sentinel.GetLocation(), err.Error())
	}
	defer client.Close()

	knownClusters := client.FindKnownClusters()

	m.Notify(&SentinelClustersMonitoredUpdate{Sentinel: sentinel, Clusters: knownClusters})

	for _, clustername := range knownClusters {

		sentinels := client.FindConnectedSentinels(clustername)

		for _, connectedsentinel := range sentinels {
			m.Notify(&SentinelAdded{Sentinel: connectedsentinel})
		}
	}
}

func (m *SentinelManager) startNewMonitor(sentinel types.Sentinel) (*Monitor, error) {

	monitor, err := NewMonitor(sentinel, m, m.redisConnection)

	if err != nil {
		logger.Error.Printf("Error starting monitor %s : %s", sentinel.GetLocation(), err.Error())
		m.Notify(&SentinelLost{Sentinel: sentinel})
		return nil, err
	}

	go monitor.StartMonitoringMasterEvents(m.switchmasterchannel)

	return monitor, nil
}

func loopEvents(events chan SentinelEvent, topology chan TopologyRequest, m *SentinelManager) {
	for {
		select {
		case event := <-events:
			updateState(event, m)
		case read := <-topology:
			read.ReplyChannel <- topologyState
		}
	}
}

func updateState(event interface{}, m *SentinelManager) {

	switch e := event.(type) {
	case *SentinelAdded:

		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)

		//if we don't know about the sentinel start monitoring it
		if _, exists := topologyState.Sentinels[uid]; !exists {

			info := &SentinelInfo{SentinelLocation: uid,
				LastUpdated: time.Now().UTC(),
				//KnownClusters: e.Clusters,
				State: SentinelMarkedUp}

			topologyState.Sentinels[uid] = info

			go m.startNewMonitor(sentinel)
			go m.exploreSentinelTopology(sentinel)

			logger.Trace.Printf("Sentinel added : %s", util.String(topologyState))
		}

	case *SentinelLost:

		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)
		currentInfo, ok := topologyState.Sentinels[uid]

		if ok {
			currentInfo.State = SentinelMarkedDown
			currentInfo.LastUpdated = time.Now().UTC()
		}

		util.Schedule(func() { m.startNewMonitor(sentinel) }, time.Second*5)

		logger.Trace.Printf("Sentinel lost : %s (scheduling new client and monitor).", util.String(topologyState))

	case *SentinelPing:
		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)
		currentInfo, ok := topologyState.Sentinels[uid]

		if ok {
			currentInfo.State = SentinelMarkedAlive
			currentInfo.LastUpdated = time.Now().UTC()
		}

	case *SentinelClustersMonitoredUpdate:
		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)

		if info, exists := topologyState.Sentinels[uid]; exists {

			info.Clusters = e.Clusters
		}

	default:
		logger.Error.Println("Unknown sentinel event : ", util.String(e))
	}
}
<commit_msg>make loop a function on the Manager type<commit_after>package sentinel

import (
	"time"

	"github.com/mdevilliers/redishappy/configuration"
	"github.com/mdevilliers/redishappy/services/logger"
	"github.com/mdevilliers/redishappy/services/redis"
	"github.com/mdevilliers/redishappy/types"
	"github.com/mdevilliers/redishappy/util"
)

const (
	SentinelMarkedUp    = 1
	SentinelMarkedDown  = 2
	SentinelMarkedAlive = 3
)

type Manager interface {
	Notify(event SentinelEvent)
	GetState(request TopologyRequest)
}

type SentinelManager struct {
	eventsChannel          chan SentinelEvent
	topologyRequestChannel chan TopologyRequest
	switchmasterchannel    chan types.MasterSwitchedEvent
	redisConnection        redis.RedisConnection
	configurationManager   *configuration.ConfigurationManager
}

var topologyState = SentinelTopology{Sentinels: map[string]*SentinelInfo{}}

func NewManager(switchmasterchannel chan types.MasterSwitchedEvent, cm *configuration.ConfigurationManager) *SentinelManager {

	events := make(chan SentinelEvent)
	requests := make(chan TopologyRequest)

	manager := &SentinelManager{eventsChannel: events,
		topologyRequestChannel: requests,
		switchmasterchannel:    switchmasterchannel,
		redisConnection:        redis.RadixRedisConnection{},
		configurationManager:   cm,
	}

	go manager.loopEvents(events, requests)
	go manager.bootstrap()
	return manager
}

func (m *SentinelManager) Notify(event SentinelEvent) {
	m.eventsChannel <- event
}

func (m *SentinelManager) GetState(request TopologyRequest) {
	m.topologyRequestChannel <- request
}

func (m *SentinelManager) ClearState() {
	topologyState = SentinelTopology{Sentinels: map[string]*SentinelInfo{}}
}

func (m *SentinelManager) GetCurrentTopology() types.MasterDetailsCollection {
	stateChannel := make(chan types.MasterDetailsCollection)
	go m.getTopology(stateChannel)
	return <-stateChannel
}

func (m *SentinelManager) getTopology(stateChannel chan types.MasterDetailsCollection) {

	topology := types.NewMasterDetailsCollection()
	configuration := m.configurationManager.GetCurrentConfiguration()

	for _, sentinel := range configuration.Sentinels {
		client, err := NewSentinelClient(sentinel, m.redisConnection)

		if err != nil {
			logger.Info.Printf("Error starting sentinel (%s) client : %s", sentinel.GetLocation(), err.Error())
			continue
		}
		defer client.Close()

		for _, clusterDetails := range configuration.Clusters {

			details, err := client.DiscoverMasterForCluster(clusterDetails.Name)
			if err != nil {
				continue
			}

			details.ExternalPort = clusterDetails.ExternalPort
			// TODO : last one wins?
			topology.AddOrReplace(details)
		}
	}
	stateChannel <- topology
}

func (m *SentinelManager) bootstrap() {

	configuration := m.configurationManager.GetCurrentConfiguration()

	for _, sentinel := range configuration.Sentinels {
		m.Notify(&SentinelAdded{Sentinel: sentinel})
	}

	util.Schedule(func() { m.bootstrap() }, time.Second*60)
}

func (m *SentinelManager) exploreSentinelTopology(sentinel types.Sentinel) {

	client, err := NewSentinelClient(sentinel, m.redisConnection)

	if err != nil {
		logger.Info.Printf("Error starting sentinel (%s) client : %s", sentinel.GetLocation(), err.Error())
	}
	defer client.Close()

	knownClusters := client.FindKnownClusters()

	m.Notify(&SentinelClustersMonitoredUpdate{Sentinel: sentinel, Clusters: knownClusters})

	for _, clustername := range knownClusters {

		sentinels := client.FindConnectedSentinels(clustername)

		for _, connectedsentinel := range sentinels {
			m.Notify(&SentinelAdded{Sentinel: connectedsentinel})
		}
	}
}

func (m *SentinelManager) startNewMonitor(sentinel types.Sentinel) (*Monitor, error) {

	monitor, err := NewMonitor(sentinel, m, m.redisConnection)

	if err != nil {
		logger.Error.Printf("Error starting monitor %s : %s", sentinel.GetLocation(), err.Error())
		m.Notify(&SentinelLost{Sentinel: sentinel})
		return nil, err
	}

	go monitor.StartMonitoringMasterEvents(m.switchmasterchannel)

	return monitor, nil
}

func (m *SentinelManager) loopEvents(events chan SentinelEvent, topology chan TopologyRequest) {
	for {
		select {
		case event := <-events:
			m.updateState(event)
		case read := <-topology:
			read.ReplyChannel <- topologyState
		}
	}
}

func (m *SentinelManager) updateState(event interface{}) {

	switch e := event.(type) {
	case *SentinelAdded:

		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)

		//if we don't know about the sentinel start monitoring it
		if _, exists := topologyState.Sentinels[uid]; !exists {

			info := &SentinelInfo{SentinelLocation: uid,
				LastUpdated: time.Now().UTC(),
				//KnownClusters: e.Clusters,
				State: SentinelMarkedUp}

			topologyState.Sentinels[uid] = info

			go m.startNewMonitor(sentinel)
			go m.exploreSentinelTopology(sentinel)

			logger.Trace.Printf("Sentinel added : %s", util.String(topologyState))
		}

	case *SentinelLost:

		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)
		currentInfo, ok := topologyState.Sentinels[uid]

		if ok {
			currentInfo.State = SentinelMarkedDown
			currentInfo.LastUpdated = time.Now().UTC()
		}

		util.Schedule(func() { m.startNewMonitor(sentinel) }, time.Second*5)

		logger.Trace.Printf("Sentinel lost : %s (scheduling new client and monitor).", util.String(topologyState))

	case *SentinelPing:
		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)
		currentInfo, ok := topologyState.Sentinels[uid]

		if ok {
			currentInfo.State = SentinelMarkedAlive
			currentInfo.LastUpdated = time.Now().UTC()
		}

	case *SentinelClustersMonitoredUpdate:
		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)

		if info, exists := topologyState.Sentinels[uid]; exists {

			info.Clusters = e.Clusters
		}

	default:
		logger.Error.Println("Unknown sentinel event : ", util.String(e))
	}
}
<|endoftext|>