<commit_before>package signalfx

import (
	"sync"
	"time"

	"github.com/coreos/fleet/log"
	"github.com/zvelo/go-signalfx/sfxproto"
	"golang.org/x/net/context"
)

// DataPointCallback is a functional callback that can be passed to
// DataPointCallback as a way to have the caller calculate and return
// their own datapoints
type DataPointCallback func(defaultDims map[string]string) *DataPoints

// Reporter is an object that tracks DataPoints and manages a Client. It is the
// recommended way to send data to SignalFX.
type Reporter struct {
	client             *Client
	defaultDimensions  map[string]string
	datapoints         *DataPoints
	buckets            map[*Bucket]interface{}
	preReportCallbacks []func()
	datapointCallbacks []DataPointCallback
	mu                 sync.Mutex
	oneShots           []*sfxproto.DataPoint
}

// NewReporter returns a new Reporter object. Any dimensions supplied will be
// appended to all DataPoints sent to SignalFX. config is copied, so future
// changes to the external config object are not reflected within the reporter.
func NewReporter(config *Config,
	defaultDimensions map[string]string) *Reporter {
	return &Reporter{
		client:            NewClient(config),
		defaultDimensions: defaultDimensions,
		datapoints:        NewDataPoints(0),
		buckets:           map[*Bucket]interface{}{},
	}
}

func (r *Reporter) lock() {
	r.mu.Lock()
}

func (r *Reporter) unlock() {
	r.mu.Unlock()
}

// NewBucket creates a new Bucket object that is tracked by the Reporter.
// Buckets are goroutine safe.
func (r *Reporter) NewBucket(metric string, dimensions map[string]string) *Bucket {
	ret := NewBucket(metric, dimensions)

	r.lock()
	defer r.unlock()

	r.buckets[ret] = nil
	return ret
}

// NewCumulative returns a new DataPoint object with type CUMULATIVE_COUNTER.
// val can be any type of int, float, string, nil, pointer to those types, or a
// Getter that returns any of those types. Literal pointers are copied by value.
// Getters that return pointer types should not have their value changed, unless
// atomically, when in a Reporter, except within a PreReportCallback, for
// goroutine safety.
func (r *Reporter) NewCumulative(metric string, val interface{}, dims map[string]string) *DataPoint {
	dp, _ := NewCumulative(metric, val, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	r.datapoints.Add(dp)
	return dp
}

// NewGauge returns a new DataPoint object with type GAUGE. val can be any type
// of int, float, string, nil, pointer to those types, or a Getter that returns
// any of those types. Literal pointers are copied by value. Getters that return
// pointer types should not have their value changed, unless atomically, when in
// a Reporter, except within a PreReportCallback, for goroutine safety.
func (r *Reporter) NewGauge(metric string, val interface{}, dims map[string]string) *DataPoint {
	dp, _ := NewGauge(metric, val, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	r.datapoints.Add(dp)
	return dp
}

// NewCounter returns a new DataPoint object with type COUNTER. val can be any
// type of int, float, string, nil, pointer to those types, or a Getter that
// returns any of those types. Literal pointers are copied by value. Getters
// that return pointer types should not have their value changed, unless
// atomically, when in a Reporter, except within a PreReportCallback, for
// goroutine safety.
func (r *Reporter) NewCounter(metric string, val interface{}, dims map[string]string) *DataPoint {
	dp, _ := NewCounter(metric, val, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	r.datapoints.Add(dp)
	return dp
}

// NewInt32 creates a new DataPoint object with type COUNTER whose value is
// bound to an Int32. All methods on Int32 are goroutine safe and it may also be
// modified in atomic operations as an int32.
func (r *Reporter) NewInt32(metric string, dims map[string]string) (*Int32, *DataPoint) {
	ret := Int32(0)
	dp, _ := NewCounter(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewCumulativeInt32 creates a new DataPoint object with type
// CUMULATIVE_COUNTER whose value is bound to an Int32. All methods on Int32 are
// goroutine safe and it may also be modified in atomic operations as an int32.
func (r *Reporter) NewCumulativeInt32(metric string, dims map[string]string) (*Int32, *DataPoint) {
	ret := Int32(0)
	dp, _ := NewCumulative(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewInt64 creates a new DataPoint object with type COUNTER whose value is
// bound to an Int64. All methods on Int64 are goroutine safe and it may also be
// modified in atomic operations as an int64.
func (r *Reporter) NewInt64(metric string, dims map[string]string) (*Int64, *DataPoint) {
	ret := Int64(0)
	dp, _ := NewCounter(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewCumulativeInt64 creates a new DataPoint object with type
// CUMULATIVE_COUNTER whose value is bound to an Int64. All methods on Int64 are
// goroutine safe and it may also be modified in atomic operations as an int64.
func (r *Reporter) NewCumulativeInt64(metric string, dims map[string]string) (*Int64, *DataPoint) {
	ret := Int64(0)
	dp, _ := NewCumulative(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewUint32 creates a new DataPoint object with type COUNTER whose value is
// bound to an Uint32. All methods on Uint32 are goroutine safe and it may also
// be modified in atomic operations as an uint32.
func (r *Reporter) NewUint32(metric string, dims map[string]string) (*Uint32, *DataPoint) {
	ret := Uint32(0)
	dp, _ := NewCounter(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewCumulativeUint32 creates a new DataPoint object with type
// CUMULATIVE_COUNTER whose value is bound to an Uint32. All methods on Uint32
// are goroutine safe and it may also be modified in atomic operations as an
// uint32.
func (r *Reporter) NewCumulativeUint32(metric string, dims map[string]string) (*Uint32, *DataPoint) {
	ret := Uint32(0)
	dp, _ := NewCumulative(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewUint64 creates a new DataPoint object with type COUNTER whose value is
// bound to an Uint64. All methods on Uint64 are goroutine safe and it may also be
// modified in atomic operations as an uint64.
func (r *Reporter) NewUint64(metric string, dims map[string]string) (*Uint64, *DataPoint) {
	ret := Uint64(0)
	dp, _ := NewCounter(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewCumulativeUint64 creates a new DataPoint object with type
// CUMULATIVE_COUNTER whose value is bound to an Uint64. All methods on Uint64 are
// goroutine safe and it may also be modified in atomic operations as an uint64.
func (r *Reporter) NewCumulativeUint64(metric string, dims map[string]string) (*Uint64, *DataPoint) {
	ret := Uint64(0)
	dp, _ := NewCumulative(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// AddDataPoint provides a way to manually add DataPoint(s) to be tracked by the
// Reporter
func (r *Reporter) AddDataPoint(vals ...*DataPoint) {
	r.datapoints.Add(vals...)
}

// AddDataPoints provides a way to manually add DataPoint to be tracked by the
// Reporter
func (r *Reporter) AddDataPoints(dps *DataPoints) {
	r.datapoints.Append(dps)
}

// RemoveDataPoint takes DataPoint(s)out of the set being tracked by the
// Reporter
func (r *Reporter) RemoveDataPoint(dps ...*DataPoint) {
	r.datapoints.Remove(dps...)
}

// RemoveDataPoints takes DataPoints out of the set being tracked by the
// Reporter
func (r *Reporter) RemoveDataPoints(dps *DataPoints) {
	r.datapoints.RemoveDataPoints(dps)
}

// RemoveBucket takes Bucket(s) out of the set being tracked by the Reporter
func (r *Reporter) RemoveBucket(bs ...*Bucket) {
	r.lock()
	defer r.unlock()

	for _, b := range bs {
		delete(r.buckets, b)
	}
}

// AddPreReportCallback adds a function that is called before Report().  This is useful for refetching
// things like runtime.Memstats() so they are only fetched once per report() call. If a DataPoint
func (r *Reporter) AddPreReportCallback(f func()) {
	r.lock()
	defer r.unlock()
	r.preReportCallbacks = append(r.preReportCallbacks, f)
}

// AddDataPointsCallback adds a callback that itself will generate datapoints to report
func (r *Reporter) AddDataPointsCallback(f DataPointCallback) {
	r.lock()
	defer r.unlock()
	r.datapointCallbacks = append(r.datapointCallbacks, f)
}

// Report sends all tracked DataPoints to SignalFX. PreReportCallbacks will be
// run before building the dataset to send. DataPoint callbacks will be executed
// and added to the dataset, but do not become tracked by the Reporter.
func (r *Reporter) Report(ctx context.Context) (*DataPoints, error) {
	if ctx == nil {
		ctx = context.Background()
	} else if ctx.Err() != nil {
		return nil, ctx.Err()
	}

	r.lock()
	defer r.unlock()

	for _, f := range r.preReportCallbacks {
		f()
	}

	ret := r.datapoints.Clone()

	for _, f := range r.datapointCallbacks {
		ret.Append(f(r.defaultDimensions))
	}

	for b := range r.buckets {
		ret.Append(b.DataPoints(r.defaultDimensions))
	}

	var (
		counters           []*DataPoint
		cumulativeCounters []*DataPoint
	)
	ret = ret.filter(func(dp *DataPoint) bool {
		if err := dp.update(); err != nil {
			return false
		}

		switch *dp.pdp.MetricType {
		case sfxproto.MetricType_COUNTER:
			if dp.pdp.Value.IntValue != nil && *dp.pdp.Value.IntValue != 0 {
				counters = append(counters, dp)
				dp.SetTime(time.Now())
				return true
			}
		case sfxproto.MetricType_CUMULATIVE_COUNTER:
			if !dp.pdp.Equal(dp.previous) {
				cumulativeCounters = append(cumulativeCounters, dp)
				dp.SetTime(time.Now())
				return true
			}
		default:
			dp.SetTime(time.Now())
			return true
		}
		return false
	})
	pdps := ret.ProtoDataPoints()

	// append all of the one-shots
	for _, pdp := range r.oneShots {
		pdps.Add(pdp)
	}

	if err := r.client.Submit(ctx, pdps); err != nil {
		return nil, err
	}

	// set submitted counters to zero
	for _, counter := range counters {
		// TODO: what should be done if this fails?
		counter.Set(0)
	}
	// remember submitted cumulative counter values
	for _, counter := range cumulativeCounters {
		counter.previous = counter.pdp
	}

	// and clear the one-shots
	r.oneShots = nil

	return ret, nil
}

func (r *Reporter) Inc(metric string, dimensions map[string]string, delta int64) error {
	r.lock()
	defer r.unlock()

	var protoDims []*sfxproto.Dimension
	for k, v := range r.defaultDimensions {
		// have to copy the values, since these are stored as
		// pointers…
		var dk, dv string
		dk = k
		dv = v
		protoDims = append(protoDims, &sfxproto.Dimension{Key: &dk, Value: &dv})
	}
	for k, v := range dimensions {
		var dk, dv string
		dk = k
		dv = v
		protoDims = append(protoDims, &sfxproto.Dimension{Key: &dk, Value: &dv})
	}
	timestamp := time.Now().UnixNano() / 1000000
	metricType := sfxproto.MetricType_COUNTER
	dp := &sfxproto.DataPoint{
		Metric:     &metric,
		Timestamp:  &timestamp,
		MetricType: &metricType,
		Dimensions: protoDims,
		Value:      &sfxproto.Datum{IntValue: &delta},
	}
	r.oneShots = append(r.oneShots, dp)
	return nil
}

// RunInBackground starts a goroutine which calls Reporter.Report on
// the specified interval.  It returns a function which may be used to
// cancel the backgrounding.
func (r *Reporter) RunInBackground(interval time.Duration) (cancel func()) {
	done := make(chan struct{})
	go func() {
		ticker := time.NewTicker(interval)
		for {
			select {
			case <-ticker.C:
				dps, err := r.Report(context.Background())
				if err != nil {
					log.Error(err)
				} else if dps != nil {
					log.Infof("reported %d datapoints", dps.Len())
				}
			case <-done:
				return
			}
		}
	}()
	return func() {
		done <- struct{}{}
	}
}
<commit_msg>Remove logging of data points submitted<commit_after>package signalfx

import (
	"sync"
	"time"

	"github.com/coreos/fleet/log"
	"github.com/zvelo/go-signalfx/sfxproto"
	"golang.org/x/net/context"
)

// DataPointCallback is a functional callback that can be passed to
// DataPointCallback as a way to have the caller calculate and return
// their own datapoints
type DataPointCallback func(defaultDims map[string]string) *DataPoints

// Reporter is an object that tracks DataPoints and manages a Client. It is the
// recommended way to send data to SignalFX.
type Reporter struct {
	client             *Client
	defaultDimensions  map[string]string
	datapoints         *DataPoints
	buckets            map[*Bucket]interface{}
	preReportCallbacks []func()
	datapointCallbacks []DataPointCallback
	mu                 sync.Mutex
	oneShots           []*sfxproto.DataPoint
}

// NewReporter returns a new Reporter object. Any dimensions supplied will be
// appended to all DataPoints sent to SignalFX. config is copied, so future
// changes to the external config object are not reflected within the reporter.
func NewReporter(config *Config,
	defaultDimensions map[string]string) *Reporter {
	return &Reporter{
		client:            NewClient(config),
		defaultDimensions: defaultDimensions,
		datapoints:        NewDataPoints(0),
		buckets:           map[*Bucket]interface{}{},
	}
}

func (r *Reporter) lock() {
	r.mu.Lock()
}

func (r *Reporter) unlock() {
	r.mu.Unlock()
}

// NewBucket creates a new Bucket object that is tracked by the Reporter.
// Buckets are goroutine safe.
func (r *Reporter) NewBucket(metric string, dimensions map[string]string) *Bucket {
	ret := NewBucket(metric, dimensions)

	r.lock()
	defer r.unlock()

	r.buckets[ret] = nil
	return ret
}

// NewCumulative returns a new DataPoint object with type CUMULATIVE_COUNTER.
// val can be any type of int, float, string, nil, pointer to those types, or a
// Getter that returns any of those types. Literal pointers are copied by value.
// Getters that return pointer types should not have their value changed, unless
// atomically, when in a Reporter, except within a PreReportCallback, for
// goroutine safety.
func (r *Reporter) NewCumulative(metric string, val interface{}, dims map[string]string) *DataPoint {
	dp, _ := NewCumulative(metric, val, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	r.datapoints.Add(dp)
	return dp
}

// NewGauge returns a new DataPoint object with type GAUGE. val can be any type
// of int, float, string, nil, pointer to those types, or a Getter that returns
// any of those types. Literal pointers are copied by value. Getters that return
// pointer types should not have their value changed, unless atomically, when in
// a Reporter, except within a PreReportCallback, for goroutine safety.
func (r *Reporter) NewGauge(metric string, val interface{}, dims map[string]string) *DataPoint {
	dp, _ := NewGauge(metric, val, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	r.datapoints.Add(dp)
	return dp
}

// NewCounter returns a new DataPoint object with type COUNTER. val can be any
// type of int, float, string, nil, pointer to those types, or a Getter that
// returns any of those types. Literal pointers are copied by value. Getters
// that return pointer types should not have their value changed, unless
// atomically, when in a Reporter, except within a PreReportCallback, for
// goroutine safety.
func (r *Reporter) NewCounter(metric string, val interface{}, dims map[string]string) *DataPoint {
	dp, _ := NewCounter(metric, val, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	r.datapoints.Add(dp)
	return dp
}

// NewInt32 creates a new DataPoint object with type COUNTER whose value is
// bound to an Int32. All methods on Int32 are goroutine safe and it may also be
// modified in atomic operations as an int32.
func (r *Reporter) NewInt32(metric string, dims map[string]string) (*Int32, *DataPoint) {
	ret := Int32(0)
	dp, _ := NewCounter(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewCumulativeInt32 creates a new DataPoint object with type
// CUMULATIVE_COUNTER whose value is bound to an Int32. All methods on Int32 are
// goroutine safe and it may also be modified in atomic operations as an int32.
func (r *Reporter) NewCumulativeInt32(metric string, dims map[string]string) (*Int32, *DataPoint) {
	ret := Int32(0)
	dp, _ := NewCumulative(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewInt64 creates a new DataPoint object with type COUNTER whose value is
// bound to an Int64. All methods on Int64 are goroutine safe and it may also be
// modified in atomic operations as an int64.
func (r *Reporter) NewInt64(metric string, dims map[string]string) (*Int64, *DataPoint) {
	ret := Int64(0)
	dp, _ := NewCounter(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewCumulativeInt64 creates a new DataPoint object with type
// CUMULATIVE_COUNTER whose value is bound to an Int64. All methods on Int64 are
// goroutine safe and it may also be modified in atomic operations as an int64.
func (r *Reporter) NewCumulativeInt64(metric string, dims map[string]string) (*Int64, *DataPoint) {
	ret := Int64(0)
	dp, _ := NewCumulative(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewUint32 creates a new DataPoint object with type COUNTER whose value is
// bound to an Uint32. All methods on Uint32 are goroutine safe and it may also
// be modified in atomic operations as an uint32.
func (r *Reporter) NewUint32(metric string, dims map[string]string) (*Uint32, *DataPoint) {
	ret := Uint32(0)
	dp, _ := NewCounter(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewCumulativeUint32 creates a new DataPoint object with type
// CUMULATIVE_COUNTER whose value is bound to an Uint32. All methods on Uint32
// are goroutine safe and it may also be modified in atomic operations as an
// uint32.
func (r *Reporter) NewCumulativeUint32(metric string, dims map[string]string) (*Uint32, *DataPoint) {
	ret := Uint32(0)
	dp, _ := NewCumulative(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewUint64 creates a new DataPoint object with type COUNTER whose value is
// bound to an Uint64. All methods on Uint64 are goroutine safe and it may also be
// modified in atomic operations as an uint64.
func (r *Reporter) NewUint64(metric string, dims map[string]string) (*Uint64, *DataPoint) {
	ret := Uint64(0)
	dp, _ := NewCounter(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// NewCumulativeUint64 creates a new DataPoint object with type
// CUMULATIVE_COUNTER whose value is bound to an Uint64. All methods on Uint64 are
// goroutine safe and it may also be modified in atomic operations as an uint64.
func (r *Reporter) NewCumulativeUint64(metric string, dims map[string]string) (*Uint64, *DataPoint) {
	ret := Uint64(0)
	dp, _ := NewCumulative(metric, &ret, sfxproto.Dimensions(r.defaultDimensions).Append(dims))
	if dp == nil {
		return nil, nil
	}
	r.datapoints.Add(dp)
	return &ret, dp
}

// AddDataPoint provides a way to manually add DataPoint(s) to be tracked by the
// Reporter
func (r *Reporter) AddDataPoint(vals ...*DataPoint) {
	r.datapoints.Add(vals...)
}

// AddDataPoints provides a way to manually add DataPoint to be tracked by the
// Reporter
func (r *Reporter) AddDataPoints(dps *DataPoints) {
	r.datapoints.Append(dps)
}

// RemoveDataPoint takes DataPoint(s)out of the set being tracked by the
// Reporter
func (r *Reporter) RemoveDataPoint(dps ...*DataPoint) {
	r.datapoints.Remove(dps...)
}

// RemoveDataPoints takes DataPoints out of the set being tracked by the
// Reporter
func (r *Reporter) RemoveDataPoints(dps *DataPoints) {
	r.datapoints.RemoveDataPoints(dps)
}

// RemoveBucket takes Bucket(s) out of the set being tracked by the Reporter
func (r *Reporter) RemoveBucket(bs ...*Bucket) {
	r.lock()
	defer r.unlock()

	for _, b := range bs {
		delete(r.buckets, b)
	}
}

// AddPreReportCallback adds a function that is called before Report().  This is useful for refetching
// things like runtime.Memstats() so they are only fetched once per report() call. If a DataPoint
func (r *Reporter) AddPreReportCallback(f func()) {
	r.lock()
	defer r.unlock()
	r.preReportCallbacks = append(r.preReportCallbacks, f)
}

// AddDataPointsCallback adds a callback that itself will generate datapoints to report
func (r *Reporter) AddDataPointsCallback(f DataPointCallback) {
	r.lock()
	defer r.unlock()
	r.datapointCallbacks = append(r.datapointCallbacks, f)
}

// Report sends all tracked DataPoints to SignalFX. PreReportCallbacks will be
// run before building the dataset to send. DataPoint callbacks will be executed
// and added to the dataset, but do not become tracked by the Reporter.
func (r *Reporter) Report(ctx context.Context) (*DataPoints, error) {
	if ctx == nil {
		ctx = context.Background()
	} else if ctx.Err() != nil {
		return nil, ctx.Err()
	}

	r.lock()
	defer r.unlock()

	for _, f := range r.preReportCallbacks {
		f()
	}

	ret := r.datapoints.Clone()

	for _, f := range r.datapointCallbacks {
		ret.Append(f(r.defaultDimensions))
	}

	for b := range r.buckets {
		ret.Append(b.DataPoints(r.defaultDimensions))
	}

	var (
		counters           []*DataPoint
		cumulativeCounters []*DataPoint
	)
	ret = ret.filter(func(dp *DataPoint) bool {
		if err := dp.update(); err != nil {
			return false
		}

		switch *dp.pdp.MetricType {
		case sfxproto.MetricType_COUNTER:
			if dp.pdp.Value.IntValue != nil && *dp.pdp.Value.IntValue != 0 {
				counters = append(counters, dp)
				dp.SetTime(time.Now())
				return true
			}
		case sfxproto.MetricType_CUMULATIVE_COUNTER:
			if !dp.pdp.Equal(dp.previous) {
				cumulativeCounters = append(cumulativeCounters, dp)
				dp.SetTime(time.Now())
				return true
			}
		default:
			dp.SetTime(time.Now())
			return true
		}
		return false
	})
	pdps := ret.ProtoDataPoints()

	// append all of the one-shots
	for _, pdp := range r.oneShots {
		pdps.Add(pdp)
	}

	if err := r.client.Submit(ctx, pdps); err != nil {
		return nil, err
	}

	// set submitted counters to zero
	for _, counter := range counters {
		// TODO: what should be done if this fails?
		counter.Set(0)
	}
	// remember submitted cumulative counter values
	for _, counter := range cumulativeCounters {
		counter.previous = counter.pdp
	}

	// and clear the one-shots
	r.oneShots = nil

	return ret, nil
}

func (r *Reporter) Inc(metric string, dimensions map[string]string, delta int64) error {
	r.lock()
	defer r.unlock()

	var protoDims []*sfxproto.Dimension
	for k, v := range r.defaultDimensions {
		// have to copy the values, since these are stored as
		// pointers…
		var dk, dv string
		dk = k
		dv = v
		protoDims = append(protoDims, &sfxproto.Dimension{Key: &dk, Value: &dv})
	}
	for k, v := range dimensions {
		var dk, dv string
		dk = k
		dv = v
		protoDims = append(protoDims, &sfxproto.Dimension{Key: &dk, Value: &dv})
	}
	timestamp := time.Now().UnixNano() / 1000000
	metricType := sfxproto.MetricType_COUNTER
	dp := &sfxproto.DataPoint{
		Metric:     &metric,
		Timestamp:  &timestamp,
		MetricType: &metricType,
		Dimensions: protoDims,
		Value:      &sfxproto.Datum{IntValue: &delta},
	}
	r.oneShots = append(r.oneShots, dp)
	return nil
}

// RunInBackground starts a goroutine which calls Reporter.Report on
// the specified interval.  It returns a function which may be used to
// cancel the backgrounding.
func (r *Reporter) RunInBackground(interval time.Duration) (cancel func()) {
	done := make(chan struct{})
	go func() {
		ticker := time.NewTicker(interval)
		for {
			select {
			case <-ticker.C:
				_, err := r.Report(context.Background())
				if err != nil {
					log.Error(err)
				}
			case <-done:
				return
			}
		}
	}()
	return func() {
		done <- struct{}{}
	}
}
<|endoftext|>


<commit_before>// Copyright 2022 Google LLC
//
// 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
//
//     https://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 ezcx

import (
	"bytes"
	"context"
	"io"
	"net/http"

	"github.com/google/uuid"
	cx "google.golang.org/genproto/googleapis/cloud/dialogflow/cx/v3"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/types/known/structpb"
)

type WebhookRequest struct {
	cx.WebhookRequest
	// 2022-10-08: Replaced context.Context with func () context.Context
	ctx func() context.Context
}

func NewWebhookRequest() *WebhookRequest {
	return new(WebhookRequest)
}

// WebhookRequest Initializations

// Initialize the PageInfo field
func (req *WebhookRequest) initPageInfo() {
	if req.PageInfo == nil {
		req.PageInfo = new(cx.PageInfo)
	}
	if req.PageInfo.FormInfo == nil {
		req.PageInfo.FormInfo = new(cx.PageInfo_FormInfo)
	}
	if req.PageInfo.FormInfo.ParameterInfo == nil {
		req.PageInfo.FormInfo.ParameterInfo = make([]*cx.PageInfo_FormInfo_ParameterInfo, 0)
	}
}

// Initialize the SessionInfo field
func (req *WebhookRequest) initSessionInfo() {
	if req.SessionInfo == nil {
		req.SessionInfo = new(cx.SessionInfo)
	}
}

// Initialize the Payload field
func (req *WebhookRequest) initPayload() {
	if req.Payload == nil {
		req.Payload = new(structpb.Struct)
	}
	if req.Payload.Fields == nil {
		req.Payload.Fields = make(map[string]*structpb.Value)
	}
}

func (req *WebhookRequest) Context() context.Context {
	return req.ctx()
}

// Sets (overrides) the PageInfo.ParameterInfos to match the provided map m
func (req *WebhookRequest) setPageFormParameters(m map[string]any) error {
	params := make([]*cx.PageInfo_FormInfo_ParameterInfo, 0)
	for k, v := range m {
		var formParameter cx.PageInfo_FormInfo_ParameterInfo
		pv, err := anyToProto(v)
		if err != nil {
			return err
		}
		formParameter.DisplayName = k
		formParameter.Value = pv
		formParameter.State = cx.PageInfo_FormInfo_ParameterInfo_FILLED
		params = append(params, &formParameter)
	}
	req.PageInfo.FormInfo.ParameterInfo = params
	return nil
}

// Sets (overrides) the SessionInfo.Parameters to match the provided map m
func (req *WebhookRequest) setSessionParameters(m map[string]any) error {
	pm, err := anyToProtoMap(m)
	if err != nil {
		return err
	}
	req.SessionInfo.Parameters = pm
	return nil
}

// Sets (overrides the SessionInfo.Parameters) to match the provided map m
func (req *WebhookRequest) setPayload(m map[string]any) error {
	pm, err := anyToProtoMap(m)
	if err != nil {
		return err
	}
	req.Payload.Fields = pm
	return nil
}

func WebhookRequestFromReader(rd io.Reader) (*WebhookRequest, error) {
	var req WebhookRequest
	b, err := io.ReadAll(rd)
	if err != nil {
		return nil, err
	}
	err = protojson.Unmarshal(b, &req)
	if err != nil {
		return nil, err
	}
	return &req, nil
}

// yaquino@2022-10-07: Refactored to flow http.Request's context to the
// WebhookRequest instance.
func WebhookRequestFromRequest(r *http.Request) (*WebhookRequest, error) {
	req, err := WebhookRequestFromReader(r.Body)
	if err != nil {
		return nil, err
	}
	return req, nil
}

func (req *WebhookRequest) ReadReader(rd io.Reader) error {
	b, err := io.ReadAll(rd)
	if err != nil {
		return err
	}
	err = protojson.Unmarshal(b, req)
	if err != nil {
		return err
	}
	return nil
}

func (req *WebhookRequest) ReadRequest(r *http.Request) error {
	return req.ReadReader(r.Body)
}

func (req *WebhookRequest) WriteRequest(w io.Writer) error {
	m := protojson.MarshalOptions{Indent: "\t"}
	b, err := m.Marshal(req)
	if err != nil {
		return err
	}
	r := bytes.NewReader(b)
	_, err = io.Copy(w, r)
	if err != nil {
		return err
	}
	return nil
}

func (req *WebhookRequest) InitializeResponse() *WebhookResponse {
	return req.initializeResponse()
}

func (req *WebhookRequest) initializeResponse() *WebhookResponse {
	resp := NewWebhookResponse()
	return req.copySession(resp)
}

func (req *WebhookRequest) copySession(res *WebhookResponse) *WebhookResponse {
	if res.SessionInfo == nil {
		res.SessionInfo = new(cx.SessionInfo)
	}
	res.SessionInfo.Session = req.SessionInfo.Session
	return res
}

func (req *WebhookRequest) CopyPageInfo(res *WebhookResponse) {
	if req.PageInfo != nil {
		res.PageInfo = req.PageInfo
	}
}

func (req *WebhookRequest) CopySessionInfo(res *WebhookResponse) *WebhookResponse {
	if req.SessionInfo != nil {
		res.SessionInfo = req.SessionInfo
	}
	return res
}

func (req *WebhookRequest) CopyPayload(res *WebhookResponse) *WebhookResponse {
	if req.Payload != nil {
		res.Payload = req.Payload
	}
	return res
}

func (req *WebhookRequest) GetPageFormParameters() map[string]any {
	params := make(map[string]any)

	// Just in case - I don't think we can iterate over a nil map.
	if req.PageInfo == nil {
		return nil
	}
	if req.PageInfo.FormInfo == nil {
		return nil
	}
	if req.PageInfo.FormInfo.ParameterInfo == nil {
		return nil
	}

	for _, paramInfo := range req.PageInfo.FormInfo.ParameterInfo {
		params[paramInfo.DisplayName] = protoToAny(paramInfo.Value)
	}

	return params
}

func (req *WebhookRequest) GetSessionParameters() map[string]any {
	if req.SessionInfo == nil {
		return nil
	}
	if req.SessionInfo.Parameters == nil {
		return nil
	}
	return protoToAnyMap(req.SessionInfo.Parameters)
}

func (req *WebhookRequest) GetSessionParameter(key string) (any, bool) {
	// Check if SessionInfo Parameters is nil.
	if req.SessionInfo == nil {
		return nil, false
	}
	if req.SessionInfo.Parameters == nil {
		return nil, false
	}
	pv, ok := req.SessionInfo.Parameters[key]
	return protoToAny(pv), ok
}

func (req *WebhookRequest) GetPayload() map[string]any {
	if req.Payload == nil {
		return nil
	}
	if req.Payload.Fields == nil {
		return nil
	}
	return protoToAnyMap(req.Payload.Fields)
}

func (req *WebhookRequest) GetPayloadParameter(key string) (any, bool) {
	// Just in case - I don't think we can iterate over a nil map.

	if req.Payload == nil {
		return nil, false
	}
	if req.Payload.Fields == nil {
		return nil, false
	}

	pv, ok := req.Payload.Fields[key]
	return protoToAny(pv), ok
}

// Testing
func NewTestingWebhookRequest(session, payload, pageform map[string]any) (*WebhookRequest, error) {
	return NewWebhookRequest().initTestingWebhookRequest(session, payload, pageform)
}

func (req *WebhookRequest) initTestingWebhookRequest(session, payload, pageform map[string]any) (*WebhookRequest, error) {
	// Provided for testing, normally http.Request.Context is flowed down.
	req.ctx = context.Background

	// All incoming WebhookRequests should have a session.
	req.initSessionInfo()
	req.SessionInfo.Session = uuid.New().String()

	// if session parameters are provided...
	if session != nil {
		err := req.setSessionParameters(session)
		if err != nil {
			return nil, err
		}
	}

	// if payload parameters are provided...
	if payload != nil {
		req.initPayload()
		err := req.setPayload(payload)
		if err != nil {
			return nil, err
		}
	}

	// if pageForm parameters are provided...
	if pageform != nil {
		req.initPageInfo()
		err := req.setPageFormParameters(pageform)
		if err != nil {
			return nil, err
		}
	}
	return req, nil
}

// yaquino: 2022-10-08Review this...!
func (req *WebhookRequest) TestCxHandler(out io.Writer, h HandlerFunc) (*WebhookResponse, error) {
	if req.ctx == nil {
		req.ctx = context.Background
	}
	res := req.initializeResponse()
	err := h(res, req)
	if err != nil {
		return nil, err
	}
	err = res.WriteResponse(out)
	if err != nil {
		return nil, err
	}
	return res, nil
}
<commit_msg>added a WriteRequest method<commit_after>// Copyright 2022 Google LLC
//
// 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
//
//     https://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 ezcx

import (
	"bytes"
	"context"
	"io"
	"net/http"

	"github.com/google/uuid"
	cx "google.golang.org/genproto/googleapis/cloud/dialogflow/cx/v3"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/types/known/structpb"
)

type WebhookRequest struct {
	cx.WebhookRequest
	// 2022-10-08: Replaced context.Context with func () context.Context
	ctx func() context.Context
}

func NewWebhookRequest() *WebhookRequest {
	return new(WebhookRequest)
}

// WebhookRequest Initializations

// Initialize the PageInfo field
func (req *WebhookRequest) initPageInfo() {
	if req.PageInfo == nil {
		req.PageInfo = new(cx.PageInfo)
	}
	if req.PageInfo.FormInfo == nil {
		req.PageInfo.FormInfo = new(cx.PageInfo_FormInfo)
	}
	if req.PageInfo.FormInfo.ParameterInfo == nil {
		req.PageInfo.FormInfo.ParameterInfo = make([]*cx.PageInfo_FormInfo_ParameterInfo, 0)
	}
}

// Initialize the SessionInfo field
func (req *WebhookRequest) initSessionInfo() {
	if req.SessionInfo == nil {
		req.SessionInfo = new(cx.SessionInfo)
	}
}

// Initialize the Payload field
func (req *WebhookRequest) initPayload() {
	if req.Payload == nil {
		req.Payload = new(structpb.Struct)
	}
	if req.Payload.Fields == nil {
		req.Payload.Fields = make(map[string]*structpb.Value)
	}
}

func (req *WebhookRequest) Context() context.Context {
	return req.ctx()
}

// Sets (overrides) the PageInfo.ParameterInfos to match the provided map m
func (req *WebhookRequest) setPageFormParameters(m map[string]any) error {
	params := make([]*cx.PageInfo_FormInfo_ParameterInfo, 0)
	for k, v := range m {
		var formParameter cx.PageInfo_FormInfo_ParameterInfo
		pv, err := anyToProto(v)
		if err != nil {
			return err
		}
		formParameter.DisplayName = k
		formParameter.Value = pv
		formParameter.State = cx.PageInfo_FormInfo_ParameterInfo_FILLED
		params = append(params, &formParameter)
	}
	req.PageInfo.FormInfo.ParameterInfo = params
	return nil
}

// Sets (overrides) the SessionInfo.Parameters to match the provided map m
func (req *WebhookRequest) setSessionParameters(m map[string]any) error {
	pm, err := anyToProtoMap(m)
	if err != nil {
		return err
	}
	req.SessionInfo.Parameters = pm
	return nil
}

// Sets (overrides the SessionInfo.Parameters) to match the provided map m
func (req *WebhookRequest) setPayload(m map[string]any) error {
	pm, err := anyToProtoMap(m)
	if err != nil {
		return err
	}
	req.Payload.Fields = pm
	return nil
}

func WebhookRequestFromReader(rd io.Reader) (*WebhookRequest, error) {
	var req WebhookRequest
	b, err := io.ReadAll(rd)
	if err != nil {
		return nil, err
	}
	err = protojson.Unmarshal(b, &req)
	if err != nil {
		return nil, err
	}
	return &req, nil
}

// yaquino@2022-10-07: Refactored to flow http.Request's context to the
// WebhookRequest instance.
func WebhookRequestFromRequest(r *http.Request) (*WebhookRequest, error) {
	req, err := WebhookRequestFromReader(r.Body)
	if err != nil {
		return nil, err
	}
	return req, nil
}

func (req *WebhookRequest) ReadReader(rd io.Reader) error {
	b, err := io.ReadAll(rd)
	if err != nil {
		return err
	}
	err = protojson.Unmarshal(b, req)
	if err != nil {
		return err
	}
	return nil
}

func (req *WebhookRequest) ReadRequest(r *http.Request) error {
	return req.ReadReader(r.Body)
}

// Is this the right format?
func (req *WebhookRequest) WriteRequest(w io.Writer) error {
	m := protojson.MarshalOptions{Indent: "\t"}
	b, err := m.Marshal(req)
	if err != nil {
		return err
	}
	r := bytes.NewReader(b)
	_, err = io.Copy(w, r)
	if err != nil {
		return err
	}
	return nil
}

func (req *WebhookRequest) InitializeResponse() *WebhookResponse {
	return req.initializeResponse()
}

func (req *WebhookRequest) initializeResponse() *WebhookResponse {
	resp := NewWebhookResponse()
	return req.copySession(resp)
}

func (req *WebhookRequest) copySession(res *WebhookResponse) *WebhookResponse {
	if res.SessionInfo == nil {
		res.SessionInfo = new(cx.SessionInfo)
	}
	res.SessionInfo.Session = req.SessionInfo.Session
	return res
}

func (req *WebhookRequest) CopyPageInfo(res *WebhookResponse) {
	if req.PageInfo != nil {
		res.PageInfo = req.PageInfo
	}
}

func (req *WebhookRequest) CopySessionInfo(res *WebhookResponse) *WebhookResponse {
	if req.SessionInfo != nil {
		res.SessionInfo = req.SessionInfo
	}
	return res
}

func (req *WebhookRequest) CopyPayload(res *WebhookResponse) *WebhookResponse {
	if req.Payload != nil {
		res.Payload = req.Payload
	}
	return res
}

func (req *WebhookRequest) GetPageFormParameters() map[string]any {
	params := make(map[string]any)

	// Just in case - I don't think we can iterate over a nil map.
	if req.PageInfo == nil {
		return nil
	}
	if req.PageInfo.FormInfo == nil {
		return nil
	}
	if req.PageInfo.FormInfo.ParameterInfo == nil {
		return nil
	}

	for _, paramInfo := range req.PageInfo.FormInfo.ParameterInfo {
		params[paramInfo.DisplayName] = protoToAny(paramInfo.Value)
	}

	return params
}

func (req *WebhookRequest) GetSessionParameters() map[string]any {
	if req.SessionInfo == nil {
		return nil
	}
	if req.SessionInfo.Parameters == nil {
		return nil
	}
	return protoToAnyMap(req.SessionInfo.Parameters)
}

func (req *WebhookRequest) GetSessionParameter(key string) (any, bool) {
	// Check if SessionInfo Parameters is nil.
	if req.SessionInfo == nil {
		return nil, false
	}
	if req.SessionInfo.Parameters == nil {
		return nil, false
	}
	pv, ok := req.SessionInfo.Parameters[key]
	return protoToAny(pv), ok
}

func (req *WebhookRequest) GetPayload() map[string]any {
	if req.Payload == nil {
		return nil
	}
	if req.Payload.Fields == nil {
		return nil
	}
	return protoToAnyMap(req.Payload.Fields)
}

func (req *WebhookRequest) GetPayloadParameter(key string) (any, bool) {
	// Just in case - I don't think we can iterate over a nil map.

	if req.Payload == nil {
		return nil, false
	}
	if req.Payload.Fields == nil {
		return nil, false
	}

	pv, ok := req.Payload.Fields[key]
	return protoToAny(pv), ok
}

// Testing
func NewTestingWebhookRequest(session, payload, pageform map[string]any) (*WebhookRequest, error) {
	return NewWebhookRequest().initTestingWebhookRequest(session, payload, pageform)
}

func (req *WebhookRequest) initTestingWebhookRequest(session, payload, pageform map[string]any) (*WebhookRequest, error) {
	// Provided for testing, normally http.Request.Context is flowed down.
	req.ctx = context.Background

	// All incoming WebhookRequests should have a session.
	req.initSessionInfo()
	req.SessionInfo.Session = uuid.New().String()

	// if session parameters are provided...
	if session != nil {
		err := req.setSessionParameters(session)
		if err != nil {
			return nil, err
		}
	}

	// if payload parameters are provided...
	if payload != nil {
		req.initPayload()
		err := req.setPayload(payload)
		if err != nil {
			return nil, err
		}
	}

	// if pageForm parameters are provided...
	if pageform != nil {
		req.initPageInfo()
		err := req.setPageFormParameters(pageform)
		if err != nil {
			return nil, err
		}
	}
	return req, nil
}

// yaquino: 2022-10-08Review this...!
func (req *WebhookRequest) TestCxHandler(out io.Writer, h HandlerFunc) (*WebhookResponse, error) {
	if req.ctx == nil {
		req.ctx = context.Background
	}
	res := req.initializeResponse()
	err := h(res, req)
	if err != nil {
		return nil, err
	}
	err = res.WriteResponse(out)
	if err != nil {
		return nil, err
	}
	return res, nil
}
<|endoftext|>


<commit_before>package nameserver

import (
	"fmt"
	"net"
	"testing"
	"time"

	"github.com/miekg/dns"
	. "github.com/weaveworks/weave/common"
	wt "github.com/weaveworks/weave/testing"
)

// Check if the names updates lead to cache invalidations
func TestServerCacheRefresh(t *testing.T) {
	const (
		containerID     = "somecontainer"
		testName1       = "first.weave.local."
		testName2       = "second.weave.local."
		refreshInterval = int(localTTL) / 3
	)

	InitDefaultLogging(testing.Verbose())
	Info.Println("TestServerCacheRefresh starting")
	clk := newMockedClock()

	Debug.Printf("Creating 2 zone databases")
	zoneConfig := ZoneConfig{
		RefreshInterval: refreshInterval,
		Clock:           clk,
	}
	dbs := newZoneDbsWithMockedMDns(2, zoneConfig)
	dbs.Start()
	defer dbs.Stop()

	Debug.Printf("Creating a cache")
	cache, err := NewCache(1024, clk)
	wt.AssertNoErr(t, err)

	Debug.Printf("Creating a real DNS server for the first zone database and with the cache")
	srv, err := NewDNSServer(DNSServerConfig{
		Zone:              dbs[0].Zone,
		Cache:             cache,
		Clock:             clk,
		ListenReadTimeout: testSocketTimeout,
		MaxAnswers:        4,
	})
	wt.AssertNoErr(t, err)
	go srv.Start()
	defer srv.Stop()
	time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start

	testPort, err := srv.GetPort()
	wt.AssertNoErr(t, err)
	wt.AssertNotEqualInt(t, testPort, 0, "listen port")

	Debug.Printf("Adding an IPs to %s", testName1)
	dbs[1].Zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.1"))

	// Zone database #2 at this point:
	//   first.weave.local  = 10.2.2.1

	// testName1 and testName2 should have no IPs yet
	qName1, _ := assertExchange(t, testName1, dns.TypeA, testPort, 1, 1, 0)
	qName2, _ := assertExchange(t, testName2, dns.TypeA, testPort, 0, 0, dns.RcodeNameError)
	assertInCache(t, cache, qName1, "after asking for first name")
	assertNotLocalInCache(t, cache, qName2, "after asking for second name")

	clk.Forward(refreshInterval / 2)

	Debug.Printf("Adding an IP to %s and to %s", testName1, testName2)
	dbs[1].Zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.2"))
	dbs[1].Zone.AddRecord(containerID, testName2, net.ParseIP("10.9.9.2"))

	// Zone database #2 at this point:
	//   first.weave.local    = 10.2.2.1 10.2.2.2
	//   second.weave.local   = 10.9.9.2
	clk.Forward(refreshInterval/2 + 2)

	// at this point, the testName1 should have been refreshed
	// so it should have two IPs, and the cache entry should have been invalidated
	assertNotInCache(t, cache, qName1, fmt.Sprintf("after asking for %s", testName1))
	assertNotLocalInCache(t, cache, qName2, fmt.Sprintf("after asking for %s", testName2))

	qName1, _ = assertExchange(t, testName1, dns.TypeA, testPort, 2, 2, 0)
	qName2, _ = assertExchange(t, testName2, dns.TypeA, testPort, 0, 0, dns.RcodeNameError)
	assertInCache(t, cache, qName1, fmt.Sprintf("after asking for %s", testName1))
	assertNotLocalInCache(t, cache, qName2, "after asking for a unknown name")

	// delete the IPs, and some time passes by so the cache should be purged...
	dbs[1].Zone.DeleteRecord(containerID, net.ParseIP("10.2.2.1"))
	dbs[1].Zone.DeleteRecord(containerID, net.ParseIP("10.2.2.2"))
	clk.Forward(refreshInterval + 1)

	qName1, _ = assertExchange(t, testName1, dns.TypeA, testPort, 0, 0, dns.RcodeNameError)
	qName2, _ = assertExchange(t, testName2, dns.TypeA, testPort, 0, 0, dns.RcodeNameError)
	assertNotLocalInCache(t, cache, qName1, "after asking for a unknown name")
	assertNotLocalInCache(t, cache, qName2, "after asking for a unknown name")

}
<commit_msg>Unit test for cache invalidations as a result of AddRecord()/DeleteRecord()/etc...<commit_after>package nameserver

import (
	"fmt"
	"net"
	"testing"
	"time"

	"github.com/miekg/dns"
	. "github.com/weaveworks/weave/common"
	wt "github.com/weaveworks/weave/testing"
)

// Check that AddRecord/DeleteRecord/... in the Zone database lead to cache invalidations
func TestServerDbCacheInvalidation(t *testing.T) {
	const (
		containerID = "somecontainer"
		testName1   = "first.weave.local."
		testName2   = "second.weave.local."
	)

	InitDefaultLogging(testing.Verbose())
	Info.Println("TestServerDbCacheInvalidation starting")

	clk := newMockedClock()

	Debug.Printf("Creating mocked mDNS client and server")
	mdnsServer1 := newMockedMDNSServerWithRecord(Record{testName1, net.ParseIP("10.2.2.9"), 0, 0, 0})
	mdnsCli1 := newMockedMDNSClient([]*mockedMDNSServer{mdnsServer1})

	Debug.Printf("Creating zone database with the mocked mDNS client and server")
	zoneConfig := ZoneConfig{
		MDNSServer: mdnsServer1,
		MDNSClient: mdnsCli1,
		Clock:      clk,
	}
	zone, err := NewZoneDb(zoneConfig)
	wt.AssertNoErr(t, err)
	err = zone.Start()
	wt.AssertNoErr(t, err)
	defer zone.Stop()

	Debug.Printf("Creating a cache")
	cache, err := NewCache(1024, clk)
	wt.AssertNoErr(t, err)

	fallbackHandler := func(w dns.ResponseWriter, req *dns.Msg) {
		m := new(dns.Msg)
		m.SetReply(req)
		if len(req.Question) == 1 {
			m.Rcode = dns.RcodeNameError
		}
		w.WriteMsg(m)
	}

	// Run another DNS server for fallback
	fallback, err := newMockedFallback(fallbackHandler, nil)
	wt.AssertNoErr(t, err)
	fallback.Start()
	defer fallback.Stop()

	Debug.Printf("Creating a real DNS server with a mocked cache")
	srv, err := NewDNSServer(DNSServerConfig{
		Zone:              zone,
		Cache:             cache,
		Clock:             clk,
		ListenReadTimeout: testSocketTimeout,
		UpstreamCfg:       fallback.CliConfig,
		MaxAnswers:        4,
	})
	wt.AssertNoErr(t, err)
	defer srv.Stop()
	go srv.Start()
	time.Sleep(100 * time.Millisecond) // Allow server goroutine to start

	testPort, err := srv.GetPort()
	wt.AssertNoErr(t, err)
	wt.AssertNotEqualInt(t, testPort, 0, "invalid listen port")

	Debug.Printf("Adding two IPs to %s", testName1)
	zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.1"))
	zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.2"))
	q, _ := assertExchange(t, testName1, dns.TypeA, testPort, 2, 2, 0)
	assertInCache(t, cache, q, fmt.Sprintf("after asking for %s", testName1))

	// Zone database at this point:
	//   first.weave.local  = 10.2.2.1 10.2.2.2

	zone.AddRecord(containerID, testName2, net.ParseIP("10.9.9.1"))
	assertInCache(t, cache, q, fmt.Sprintf("after adding a new IP for %s", testName2))

	// we should have an entry in the cache for this query
	// if we add another IP, that cache entry should be removed
	Debug.Printf("Adding a new IP to %s: the cache entry should be removed", testName1)
	zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.3"))
	assertNotInCache(t, cache, q, fmt.Sprintf("after adding a new IP for %s", testName1))

	// Zone database at this point:
	//   first.weave.local  = 10.2.2.1 10.2.2.2 10.2.2.3
	//   second.weave.local = 10.9.9.1

	Debug.Printf("Querying again (so a cache entry will be created)")
	q, _ = assertExchange(t, testName1, dns.TypeA, testPort, 3, 4, 0)
	assertInCache(t, cache, q, "after asking about the name")
	Debug.Printf("... and removing one of the IP addresses")
	zone.DeleteRecord(containerID, net.ParseIP("10.2.2.2"))
	assertNotInCache(t, cache, q, "after deleting IP for 10.2.2.2")

	// Zone database at this point:
	//   first.weave.local  = 10.2.2.1 10.2.2.3
	//   second.weave.local = 10.9.9.1

	// generate cache responses
	Debug.Printf("Querying for a raddr")
	qname, _ := assertExchange(t, testName1, dns.TypeA, testPort, 2, 2, 0)
	qptr, _ := assertExchange(t, "1.2.2.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0)
	qotherName, _ := assertExchange(t, testName2, dns.TypeA, testPort, 1, 1, 0)
	qotherPtr, _ := assertExchange(t, "1.9.9.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0)
	qwrongName, _ := assertExchange(t, "wrong.weave.local.", dns.TypeA, testPort, 0, 0, dns.RcodeNameError)
	assertInCache(t, cache, qname, "after asking for name")
	assertInCache(t, cache, qptr, "after asking for address")
	assertInCache(t, cache, qotherName, "after asking for second name")
	assertInCache(t, cache, qotherPtr, "after asking for second address")
	assertNotLocalInCache(t, cache, qwrongName, "after asking for a wrong name")

	// now we will check if a removal affects all the responses
	Debug.Printf("... and removing an IP should invalidate both the cached responses for name and raddr")
	zone.DeleteRecord(containerID, net.ParseIP("10.2.2.1"))
	assertNotInCache(t, cache, qptr, "after deleting record")
	assertNotInCache(t, cache, qname, "after deleting record")
	assertInCache(t, cache, qotherName, "after deleting record")

	// Zone database at this point:
	//   first.weave.local  = 10.2.2.3
	//   second.weave.local = 10.9.9.1

	// generate cache responses
	Debug.Printf("Querying for a raddr")
	qptr, _ = assertExchange(t, "3.2.2.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0)
	qname, _ = assertExchange(t, testName1, dns.TypeA, testPort, 1, 1, 0)
	qotherName, _ = assertExchange(t, testName2, dns.TypeA, testPort, 1, 1, 0)
	qotherPtr, _ = assertExchange(t, "1.9.9.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0)
	assertInCache(t, cache, qname, "after asking for name")
	assertInCache(t, cache, qptr, "after asking for PTR")
	assertInCache(t, cache, qotherName, "after asking for second name")
	assertInCache(t, cache, qotherPtr, "after asking for second address")

	// let's repeat this, but adding an IP
	Debug.Printf("... and adding a new IP should invalidate both the cached responses for the name")
	zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.7"))
	assertNotInCache(t, cache, qname, "after adding a new IP")
	assertInCache(t, cache, qotherName, "after adding a new IP")
	assertInCache(t, cache, qotherPtr, "after adding a new IP")

	// check that after some time, the cache entry is expired
	clk.Forward(int(localTTL) + 1)
	assertNotInCache(t, cache, qotherName, "after passing some time")
	assertNotInCache(t, cache, qwrongName, "after passing some time")

	// Zone database at this point:
	//   first.weave.local  = 10.2.2.3 10.2.2.7
	//   second.weave.local = 10.9.9.1

	zone.DeleteRecordsFor(containerID)
	assertNotInCache(t, cache, qotherName, "after removing container")
	assertNotInCache(t, cache, qotherPtr, "after removing container")
}
// Check if the names updates lead to cache invalidations
func TestServerCacheRefresh(t *testing.T) {
	const (
		containerID     = "somecontainer"
		testName1       = "first.weave.local."
		testName2       = "second.weave.local."
		refreshInterval = int(localTTL) / 3
	)

	InitDefaultLogging(testing.Verbose())
	Info.Println("TestServerCacheRefresh starting")
	clk := newMockedClock()

	Debug.Printf("Creating 2 zone databases")
	zoneConfig := ZoneConfig{
		RefreshInterval: refreshInterval,
		Clock:           clk,
	}
	dbs := newZoneDbsWithMockedMDns(2, zoneConfig)
	dbs.Start()
	defer dbs.Stop()

	Debug.Printf("Creating a cache")
	cache, err := NewCache(1024, clk)
	wt.AssertNoErr(t, err)

	Debug.Printf("Creating a real DNS server for the first zone database and with the cache")
	srv, err := NewDNSServer(DNSServerConfig{
		Zone:              dbs[0].Zone,
		Cache:             cache,
		Clock:             clk,
		ListenReadTimeout: testSocketTimeout,
		MaxAnswers:        4,
	})
	wt.AssertNoErr(t, err)
	go srv.Start()
	defer srv.Stop()
	time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start

	testPort, err := srv.GetPort()
	wt.AssertNoErr(t, err)
	wt.AssertNotEqualInt(t, testPort, 0, "listen port")

	Debug.Printf("Adding an IPs to %s", testName1)
	dbs[1].Zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.1"))

	// Zone database #2 at this point:
	//   first.weave.local  = 10.2.2.1

	// testName1 and testName2 should have no IPs yet
	qName1, _ := assertExchange(t, testName1, dns.TypeA, testPort, 1, 1, 0)
	qName2, _ := assertExchange(t, testName2, dns.TypeA, testPort, 0, 0, dns.RcodeNameError)
	assertInCache(t, cache, qName1, "after asking for first name")
	assertNotLocalInCache(t, cache, qName2, "after asking for second name")

	clk.Forward(refreshInterval / 2)

	Debug.Printf("Adding an IP to %s and to %s", testName1, testName2)
	dbs[1].Zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.2"))
	dbs[1].Zone.AddRecord(containerID, testName2, net.ParseIP("10.9.9.2"))

	// Zone database #2 at this point:
	//   first.weave.local    = 10.2.2.1 10.2.2.2
	//   second.weave.local   = 10.9.9.2
	clk.Forward(refreshInterval/2 + 2)

	// at this point, the testName1 should have been refreshed
	// so it should have two IPs, and the cache entry should have been invalidated
	assertNotInCache(t, cache, qName1, fmt.Sprintf("after asking for %s", testName1))
	assertNotLocalInCache(t, cache, qName2, fmt.Sprintf("after asking for %s", testName2))

	qName1, _ = assertExchange(t, testName1, dns.TypeA, testPort, 2, 2, 0)
	qName2, _ = assertExchange(t, testName2, dns.TypeA, testPort, 0, 0, dns.RcodeNameError)
	assertInCache(t, cache, qName1, fmt.Sprintf("after asking for %s", testName1))
	assertNotLocalInCache(t, cache, qName2, "after asking for a unknown name")

	// delete the IPs, and some time passes by so the cache should be purged...
	dbs[1].Zone.DeleteRecord(containerID, net.ParseIP("10.2.2.1"))
	dbs[1].Zone.DeleteRecord(containerID, net.ParseIP("10.2.2.2"))
	clk.Forward(refreshInterval + 1)

	qName1, _ = assertExchange(t, testName1, dns.TypeA, testPort, 0, 0, dns.RcodeNameError)
	qName2, _ = assertExchange(t, testName2, dns.TypeA, testPort, 0, 0, dns.RcodeNameError)
	assertNotLocalInCache(t, cache, qName1, "after asking for a unknown name")
	assertNotLocalInCache(t, cache, qName2, "after asking for a unknown name")

}
<|endoftext|>


<commit_before><commit_msg>Added program to create basic itinerary using goroutines<commit_after><|endoftext|>


<commit_before>package connection

import (
	"bytes"
	"errors"
	"os"
	// "strconv"
	"syscall"
	"time"
	"unsafe"
)

const initialBaudRate uint32 = syscall.B9600
const connectionTimeout time.Duration = time.Second * 10
const handshakeRequestChar byte = '.'
const handshakeAckChar byte = ','

type ConnectionState int

const (
	Inactive ConnectionState = iota
	Handshaking
	Active
)

func (s ConnectionState) String() string {
	switch s {
	case Inactive:
		return "Inactive"
	case Handshaking:
		return "Handshaking"
	case Active:
		return "Active"
	}
	return "Unknown"
}

func getVtime(duration time.Duration) uint8 {
	const (
		MINTIMEOUT = 1
		MAXTIMEOUT = 255
	)
	vtime := (duration.Nanoseconds() / 1e6 / 100)
	if vtime < MINTIMEOUT {
		vtime = MINTIMEOUT
	} else if vtime > MAXTIMEOUT {
		vtime = MAXTIMEOUT
	}
	return uint8(vtime)
}

type Serial struct {
	DeviceName string
	BaudRate   uint32
}

type Connection struct {
	serial *Serial
	file   *os.File
	State  ConnectionState
}

func (c *Connection) setBaudRate(rate uint32) error {
	// Create the term IO settings structure
	term := syscall.Termios{
		Iflag:  syscall.IGNPAR,
		Cflag:  syscall.CS8 | syscall.CREAD | syscall.CLOCAL | rate,
		Cc:     [32]uint8{syscall.VMIN: 0, syscall.VTIME: getVtime(connectionTimeout)},
		Ispeed: rate,
		Ospeed: rate,
	}

	// Make the IOCTL system call to configure the term
	if _, _, errno := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(c.file.Fd()),
		uintptr(syscall.TCSETS),
		uintptr(unsafe.Pointer(&term)),
	); errno != 0 {
		// TODO: include errno in this
		return errors.New("Encountered error doing IOCTL syscall")
	}

	if err := syscall.SetNonblock(int(c.file.Fd()), false); err != nil {
		return err
	}

	return nil
}

func (c *Connection) performHandshake() error {
	c.State = Handshaking

	// Initiate comms
	_, err := c.Write([]byte(" "))
	if err != nil {
		return err
	}

	// TODO: Look for handshake query byte and return response
	buf := make([]byte, 1)
	n, err := c.Read(buf)
	if err != nil {
		return err
	}

	if n > 0 && buf[0] == handshakeRequestChar { // TODO: do I care about the num bytes?
		_, err = c.Write([]byte{handshakeAckChar})
		if err != nil {
			return err
		}
	}

	return nil
}

func (c *Connection) Connect() error {
	// Open the file
	var err error
	c.file, err = os.OpenFile(c.serial.DeviceName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
	if err != nil {
		return err
	}

	// Create a connection using a safe baud rate
	err = c.setBaudRate(initialBaudRate)
	if err != nil {
		return err
	}

	// Perform handshake with EC
	err = c.performHandshake()
	if err != nil {
		return err
	}

	// Change baud rate to what was requested
	/*
	   err = c.setBaudRate(c.serial.BaudRate)
	   if err != nil {
	       return err
	   }
	*/

	c.State = Active

	return nil
}

func (c *Connection) Disconnect() (err error) {
	return c.file.Close()
}

func (c *Connection) Read(b []byte) (n int, err error) {
	return c.file.Read(b)
}

func (c *Connection) Write(b []byte) (n int, err error) {
	return c.file.Write(b)
}

func (c *Connection) String() string {
	var buf bytes.Buffer

	// Device name
	buf.WriteString("Device: ")
	buf.WriteString(c.serial.DeviceName)
	buf.WriteString("\n")

	// Baud rate
	buf.WriteString("Baud rate: ")
	buf.WriteString("TODO")
	// buf.WriteString(strconv.Itoa(int(c.serial.BaudRate))) // TODO: whoops
	buf.WriteString("\n")

	// State
	buf.WriteString("Connection state: ")
	buf.WriteString(c.State.String())
	buf.WriteString("\n")

	return buf.String()
}

func New(ser *Serial) (*Connection, error) {
	c := new(Connection)
	c.serial = ser
	return c, nil
}
<commit_msg>EVERYTHING IS GOING TO CHANGE!<commit_after>package connection

import (
	"bytes"
	"errors"
	"os"
	"strconv"
	"syscall"
	"time"
	"unsafe"
)

const initialBaudRate uint32 = syscall.B9600
const connectionTimeout time.Duration = time.Second * 10
const handshakeRequestChar byte = ','
const AckChar byte = ':'

type ConnectionState int

const (
	Inactive ConnectionState = iota
	Handshaking
	Active
)

func (s ConnectionState) String() string {
	switch s {
	case Inactive:
		return "Inactive"
	case Handshaking:
		return "Handshaking"
	case Active:
		return "Active"
	}
	return "Unknown"
}

func getVtime(duration time.Duration) uint8 {
	const (
		MINTIMEOUT = 1
		MAXTIMEOUT = 255
	)
	vtime := (duration.Nanoseconds() / 1e6 / 100)
	if vtime < MINTIMEOUT {
		vtime = MINTIMEOUT
	} else if vtime > MAXTIMEOUT {
		vtime = MAXTIMEOUT
	}
	return uint8(vtime)
}

type Serial struct {
	DeviceName string
	BaudRate   uint32
}

type Connection struct {
	serial *Serial
	file   *os.File
	State  ConnectionState
}

func (c *Connection) setBaudRate(rate uint32) error {
	// Create the term IO settings structure
	term := syscall.Termios{
		Iflag:  syscall.IGNPAR,
		Cflag:  syscall.CS8 | syscall.CREAD | syscall.CLOCAL | rate,
		Cc:     [32]uint8{syscall.VMIN: 0, syscall.VTIME: getVtime(connectionTimeout)},
		Ispeed: rate,
		Ospeed: rate,
	}

	// Make the IOCTL system call to configure the term
	if _, _, errno := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(c.file.Fd()),
		uintptr(syscall.TCSETS),
		uintptr(unsafe.Pointer(&term)),
	); errno != 0 {
		// TODO: include errno in this
		return errors.New("Encountered error doing IOCTL syscall")
	}

	if err := syscall.SetNonblock(int(c.file.Fd()), false); err != nil {
		return err
	}

	return nil
}

func (c *Connection) performHandshake() error {
	c.State = Handshaking

	// Initiate comms
	// _, err := c.Write([]byte(" "))
	// if err != nil {
	// 	return err
	// }

	// TODO: Look for handshake query byte and return response
	// buf := make([]byte, 1)
	// n, err := c.Read(buf)
	// if err != nil {
	// 	return err
	// }

	_, err := c.Write([]byte{handshakeRequestChar})
	// _, err = c.Write([]byte{handshakeAckChar, c.serial.BaudRate})
	if err != nil {
		return err
	}

	// Wait for AckChar
	// if buf[0] == handshakeRequestChar { // TODO: do I care about the num bytes?
	// }

	return nil
}

func (c *Connection) Connect() error {
	// Open the file
	var err error
	c.file, err = os.OpenFile(c.serial.DeviceName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
	if err != nil {
		return err
	}

	// Create a connection using a safe baud rate
	err = c.setBaudRate(initialBaudRate)
	if err != nil {
		return err
	}

	// Perform handshake with EC
	err = c.performHandshake()
	if err != nil {
		return err
	}

	// Change baud rate to what was requested
	/*
	   err = c.setBaudRate(c.serial.BaudRate)
	   if err != nil {
	       return err
	   }
	*/

	c.State = Active

	return nil
}

func (c *Connection) Disconnect() (err error) {
	return c.file.Close()
}

func (c *Connection) Read(b []byte) (n int, err error) {
	return c.file.Read(b)
}

func (c *Connection) Write(b []byte) (n int, err error) {
	return c.file.Write(b)
}

func (c *Connection) String() string {
	var buf bytes.Buffer

	// Device name
	buf.WriteString("Device: ")
	buf.WriteString(c.serial.DeviceName)
	buf.WriteString("\n")

	// Baud rate
	buf.WriteString("Baud rate: ")
	buf.WriteString("TODO")
	buf.WriteString(strconv.Itoa(int(c.serial.BaudRate))) // TODO: whoops
	buf.WriteString("\n")

	// State
	buf.WriteString("Connection state: ")
	buf.WriteString(c.State.String())
	buf.WriteString("\n")

	return buf.String()
}

func New(ser *Serial) (*Connection, error) {
	c := new(Connection)
	c.serial = ser
	return c, nil
}
<|endoftext|>


<commit_before>package cli

import (
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/99designs/aws-vault/prompt"
	"github.com/99designs/aws-vault/server"
	"github.com/99designs/aws-vault/vault"
	"github.com/99designs/keyring"
	"gopkg.in/alecthomas/kingpin.v2"
)

type ExecCommandInput struct {
	Profile      string
	Command      string
	Args         []string
	Keyring      keyring.Keyring
	Duration     time.Duration
	RoleDuration time.Duration
	MfaToken     string
	MfaPrompt    prompt.PromptFunc
	StartServer  bool
	Signals      chan os.Signal
	NoSession    bool
}

func ConfigureExecCommand(app *kingpin.Application) {
	input := ExecCommandInput{}

	cmd := app.Command("exec", "Executes a command with AWS credentials in the environment")
	cmd.Flag("no-session", "Use root credentials, no session created").
		Short('n').
		BoolVar(&input.NoSession)

	cmd.Flag("session-ttl", "Expiration time for aws session").
		Default("4h").
		OverrideDefaultFromEnvar("AWS_SESSION_TTL").
		Short('t').
		DurationVar(&input.Duration)

	cmd.Flag("assume-role-ttl", "Expiration time for aws assumed role").
		Default("15m").
		OverrideDefaultFromEnvar("AWS_ASSUME_ROLE_TTL").
		DurationVar(&input.RoleDuration)

	cmd.Flag("mfa-token", "The mfa token to use").
		Short('m').
		StringVar(&input.MfaToken)

	cmd.Flag("server", "Run the server in the background for credentials").
		Short('s').
		BoolVar(&input.StartServer)

	cmd.Arg("profile", "Name of the profile").
		Required().
		StringVar(&input.Profile)

	cmd.Arg("cmd", "Command to execute").
		Default(os.Getenv("SHELL")).
		StringVar(&input.Command)

	cmd.Arg("args", "Command arguments").
		StringsVar(&input.Args)

	cmd.Action(func(c *kingpin.ParseContext) error {
		input.Keyring = keyringImpl
		input.MfaPrompt = prompt.Method(GlobalFlags.PromptDriver)
		input.Signals = make(chan os.Signal)
		signal.Notify(input.Signals, os.Interrupt, os.Kill)
		ExecCommand(app, input)
		return nil
	})
}

func ExecCommand(app *kingpin.Application, input ExecCommandInput) {
	if os.Getenv("AWS_VAULT") != "" {
		app.Fatalf("aws-vault sessions should be nested with care, unset $AWS_VAULT to force")
		return
	}

	var setEnv = true

	if input.NoSession && input.StartServer {
		app.Fatalf("Can't start a credential server without a session")
		return
	}

	creds, err := vault.NewVaultCredentials(input.Keyring, input.Profile, vault.VaultOptions{
		SessionDuration:    input.Duration,
		AssumeRoleDuration: input.RoleDuration,
		MfaToken:           input.MfaToken,
		MfaPrompt:          input.MfaPrompt,
		NoSession:          input.NoSession,
		Config:             awsConfig,
	})
	if err != nil {
		app.Fatalf("%v", err)
	}

	val, err := creds.Get()
	if err != nil {
		app.Fatalf(awsConfig.FormatCredentialError(err, input.Profile))
	}

	if input.StartServer {
		if err := server.StartCredentialsServer(creds); err != nil {
			app.Fatalf("Failed to start credential server: %v", err)
		} else {
			setEnv = false
		}
	}

	env := environ(os.Environ())
	env.Set("AWS_VAULT", input.Profile)

	env.Unset("AWS_ACCESS_KEY_ID")
	env.Unset("AWS_SECRET_ACCESS_KEY")
	env.Unset("AWS_CREDENTIAL_FILE")
	env.Unset("AWS_DEFAULT_PROFILE")
	env.Unset("AWS_PROFILE")

	if profile, _ := awsConfig.Profile(input.Profile); profile.Region != "" {
		log.Printf("Setting subprocess env: AWS_DEFAULT_REGION=%s, AWS_REGION=%s", profile.Region, profile.Region)
		env.Set("AWS_DEFAULT_REGION", profile.Region)
		env.Set("AWS_REGION", profile.Region)
	}

	if setEnv {
		log.Println("Setting subprocess env: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY")
		env.Set("AWS_ACCESS_KEY_ID", val.AccessKeyID)
		env.Set("AWS_SECRET_ACCESS_KEY", val.SecretAccessKey)

		if val.SessionToken != "" {
			log.Println("Setting subprocess env: AWS_SESSION_TOKEN, AWS_SECURITY_TOKEN")
			env.Set("AWS_SESSION_TOKEN", val.SessionToken)
			env.Set("AWS_SECURITY_TOKEN", val.SessionToken)
		}
	}

	cmd := exec.Command(input.Command, input.Args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Start(); err != nil {
		app.Errorf("%v", err)
		return
	}
	// wait for the command to finish
	waitCh := make(chan error, 1)
	go func() {
		waitCh <- cmd.Wait()
		close(waitCh)
	}()

	for {
		select {
		case sig := <-input.Signals:
			if err = cmd.Process.Signal(sig); err != nil {
				app.Errorf("%v", err)
				break
			}
		case err := <-waitCh:
			var waitStatus syscall.WaitStatus
			if exitError, ok := err.(*exec.ExitError); ok {
				waitStatus = exitError.Sys().(syscall.WaitStatus)
				os.Exit(waitStatus.ExitStatus())
			}
			if err != nil {
				app.Errorf("%v", err)
				return
			}
			return
		}
	}
}

// environ is a slice of strings representing the environment, in the form "key=value".
type environ []string

// Unset an environment variable by key
func (e *environ) Unset(key string) {
	for i := range *e {
		if strings.HasPrefix((*e)[i], key+"=") {
			(*e)[i] = (*e)[len(*e)-1]
			*e = (*e)[:len(*e)-1]
			break
		}
	}
}

// Set adds an environment variable, replacing any existing ones of the same key
func (e *environ) Set(key, val string) {
	e.Unset(key)
	*e = append(*e, key+"="+val)
}
<commit_msg>Use app.Fatal instead of app.Error<commit_after>package cli

import (
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/99designs/aws-vault/prompt"
	"github.com/99designs/aws-vault/server"
	"github.com/99designs/aws-vault/vault"
	"github.com/99designs/keyring"
	"gopkg.in/alecthomas/kingpin.v2"
)

type ExecCommandInput struct {
	Profile      string
	Command      string
	Args         []string
	Keyring      keyring.Keyring
	Duration     time.Duration
	RoleDuration time.Duration
	MfaToken     string
	MfaPrompt    prompt.PromptFunc
	StartServer  bool
	Signals      chan os.Signal
	NoSession    bool
}

func ConfigureExecCommand(app *kingpin.Application) {
	input := ExecCommandInput{}

	cmd := app.Command("exec", "Executes a command with AWS credentials in the environment")
	cmd.Flag("no-session", "Use root credentials, no session created").
		Short('n').
		BoolVar(&input.NoSession)

	cmd.Flag("session-ttl", "Expiration time for aws session").
		Default("4h").
		OverrideDefaultFromEnvar("AWS_SESSION_TTL").
		Short('t').
		DurationVar(&input.Duration)

	cmd.Flag("assume-role-ttl", "Expiration time for aws assumed role").
		Default("15m").
		OverrideDefaultFromEnvar("AWS_ASSUME_ROLE_TTL").
		DurationVar(&input.RoleDuration)

	cmd.Flag("mfa-token", "The mfa token to use").
		Short('m').
		StringVar(&input.MfaToken)

	cmd.Flag("server", "Run the server in the background for credentials").
		Short('s').
		BoolVar(&input.StartServer)

	cmd.Arg("profile", "Name of the profile").
		Required().
		StringVar(&input.Profile)

	cmd.Arg("cmd", "Command to execute").
		Default(os.Getenv("SHELL")).
		StringVar(&input.Command)

	cmd.Arg("args", "Command arguments").
		StringsVar(&input.Args)

	cmd.Action(func(c *kingpin.ParseContext) error {
		input.Keyring = keyringImpl
		input.MfaPrompt = prompt.Method(GlobalFlags.PromptDriver)
		input.Signals = make(chan os.Signal)
		signal.Notify(input.Signals, os.Interrupt, os.Kill)
		ExecCommand(app, input)
		return nil
	})
}

func ExecCommand(app *kingpin.Application, input ExecCommandInput) {
	if os.Getenv("AWS_VAULT") != "" {
		app.Fatalf("aws-vault sessions should be nested with care, unset $AWS_VAULT to force")
		return
	}

	var setEnv = true

	if input.NoSession && input.StartServer {
		app.Fatalf("Can't start a credential server without a session")
		return
	}

	creds, err := vault.NewVaultCredentials(input.Keyring, input.Profile, vault.VaultOptions{
		SessionDuration:    input.Duration,
		AssumeRoleDuration: input.RoleDuration,
		MfaToken:           input.MfaToken,
		MfaPrompt:          input.MfaPrompt,
		NoSession:          input.NoSession,
		Config:             awsConfig,
	})
	if err != nil {
		app.Fatalf("%v", err)
	}

	val, err := creds.Get()
	if err != nil {
		app.Fatalf(awsConfig.FormatCredentialError(err, input.Profile))
	}

	if input.StartServer {
		if err := server.StartCredentialsServer(creds); err != nil {
			app.Fatalf("Failed to start credential server: %v", err)
		} else {
			setEnv = false
		}
	}

	env := environ(os.Environ())
	env.Set("AWS_VAULT", input.Profile)

	env.Unset("AWS_ACCESS_KEY_ID")
	env.Unset("AWS_SECRET_ACCESS_KEY")
	env.Unset("AWS_CREDENTIAL_FILE")
	env.Unset("AWS_DEFAULT_PROFILE")
	env.Unset("AWS_PROFILE")

	if profile, _ := awsConfig.Profile(input.Profile); profile.Region != "" {
		log.Printf("Setting subprocess env: AWS_DEFAULT_REGION=%s, AWS_REGION=%s", profile.Region, profile.Region)
		env.Set("AWS_DEFAULT_REGION", profile.Region)
		env.Set("AWS_REGION", profile.Region)
	}

	if setEnv {
		log.Println("Setting subprocess env: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY")
		env.Set("AWS_ACCESS_KEY_ID", val.AccessKeyID)
		env.Set("AWS_SECRET_ACCESS_KEY", val.SecretAccessKey)

		if val.SessionToken != "" {
			log.Println("Setting subprocess env: AWS_SESSION_TOKEN, AWS_SECURITY_TOKEN")
			env.Set("AWS_SESSION_TOKEN", val.SessionToken)
			env.Set("AWS_SECURITY_TOKEN", val.SessionToken)
		}
	}

	cmd := exec.Command(input.Command, input.Args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Start(); err != nil {
		app.Fatalf("%v", err)
	}
	// wait for the command to finish
	waitCh := make(chan error, 1)
	go func() {
		waitCh <- cmd.Wait()
		close(waitCh)
	}()

	for {
		select {
		case sig := <-input.Signals:
			if err = cmd.Process.Signal(sig); err != nil {
				app.Errorf("%v", err)
				break
			}
		case err := <-waitCh:
			var waitStatus syscall.WaitStatus
			if exitError, ok := err.(*exec.ExitError); ok {
				waitStatus = exitError.Sys().(syscall.WaitStatus)
				os.Exit(waitStatus.ExitStatus())
			}
			if err != nil {
				app.Fatalf("%v", err)
			}
			return
		}
	}
}

// environ is a slice of strings representing the environment, in the form "key=value".
type environ []string

// Unset an environment variable by key
func (e *environ) Unset(key string) {
	for i := range *e {
		if strings.HasPrefix((*e)[i], key+"=") {
			(*e)[i] = (*e)[len(*e)-1]
			*e = (*e)[:len(*e)-1]
			break
		}
	}
}

// Set adds an environment variable, replacing any existing ones of the same key
func (e *environ) Set(key, val string) {
	e.Unset(key)
	*e = append(*e, key+"="+val)
}
<|endoftext|>


<commit_before>// +build !windows

package main

import (
	"os"
	"os/exec"
	"syscall"
)

// CmdDaemon execs dockerd with the same flags
// TODO: add a deprecation warning?
func (p DaemonProxy) CmdDaemon(args ...string) error {
	// Use os.Args[1:] so that "global" args are passed to dockerd
	args = stripDaemonArg(os.Args[1:])

	// TODO: check dirname args[0] first
	binaryAbsPath, err := exec.LookPath(daemonBinary)
	if err != nil {
		return err
	}

	return syscall.Exec(
		binaryAbsPath,
		append([]string{daemonBinary}, args...),
		os.Environ())
}

// stripDaemonArg removes the `daemon` argument from the list
func stripDaemonArg(args []string) []string {
	for i, arg := range args {
		if arg == "daemon" {
			return append(args[:i], args[i+1:]...)
		}
	}
	return args
}
<commit_msg>When exec'ing dockerd, look for it in the same directory as the docker binary first, before checking path.<commit_after>// +build !windows

package main

import (
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
)

// CmdDaemon execs dockerd with the same flags
func (p DaemonProxy) CmdDaemon(args ...string) error {
	// Use os.Args[1:] so that "global" args are passed to dockerd
	args = stripDaemonArg(os.Args[1:])

	binaryPath, err := findDaemonBinary()
	if err != nil {
		return err
	}

	return syscall.Exec(
		binaryPath,
		append([]string{daemonBinary}, args...),
		os.Environ())
}

// findDaemonBinary looks for the path to the dockerd binary starting with
// the directory of the current executable (if one exists) and followed by $PATH
func findDaemonBinary() (string, error) {
	execDirname := filepath.Dir(os.Args[0])
	if execDirname != "" {
		binaryPath := filepath.Join(execDirname, daemonBinary)
		if _, err := os.Stat(binaryPath); err == nil {
			return binaryPath, nil
		}
	}

	return exec.LookPath(daemonBinary)
}

// stripDaemonArg removes the `daemon` argument from the list
func stripDaemonArg(args []string) []string {
	for i, arg := range args {
		if arg == "daemon" {
			return append(args[:i], args[i+1:]...)
		}
	}
	return args
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/longears/sortpixels/myimage"
	"github.com/longears/sortpixels/utils"
	"os"
	"runtime"
	"runtime/debug"
	"strings"
)

// How many times to repeat the vertical & horizontal sort step
const N_SORTS = 6

// How many threads to run in parallel
var THREADPOOL_SIZE int

func init() {
	THREADPOOL_SIZE = runtime.NumCPU()
	runtime.GOMAXPROCS(runtime.NumCPU())
}

//================================================================================
// IMAGE MODIFICATION ALGORITHMS

// Read the image from the path inFn,
// sort the pixels,
// and save the result to the path outFn.
// Return an error if the input file is not decodable as an image.
func sortPixels(inFn, outFn string) {
	myImage := myimage.MakeMyImageFromPath(inFn)

	fmt.Println("  sorting")
	for ii := 0; ii < N_SORTS; ii++ {
		myImage.SortColumns("v", THREADPOOL_SIZE)
		myImage.SortRows("h2", THREADPOOL_SIZE)
	}
	myImage.SortColumns("v", THREADPOOL_SIZE)

	myImage.SaveAs(outFn)
}

func congregatePixels(inFn, outFn string) {
	myImage := myimage.MakeMyImageFromPath(inFn)

	fmt.Println("  resizing")
	myImage = myImage.ThumbnailByPixels(512)

	fmt.Println("  scrambling")
	myImage.SortColumns("random", THREADPOOL_SIZE)
	myImage.SortRows("random", THREADPOOL_SIZE)

	fmt.Println("  congregating (large scale)")
	myImage.Congregate(0, 55) // maxMoveDist, percent of image visited per iteration
	fmt.Println("  congregating (small scale)")
	myImage.Congregate(8, 75) // maxMoveDist, percent of image visited per iteration

	myImage.SaveAs(outFn)
}

//================================================================================
// MAIN

func main() {
	fmt.Println("------------------------------------------------------------\\")
	defer fmt.Println("------------------------------------------------------------/")

	// handle command line
	if len(os.Args) < 2 {
		fmt.Println()
		fmt.Println("  usage:  sort  input.png  [input2.jpg input3.png ...]")
		fmt.Println()
		fmt.Println("  Sort the pixels in the image(s) and save to the ./output/ folder.")
		fmt.Println()
		return
	}

	// make output directory if needed
	if !utils.PathExists("output") {
		err := os.Mkdir("output", 0755)
		if err != nil {
			panic(fmt.Sprintf("%v", err))
		}
	}

	// open, sort, and save input images
	for inputII := 1; inputII < len(os.Args); inputII++ {
		inFn := os.Args[inputII]

		// build outFn from inFn
		outFn := inFn
		if strings.Contains(outFn, ".") {
			dotii := strings.LastIndex(outFn, ".")
			outFn = outFn[:dotii] + ".sorted.png"
		} else {
			outFn += ".sorted"
		}
		if strings.Contains(outFn, "/") {
			outFn = outFn[strings.LastIndex(outFn, "/")+1:]
		}
		outFn = "output/" + outFn

		// read, sort, and save (unless file has already been sorted)
		fmt.Println(inFn)
		if utils.PathExists(outFn) {
			fmt.Println("  SKIPPING: already exists")
		} else {
			//sortPixels(inFn, outFn)
			congregatePixels(inFn, outFn)
		}

		// attempt to give memory back to the OS
		debug.FreeOSMemory()

		fmt.Println()
	}
}
<commit_msg>sort and congregate<commit_after>package main

import (
	"fmt"
	"github.com/longears/sortpixels/myimage"
	"github.com/longears/sortpixels/utils"
	"os"
	"runtime"
	"runtime/debug"
	"strings"
)

// How many times to repeat the vertical & horizontal sort step
const N_SORTS = 6

// How many threads to run in parallel
var THREADPOOL_SIZE int

func init() {
	THREADPOOL_SIZE = runtime.NumCPU()
	runtime.GOMAXPROCS(runtime.NumCPU())
}

//================================================================================
// IMAGE MODIFICATION ALGORITHMS

// Read the image from the path inFn,
// sort the pixels,
// and save the result to the path outFn.
// Return an error if the input file is not decodable as an image.
func sortPixels(inFn, outFn string) {
	myImage := myimage.MakeMyImageFromPath(inFn)

	fmt.Println("  sorting")
	for ii := 0; ii < N_SORTS; ii++ {
		myImage.SortColumns("v", THREADPOOL_SIZE)
		myImage.SortRows("h2", THREADPOOL_SIZE)
	}
	myImage.SortColumns("v", THREADPOOL_SIZE)

	myImage.SaveAs(outFn)
}

func congregatePixels(inFn, outFn string) {
	myImage := myimage.MakeMyImageFromPath(inFn)

	fmt.Println("  resizing")
	myImage = myImage.ThumbnailByPixels(512)

	fmt.Println("  scrambling")
	myImage.SortColumns("random", THREADPOOL_SIZE)
	myImage.SortRows("random", THREADPOOL_SIZE)

	fmt.Println("  congregating (large scale)")
	myImage.Congregate(0, 55) // maxMoveDist, percent of image visited per iteration
	fmt.Println("  congregating (small scale)")
	myImage.Congregate(8, 75) // maxMoveDist, percent of image visited per iteration

	myImage.SaveAs(outFn)
}

//================================================================================
// MAIN

func main() {
	fmt.Println("------------------------------------------------------------\\")
	defer fmt.Println("------------------------------------------------------------/")

	// handle command line
	if len(os.Args) < 2 {
		fmt.Println()
		fmt.Println("  usage:  sort  input.png  [input2.jpg input3.png ...]")
		fmt.Println()
		fmt.Println("  Sort the pixels in the image(s) and save to the ./output/ folder.")
		fmt.Println()
		return
	}

	// make output directory if needed
	if !utils.PathExists("output") {
		err := os.Mkdir("output", 0755)
		if err != nil {
			panic(fmt.Sprintf("%v", err))
		}
	}

	// open, sort, and save input images
	for inputII := 1; inputII < len(os.Args); inputII++ {
		inFn := os.Args[inputII]

		// build outFn from inFn
		outFn := inFn
		if strings.Contains(outFn, ".") {
			dotii := strings.LastIndex(outFn, ".")
			outFn = outFn[:dotii] + ".congregated.png"
		} else {
			outFn += ".congregated"
		}
		if strings.Contains(outFn, "/") {
			outFn = outFn[strings.LastIndex(outFn, "/")+1:]
		}
		outFn = "output/" + outFn

		// read, sort, and save (unless file has already been sorted)
		fmt.Println(inFn)
		if utils.PathExists(outFn) {
			fmt.Println("  SKIPPING: already exists")
		} else {
			congregatePixels(inFn, outFn)
		}

		// attempt to give memory back to the OS
		debug.FreeOSMemory()

		// build outFn from inFn
		outFn = inFn
		if strings.Contains(outFn, ".") {
			dotii := strings.LastIndex(outFn, ".")
			outFn = outFn[:dotii] + ".sorted.png"
		} else {
			outFn += ".sorted"
		}
		if strings.Contains(outFn, "/") {
			outFn = outFn[strings.LastIndex(outFn, "/")+1:]
		}
		outFn = "output/" + outFn

		// read, sort, and save (unless file has already been sorted)
		fmt.Println(inFn)
		if utils.PathExists(outFn) {
			fmt.Println("  SKIPPING: already exists")
		} else {
			sortPixels(inFn, outFn)
		}

		// attempt to give memory back to the OS
		debug.FreeOSMemory()

		fmt.Println()
	}
}
<|endoftext|>


<commit_before>package main

func dictAdditions() map[string]string {
	dict := parseWikipediaFormat(additions)
	dict = expandCase(dict)
	return dict
}

// arent
var additions = `
bianry->binary
transcation->transaction
tood->todo
decscription->description
keynode->keynote
entreperure->entrepreneur
entreprenuer->entrepreneur
nuetral->neutral
laready->already
varaible->variable
datbase->database
requrement->requirement
brocoli->broccoli
brocolli->broccoli
dependancies->dependencies
emtpy->empty
fandation->foundation
environemnt->environment
verious->various
respository->repository
respositories->repositories
gloabl->global
fragement->fragment
upsteam->upstream
specifing->specifying
overriden->overridden
accesss->access
adderss->address
dashbaord->dashboard
auhtenticate->authenticate
retunred->returned
langauge->language
specifing->specifying
heirachy->hierarchy
authenticor->authenticator
availabale->available
positve->positive
satifies->satisfies
capialized->capitalized
versoin->version
obvioulsy->obviously
fundemental->fundamental
crytopgraphic->cryptographic
appication->application
accending->ascending
consisent->consistent
percision->precision
determinsitic->deterministic
elasped->elapsed
udpated->updated
undescore->underscore
represenation->representation
registery->registry
redundent->redundant
puncutation->punctuation
genrates->generates
finallizes->finalizes
expoch->epoch
equivalant->equivalent
determinsitic->deterministic
normallized->normalized
elasped->elapsed
machiens->machines
demonstates->demonstrates
collumn->column
verical->vertical
refernece->reference
opartor->operator
elimiate->eliminate
coalese->coalesce
extenion->extension
affliated->affiliated
hesistate->hesitate
arrary->array
hunman->human
currate->curate
retuns->returns
interfce->interface
alrorythm->algorithm
credentaisl->credentials
closeing->closing
Constructur->Constructor
Depdending->Depending
Disclamer->Disclaimer
Elimintates->Eliminates
Fowrards->Forwards
Instalation->Installation
Numerious->Numerous
Specifcation->Specification
Wheter->Whether
aforementioend->aforementioned
annonymouse->anonymous
approstraphe->apostrophe
apporach->approach
aribtrary->arbitrary
asychronous->asynchronous
avaiable->available
cahched->cached
calback->callback
careflly->carefully
commmand->command
compatibilty->compatibility
comptability->compatibility
conatins->contains
conditon->condition
configuraiton->configuration
consitency->consistency
contructed->constructed
contructor->constructor
december->December
declareation->declaration
decomposeion->decomposition
deliviered->delivered
depedencies->dependencies
depedency->dependency
deperecation->deprecation
descriminant->discriminant
diffucult->difficult
documenation->documentation
dyamically->dynamically
embeded->embedded
everwhere->everywhere
exising->existing
explicitely->explicitly
explicity->explicitly
expliots->exploits
exprimental->experimental
extactly->exactly
functionlity->functionality
functtion->function
idiosynchracies->idiosyncrasies
immidiate->immediate
implemention->implementation
implentation->implementation
implicitely->implicitly
implimenation->implementation
incldue->include
incorect->incorrect
incorectly->incorrectly
inferrence->inference
milisecond->millisecond
mimimum->minimum
minimium->minimum
misinterpretting->misinterpreting
momment->moment
muliple->multiple
mulitple->multiple
nubmers->numbers
officiallly->officially
otherhand->other hand
optinally->optimally
ouput->output
outputed->outputted
pacakge->package
packge->package
paramter->parameter
paramters->parameters
paricular->particular
performaces->performances
permisson->permission
precedeed->preceded
precendence->precedence
programattically->programmatically
programmar->programmer
programms->programs
properites->properties
propeties->properties
protototype->prototype
publsih->publish
quuery->query
requried->required
retrived->retrieved
ridiculus->ridiculous
seperator->separator
similarlly->similarly
simplfy->simplify
singals->signals
spanish->Spanish
specifcally->specifically
specifed->specified
specifiy->specify
straitforward->straightforward
subsequant->subsequent
successfuly->successfully
supportied->supported
supression->suppression
synchornously->synchronously
syncronously->synchronously
tutorual->tutorial
unintuive->unintuitive
writting->writing
Euclidian->Euclidean
`
<commit_msg>corretly->correctly<commit_after>package main

func dictAdditions() map[string]string {
	dict := parseWikipediaFormat(additions)
	dict = expandCase(dict)
	return dict
}

// arent
var additions = `
corretly->correctly
bianry->binary
transcation->transaction
tood->todo
decscription->description
keynode->keynote
entreperure->entrepreneur
entreprenuer->entrepreneur
nuetral->neutral
laready->already
varaible->variable
datbase->database
requrement->requirement
brocoli->broccoli
brocolli->broccoli
dependancies->dependencies
emtpy->empty
fandation->foundation
environemnt->environment
verious->various
respository->repository
respositories->repositories
gloabl->global
fragement->fragment
upsteam->upstream
specifing->specifying
overriden->overridden
accesss->access
adderss->address
dashbaord->dashboard
auhtenticate->authenticate
retunred->returned
langauge->language
specifing->specifying
heirachy->hierarchy
authenticor->authenticator
availabale->available
positve->positive
satifies->satisfies
capialized->capitalized
versoin->version
obvioulsy->obviously
fundemental->fundamental
crytopgraphic->cryptographic
appication->application
accending->ascending
consisent->consistent
percision->precision
determinsitic->deterministic
elasped->elapsed
udpated->updated
undescore->underscore
represenation->representation
registery->registry
redundent->redundant
puncutation->punctuation
genrates->generates
finallizes->finalizes
expoch->epoch
equivalant->equivalent
determinsitic->deterministic
normallized->normalized
elasped->elapsed
machiens->machines
demonstates->demonstrates
collumn->column
verical->vertical
refernece->reference
opartor->operator
elimiate->eliminate
coalese->coalesce
extenion->extension
affliated->affiliated
hesistate->hesitate
arrary->array
hunman->human
currate->curate
retuns->returns
interfce->interface
alrorythm->algorithm
credentaisl->credentials
closeing->closing
Constructur->Constructor
Depdending->Depending
Disclamer->Disclaimer
Elimintates->Eliminates
Fowrards->Forwards
Instalation->Installation
Numerious->Numerous
Specifcation->Specification
Wheter->Whether
aforementioend->aforementioned
annonymouse->anonymous
approstraphe->apostrophe
apporach->approach
aribtrary->arbitrary
asychronous->asynchronous
avaiable->available
cahched->cached
calback->callback
careflly->carefully
commmand->command
compatibilty->compatibility
comptability->compatibility
conatins->contains
conditon->condition
configuraiton->configuration
consitency->consistency
contructed->constructed
contructor->constructor
december->December
declareation->declaration
decomposeion->decomposition
deliviered->delivered
depedencies->dependencies
depedency->dependency
deperecation->deprecation
descriminant->discriminant
diffucult->difficult
documenation->documentation
dyamically->dynamically
embeded->embedded
everwhere->everywhere
exising->existing
explicitely->explicitly
explicity->explicitly
expliots->exploits
exprimental->experimental
extactly->exactly
functionlity->functionality
functtion->function
idiosynchracies->idiosyncrasies
immidiate->immediate
implemention->implementation
implentation->implementation
implicitely->implicitly
implimenation->implementation
incldue->include
incorect->incorrect
incorectly->incorrectly
inferrence->inference
milisecond->millisecond
mimimum->minimum
minimium->minimum
misinterpretting->misinterpreting
momment->moment
muliple->multiple
mulitple->multiple
nubmers->numbers
officiallly->officially
otherhand->other hand
optinally->optimally
ouput->output
outputed->outputted
pacakge->package
packge->package
paramter->parameter
paramters->parameters
paricular->particular
performaces->performances
permisson->permission
precedeed->preceded
precendence->precedence
programattically->programmatically
programmar->programmer
programms->programs
properites->properties
propeties->properties
protototype->prototype
publsih->publish
quuery->query
requried->required
retrived->retrieved
ridiculus->ridiculous
seperator->separator
similarlly->similarly
simplfy->simplify
singals->signals
spanish->Spanish
specifcally->specifically
specifed->specified
specifiy->specify
straitforward->straightforward
subsequant->subsequent
successfuly->successfully
supportied->supported
supression->suppression
synchornously->synchronously
syncronously->synchronously
tutorual->tutorial
unintuive->unintuitive
writting->writing
Euclidian->Euclidean
`
<|endoftext|>


<commit_before>// +build appengine

package main

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"sort"
	"sync"

	"golang.org/x/net/context"

	"github.com/hsluo/slack-bot"

	"google.golang.org/appengine"
	l "google.golang.org/appengine/log"
	"google.golang.org/appengine/urlfetch"
)

type VoteResult map[string]StringSet

func (vr VoteResult) hasVoted(user string) bool {
	for _, v := range vr {
		if v.contains(user) {
			return true
		}
	}
	return false
}

func (vr VoteResult) String() string {
	options := make([]string, 0, len(vr))
	for k, _ := range vr {
		options = append(options, k)
	}
	sort.Strings(options)
	var buf bytes.Buffer
	buf.WriteString("Result:\n")
	for i := range options {
		buf.WriteString(fmt.Sprintf("%s: %v\n", options[i], vr[options[i]].toSlice()))
	}
	return buf.String()
}

var (
	votes      = newStringSet()
	voteResult = VoteResult{}
	m          sync.Mutex
)

func vote(rw http.ResponseWriter, req *http.Request) {
	var (
		c         = appengine.NewContext(req)
		channelId = req.PostFormValue("channel_id")
		text      = req.PostFormValue("text")
		userId    = req.PostFormValue("user_id")
	)
	m.Lock()
	if text == "start" {
		if startVote(channelId) {
			err := annouce(c, channelId, fmt.Sprintf("<@%s> just starts a vote!", userId))
			if err != nil {
				fmt.Fprintln(rw, err)
			} else {
				fmt.Fprintln(rw, "vote starts now")
			}
		} else {
			fmt.Fprintln(rw, "we're voting")
		}
	} else if text == "done" {
		annouce(c, channelId, voteResult.String())
		fmt.Fprintln(rw, "vote ends")
		delete(votes.set, channelId)
	} else if votes.contains(channelId) {
		userName := req.PostFormValue("user_name")
		if voters, ok := voteResult[text]; ok {
			if !voteResult.hasVoted(userName) {
				voters.add(userName)
			}
		} else {
			voters = newStringSet()
			voters.add(userName)
			voteResult[text] = voters
		}
		fmt.Fprintln(rw, voteResult)
	} else {
		fmt.Fprintln(rw, "Not voting")
	}
	m.Unlock()
}

func startVote(channelId string) bool {
	if votes.contains(channelId) {
		return false
	} else {
		votes.add(channelId)
		return true
	}
}

func annouce(c context.Context, channelId, text string) error {
	client := urlfetch.Client(c)
	err := bot.WithClient(client).ChatPostMessage(url.Values{
		"channel": {channelId},
		"text":    {text},
	})
	return err
}

// count active users in channel with channels.info then users.getPresence
// very slow due to network
func activeUsersInChannel(c context.Context, channelId string) (users []string, err error) {
	bot := bot.WithClient(urlfetch.Client(c))
	members, err := bot.ChannelsInfo(channelId)
	l.Infof(c, "check %v", members)
	active := make(chan string, len(members))
	var wg sync.WaitGroup
	for i := range members {
		wg.Add(1)
		go func(user string, active chan string, wg *sync.WaitGroup) {
			defer wg.Done()
			l.Infof(c, "begin "+user)
			if p, err := bot.UsersGetPresence(user); err != nil {
				l.Errorf(c, "%s", err)
				return
			} else if p == "active" {
				active <- user
			}
			l.Infof(c, "done "+user)
		}(members[i], active, &wg)
	}
	wg.Wait()
	l.Infof(c, "done wait")
	close(active)
	users = make([]string, len(members))
	for user := range active {
		users = append(users, user)
	}
	return
}

func init() {
	log.Println("vote init")
	http.HandleFunc("/cmds/vote",
		slack.ValidateCommand(http.HandlerFunc(vote), credentials.Commands))
}
<commit_msg>add log for vote calls<commit_after>// +build appengine

package main

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"sort"
	"sync"

	"golang.org/x/net/context"

	"github.com/hsluo/slack-bot"

	"google.golang.org/appengine"
	l "google.golang.org/appengine/log"
	"google.golang.org/appengine/urlfetch"
)

type VoteResult map[string]StringSet

func (vr VoteResult) hasVoted(user string) bool {
	for _, v := range vr {
		if v.contains(user) {
			return true
		}
	}
	return false
}

func (vr VoteResult) String() string {
	options := make([]string, 0, len(vr))
	for k, _ := range vr {
		options = append(options, k)
	}
	sort.Strings(options)
	var buf bytes.Buffer
	buf.WriteString("Result:\n")
	for i := range options {
		buf.WriteString(fmt.Sprintf("%s: %v\n", options[i], vr[options[i]].toSlice()))
	}
	return buf.String()
}

var (
	votes      = newStringSet()
	voteResult = VoteResult{}
	m          sync.Mutex
)

func vote(rw http.ResponseWriter, req *http.Request) {
	var (
		c         = appengine.NewContext(req)
		channelId = req.PostFormValue("channel_id")
		text      = req.PostFormValue("text")
		userId    = req.PostFormValue("user_id")
	)
	l.Infof(c, "%v", req.PostForm)
	m.Lock()
	if text == "start" {
		if startVote(channelId) {
			err := annouce(c, channelId, fmt.Sprintf("<@%s> just starts a vote!", userId))
			if err != nil {
				fmt.Fprintln(rw, err)
			} else {
				fmt.Fprintln(rw, "vote starts now")
			}
		} else {
			fmt.Fprintln(rw, "we're voting")
		}
	} else if text == "done" {
		annouce(c, channelId, voteResult.String())
		fmt.Fprintln(rw, "vote ends")
		delete(votes.set, channelId)
	} else if votes.contains(channelId) {
		userName := req.PostFormValue("user_name")
		if voters, ok := voteResult[text]; ok {
			if !voteResult.hasVoted(userName) {
				voters.add(userName)
			}
		} else {
			voters = newStringSet()
			voters.add(userName)
			voteResult[text] = voters
		}
		fmt.Fprintln(rw, voteResult)
	} else {
		fmt.Fprintln(rw, "Not voting")
	}
	m.Unlock()
}

func startVote(channelId string) bool {
	if votes.contains(channelId) {
		return false
	} else {
		votes.add(channelId)
		return true
	}
}

func annouce(c context.Context, channelId, text string) error {
	client := urlfetch.Client(c)
	err := bot.WithClient(client).ChatPostMessage(url.Values{
		"channel": {channelId},
		"text":    {text},
	})
	return err
}

// count active users in channel with channels.info then users.getPresence
// very slow due to network
func activeUsersInChannel(c context.Context, channelId string) (users []string, err error) {
	bot := bot.WithClient(urlfetch.Client(c))
	members, err := bot.ChannelsInfo(channelId)
	l.Infof(c, "check %v", members)
	active := make(chan string, len(members))
	var wg sync.WaitGroup
	for i := range members {
		wg.Add(1)
		go func(user string, active chan string, wg *sync.WaitGroup) {
			defer wg.Done()
			l.Infof(c, "begin "+user)
			if p, err := bot.UsersGetPresence(user); err != nil {
				l.Errorf(c, "%s", err)
				return
			} else if p == "active" {
				active <- user
			}
			l.Infof(c, "done "+user)
		}(members[i], active, &wg)
	}
	wg.Wait()
	l.Infof(c, "done wait")
	close(active)
	users = make([]string, len(members))
	for user := range active {
		users = append(users, user)
	}
	return
}

func init() {
	log.Println("vote init")
	http.HandleFunc("/cmds/vote",
		slack.ValidateCommand(http.HandlerFunc(vote), credentials.Commands))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/draaglom/xctools/xcassets"
)

func main() {
	var target = flag.String("target", "all", "The icon sizes to generate: options are 'iphone', 'ipad', 'mac', 'ios', 'all'")
	flag.Parse()
	dir, err := os.Getwd()
	if err != nil {
		fmt.Println("Can't get working dir:", err)
		os.Exit(-1)
	}
	source := flag.Arg(0)
	if len(source) == 0 {
		source = "source.png"
	}
	if source[:2] == "./" {
		source = strings.Replace(source, "./", dir, 1)
	}
	dest := flag.Arg(1)
	if len(dest) == 0 {
		dest = "./"
	}
	if dest[:2] == "./" {
		dest = strings.Replace(dest, "./", dir, 1)
	}
	var formats []xcassets.Image
	switch {
	case *target == "all":
		formats = xcassets.All
	case *target == "ios":
		formats = xcassets.IOS
	case *target == "mac":
		formats = xcassets.Mac
	case *target == "ipad":
		formats = xcassets.Ipad
	case *target == "iphone":
		formats = xcassets.Iphone
	default:
		fmt.Println("Not a valid icon set:", *target)
		os.Exit(-1)
	}
	err = xcassets.GenerateAppIconSet(source, dest, formats)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}
<commit_msg>Make the usage message more helpful.<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/draaglom/xctools/xcassets"
)

func main() {
	var target = flag.String("target", "all", "The icon sizes to generate: options are 'iphone', 'ipad', 'mac', 'ios', 'all'; defaults to 'all'.")
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "%s source.png /path/to/Images.xcassets\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()
	dir, err := os.Getwd()
	if err != nil {
		fmt.Println("Can't get working dir:", err)
		os.Exit(-1)
	}
	source := flag.Arg(0)
	if len(source) == 0 {
		source = "source.png"
	}
	if source[:2] == "./" {
		source = strings.Replace(source, "./", dir, 1)
	}
	dest := flag.Arg(1)
	if len(dest) == 0 {
		dest = "./"
	}
	if dest[:2] == "./" {
		dest = strings.Replace(dest, "./", dir, 1)
	}
	var formats []xcassets.Image
	switch {
	case *target == "all":
		formats = xcassets.All
	case *target == "ios":
		formats = xcassets.IOS
	case *target == "mac":
		formats = xcassets.Mac
	case *target == "ipad":
		formats = xcassets.Ipad
	case *target == "iphone":
		formats = xcassets.Iphone
	default:
		fmt.Println("Not a valid icon set:", *target)
		os.Exit(-1)
	}
	err = xcassets.GenerateAppIconSet(source, dest, formats)
	if err != nil {
		fmt.Println(err)
		flag.Usage()
		os.Exit(-1)
	}
}
<|endoftext|>