<commit_before>package manager

import (
	"context"
	"errors"
	"sync"
	"time"

	"github.com/cortexproject/cortex/pkg/querier/series"
	"github.com/cortexproject/cortex/pkg/util"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/rules"
	"github.com/prometheus/prometheus/storage"
)

const (
	AlertForStateMetricName = "ALERTS_FOR_STATE"
	statusSuccess           = "success"
	statusFailure           = "failure"
)

type NoopAppender struct{}

func (a NoopAppender) Appender(_ context.Context) storage.Appender             { return a }
func (a NoopAppender) Add(l labels.Labels, t int64, v float64) (uint64, error) { return 0, nil }
func (a NoopAppender) AddFast(ref uint64, t int64, v float64) error {
	return errors.New("unimplemented")
}
func (a NoopAppender) Commit() error   { return nil }
func (a NoopAppender) Rollback() error { return nil }

func ForStateMetric(base labels.Labels, alertName string) labels.Labels {
	b := labels.NewBuilder(base)
	b.Set(labels.MetricName, AlertForStateMetricName)
	b.Set(labels.AlertName, alertName)
	return b.Labels()
}

type Metrics struct {
	Evaluations *prometheus.CounterVec
	Samples     prometheus.Gauge       // in memory samples
	CacheHits   *prometheus.CounterVec // cache hits on in memory samples
}

func NewMetrics(r prometheus.Registerer) *Metrics {
	return &Metrics{
		Evaluations: promauto.With(r).NewCounterVec(prometheus.CounterOpts{
			Namespace: "loki",
			Name:      "ruler_memory_for_state_evaluations",
		}, []string{"status", "tenant"}),
		Samples: promauto.With(r).NewGauge(prometheus.GaugeOpts{
			Namespace: "loki",
			Name:      "ruler_memory_samples",
		}),
		CacheHits: promauto.With(r).NewCounterVec(prometheus.CounterOpts{
			Namespace: "loki",
			Name:      "ruler_memory_for_state_cache_hits",
		}, []string{"tenant"}),
	}
}

type RuleIter interface {
	AlertingRules() []*rules.AlertingRule
}

type MemStore struct {
	mtx       sync.Mutex
	userID    string
	queryFunc rules.QueryFunc
	metrics   *Metrics
	mgr       RuleIter
	logger    log.Logger
	rules     map[string]*RuleCache

	initiated       chan struct{}
	done            chan struct{}
	cleanupInterval time.Duration
}

func NewMemStore(userID string, queryFunc rules.QueryFunc, metrics *Metrics, cleanupInterval time.Duration, logger log.Logger) *MemStore {
	s := &MemStore{
		userID:          userID,
		metrics:         metrics,
		queryFunc:       queryFunc,
		logger:          logger,
		cleanupInterval: cleanupInterval,
		rules:           make(map[string]*RuleCache),

		initiated: make(chan struct{}), // blocks execution until Start() is called
		done:      make(chan struct{}),
	}
	return s

}

// Calling Start will set the RuleIter, unblock the MemStore, and start the run() function in a separate goroutine.
func (m *MemStore) Start(iter RuleIter) {
	m.mgr = iter
	close(m.initiated)
	go m.run()
}

func (m *MemStore) Stop() {
	select {
	case <-m.initiated:
	default:
		// If initiated is blocked, the MemStore has yet to start: easy no-op.
		return
	}

	// Need to nil all series & decrement gauges
	m.mtx.Lock()
	defer m.mtx.Unlock()

	select {
	// ensures Stop() is idempotent
	case <-m.done:
		return
	default:
		for ruleKey, cache := range m.rules {
			// Force cleanup of all samples older than time.Now (all of them).
			_ = cache.CleanupOldSamples(time.Now())
			delete(m.rules, ruleKey)
		}
		close(m.done)
	}
}

// run periodically cleans up old series/samples to ensure memory consumption doesn't grow unbounded.
func (m *MemStore) run() {
	<-m.initiated
	t := time.NewTicker(m.cleanupInterval)
	for {
		select {
		case <-m.done:
			t.Stop()
			return
		case <-t.C:
			m.mtx.Lock()
			holdDurs := make(map[string]time.Duration)
			for _, rule := range m.mgr.AlertingRules() {
				holdDurs[rule.Name()] = rule.HoldDuration()
			}

			for ruleKey, cache := range m.rules {
				dur, ok := holdDurs[ruleKey]

				// rule is no longer being tracked, remove it
				if !ok {
					_ = cache.CleanupOldSamples(time.Now())
					delete(m.rules, ruleKey)
					continue
				}

				// trim older samples out of tracking bounds, doubled to buffer.
				if empty := cache.CleanupOldSamples(time.Now().Add(-2 * dur)); empty {
					delete(m.rules, ruleKey)
				}

			}

			m.mtx.Unlock()
		}
	}
}

// implement storage.Queryable. It is only called with the desired ts as maxtime. Mint is
// parameterized via the outage tolerance, but since we're synthetically generating these,
// we only care about the desired time.
func (m *MemStore) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
	<-m.initiated
	return &memStoreQuerier{
		ts:       util.TimeFromMillis(maxt),
		MemStore: m,
		ctx:      ctx,
	}, nil

}

type memStoreQuerier struct {
	ts  time.Time
	ctx context.Context
	*MemStore
}

// Select implements storage.Querier but takes advantage of the fact that it's only called when restoring for state
// in order to lookup & cache previous rule evaluations. This results in a sort of synthetic metric store.
func (m *memStoreQuerier) Select(sortSeries bool, params *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet {
	b := labels.NewBuilder(nil)
	var ruleKey string
	for _, matcher := range matchers {
		// Since Select is only called to restore the for state of an alert, we can deduce two things:
		// 1) The matchers will all be in the form {foo="bar"}. This means we can construct the cache entry from these matchers.
		// 2) The alertname label value can be used to discover the rule this query is associated with.
		b.Set(matcher.Name, matcher.Value)
		if matcher.Name == labels.AlertName && matcher.Type == labels.MatchEqual {
			ruleKey = matcher.Value
		}
	}
	ls := b.Labels()
	if ruleKey == "" {
		level.Error(m.logger).Log("msg", "MemStoreQuerier.Select called in an unexpected fashion without alertname or ALERTS_FOR_STATE labels")
		return storage.NoopSeriesSet()
	}

	var rule *rules.AlertingRule

	// go fetch the rule via the alertname
	for _, x := range m.mgr.AlertingRules() {
		if x.Name() == ruleKey {
			rule = x
			break
		}
	}

	// should not happen
	if rule == nil {
		level.Error(m.logger).Log("msg", "failure trying to restore for state for untracked alerting rule", "name", ruleKey)
		return storage.NoopSeriesSet()
	}

	level.Debug(m.logger).Log("msg", "restoring for state via evaluation", "rule", ruleKey, "tenant", m.MemStore.userID)

	m.mtx.Lock()
	defer m.mtx.Unlock()
	cache, ok := m.rules[ruleKey]

	// no timestamp results are cached for this rule at all; Create it.
	if !ok {
		cache = NewRuleCache(m.metrics)
		m.rules[ruleKey] = cache
	}

	smpl, cached := cache.Get(m.ts, ls)
	if cached {
		m.metrics.CacheHits.WithLabelValues(m.userID).Inc()
		level.Debug(m.logger).Log("msg", "result cached", "rule", ruleKey, "tenant", m.MemStore.userID)
		// Assuming the result is cached but the desired series is not in the result, it wouldn't be considered active.
		if smpl == nil {
			return storage.NoopSeriesSet()
		}

		// If the labelset is cached we can consider it active. Return the for state sample active immediately.
		return series.NewConcreteSeriesSet(
			[]storage.Series{
				series.NewConcreteSeries(smpl.Metric, []model.SamplePair{
					{Timestamp: model.Time(util.TimeToMillis(m.ts)), Value: model.SampleValue(smpl.V)},
				}),
			},
		)
	}

	// see if alert condition had any inhabitants at ts-forDuration. We can assume it's still firing because
	// that's the only condition under which this is queried (via RestoreForState).
	vec, err := m.queryFunc(m.ctx, rule.Query().String(), m.ts.Add(-rule.HoldDuration()))
	if err != nil {
		level.Info(m.logger).Log("msg", "error querying for rule", "rule", ruleKey, "tenant", m.MemStore.userID, "err", err.Error())
		m.metrics.Evaluations.WithLabelValues(statusFailure, m.userID).Inc()
		return storage.NoopSeriesSet()
	}
	m.metrics.Evaluations.WithLabelValues(statusSuccess, m.userID).Inc()
	level.Debug(m.logger).Log("msg", "rule state successfully restored", "rule", ruleKey, "tenant", m.MemStore.userID, "len", len(vec))

	// translate the result into the ALERTS_FOR_STATE series for caching,
	// considered active & written at the timetamp requested
	forStateVec := make(promql.Vector, 0, len(vec))
	for _, smpl := range vec {

		ts := util.TimeToMillis(m.ts)

		forStateVec = append(forStateVec, promql.Sample{
			Metric: ForStateMetric(smpl.Metric, rule.Name()),
			Point: promql.Point{
				T: ts,
				V: float64(ts),
			},
		})

	}

	// cache the result of the evaluation at this timestamp
	cache.Set(m.ts, forStateVec)

	// Finally return the series if it exists.
	// Calling cache.Get leverages the existing code to return only single sample.
	smpl, ok = cache.Get(m.ts, ls)
	if !ok || smpl == nil {
		return storage.NoopSeriesSet()
	}
	// If the labelset is cached we can consider it active. Return the for state sample active immediately.
	return series.NewConcreteSeriesSet(
		[]storage.Series{
			series.NewConcreteSeries(smpl.Metric, []model.SamplePair{
				{Timestamp: model.Time(util.TimeToMillis(m.ts)), Value: model.SampleValue(smpl.V)},
			}),
		},
	)
}

// LabelValues returns all potential values for a label name.
func (*memStoreQuerier) LabelValues(name string) ([]string, storage.Warnings, error) {
	return nil, nil, errors.New("unimplemented")
}

// LabelNames returns all the unique label names present in the block in sorted order.
func (*memStoreQuerier) LabelNames() ([]string, storage.Warnings, error) {
	return nil, nil, errors.New("unimplemented")
}

// Close releases the resources of the Querier.
func (*memStoreQuerier) Close() error { return nil }

type RuleCache struct {
	mtx     sync.Mutex
	metrics *Metrics
	data    map[int64]map[uint64]promql.Sample
}

func NewRuleCache(metrics *Metrics) *RuleCache {
	return &RuleCache{
		data:    make(map[int64]map[uint64]promql.Sample),
		metrics: metrics,
	}
}

func (c *RuleCache) Set(ts time.Time, vec promql.Vector) {
	c.mtx.Lock()
	defer c.mtx.Unlock()
	tsMap, ok := c.data[ts.UnixNano()]
	if !ok {
		tsMap = make(map[uint64]promql.Sample)
		c.data[ts.UnixNano()] = tsMap
	}

	for _, sample := range vec {
		tsMap[sample.Metric.Hash()] = sample
	}
	c.metrics.Samples.Add(float64(len(vec)))
}

// Get returns ok if that timestamp's result is cached.
func (c *RuleCache) Get(ts time.Time, ls labels.Labels) (*promql.Sample, bool) {
	c.mtx.Lock()
	defer c.mtx.Unlock()

	match, ok := c.data[ts.UnixNano()]
	if !ok {
		return nil, false
	}

	smp, ok := match[ls.Hash()]
	if !ok {
		return nil, true
	}
	return &smp, true

}

// CleanupOldSamples removes samples that are outside of the rule's `For` duration.
func (c *RuleCache) CleanupOldSamples(olderThan time.Time) (empty bool) {
	c.mtx.Lock()
	defer c.mtx.Unlock()

	ns := olderThan.UnixNano()

	// This could be more efficient (logarithmic instead of linear)
	for ts, tsMap := range c.data {
		if ts < ns {
			delete(c.data, ts)
			c.metrics.Samples.Add(-float64(len(tsMap)))
		}

	}
	return len(c.data) == 0
}
<commit_msg>add _total suffix to ruler counter metrics (#2582)<commit_after>package manager

import (
	"context"
	"errors"
	"sync"
	"time"

	"github.com/cortexproject/cortex/pkg/querier/series"
	"github.com/cortexproject/cortex/pkg/util"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/rules"
	"github.com/prometheus/prometheus/storage"
)

const (
	AlertForStateMetricName = "ALERTS_FOR_STATE"
	statusSuccess           = "success"
	statusFailure           = "failure"
)

type NoopAppender struct{}

func (a NoopAppender) Appender(_ context.Context) storage.Appender             { return a }
func (a NoopAppender) Add(l labels.Labels, t int64, v float64) (uint64, error) { return 0, nil }
func (a NoopAppender) AddFast(ref uint64, t int64, v float64) error {
	return errors.New("unimplemented")
}
func (a NoopAppender) Commit() error   { return nil }
func (a NoopAppender) Rollback() error { return nil }

func ForStateMetric(base labels.Labels, alertName string) labels.Labels {
	b := labels.NewBuilder(base)
	b.Set(labels.MetricName, AlertForStateMetricName)
	b.Set(labels.AlertName, alertName)
	return b.Labels()
}

type Metrics struct {
	Evaluations *prometheus.CounterVec
	Samples     prometheus.Gauge       // in memory samples
	CacheHits   *prometheus.CounterVec // cache hits on in memory samples
}

func NewMetrics(r prometheus.Registerer) *Metrics {
	return &Metrics{
		Evaluations: promauto.With(r).NewCounterVec(prometheus.CounterOpts{
			Namespace: "loki",
			Name:      "ruler_memory_for_state_evaluations_total",
		}, []string{"status", "tenant"}),
		Samples: promauto.With(r).NewGauge(prometheus.GaugeOpts{
			Namespace: "loki",
			Name:      "ruler_memory_samples",
		}),
		CacheHits: promauto.With(r).NewCounterVec(prometheus.CounterOpts{
			Namespace: "loki",
			Name:      "ruler_memory_for_state_cache_hits_total",
		}, []string{"tenant"}),
	}
}

type RuleIter interface {
	AlertingRules() []*rules.AlertingRule
}

type MemStore struct {
	mtx       sync.Mutex
	userID    string
	queryFunc rules.QueryFunc
	metrics   *Metrics
	mgr       RuleIter
	logger    log.Logger
	rules     map[string]*RuleCache

	initiated       chan struct{}
	done            chan struct{}
	cleanupInterval time.Duration
}

func NewMemStore(userID string, queryFunc rules.QueryFunc, metrics *Metrics, cleanupInterval time.Duration, logger log.Logger) *MemStore {
	s := &MemStore{
		userID:          userID,
		metrics:         metrics,
		queryFunc:       queryFunc,
		logger:          logger,
		cleanupInterval: cleanupInterval,
		rules:           make(map[string]*RuleCache),

		initiated: make(chan struct{}), // blocks execution until Start() is called
		done:      make(chan struct{}),
	}
	return s

}

// Calling Start will set the RuleIter, unblock the MemStore, and start the run() function in a separate goroutine.
func (m *MemStore) Start(iter RuleIter) {
	m.mgr = iter
	close(m.initiated)
	go m.run()
}

func (m *MemStore) Stop() {
	select {
	case <-m.initiated:
	default:
		// If initiated is blocked, the MemStore has yet to start: easy no-op.
		return
	}

	// Need to nil all series & decrement gauges
	m.mtx.Lock()
	defer m.mtx.Unlock()

	select {
	// ensures Stop() is idempotent
	case <-m.done:
		return
	default:
		for ruleKey, cache := range m.rules {
			// Force cleanup of all samples older than time.Now (all of them).
			_ = cache.CleanupOldSamples(time.Now())
			delete(m.rules, ruleKey)
		}
		close(m.done)
	}
}

// run periodically cleans up old series/samples to ensure memory consumption doesn't grow unbounded.
func (m *MemStore) run() {
	<-m.initiated
	t := time.NewTicker(m.cleanupInterval)
	for {
		select {
		case <-m.done:
			t.Stop()
			return
		case <-t.C:
			m.mtx.Lock()
			holdDurs := make(map[string]time.Duration)
			for _, rule := range m.mgr.AlertingRules() {
				holdDurs[rule.Name()] = rule.HoldDuration()
			}

			for ruleKey, cache := range m.rules {
				dur, ok := holdDurs[ruleKey]

				// rule is no longer being tracked, remove it
				if !ok {
					_ = cache.CleanupOldSamples(time.Now())
					delete(m.rules, ruleKey)
					continue
				}

				// trim older samples out of tracking bounds, doubled to buffer.
				if empty := cache.CleanupOldSamples(time.Now().Add(-2 * dur)); empty {
					delete(m.rules, ruleKey)
				}

			}

			m.mtx.Unlock()
		}
	}
}

// implement storage.Queryable. It is only called with the desired ts as maxtime. Mint is
// parameterized via the outage tolerance, but since we're synthetically generating these,
// we only care about the desired time.
func (m *MemStore) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
	<-m.initiated
	return &memStoreQuerier{
		ts:       util.TimeFromMillis(maxt),
		MemStore: m,
		ctx:      ctx,
	}, nil

}

type memStoreQuerier struct {
	ts  time.Time
	ctx context.Context
	*MemStore
}

// Select implements storage.Querier but takes advantage of the fact that it's only called when restoring for state
// in order to lookup & cache previous rule evaluations. This results in a sort of synthetic metric store.
func (m *memStoreQuerier) Select(sortSeries bool, params *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet {
	b := labels.NewBuilder(nil)
	var ruleKey string
	for _, matcher := range matchers {
		// Since Select is only called to restore the for state of an alert, we can deduce two things:
		// 1) The matchers will all be in the form {foo="bar"}. This means we can construct the cache entry from these matchers.
		// 2) The alertname label value can be used to discover the rule this query is associated with.
		b.Set(matcher.Name, matcher.Value)
		if matcher.Name == labels.AlertName && matcher.Type == labels.MatchEqual {
			ruleKey = matcher.Value
		}
	}
	ls := b.Labels()
	if ruleKey == "" {
		level.Error(m.logger).Log("msg", "MemStoreQuerier.Select called in an unexpected fashion without alertname or ALERTS_FOR_STATE labels")
		return storage.NoopSeriesSet()
	}

	var rule *rules.AlertingRule

	// go fetch the rule via the alertname
	for _, x := range m.mgr.AlertingRules() {
		if x.Name() == ruleKey {
			rule = x
			break
		}
	}

	// should not happen
	if rule == nil {
		level.Error(m.logger).Log("msg", "failure trying to restore for state for untracked alerting rule", "name", ruleKey)
		return storage.NoopSeriesSet()
	}

	level.Debug(m.logger).Log("msg", "restoring for state via evaluation", "rule", ruleKey, "tenant", m.MemStore.userID)

	m.mtx.Lock()
	defer m.mtx.Unlock()
	cache, ok := m.rules[ruleKey]

	// no timestamp results are cached for this rule at all; Create it.
	if !ok {
		cache = NewRuleCache(m.metrics)
		m.rules[ruleKey] = cache
	}

	smpl, cached := cache.Get(m.ts, ls)
	if cached {
		m.metrics.CacheHits.WithLabelValues(m.userID).Inc()
		level.Debug(m.logger).Log("msg", "result cached", "rule", ruleKey, "tenant", m.MemStore.userID)
		// Assuming the result is cached but the desired series is not in the result, it wouldn't be considered active.
		if smpl == nil {
			return storage.NoopSeriesSet()
		}

		// If the labelset is cached we can consider it active. Return the for state sample active immediately.
		return series.NewConcreteSeriesSet(
			[]storage.Series{
				series.NewConcreteSeries(smpl.Metric, []model.SamplePair{
					{Timestamp: model.Time(util.TimeToMillis(m.ts)), Value: model.SampleValue(smpl.V)},
				}),
			},
		)
	}

	// see if alert condition had any inhabitants at ts-forDuration. We can assume it's still firing because
	// that's the only condition under which this is queried (via RestoreForState).
	vec, err := m.queryFunc(m.ctx, rule.Query().String(), m.ts.Add(-rule.HoldDuration()))
	if err != nil {
		level.Info(m.logger).Log("msg", "error querying for rule", "rule", ruleKey, "tenant", m.MemStore.userID, "err", err.Error())
		m.metrics.Evaluations.WithLabelValues(statusFailure, m.userID).Inc()
		return storage.NoopSeriesSet()
	}
	m.metrics.Evaluations.WithLabelValues(statusSuccess, m.userID).Inc()
	level.Debug(m.logger).Log("msg", "rule state successfully restored", "rule", ruleKey, "tenant", m.MemStore.userID, "len", len(vec))

	// translate the result into the ALERTS_FOR_STATE series for caching,
	// considered active & written at the timetamp requested
	forStateVec := make(promql.Vector, 0, len(vec))
	for _, smpl := range vec {

		ts := util.TimeToMillis(m.ts)

		forStateVec = append(forStateVec, promql.Sample{
			Metric: ForStateMetric(smpl.Metric, rule.Name()),
			Point: promql.Point{
				T: ts,
				V: float64(ts),
			},
		})

	}

	// cache the result of the evaluation at this timestamp
	cache.Set(m.ts, forStateVec)

	// Finally return the series if it exists.
	// Calling cache.Get leverages the existing code to return only single sample.
	smpl, ok = cache.Get(m.ts, ls)
	if !ok || smpl == nil {
		return storage.NoopSeriesSet()
	}
	// If the labelset is cached we can consider it active. Return the for state sample active immediately.
	return series.NewConcreteSeriesSet(
		[]storage.Series{
			series.NewConcreteSeries(smpl.Metric, []model.SamplePair{
				{Timestamp: model.Time(util.TimeToMillis(m.ts)), Value: model.SampleValue(smpl.V)},
			}),
		},
	)
}

// LabelValues returns all potential values for a label name.
func (*memStoreQuerier) LabelValues(name string) ([]string, storage.Warnings, error) {
	return nil, nil, errors.New("unimplemented")
}

// LabelNames returns all the unique label names present in the block in sorted order.
func (*memStoreQuerier) LabelNames() ([]string, storage.Warnings, error) {
	return nil, nil, errors.New("unimplemented")
}

// Close releases the resources of the Querier.
func (*memStoreQuerier) Close() error { return nil }

type RuleCache struct {
	mtx     sync.Mutex
	metrics *Metrics
	data    map[int64]map[uint64]promql.Sample
}

func NewRuleCache(metrics *Metrics) *RuleCache {
	return &RuleCache{
		data:    make(map[int64]map[uint64]promql.Sample),
		metrics: metrics,
	}
}

func (c *RuleCache) Set(ts time.Time, vec promql.Vector) {
	c.mtx.Lock()
	defer c.mtx.Unlock()
	tsMap, ok := c.data[ts.UnixNano()]
	if !ok {
		tsMap = make(map[uint64]promql.Sample)
		c.data[ts.UnixNano()] = tsMap
	}

	for _, sample := range vec {
		tsMap[sample.Metric.Hash()] = sample
	}
	c.metrics.Samples.Add(float64(len(vec)))
}

// Get returns ok if that timestamp's result is cached.
func (c *RuleCache) Get(ts time.Time, ls labels.Labels) (*promql.Sample, bool) {
	c.mtx.Lock()
	defer c.mtx.Unlock()

	match, ok := c.data[ts.UnixNano()]
	if !ok {
		return nil, false
	}

	smp, ok := match[ls.Hash()]
	if !ok {
		return nil, true
	}
	return &smp, true

}

// CleanupOldSamples removes samples that are outside of the rule's `For` duration.
func (c *RuleCache) CleanupOldSamples(olderThan time.Time) (empty bool) {
	c.mtx.Lock()
	defer c.mtx.Unlock()

	ns := olderThan.UnixNano()

	// This could be more efficient (logarithmic instead of linear)
	for ts, tsMap := range c.data {
		if ts < ns {
			delete(c.data, ts)
			c.metrics.Samples.Add(-float64(len(tsMap)))
		}

	}
	return len(c.data) == 0
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package newton

import (
	"github.com/szabba/md/src/vect"
)

type Integrator func(xs, vs []vect.Vector, a vect.Vector, dt float64)

// Performs a step of an Euler integration
func Euler(xs, vs []vect.Vector, a vect.Vector, dt float64) {

	v := vs[0].Plus(a.Scale(dt))
	x := xs[0].Plus(vs[0].Scale(dt))

	Shift(vs, v)
	Shift(xs, x)
}

// Performs a step of a Verlet integrator
//
// Note that v[0] will not be calculated until the next step
func Verlet(xs, vs []vect.Vector, a vect.Vector, dt float64) {

	xNext := xs[0].Scale(2).Minus(xs[1]).Plus(a.Scale(dt * dt))
	vs[0] = xNext.Minus(xs[1]).Scale(1 / (2 * dt))

	Shift(vs, vect.Zero)
	Shift(xs, xNext)
}

func Step(alg Integrator, bs []Body, f Force, dt float64) {

	as := make([]vect.Vector, len(bs))

	for i, _ := range bs {

		as[i] = f.Accel(bs, i)
	}

	for i, body := range bs {

		alg(body.Xs, body.Vs, as[i], dt)
	}
}
<commit_msg>Refactor the newton.Integrator interface<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package newton

import (
	"github.com/szabba/md/src/vect"
)

// An integrator algorithm
type Integrator interface {
	// The total number of states kept at once by the algorithm
	StateLen() int
	// The location of the current state in the sequence of states kept track
	// of
	CurrentAt() int
	// Performs the integration for a single body
	Integrate(b Body, a vect.Vector, dt float64)
}

// Performs a step of an Euler integration
func Euler(xs, vs []vect.Vector, a vect.Vector, dt float64) {

	v := vs[0].Plus(a.Scale(dt))
	x := xs[0].Plus(vs[0].Scale(dt))

	Shift(vs, v)
	Shift(xs, x)
}

// Performs a step of a Verlet integrator
//
// Note that v[0] will not be calculated until the next step
func Verlet(xs, vs []vect.Vector, a vect.Vector, dt float64) {

	xNext := xs[0].Scale(2).Minus(xs[1]).Plus(a.Scale(dt * dt))
	vs[0] = xNext.Minus(xs[1]).Scale(1 / (2 * dt))

	Shift(vs, vect.Zero)
	Shift(xs, xNext)
}

func Step(alg Integrator, bs []Body, f Force, dt float64) {

	as := make([]vect.Vector, len(bs))

	for i, _ := range bs {

		as[i] = f.Accel(bs, i)
	}

	for i, body := range bs {

		alg(body.Xs, body.Vs, as[i], dt)
	}
}
<|endoftext|>


<commit_before>package config

import (
	"github.com/asaskevich/govalidator"
	"os"
)

func init() {
	govalidator.TagMap["dir"] = govalidator.Validator(func(path string) bool {
		if stat, err := os.Stat(path); err == nil && stat.IsDir() {
			return true
		}
		return false
	})
}

func Validate(config Base) (bool, error) {
	return govalidator.ValidateStruct(config)
}
<commit_msg>Fix dir validator<commit_after>package config

import (
	"github.com/asaskevich/govalidator"
	"os"
)

func init() {
	govalidator.TagMap["dir"] = govalidator.Validator(func(path string) bool {
		// if path is an empty string - skip validation, should be enforced by "required" validation
		if len(path) == 0 {
			return true
		}

		if stat, err := os.Stat(path); err == nil && stat.IsDir() {
			return true
		}
		return false
	})
}

func Validate(config Base) (bool, error) {
	return govalidator.ValidateStruct(config)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tools

import (
	"errors"
	"fmt"

	"github.com/coreos/go-etcd/etcd"
)

type EtcdResponseWithError struct {
	R *etcd.Response
	E error
}

// TestLogger is a type passed to Test functions to support formatted test logs.
type TestLogger interface {
	Errorf(format string, args ...interface{})
	Logf(format string, args ...interface{})
}

type FakeEtcdClient struct {
	watchCompletedChan chan bool

	Data        map[string]EtcdResponseWithError
	DeletedKeys []string
	Err         error
	t           TestLogger
	Ix          int
	TestIndex   bool
	ChangeIndex uint64

	// Will become valid after Watch is called; tester may write to it. Tester may
	// also read from it to verify that it's closed after injecting an error.
	WatchResponse chan *etcd.Response
	// Write to this to prematurely stop a Watch that is running in a goroutine.
	WatchInjectError chan<- error
	WatchStop        chan<- bool
}

func MakeFakeEtcdClient(t TestLogger) *FakeEtcdClient {
	ret := &FakeEtcdClient{
		t:    t,
		Data: map[string]EtcdResponseWithError{},
	}
	// There are three publicly accessible channels in FakeEtcdClient:
	//  - WatchResponse
	//  - WatchInjectError
	//  - WatchStop
	// They are only available when Watch() is called.  If users of
	// FakeEtcdClient want to use any of these channels, they have to call
	// WaitForWatchCompletion before any operation on these channels.
	// Internally, FakeEtcdClient use watchCompletedChan to indicate if the
	// Watch() method has been called. WaitForWatchCompletion() will wait
	// on this channel. WaitForWatchCompletion() will return only when
	// WatchResponse, WatchInjectError and WatchStop are ready to read/write.
	ret.watchCompletedChan = make(chan bool)
	return ret
}

func (f *FakeEtcdClient) generateIndex() uint64 {
	if !f.TestIndex {
		return 0
	}

	f.ChangeIndex++
	return f.ChangeIndex
}

func (f *FakeEtcdClient) AddChild(key, data string, ttl uint64) (*etcd.Response, error) {
	f.Ix = f.Ix + 1
	return f.Set(fmt.Sprintf("%s/%d", key, f.Ix), data, ttl)
}

func (f *FakeEtcdClient) Get(key string, sort, recursive bool) (*etcd.Response, error) {
	result := f.Data[key]
	if result.R == nil {
		f.t.Errorf("Unexpected get for %s", key)
		return &etcd.Response{}, EtcdErrorNotFound
	}
	f.t.Logf("returning %v: %v %#v", key, result.R, result.E)
	return result.R, result.E
}

func (f *FakeEtcdClient) nodeExists(key string) bool {
	result, ok := f.Data[key]
	return ok && result.R != nil && result.R.Node != nil
}

func (f *FakeEtcdClient) Set(key, value string, ttl uint64) (*etcd.Response, error) {
	if f.Err != nil {
		return nil, f.Err
	}

	i := f.generateIndex()

	if f.nodeExists(key) {
		prevResult := f.Data[key]
		createdIndex := prevResult.R.Node.CreatedIndex
		result := EtcdResponseWithError{
			R: &etcd.Response{
				Node: &etcd.Node{
					Value:         value,
					CreatedIndex:  createdIndex,
					ModifiedIndex: i,
				},
			},
		}
		f.Data[key] = result
		return result.R, nil
	}

	result := EtcdResponseWithError{
		R: &etcd.Response{
			Node: &etcd.Node{
				Value:         value,
				CreatedIndex:  i,
				ModifiedIndex: i,
			},
		},
	}
	f.Data[key] = result
	return result.R, nil
}

func (f *FakeEtcdClient) CompareAndSwap(key, value string, ttl uint64, prevValue string, prevIndex uint64) (*etcd.Response, error) {
	if f.Err != nil {
		return nil, f.Err
	}

	if !f.TestIndex {
		f.t.Errorf("Enable TestIndex for test involving CompareAndSwap")
		return nil, errors.New("Enable TestIndex for test involving CompareAndSwap")
	}

	if prevValue == "" && prevIndex == 0 {
		return nil, errors.New("Either prevValue or prevIndex must be specified.")
	}

	if !f.nodeExists(key) {
		return nil, EtcdErrorNotFound
	}

	prevNode := f.Data[key].R.Node

	if prevValue != "" && prevValue != prevNode.Value {
		return nil, EtcdErrorTestFailed
	}

	if prevIndex != 0 && prevIndex != prevNode.ModifiedIndex {
		return nil, EtcdErrorTestFailed
	}

	return f.Set(key, value, ttl)
}

func (f *FakeEtcdClient) Create(key, value string, ttl uint64) (*etcd.Response, error) {
	if f.nodeExists(key) {
		return nil, EtcdErrorNodeExist
	}

	return f.Set(key, value, ttl)
}

func (f *FakeEtcdClient) Delete(key string, recursive bool) (*etcd.Response, error) {
	if f.Err != nil {
		return nil, f.Err
	}

	f.Data[key] = EtcdResponseWithError{
		R: &etcd.Response{
			Node: nil,
		},
		E: EtcdErrorNotFound,
	}

	f.DeletedKeys = append(f.DeletedKeys, key)
	return &etcd.Response{}, nil
}

func (f *FakeEtcdClient) WaitForWatchCompletion() {
	<-f.watchCompletedChan
}

func (f *FakeEtcdClient) Watch(prefix string, waitIndex uint64, recursive bool, receiver chan *etcd.Response, stop chan bool) (*etcd.Response, error) {
	f.WatchResponse = receiver
	f.WatchStop = stop
	injectedError := make(chan error)

	defer close(injectedError)
	f.WatchInjectError = injectedError

	if receiver == nil {
		return f.Get(prefix, false, recursive)
	}

	f.watchCompletedChan <- true
	select {
	case <-stop:
		return nil, etcd.ErrWatchStoppedByUser
	case err := <-injectedError:
		// Emulate etcd's behavior.
		close(receiver)
		return nil, err
	}
	// Never get here.
	return nil, nil
}
<commit_msg>Implement FakeEtcdClient.ExpectNotFoundGet<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tools

import (
	"errors"
	"fmt"

	"github.com/coreos/go-etcd/etcd"
)

type EtcdResponseWithError struct {
	R *etcd.Response
	E error
}

// TestLogger is a type passed to Test functions to support formatted test logs.
type TestLogger interface {
	Errorf(format string, args ...interface{})
	Logf(format string, args ...interface{})
}

type FakeEtcdClient struct {
	watchCompletedChan chan bool

	Data                 map[string]EtcdResponseWithError
	DeletedKeys          []string
	expectNotFoundGetSet map[string]struct{}
	Err                  error
	t                    TestLogger
	Ix                   int
	TestIndex            bool
	ChangeIndex          uint64

	// Will become valid after Watch is called; tester may write to it. Tester may
	// also read from it to verify that it's closed after injecting an error.
	WatchResponse chan *etcd.Response
	// Write to this to prematurely stop a Watch that is running in a goroutine.
	WatchInjectError chan<- error
	WatchStop        chan<- bool
}

func MakeFakeEtcdClient(t TestLogger) *FakeEtcdClient {
	ret := &FakeEtcdClient{
		t:                    t,
		expectNotFoundGetSet: map[string]struct{}{},
		Data:                 map[string]EtcdResponseWithError{},
	}
	// There are three publicly accessible channels in FakeEtcdClient:
	//  - WatchResponse
	//  - WatchInjectError
	//  - WatchStop
	// They are only available when Watch() is called.  If users of
	// FakeEtcdClient want to use any of these channels, they have to call
	// WaitForWatchCompletion before any operation on these channels.
	// Internally, FakeEtcdClient use watchCompletedChan to indicate if the
	// Watch() method has been called. WaitForWatchCompletion() will wait
	// on this channel. WaitForWatchCompletion() will return only when
	// WatchResponse, WatchInjectError and WatchStop are ready to read/write.
	ret.watchCompletedChan = make(chan bool)
	return ret
}

func (f *FakeEtcdClient) ExpectNotFoundGet(key string) {
	f.expectNotFoundGetSet[key] = struct{}{}
}

func (f *FakeEtcdClient) generateIndex() uint64 {
	if !f.TestIndex {
		return 0
	}

	f.ChangeIndex++
	return f.ChangeIndex
}

func (f *FakeEtcdClient) AddChild(key, data string, ttl uint64) (*etcd.Response, error) {
	f.Ix = f.Ix + 1
	return f.Set(fmt.Sprintf("%s/%d", key, f.Ix), data, ttl)
}

func (f *FakeEtcdClient) Get(key string, sort, recursive bool) (*etcd.Response, error) {
	result := f.Data[key]
	if result.R == nil {
		if _, ok := f.expectNotFoundGetSet[key]; !ok {
			f.t.Errorf("Unexpected get for %s", key)
		}
		return &etcd.Response{}, EtcdErrorNotFound
	}
	f.t.Logf("returning %v: %v %#v", key, result.R, result.E)
	return result.R, result.E
}

func (f *FakeEtcdClient) nodeExists(key string) bool {
	result, ok := f.Data[key]
	return ok && result.R != nil && result.R.Node != nil
}

func (f *FakeEtcdClient) Set(key, value string, ttl uint64) (*etcd.Response, error) {
	if f.Err != nil {
		return nil, f.Err
	}

	i := f.generateIndex()

	if f.nodeExists(key) {
		prevResult := f.Data[key]
		createdIndex := prevResult.R.Node.CreatedIndex
		result := EtcdResponseWithError{
			R: &etcd.Response{
				Node: &etcd.Node{
					Value:         value,
					CreatedIndex:  createdIndex,
					ModifiedIndex: i,
				},
			},
		}
		f.Data[key] = result
		return result.R, nil
	}

	result := EtcdResponseWithError{
		R: &etcd.Response{
			Node: &etcd.Node{
				Value:         value,
				CreatedIndex:  i,
				ModifiedIndex: i,
			},
		},
	}
	f.Data[key] = result
	return result.R, nil
}

func (f *FakeEtcdClient) CompareAndSwap(key, value string, ttl uint64, prevValue string, prevIndex uint64) (*etcd.Response, error) {
	if f.Err != nil {
		return nil, f.Err
	}

	if !f.TestIndex {
		f.t.Errorf("Enable TestIndex for test involving CompareAndSwap")
		return nil, errors.New("Enable TestIndex for test involving CompareAndSwap")
	}

	if prevValue == "" && prevIndex == 0 {
		return nil, errors.New("Either prevValue or prevIndex must be specified.")
	}

	if !f.nodeExists(key) {
		return nil, EtcdErrorNotFound
	}

	prevNode := f.Data[key].R.Node

	if prevValue != "" && prevValue != prevNode.Value {
		return nil, EtcdErrorTestFailed
	}

	if prevIndex != 0 && prevIndex != prevNode.ModifiedIndex {
		return nil, EtcdErrorTestFailed
	}

	return f.Set(key, value, ttl)
}

func (f *FakeEtcdClient) Create(key, value string, ttl uint64) (*etcd.Response, error) {
	if f.nodeExists(key) {
		return nil, EtcdErrorNodeExist
	}

	return f.Set(key, value, ttl)
}

func (f *FakeEtcdClient) Delete(key string, recursive bool) (*etcd.Response, error) {
	if f.Err != nil {
		return nil, f.Err
	}

	f.Data[key] = EtcdResponseWithError{
		R: &etcd.Response{
			Node: nil,
		},
		E: EtcdErrorNotFound,
	}

	f.DeletedKeys = append(f.DeletedKeys, key)
	return &etcd.Response{}, nil
}

func (f *FakeEtcdClient) WaitForWatchCompletion() {
	<-f.watchCompletedChan
}

func (f *FakeEtcdClient) Watch(prefix string, waitIndex uint64, recursive bool, receiver chan *etcd.Response, stop chan bool) (*etcd.Response, error) {
	f.WatchResponse = receiver
	f.WatchStop = stop
	injectedError := make(chan error)

	defer close(injectedError)
	f.WatchInjectError = injectedError

	if receiver == nil {
		return f.Get(prefix, false, recursive)
	}

	f.watchCompletedChan <- true
	select {
	case <-stop:
		return nil, etcd.ErrWatchStoppedByUser
	case err := <-injectedError:
		// Emulate etcd's behavior.
		close(receiver)
		return nil, err
	}
	// Never get here.
	return nil, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package keymutex

import (
	"fmt"
	"github.com/golang/glog"
	"sync"
)

// KeyMutex is a thread-safe interface for acquiring locks on arbitrary strings.
type KeyMutex interface {
	// Aquires a lock associated with the specified ID, creates the lock if one doesn't already exist.
	LockKey(id string)

	// Releases the lock associated with the specified ID.
	// Returns an error if the specified ID doesn't exist.
	UnlockKey(id string) error
}

// Returns a new instance of a key mutex.
func NewKeyMutex() KeyMutex {
	return &keyMutex{
		mutexMap: make(map[string]*sync.Mutex),
	}
}

type keyMutex struct {
	sync.RWMutex
	mutexMap map[string]*sync.Mutex
}

// Aquires a lock associated with the specified ID (creates the lock if one doesn't already exist).
func (km *keyMutex) LockKey(id string) {
	glog.V(5).Infof("LockKey(...) called for id %q\r\n", id)
	mutex := km.getOrCreateLock(id)
	mutex.Lock()
	glog.V(5).Infof("LockKey(...) for id %q completed.\r\n", id)
}

// Releases the lock associated with the specified ID.
// Returns an error if the specified ID doesn't exist.
func (km *keyMutex) UnlockKey(id string) error {
	glog.V(5).Infof("UnlockKey(...) called for id %q\r\n", id)
	km.RLock()
	defer km.RUnlock()
	mutex, exists := km.mutexMap[id]
	if !exists {
		return fmt.Errorf("id %q not found", id)
	}
	glog.V(5).Infof("UnlockKey(...) for id. Mutex found, trying to unlock it. %q\r\n", id)

	mutex.Unlock()
	glog.V(5).Infof("UnlockKey(...) for id %q completed.\r\n", id)
	return nil
}

// Returns lock associated with the specified ID, or creates the lock if one doesn't already exist.
func (km *keyMutex) getOrCreateLock(id string) *sync.Mutex {
	km.Lock()
	defer km.Unlock()

	if _, exists := km.mutexMap[id]; !exists {
		km.mutexMap[id] = &sync.Mutex{}
	}

	return km.mutexMap[id]
}
<commit_msg>Spelling fixes inspired by github.com/client9/misspell<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package keymutex

import (
	"fmt"
	"github.com/golang/glog"
	"sync"
)

// KeyMutex is a thread-safe interface for acquiring locks on arbitrary strings.
type KeyMutex interface {
	// Acquires a lock associated with the specified ID, creates the lock if one doesn't already exist.
	LockKey(id string)

	// Releases the lock associated with the specified ID.
	// Returns an error if the specified ID doesn't exist.
	UnlockKey(id string) error
}

// Returns a new instance of a key mutex.
func NewKeyMutex() KeyMutex {
	return &keyMutex{
		mutexMap: make(map[string]*sync.Mutex),
	}
}

type keyMutex struct {
	sync.RWMutex
	mutexMap map[string]*sync.Mutex
}

// Acquires a lock associated with the specified ID (creates the lock if one doesn't already exist).
func (km *keyMutex) LockKey(id string) {
	glog.V(5).Infof("LockKey(...) called for id %q\r\n", id)
	mutex := km.getOrCreateLock(id)
	mutex.Lock()
	glog.V(5).Infof("LockKey(...) for id %q completed.\r\n", id)
}

// Releases the lock associated with the specified ID.
// Returns an error if the specified ID doesn't exist.
func (km *keyMutex) UnlockKey(id string) error {
	glog.V(5).Infof("UnlockKey(...) called for id %q\r\n", id)
	km.RLock()
	defer km.RUnlock()
	mutex, exists := km.mutexMap[id]
	if !exists {
		return fmt.Errorf("id %q not found", id)
	}
	glog.V(5).Infof("UnlockKey(...) for id. Mutex found, trying to unlock it. %q\r\n", id)

	mutex.Unlock()
	glog.V(5).Infof("UnlockKey(...) for id %q completed.\r\n", id)
	return nil
}

// Returns lock associated with the specified ID, or creates the lock if one doesn't already exist.
func (km *keyMutex) getOrCreateLock(id string) *sync.Mutex {
	km.Lock()
	defer km.Unlock()

	if _, exists := km.mutexMap[id]; !exists {
		km.mutexMap[id] = &sync.Mutex{}
	}

	return km.mutexMap[id]
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/gob"
	"reflect"
	"testing"

	"github.com/hashicorp/hil/ast"
)

func TestNewRawConfig(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
		"bar": `${file("boom.txt")}`,
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	if len(rc.Interpolations) != 2 {
		t.Fatalf("bad: %#v", rc.Interpolations)
	}
	if len(rc.Variables) != 1 {
		t.Fatalf("bad: %#v", rc.Variables)
	}
}

func TestRawConfig_basic(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Before interpolate, Config() should be the raw
	if !reflect.DeepEqual(rc.Config(), raw) {
		t.Fatalf("bad: %#v", rc.Config())
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: "baz",
			Type:  ast.TypeString,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{
		"foo": "baz",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}
	if len(rc.UnknownKeys()) != 0 {
		t.Fatalf("bad: %#v", rc.UnknownKeys())
	}
}

func TestRawConfig_double(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: "baz",
			Type:  ast.TypeString,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{
		"foo": "baz",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	vars = map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: "what",
			Type:  ast.TypeString,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual = rc.Config()
	expected = map[string]interface{}{
		"foo": "what",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestRawConfigInterpolate_escaped(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "bar-$${baz}",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Before interpolate, Config() should be the raw
	if !reflect.DeepEqual(rc.Config(), raw) {
		t.Fatalf("bad: %#v", rc.Config())
	}

	if err := rc.Interpolate(nil); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{
		"foo": "bar-${baz}",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}
	if len(rc.UnknownKeys()) != 0 {
		t.Fatalf("bad: %#v", rc.UnknownKeys())
	}
}

func TestRawConfig_merge(t *testing.T) {
	raw1 := map[string]interface{}{
		"foo": "${var.foo}",
		"bar": "${var.bar}",
	}

	rc1, err := NewRawConfig(raw1)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	{
		vars := map[string]ast.Variable{
			"var.foo": ast.Variable{
				Value: "foovalue",
				Type:  ast.TypeString,
			},
			"var.bar": ast.Variable{
				Value: "nope",
				Type:  ast.TypeString,
			},
		}
		if err := rc1.Interpolate(vars); err != nil {
			t.Fatalf("err: %s", err)
		}
	}

	raw2 := map[string]interface{}{
		"bar": "${var.bar}",
		"baz": "${var.baz}",
	}

	rc2, err := NewRawConfig(raw2)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	{
		vars := map[string]ast.Variable{
			"var.bar": ast.Variable{
				Value: "barvalue",
				Type:  ast.TypeString,
			},
			"var.baz": ast.Variable{
				Value: UnknownVariableValue,
				Type:  ast.TypeUnknown,
			},
		}
		if err := rc2.Interpolate(vars); err != nil {
			t.Fatalf("err: %s", err)
		}
	}

	// Merge the two
	rc3 := rc1.Merge(rc2)

	// Raw should be merged
	raw3 := map[string]interface{}{
		"foo": "${var.foo}",
		"bar": "${var.bar}",
		"baz": "${var.baz}",
	}
	if !reflect.DeepEqual(rc3.Raw, raw3) {
		t.Fatalf("bad: %#v", rc3.Raw)
	}

	actual := rc3.Config()
	expected := map[string]interface{}{
		"foo": "foovalue",
		"bar": "barvalue",
		"baz": UnknownVariableValue,
	}
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	expectedKeys := []string{"baz"}
	if !reflect.DeepEqual(rc3.UnknownKeys(), expectedKeys) {
		t.Fatalf("bad: %#v", rc3.UnknownKeys())
	}
}

func TestRawConfig_syntax(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var",
	}

	if _, err := NewRawConfig(raw); err == nil {
		t.Fatal("should error")
	}
}

func TestRawConfig_unknown(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: UnknownVariableValue,
			Type:  ast.TypeUnknown,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{"foo": UnknownVariableValue}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	expectedKeys := []string{"foo"}
	if !reflect.DeepEqual(rc.UnknownKeys(), expectedKeys) {
		t.Fatalf("bad: %#v", rc.UnknownKeys())
	}
}

func TestRawConfig_unknownPartial(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}/32",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: UnknownVariableValue,
			Type:  ast.TypeUnknown,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{"foo": UnknownVariableValue}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	expectedKeys := []string{"foo"}
	if !reflect.DeepEqual(rc.UnknownKeys(), expectedKeys) {
		t.Fatalf("bad: %#v", rc.UnknownKeys())
	}
}

func TestRawConfig_unknownPartialList(t *testing.T) {
	raw := map[string]interface{}{
		"foo": []interface{}{
			"${var.bar}/32",
		},
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: UnknownVariableValue,
			Type:  ast.TypeUnknown,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{"foo": []interface{}{UnknownVariableValue}}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	expectedKeys := []string{"foo"}
	if !reflect.DeepEqual(rc.UnknownKeys(), expectedKeys) {
		t.Fatalf("bad: %#v", rc.UnknownKeys())
	}
}

// This tests a race found where we were not maintaining the "slice index"
// accounting properly. The result would be that some computed keys would
// look like they had no slice index when they in fact do. This test is not
// very reliable but it did fail before the fix and passed after.
func TestRawConfig_sliceIndexLoss(t *testing.T) {
	raw := map[string]interface{}{
		"slice": []map[string]interface{}{
			map[string]interface{}{
				"foo": []interface{}{"foo/${var.unknown}"},
				"bar": []interface{}{"bar"},
			},
		},
	}

	vars := map[string]ast.Variable{
		"var.unknown": ast.Variable{
			Value: UnknownVariableValue,
			Type:  ast.TypeUnknown,
		},
		"var.known": ast.Variable{
			Value: "123456",
			Type:  ast.TypeString,
		},
	}

	// We run it a lot because its fast and we try to get a race out
	for i := 0; i < 50; i++ {
		rc, err := NewRawConfig(raw)
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		if err := rc.Interpolate(vars); err != nil {
			t.Fatalf("err: %s", err)
		}

		expectedKeys := []string{"slice.0.foo"}
		if !reflect.DeepEqual(rc.UnknownKeys(), expectedKeys) {
			t.Fatalf("bad: %#v", rc.UnknownKeys())
		}
	}
}

func TestRawConfigValue(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	rc.Key = ""
	if rc.Value() != nil {
		t.Fatalf("bad: %#v", rc.Value())
	}

	rc.Key = "foo"
	if rc.Value() != "${var.bar}" {
		t.Fatalf("err: %#v", rc.Value())
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: "baz",
			Type:  ast.TypeString,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	if rc.Value() != "baz" {
		t.Fatalf("bad: %#v", rc.Value())
	}
}

func TestRawConfig_implGob(t *testing.T) {
	var _ gob.GobDecoder = new(RawConfig)
	var _ gob.GobEncoder = new(RawConfig)
}

// verify that RawMap returns a identical copy
func TestNewRawConfig_rawMap(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
		"bar": `${file("boom.txt")}`,
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	rawCopy := rc.RawMap()
	if !reflect.DeepEqual(raw, rawCopy) {
		t.Fatalf("bad: %#v", rawCopy)
	}

	// make sure they aren't the same map
	raw["test"] = "value"
	if reflect.DeepEqual(raw, rawCopy) {
		t.Fatal("RawMap() didn't return a copy")
	}
}
<commit_msg>config: validate that RawConfig.Copy doesn't copy the interpolated values<commit_after>package config

import (
	"encoding/gob"
	"reflect"
	"testing"

	"github.com/hashicorp/hil/ast"
)

func TestNewRawConfig(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
		"bar": `${file("boom.txt")}`,
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	if len(rc.Interpolations) != 2 {
		t.Fatalf("bad: %#v", rc.Interpolations)
	}
	if len(rc.Variables) != 1 {
		t.Fatalf("bad: %#v", rc.Variables)
	}
}

func TestRawConfig_basic(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Before interpolate, Config() should be the raw
	if !reflect.DeepEqual(rc.Config(), raw) {
		t.Fatalf("bad: %#v", rc.Config())
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: "baz",
			Type:  ast.TypeString,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{
		"foo": "baz",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}
	if len(rc.UnknownKeys()) != 0 {
		t.Fatalf("bad: %#v", rc.UnknownKeys())
	}
}

func TestRawConfig_double(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: "baz",
			Type:  ast.TypeString,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{
		"foo": "baz",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	vars = map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: "what",
			Type:  ast.TypeString,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual = rc.Config()
	expected = map[string]interface{}{
		"foo": "what",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestRawConfigInterpolate_escaped(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "bar-$${baz}",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Before interpolate, Config() should be the raw
	if !reflect.DeepEqual(rc.Config(), raw) {
		t.Fatalf("bad: %#v", rc.Config())
	}

	if err := rc.Interpolate(nil); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{
		"foo": "bar-${baz}",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}
	if len(rc.UnknownKeys()) != 0 {
		t.Fatalf("bad: %#v", rc.UnknownKeys())
	}
}

func TestRawConfig_merge(t *testing.T) {
	raw1 := map[string]interface{}{
		"foo": "${var.foo}",
		"bar": "${var.bar}",
	}

	rc1, err := NewRawConfig(raw1)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	{
		vars := map[string]ast.Variable{
			"var.foo": ast.Variable{
				Value: "foovalue",
				Type:  ast.TypeString,
			},
			"var.bar": ast.Variable{
				Value: "nope",
				Type:  ast.TypeString,
			},
		}
		if err := rc1.Interpolate(vars); err != nil {
			t.Fatalf("err: %s", err)
		}
	}

	raw2 := map[string]interface{}{
		"bar": "${var.bar}",
		"baz": "${var.baz}",
	}

	rc2, err := NewRawConfig(raw2)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	{
		vars := map[string]ast.Variable{
			"var.bar": ast.Variable{
				Value: "barvalue",
				Type:  ast.TypeString,
			},
			"var.baz": ast.Variable{
				Value: UnknownVariableValue,
				Type:  ast.TypeUnknown,
			},
		}
		if err := rc2.Interpolate(vars); err != nil {
			t.Fatalf("err: %s", err)
		}
	}

	// Merge the two
	rc3 := rc1.Merge(rc2)

	// Raw should be merged
	raw3 := map[string]interface{}{
		"foo": "${var.foo}",
		"bar": "${var.bar}",
		"baz": "${var.baz}",
	}
	if !reflect.DeepEqual(rc3.Raw, raw3) {
		t.Fatalf("bad: %#v", rc3.Raw)
	}

	actual := rc3.Config()
	expected := map[string]interface{}{
		"foo": "foovalue",
		"bar": "barvalue",
		"baz": UnknownVariableValue,
	}
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	expectedKeys := []string{"baz"}
	if !reflect.DeepEqual(rc3.UnknownKeys(), expectedKeys) {
		t.Fatalf("bad: %#v", rc3.UnknownKeys())
	}
}

func TestRawConfig_syntax(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var",
	}

	if _, err := NewRawConfig(raw); err == nil {
		t.Fatal("should error")
	}
}

func TestRawConfig_unknown(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: UnknownVariableValue,
			Type:  ast.TypeUnknown,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{"foo": UnknownVariableValue}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	expectedKeys := []string{"foo"}
	if !reflect.DeepEqual(rc.UnknownKeys(), expectedKeys) {
		t.Fatalf("bad: %#v", rc.UnknownKeys())
	}
}

func TestRawConfig_unknownPartial(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}/32",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: UnknownVariableValue,
			Type:  ast.TypeUnknown,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{"foo": UnknownVariableValue}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	expectedKeys := []string{"foo"}
	if !reflect.DeepEqual(rc.UnknownKeys(), expectedKeys) {
		t.Fatalf("bad: %#v", rc.UnknownKeys())
	}
}

func TestRawConfig_unknownPartialList(t *testing.T) {
	raw := map[string]interface{}{
		"foo": []interface{}{
			"${var.bar}/32",
		},
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: UnknownVariableValue,
			Type:  ast.TypeUnknown,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := rc.Config()
	expected := map[string]interface{}{"foo": []interface{}{UnknownVariableValue}}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	expectedKeys := []string{"foo"}
	if !reflect.DeepEqual(rc.UnknownKeys(), expectedKeys) {
		t.Fatalf("bad: %#v", rc.UnknownKeys())
	}
}

// This tests a race found where we were not maintaining the "slice index"
// accounting properly. The result would be that some computed keys would
// look like they had no slice index when they in fact do. This test is not
// very reliable but it did fail before the fix and passed after.
func TestRawConfig_sliceIndexLoss(t *testing.T) {
	raw := map[string]interface{}{
		"slice": []map[string]interface{}{
			map[string]interface{}{
				"foo": []interface{}{"foo/${var.unknown}"},
				"bar": []interface{}{"bar"},
			},
		},
	}

	vars := map[string]ast.Variable{
		"var.unknown": ast.Variable{
			Value: UnknownVariableValue,
			Type:  ast.TypeUnknown,
		},
		"var.known": ast.Variable{
			Value: "123456",
			Type:  ast.TypeString,
		},
	}

	// We run it a lot because its fast and we try to get a race out
	for i := 0; i < 50; i++ {
		rc, err := NewRawConfig(raw)
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		if err := rc.Interpolate(vars); err != nil {
			t.Fatalf("err: %s", err)
		}

		expectedKeys := []string{"slice.0.foo"}
		if !reflect.DeepEqual(rc.UnknownKeys(), expectedKeys) {
			t.Fatalf("bad: %#v", rc.UnknownKeys())
		}
	}
}

func TestRawConfigCopy(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	rc.Key = "foo"
	if rc.Value() != "${var.bar}" {
		t.Fatalf("err: %#v", rc.Value())
	}

	// Interpolate the first one
	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: "baz",
			Type:  ast.TypeString,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	if rc.Value() != "baz" {
		t.Fatalf("bad: %#v", rc.Value())
	}

	// Copy and interpolate
	{
		rc2 := rc.Copy()
		if rc2.Value() != "${var.bar}" {
			t.Fatalf("err: %#v", rc2.Value())
		}

		vars := map[string]ast.Variable{
			"var.bar": ast.Variable{
				Value: "qux",
				Type:  ast.TypeString,
			},
		}
		if err := rc2.Interpolate(vars); err != nil {
			t.Fatalf("err: %s", err)
		}

		if rc2.Value() != "qux" {
			t.Fatalf("bad: %#v", rc2.Value())
		}
	}
}

func TestRawConfigValue(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	rc.Key = ""
	if rc.Value() != nil {
		t.Fatalf("bad: %#v", rc.Value())
	}

	rc.Key = "foo"
	if rc.Value() != "${var.bar}" {
		t.Fatalf("err: %#v", rc.Value())
	}

	vars := map[string]ast.Variable{
		"var.bar": ast.Variable{
			Value: "baz",
			Type:  ast.TypeString,
		},
	}
	if err := rc.Interpolate(vars); err != nil {
		t.Fatalf("err: %s", err)
	}

	if rc.Value() != "baz" {
		t.Fatalf("bad: %#v", rc.Value())
	}
}

func TestRawConfig_implGob(t *testing.T) {
	var _ gob.GobDecoder = new(RawConfig)
	var _ gob.GobEncoder = new(RawConfig)
}

// verify that RawMap returns a identical copy
func TestNewRawConfig_rawMap(t *testing.T) {
	raw := map[string]interface{}{
		"foo": "${var.bar}",
		"bar": `${file("boom.txt")}`,
	}

	rc, err := NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	rawCopy := rc.RawMap()
	if !reflect.DeepEqual(raw, rawCopy) {
		t.Fatalf("bad: %#v", rawCopy)
	}

	// make sure they aren't the same map
	raw["test"] = "value"
	if reflect.DeepEqual(raw, rawCopy) {
		t.Fatal("RawMap() didn't return a copy")
	}
}
<|endoftext|>


<commit_before>package gps

import (
	"strings"

	"github.com/armon/go-radix"
)

// Typed implementations of radix trees. These are just simple wrappers that let
// us avoid having to type assert anywhere else, cleaning up other code a bit.
//
// Some of the more annoying things to implement (like walks) aren't
// implemented. They can be added if/when we actually need them.
//
// Oh generics, where art thou...

type deducerTrie struct {
	t *radix.Tree
}

func newDeducerTrie() deducerTrie {
	return deducerTrie{
		t: radix.New(),
	}
}

// Delete is used to delete a key, returning the previous value and if it was deleted
func (t deducerTrie) Delete(s string) (pathDeducer, bool) {
	if v, had := t.t.Delete(s); had {
		return v.(pathDeducer), had
	}
	return nil, false
}

// Get is used to lookup a specific key, returning the value and if it was found
func (t deducerTrie) Get(s string) (pathDeducer, bool) {
	if v, has := t.t.Get(s); has {
		return v.(pathDeducer), has
	}
	return nil, false
}

// Insert is used to add a newentry or update an existing entry. Returns if updated.
func (t deducerTrie) Insert(s string, v pathDeducer) (pathDeducer, bool) {
	if v2, had := t.t.Insert(s, v); had {
		return v2.(pathDeducer), had
	}
	return nil, false
}

// Len is used to return the number of elements in the tree
func (t deducerTrie) Len() int {
	return t.t.Len()
}

// LongestPrefix is like Get, but instead of an exact match, it will return the
// longest prefix match.
func (t deducerTrie) LongestPrefix(s string) (string, pathDeducer, bool) {
	if p, v, has := t.t.LongestPrefix(s); has {
		return p, v.(pathDeducer), has
	}
	return "", nil, false
}

// ToMap is used to walk the tree and convert it to a map.
func (t deducerTrie) ToMap() map[string]pathDeducer {
	m := make(map[string]pathDeducer)
	t.t.Walk(func(s string, v interface{}) bool {
		m[s] = v.(pathDeducer)
		return false
	})

	return m
}

type prTrie struct {
	t *radix.Tree
}

func newProjectRootTrie() prTrie {
	return prTrie{
		t: radix.New(),
	}
}

// Delete is used to delete a key, returning the previous value and if it was deleted
func (t prTrie) Delete(s string) (ProjectRoot, bool) {
	if v, had := t.t.Delete(s); had {
		return v.(ProjectRoot), had
	}
	return "", false
}

// Get is used to lookup a specific key, returning the value and if it was found
func (t prTrie) Get(s string) (ProjectRoot, bool) {
	if v, has := t.t.Get(s); has {
		return v.(ProjectRoot), has
	}
	return "", false
}

// Insert is used to add a newentry or update an existing entry. Returns if updated.
func (t prTrie) Insert(s string, v ProjectRoot) (ProjectRoot, bool) {
	if v2, had := t.t.Insert(s, v); had {
		return v2.(ProjectRoot), had
	}
	return "", false
}

// Len is used to return the number of elements in the tree
func (t prTrie) Len() int {
	return t.t.Len()
}

// LongestPrefix is like Get, but instead of an exact match, it will return the
// longest prefix match.
func (t prTrie) LongestPrefix(s string) (string, ProjectRoot, bool) {
	if p, v, has := t.t.LongestPrefix(s); has && isPathPrefixOrEqual(p, s) {
		return p, v.(ProjectRoot), has
	}
	return "", "", false
}

// ToMap is used to walk the tree and convert it to a map.
func (t prTrie) ToMap() map[string]ProjectRoot {
	m := make(map[string]ProjectRoot)
	t.t.Walk(func(s string, v interface{}) bool {
		m[s] = v.(ProjectRoot)
		return false
	})

	return m
}

// isPathPrefixOrEqual is an additional helper check to ensure that the literal
// string prefix returned from a radix tree prefix match is also a tree match.
//
// The radix tree gets it mostly right, but we have to guard against
// possibilities like this:
//
// github.com/sdboyer/foo
// github.com/sdboyer/foobar/baz
//
// The latter would incorrectly be conflated with the former. As we know we're
// operating on strings that describe paths, guard against this case by
// verifying that either the input is the same length as the match (in which
// case we know they're equal), or that the next character is a "/".
func isPathPrefixOrEqual(pre, path string) bool {
	prflen, pathlen := len(pre), len(path)
	if pathlen == prflen+1 {
		// this can never be the case
		return false
	}

	// we assume something else (a trie) has done equality check up to the point
	// of the prefix, so we just check len
	return prflen == pathlen || strings.Index(path[prflen:], "/") == 0
}
<commit_msg>Improve some var names and docs in typed_radix<commit_after>package gps

import (
	"strings"

	"github.com/armon/go-radix"
)

// Typed implementations of radix trees. These are just simple wrappers that let
// us avoid having to type assert anywhere else, cleaning up other code a bit.
//
// Some of the more annoying things to implement (like walks) aren't
// implemented. They can be added if/when we actually need them.
//
// Oh generics, where art thou...

type deducerTrie struct {
	t *radix.Tree
}

func newDeducerTrie() deducerTrie {
	return deducerTrie{
		t: radix.New(),
	}
}

// Delete is used to delete a key, returning the previous value and if it was deleted
func (t deducerTrie) Delete(s string) (pathDeducer, bool) {
	if d, had := t.t.Delete(s); had {
		return d.(pathDeducer), had
	}
	return nil, false
}

// Get is used to lookup a specific key, returning the value and if it was found
func (t deducerTrie) Get(s string) (pathDeducer, bool) {
	if d, has := t.t.Get(s); has {
		return d.(pathDeducer), has
	}
	return nil, false
}

// Insert is used to add a newentry or update an existing entry. Returns if updated.
func (t deducerTrie) Insert(s string, d pathDeducer) (pathDeducer, bool) {
	if d2, had := t.t.Insert(s, d); had {
		return d2.(pathDeducer), had
	}
	return nil, false
}

// Len is used to return the number of elements in the tree
func (t deducerTrie) Len() int {
	return t.t.Len()
}

// LongestPrefix is like Get, but instead of an exact match, it will return the
// longest prefix match.
func (t deducerTrie) LongestPrefix(s string) (string, pathDeducer, bool) {
	if p, d, has := t.t.LongestPrefix(s); has {
		return p, d.(pathDeducer), has
	}
	return "", nil, false
}

// ToMap is used to walk the tree and convert it to a map.
func (t deducerTrie) ToMap() map[string]pathDeducer {
	m := make(map[string]pathDeducer)
	t.t.Walk(func(s string, d interface{}) bool {
		m[s] = d.(pathDeducer)
		return false
	})

	return m
}

type prTrie struct {
	t *radix.Tree
}

func newProjectRootTrie() prTrie {
	return prTrie{
		t: radix.New(),
	}
}

// Delete is used to delete a key, returning the previous value and if it was deleted
func (t prTrie) Delete(s string) (ProjectRoot, bool) {
	if pr, had := t.t.Delete(s); had {
		return pr.(ProjectRoot), had
	}
	return "", false
}

// Get is used to lookup a specific key, returning the value and if it was found
func (t prTrie) Get(s string) (ProjectRoot, bool) {
	if pr, has := t.t.Get(s); has {
		return pr.(ProjectRoot), has
	}
	return "", false
}

// Insert is used to add a newentry or update an existing entry. Returns if updated.
func (t prTrie) Insert(s string, pr ProjectRoot) (ProjectRoot, bool) {
	if pr2, had := t.t.Insert(s, pr); had {
		return pr2.(ProjectRoot), had
	}
	return "", false
}

// Len is used to return the number of elements in the tree
func (t prTrie) Len() int {
	return t.t.Len()
}

// LongestPrefix is like Get, but instead of an exact match, it will return the
// longest prefix match.
func (t prTrie) LongestPrefix(s string) (string, ProjectRoot, bool) {
	if p, pr, has := t.t.LongestPrefix(s); has && isPathPrefixOrEqual(p, s) {
		return p, pr.(ProjectRoot), has
	}
	return "", "", false
}

// ToMap is used to walk the tree and convert it to a map.
func (t prTrie) ToMap() map[string]ProjectRoot {
	m := make(map[string]ProjectRoot)
	t.t.Walk(func(s string, pr interface{}) bool {
		m[s] = pr.(ProjectRoot)
		return false
	})

	return m
}

// isPathPrefixOrEqual is an additional helper check to ensure that the literal
// string prefix returned from a radix tree prefix match is also a path tree
// match.
//
// The radix tree gets it mostly right, but we have to guard against
// possibilities like this:
//
// github.com/sdboyer/foo
// github.com/sdboyer/foobar/baz
//
// The latter would incorrectly be conflated with the former. As we know we're
// operating on strings that describe import paths, guard against this case by
// verifying that either the input is the same length as the match (in which
// case we know they're equal), or that the next character is a "/". (Import
// paths are defined to always use "/", not the OS-specific path separator.)
func isPathPrefixOrEqual(pre, path string) bool {
	prflen, pathlen := len(pre), len(path)
	if pathlen == prflen+1 {
		// this can never be the case
		return false
	}

	// we assume something else (a trie) has done equality check up to the point
	// of the prefix, so we just check len
	return prflen == pathlen || strings.Index(path[prflen:], "/") == 0
}
<|endoftext|>


<commit_before>package awsCertlint

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/mozilla/tls-observatory/certificate"
	"github.com/mozilla/tls-observatory/logger"
	"github.com/mozilla/tls-observatory/worker"
)

var (
	workerName = "awsCertlint"
	workerDesc = "Runs the awslabs certificate linter and saves output"

	certlintDirectory = "/go/certlint" // path from tools/Dockerfile-scanner
	binaryPath        = "bin/certlint" // path inside `certlintDirectory`

	log = logger.GetLogger()
)

type Result struct {
	Bugs          []string `json:"bugs"`
	Informational []string `json:"informational"`
	Notices       []string `json:"notices"`
	Warnings      []string `json:"warnings"`
	Errors        []string `json:"errors"`
	FatalErrors   []string `json:"fatalErrors"`
}

func init() {
	runner := new(eval)
	worker.RegisterPrinter(workerName, worker.Info{Runner: runner, Description: workerDesc})

	// override certlintDirectory if TLS_AWSCERTLINT_DIR
	if path := os.Getenv("TLS_AWSCERTLINT_DIR"); path != "" {
		certlintDirectory = path
	}

	// Verify code was pulled down
	fullPath := filepath.Join(certlintDirectory, binaryPath)
	_, err := os.Stat(fullPath)
	if err != nil && os.IsNotExist(err) {
		log.Printf("Could not find awslabs/certlint (tried %s), disabling worker\n", fullPath)
		//return
	}
	worker.RegisterWorker(workerName, worker.Info{Runner: runner, Description: workerDesc})
}

type eval struct{}

func (e eval) Run(in worker.Input, resChan chan worker.Result) {
	result := worker.Result{
		WorkerName: workerName,
	}
	lintResult, err := e.runCertlint(in.Certificate)
	if err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("%s for certificate %d", err.Error(), in.Certificate.ID)},
			Result:     nil,
		}
		return
	}

	// Marshal the response
	bs, err := json.Marshal(&lintResult)
	if err != nil {
		result.Success = false
		result.Errors = append(result.Errors, err.Error())
	} else {
		result.Success = true
		result.Result = bs
	}
	resChan <- result
}

func (e eval) runCertlint(cert certificate.Certificate) (*Result, error) {
	tmp, err := ioutil.TempFile("", "awslabs-certlint")
	if err != nil {
		return nil, fmt.Errorf("error creating temp file for certificate %d", cert.ID)
	}
	defer os.Remove(tmp.Name())
	x509Cert, err := cert.ToX509()
	if err != nil {
		return nil, fmt.Errorf("error converting certificate %d to x509.Certificate", cert.ID)
	}
	if err := ioutil.WriteFile(tmp.Name(), x509Cert.Raw, 0644); err != nil {
		return nil, fmt.Errorf("error writing x509.Certificate to temp file, err=%v", err)
	}

	// Run certlint over certificate
	cmd := exec.Command("ruby", "-I", "lib:ext", binaryPath, tmp.Name())
	cmd.Dir = certlintDirectory

	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	if err := cmd.Start(); err != nil {
		return nil, fmt.Errorf("error starting awslabs/certlint on certificate %d, err=%v, out=%q", cert.ID, err, strings.TrimSpace(stderr.String()))
	}

	waitChan := make(chan error, 1)
	go func() {
		waitChan <- cmd.Wait()
	}()

	select {
	case <-time.After(30 * time.Second):
		err := cmd.Process.Kill()
		return nil, fmt.Errorf("timed out waiting for awslabs/certlint on certificate %d, kill error=%v", cert.ID, err)
	case err := <-waitChan:
		if err != nil {
			return nil, fmt.Errorf("error running awslabs/certlint on certificate %d, err=%v, out=%q", cert.ID, err, strings.TrimSpace(stderr.String()))
		}
	}

	return e.parseResponse(stdout)
}

// From: https://github.com/awslabs/certlint#output
//
// * B: Bug. Your certificate has a feature not handled by certlint.
// * I: Information.  These are purely informational; no action is needed.
// * N: Notice.  These are items known to cause issues with one or more implementations of certificate processing but are not errors according to the standard.
// * W: Warning.  These are issues where a standard recommends differently but the standard uses terms such as "SHOULD" or "MAY".
// * E: Error.  These are issues where the certificate is not compliant with the standard.
// * F: Fatal Error.  These errors are fatal to the checks and prevent most further checks from being executed.  These are extremely bad errors.
func (e eval) parseResponse(resp bytes.Buffer) (*Result, error) {
	out := &Result{}

	r := bufio.NewScanner(&resp)
	for r.Scan() {
		line := strings.TrimSpace(r.Text())
		if line == "" {
			continue
		}

		// Match first letter of each line, which signifies its type.
		switch line[0] {
		case 'B':
			out.Bugs = append(out.Bugs, strings.TrimSpace(line[2:]))
		case 'I':
			out.Informational = append(out.Informational, strings.TrimSpace(line[2:]))
		case 'N':
			out.Notices = append(out.Notices, strings.TrimSpace(line[2:]))
		case 'W':
			out.Warnings = append(out.Warnings, strings.TrimSpace(line[2:]))
		case 'E':
			out.Errors = append(out.Errors, strings.TrimSpace(line[2:]))
		case 'F':
			out.FatalErrors = append(out.FatalErrors, strings.TrimSpace(line[2:]))
		}
	}
	return out, nil
}

// AnalysisPrinter outputs the results from awslabs/certlint over a given certificate
func (e eval) AnalysisPrinter(input []byte, printAll interface{}) (results []string, err error) {
	var result Result
	if err := json.Unmarshal(input, &result); err != nil {
		return nil, fmt.Errorf("awsCertlint Worker: failed to parse results: err=%v", err)
	}

	// Build results for webview
	for i := range result.FatalErrors {
		results = append(results, fmt.Sprintf(" - Fatal Error: %s", result.FatalErrors[i]))
	}
	for i := range result.Errors {
		results = append(results, fmt.Sprintf(" - Error: %s", result.Errors[i]))
	}
	for i := range result.Warnings {
		results = append(results, fmt.Sprintf(" - Warning: %s", result.Warnings[i]))
	}
	for i := range result.Informational {
		results = append(results, fmt.Sprintf(" - Information: %s", result.Informational[i]))
	}
	for i := range result.Notices {
		results = append(results, fmt.Sprintf(" - Notice: %s", result.Notices[i]))
	}
	for i := range result.Bugs {
		results = append(results, fmt.Sprintf(" - Bug: %s", result.Bugs[i]))
	}

	// Add header and summary as first two lines
	headers := []string{
		"* awslabs/certlint",
	}

	// We only want one summary line, so match in order of severity
	switch true {
	case len(result.FatalErrors) > 0 || len(result.Errors) > 0:
		headers = append(headers, fmt.Sprintf(" - %d errors, %d fatal", len(result.Errors), len(result.FatalErrors)))
	case len(result.Warnings) > 0:
		headers = append(headers, fmt.Sprintf(" - %d warnings found", len(result.Warnings)))
	}

	// Add header(s) as first line, print notice if nothing was found
	results = append(headers, results...)
	if len(results) == 1 {
		results = append(results, " - No messages")
	}
	return results, nil
}
<commit_msg>awsCertLint: silence log if binary not present<commit_after>package awsCertlint

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/mozilla/tls-observatory/certificate"
	"github.com/mozilla/tls-observatory/logger"
	"github.com/mozilla/tls-observatory/worker"
)

var (
	workerName = "awsCertlint"
	workerDesc = "Runs the awslabs certificate linter and saves output"

	certlintDirectory = "/go/certlint" // path from tools/Dockerfile-scanner
	binaryPath        = "bin/certlint" // path inside `certlintDirectory`

	log = logger.GetLogger()
)

type Result struct {
	Bugs          []string `json:"bugs"`
	Informational []string `json:"informational"`
	Notices       []string `json:"notices"`
	Warnings      []string `json:"warnings"`
	Errors        []string `json:"errors"`
	FatalErrors   []string `json:"fatalErrors"`
}

func init() {
	runner := new(eval)
	worker.RegisterPrinter(workerName, worker.Info{Runner: runner, Description: workerDesc})

	// override certlintDirectory if TLS_AWSCERTLINT_DIR
	if path := os.Getenv("TLS_AWSCERTLINT_DIR"); path != "" {
		certlintDirectory = path
	}

	// Verify code was pulled down
	fullPath := filepath.Join(certlintDirectory, binaryPath)
	_, err := os.Stat(fullPath)
	if err != nil && os.IsNotExist(err) {
		log.Debug("Could not find awslabs/certlint (tried %s), disabling worker\n", fullPath)
		return
	}
	worker.RegisterWorker(workerName, worker.Info{Runner: runner, Description: workerDesc})
}

type eval struct{}

func (e eval) Run(in worker.Input, resChan chan worker.Result) {
	result := worker.Result{
		WorkerName: workerName,
	}
	lintResult, err := e.runCertlint(in.Certificate)
	if err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("%s for certificate %d", err.Error(), in.Certificate.ID)},
			Result:     nil,
		}
		return
	}

	// Marshal the response
	bs, err := json.Marshal(&lintResult)
	if err != nil {
		result.Success = false
		result.Errors = append(result.Errors, err.Error())
	} else {
		result.Success = true
		result.Result = bs
	}
	resChan <- result
}

func (e eval) runCertlint(cert certificate.Certificate) (*Result, error) {
	tmp, err := ioutil.TempFile("", "awslabs-certlint")
	if err != nil {
		return nil, fmt.Errorf("error creating temp file for certificate %d", cert.ID)
	}
	defer os.Remove(tmp.Name())
	x509Cert, err := cert.ToX509()
	if err != nil {
		return nil, fmt.Errorf("error converting certificate %d to x509.Certificate", cert.ID)
	}
	if err := ioutil.WriteFile(tmp.Name(), x509Cert.Raw, 0644); err != nil {
		return nil, fmt.Errorf("error writing x509.Certificate to temp file, err=%v", err)
	}

	// Run certlint over certificate
	cmd := exec.Command("ruby", "-I", "lib:ext", binaryPath, tmp.Name())
	cmd.Dir = certlintDirectory

	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	if err := cmd.Start(); err != nil {
		return nil, fmt.Errorf("error starting awslabs/certlint on certificate %d, err=%v, out=%q", cert.ID, err, strings.TrimSpace(stderr.String()))
	}

	waitChan := make(chan error, 1)
	go func() {
		waitChan <- cmd.Wait()
	}()

	select {
	case <-time.After(30 * time.Second):
		err := cmd.Process.Kill()
		return nil, fmt.Errorf("timed out waiting for awslabs/certlint on certificate %d, kill error=%v", cert.ID, err)
	case err := <-waitChan:
		if err != nil {
			return nil, fmt.Errorf("error running awslabs/certlint on certificate %d, err=%v, out=%q", cert.ID, err, strings.TrimSpace(stderr.String()))
		}
	}

	return e.parseResponse(stdout)
}

// From: https://github.com/awslabs/certlint#output
//
// * B: Bug. Your certificate has a feature not handled by certlint.
// * I: Information.  These are purely informational; no action is needed.
// * N: Notice.  These are items known to cause issues with one or more implementations of certificate processing but are not errors according to the standard.
// * W: Warning.  These are issues where a standard recommends differently but the standard uses terms such as "SHOULD" or "MAY".
// * E: Error.  These are issues where the certificate is not compliant with the standard.
// * F: Fatal Error.  These errors are fatal to the checks and prevent most further checks from being executed.  These are extremely bad errors.
func (e eval) parseResponse(resp bytes.Buffer) (*Result, error) {
	out := &Result{}

	r := bufio.NewScanner(&resp)
	for r.Scan() {
		line := strings.TrimSpace(r.Text())
		if line == "" {
			continue
		}

		// Match first letter of each line, which signifies its type.
		switch line[0] {
		case 'B':
			out.Bugs = append(out.Bugs, strings.TrimSpace(line[2:]))
		case 'I':
			out.Informational = append(out.Informational, strings.TrimSpace(line[2:]))
		case 'N':
			out.Notices = append(out.Notices, strings.TrimSpace(line[2:]))
		case 'W':
			out.Warnings = append(out.Warnings, strings.TrimSpace(line[2:]))
		case 'E':
			out.Errors = append(out.Errors, strings.TrimSpace(line[2:]))
		case 'F':
			out.FatalErrors = append(out.FatalErrors, strings.TrimSpace(line[2:]))
		}
	}
	return out, nil
}

// AnalysisPrinter outputs the results from awslabs/certlint over a given certificate
func (e eval) AnalysisPrinter(input []byte, printAll interface{}) (results []string, err error) {
	var result Result
	if err := json.Unmarshal(input, &result); err != nil {
		return nil, fmt.Errorf("awsCertlint Worker: failed to parse results: err=%v", err)
	}

	// Build results for webview
	for i := range result.FatalErrors {
		results = append(results, fmt.Sprintf(" - Fatal Error: %s", result.FatalErrors[i]))
	}
	for i := range result.Errors {
		results = append(results, fmt.Sprintf(" - Error: %s", result.Errors[i]))
	}
	for i := range result.Warnings {
		results = append(results, fmt.Sprintf(" - Warning: %s", result.Warnings[i]))
	}
	for i := range result.Informational {
		results = append(results, fmt.Sprintf(" - Information: %s", result.Informational[i]))
	}
	for i := range result.Notices {
		results = append(results, fmt.Sprintf(" - Notice: %s", result.Notices[i]))
	}
	for i := range result.Bugs {
		results = append(results, fmt.Sprintf(" - Bug: %s", result.Bugs[i]))
	}

	// Add header and summary as first two lines
	headers := []string{
		"* awslabs/certlint",
	}

	// We only want one summary line, so match in order of severity
	switch true {
	case len(result.FatalErrors) > 0 || len(result.Errors) > 0:
		headers = append(headers, fmt.Sprintf(" - %d errors, %d fatal", len(result.Errors), len(result.FatalErrors)))
	case len(result.Warnings) > 0:
		headers = append(headers, fmt.Sprintf(" - %d warnings found", len(result.Warnings)))
	}

	// Add header(s) as first line, print notice if nothing was found
	results = append(headers, results...)
	if len(results) == 1 {
		results = append(results, " - No messages")
	}
	return results, nil
}
<|endoftext|>


<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

type symtype int

// symtype enumerates the types of symbols found in the program text.
const (
	IDSymbol     symtype = iota // Identifiers
	CaprefSymbol                // Capture group references
	DefSymbol                   // Definitions

	endSymbol
)

type symbol struct {
	name    string      // Symbol name
	kind    symtype     // Type
	binding interface{} // Binding to storage allocated
	loc     *position   // Source file position
	addr    int         // Address offset in another structure
	typ     Type        // Type of this symbol
}

type scope map[string][]*symbol

type SymbolTable []*scope

func (s *SymbolTable) EnterScope(sc *scope) *scope {
	if sc == nil {
		sc = &scope{}
	}
	*s = append(*s, sc)
	return sc
}

func (s *SymbolTable) ExitScope() {
	if len(*s) > 1 {
		*s = (*s)[:len(*s)-1]
	}
}

func (s *SymbolTable) CurrentScope() *scope {
	return (*s)[len(*s)-1]
}

func (s *SymbolTable) Lookup(name string, kind symtype) (*symbol, bool) {
	for i := len(*s) - 1; i >= 0; i-- {
		if r, ok := (*(*s)[i])[name]; ok && r[kind] != nil {
			return r[kind], ok
		}
	}
	return nil, false
}

func (s *SymbolTable) Add(name string, kind symtype, loc *position) (sym *symbol) {
	sym = &symbol{name, kind, nil, loc, 0, Int}
	cs := s.CurrentScope()
	if _, ok := (*cs)[name]; !ok {
		(*cs)[name] = make([]*symbol, endSymbol)
	}
	(*cs)[name][kind] = sym
	return sym
}
<commit_msg>Renames symtype to SymbolClass.<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

type SymbolClass int

// symtype enumerates the types of symbols found in the program text.
const (
	IDSymbol     SymbolClass = iota // Identifiers
	CaprefSymbol                    // Capture group references
	DefSymbol                       // Definitions

	endSymbol
)

type symbol struct {
	name    string      // Symbol name
	class   SymbolClass // Type
	binding interface{} // Binding to storage allocated
	loc     *position   // Source file position
	addr    int         // Address offset in another structure
	typ     Type        // Type of this symbol
}

type scope map[string][]*symbol

type SymbolTable []*scope

func (s *SymbolTable) EnterScope(sc *scope) *scope {
	if sc == nil {
		sc = &scope{}
	}
	*s = append(*s, sc)
	return sc
}

func (s *SymbolTable) ExitScope() {
	if len(*s) > 1 {
		*s = (*s)[:len(*s)-1]
	}
}

func (s *SymbolTable) CurrentScope() *scope {
	return (*s)[len(*s)-1]
}

func (s *SymbolTable) Lookup(name string, class SymbolClass) (*symbol, bool) {
	for i := len(*s) - 1; i >= 0; i-- {
		if r, ok := (*(*s)[i])[name]; ok && r[class] != nil {
			return r[class], ok
		}
	}
	return nil, false
}

func (s *SymbolTable) Add(name string, class SymbolClass, loc *position) (sym *symbol) {
	sym = &symbol{name, class, nil, loc, 0, Int}
	cs := s.CurrentScope()
	if _, ok := (*cs)[name]; !ok {
		(*cs)[name] = make([]*symbol, endSymbol)
	}
	(*cs)[name][class] = sym
	return sym
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package sinkbinding

import (
	"context"
	"fmt"

	sbinformer "knative.dev/eventing/pkg/client/injection/informers/sources/v1/sinkbinding"
	"knative.dev/pkg/client/injection/ducks/duck/v1/podspecable"
	"knative.dev/pkg/client/injection/kube/informers/core/v1/namespace"
	"knative.dev/pkg/reconciler"
	"knative.dev/pkg/resolver"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	v1 "knative.dev/eventing/pkg/apis/sources/v1"
	"knative.dev/pkg/apis/duck"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/injection/clients/dynamicclient"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/tracker"
	"knative.dev/pkg/webhook/psbinding"
)

const (
	controllerAgentName = "sinkbinding-controller"
)

type SinkBindingSubResourcesReconciler struct {
	res *resolver.URIResolver
}

// NewController returns a new SinkBinding reconciler.
func NewController(
	ctx context.Context,
	cmw configmap.Watcher,
) *controller.Impl {
	logger := logging.FromContext(ctx)

	sbInformer := sbinformer.Get(ctx)
	dc := dynamicclient.Get(ctx)
	psInformerFactory := podspecable.Get(ctx)
	namespaceInformer := namespace.Get(ctx)
	c := &psbinding.BaseReconciler{
		LeaderAwareFuncs: reconciler.LeaderAwareFuncs{
			PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error {
				all, err := sbInformer.Lister().List(labels.Everything())
				if err != nil {
					return err
				}
				for _, elt := range all {
					enq(bkt, types.NamespacedName{
						Namespace: elt.GetNamespace(),
						Name:      elt.GetName(),
					})
				}
				return nil
			},
		},
		GVR: v1.SchemeGroupVersion.WithResource("sinkbindings"),
		Get: func(namespace string, name string) (psbinding.Bindable, error) {
			return sbInformer.Lister().SinkBindings(namespace).Get(name)
		},
		DynamicClient: dc,
		Recorder: record.NewBroadcaster().NewRecorder(
			scheme.Scheme, corev1.EventSource{Component: controllerAgentName}),
		NamespaceLister: namespaceInformer.Lister(),
	}
	impl := controller.NewImpl(c, logger, "SinkBindings")

	logger.Info("Setting up event handlers")

	sbInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
	namespaceInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))

	sbResolver := resolver.NewURIResolver(ctx, impl.EnqueueKey)
	c.SubResourcesReconciler = &SinkBindingSubResourcesReconciler{
		res: sbResolver,
	}

	c.WithContext = func(ctx context.Context, b psbinding.Bindable) (context.Context, error) {
		return v1.WithURIResolver(ctx, sbResolver), nil
	}
	c.Tracker = tracker.New(impl.EnqueueKey, controller.GetTrackerLease(ctx))
	c.Factory = &duck.CachedInformerFactory{
		Delegate: &duck.EnqueueInformerFactory{
			Delegate:     psInformerFactory,
			EventHandler: controller.HandleAll(c.Tracker.OnChanged),
		},
	}

	return impl
}

func ListAll(ctx context.Context, handler cache.ResourceEventHandler) psbinding.ListAll {
	fbInformer := sbinformer.Get(ctx)

	// Whenever a SinkBinding changes our webhook programming might change.
	fbInformer.Informer().AddEventHandler(handler)

	return func() ([]psbinding.Bindable, error) {
		l, err := fbInformer.Lister().List(labels.Everything())
		if err != nil {
			return nil, err
		}
		bl := make([]psbinding.Bindable, 0, len(l))
		for _, elt := range l {
			bl = append(bl, elt)
		}
		return bl, nil
	}

}

func WithContextFactory(ctx context.Context, handler func(types.NamespacedName)) psbinding.BindableContext {
	r := resolver.NewURIResolver(ctx, handler)

	return func(ctx context.Context, b psbinding.Bindable) (context.Context, error) {
		return v1.WithURIResolver(ctx, r), nil
	}
}

func (s *SinkBindingSubResourcesReconciler) Reconcile(ctx context.Context, b psbinding.Bindable) error {
	sb := b.(*v1.SinkBinding)
	if s.res == nil {
		err := fmt.Errorf("Resolver is nil")
		logging.FromContext(ctx).Errorf("%w", err)
		sb.Status.MarkBindingUnavailable("NoResolver", "No Resolver associated with context for sink")
		return err
	}
	uri, err := s.res.URIFromDestinationV1(ctx, sb.Spec.Sink, sb)
	if err != nil {
		logging.FromContext(ctx).Errorf("Failed to get URI from Destination: %w", err)
		sb.Status.MarkBindingUnavailable("NoURI", "URI could not be extracted from destination ")
		return err
	}
	sb.Status.MarkSink(uri)
	return nil
}

// I'm just here so I won't get fined
func (*SinkBindingSubResourcesReconciler) ReconcileDeletion(ctx context.Context, b psbinding.Bindable) error {
	return nil
}
<commit_msg>SinkBindings track their sinks (#5410)<commit_after>/*
Copyright 2020 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package sinkbinding

import (
	"context"
	"fmt"

	sbinformer "knative.dev/eventing/pkg/client/injection/informers/sources/v1/sinkbinding"
	"knative.dev/pkg/client/injection/ducks/duck/v1/podspecable"
	"knative.dev/pkg/client/injection/kube/informers/core/v1/namespace"
	"knative.dev/pkg/reconciler"
	"knative.dev/pkg/resolver"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	v1 "knative.dev/eventing/pkg/apis/sources/v1"
	"knative.dev/pkg/apis/duck"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/injection/clients/dynamicclient"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/tracker"
	"knative.dev/pkg/webhook/psbinding"
)

const (
	controllerAgentName = "sinkbinding-controller"
)

type SinkBindingSubResourcesReconciler struct {
	res     *resolver.URIResolver
	tracker tracker.Interface
}

// NewController returns a new SinkBinding reconciler.
func NewController(
	ctx context.Context,
	cmw configmap.Watcher,
) *controller.Impl {
	logger := logging.FromContext(ctx)

	sbInformer := sbinformer.Get(ctx)
	dc := dynamicclient.Get(ctx)
	psInformerFactory := podspecable.Get(ctx)
	namespaceInformer := namespace.Get(ctx)
	c := &psbinding.BaseReconciler{
		LeaderAwareFuncs: reconciler.LeaderAwareFuncs{
			PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error {
				all, err := sbInformer.Lister().List(labels.Everything())
				if err != nil {
					return err
				}
				for _, elt := range all {
					enq(bkt, types.NamespacedName{
						Namespace: elt.GetNamespace(),
						Name:      elt.GetName(),
					})
				}
				return nil
			},
		},
		GVR: v1.SchemeGroupVersion.WithResource("sinkbindings"),
		Get: func(namespace string, name string) (psbinding.Bindable, error) {
			return sbInformer.Lister().SinkBindings(namespace).Get(name)
		},
		DynamicClient: dc,
		Recorder: record.NewBroadcaster().NewRecorder(
			scheme.Scheme, corev1.EventSource{Component: controllerAgentName}),
		NamespaceLister: namespaceInformer.Lister(),
	}
	impl := controller.NewImpl(c, logger, "SinkBindings")

	logger.Info("Setting up event handlers")

	sbInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
	namespaceInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))

	sbResolver := resolver.NewURIResolver(ctx, impl.EnqueueKey)
	tracker := tracker.New(impl.EnqueueKey, controller.GetTrackerLease(ctx))
	c.SubResourcesReconciler = &SinkBindingSubResourcesReconciler{
		res:     sbResolver,
		tracker: tracker,
	}

	c.WithContext = func(ctx context.Context, b psbinding.Bindable) (context.Context, error) {
		return v1.WithURIResolver(ctx, sbResolver), nil
	}
	c.Tracker = tracker
	c.Factory = &duck.CachedInformerFactory{
		Delegate: &duck.EnqueueInformerFactory{
			Delegate:     psInformerFactory,
			EventHandler: controller.HandleAll(c.Tracker.OnChanged),
		},
	}

	return impl
}

func ListAll(ctx context.Context, handler cache.ResourceEventHandler) psbinding.ListAll {
	fbInformer := sbinformer.Get(ctx)

	// Whenever a SinkBinding changes our webhook programming might change.
	fbInformer.Informer().AddEventHandler(handler)

	return func() ([]psbinding.Bindable, error) {
		l, err := fbInformer.Lister().List(labels.Everything())
		if err != nil {
			return nil, err
		}
		bl := make([]psbinding.Bindable, 0, len(l))
		for _, elt := range l {
			bl = append(bl, elt)
		}
		return bl, nil
	}

}

func WithContextFactory(ctx context.Context, handler func(types.NamespacedName)) psbinding.BindableContext {
	r := resolver.NewURIResolver(ctx, handler)

	return func(ctx context.Context, b psbinding.Bindable) (context.Context, error) {
		return v1.WithURIResolver(ctx, r), nil
	}
}

func (s *SinkBindingSubResourcesReconciler) Reconcile(ctx context.Context, b psbinding.Bindable) error {
	sb := b.(*v1.SinkBinding)
	if s.res == nil {
		err := fmt.Errorf("Resolver is nil")
		logging.FromContext(ctx).Errorf("%w", err)
		sb.Status.MarkBindingUnavailable("NoResolver", "No Resolver associated with context for sink")
		return err
	}
	if sb.Spec.Sink.Ref != nil {
		s.tracker.TrackReference(tracker.Reference{
			APIVersion: sb.Spec.Sink.Ref.APIVersion,
			Kind:       sb.Spec.Sink.Ref.Kind,
			Namespace:  sb.Spec.Sink.Ref.Namespace,
			Name:       sb.Spec.Sink.Ref.Name,
		}, b)
	}
	uri, err := s.res.URIFromDestinationV1(ctx, sb.Spec.Sink, sb)
	if err != nil {
		logging.FromContext(ctx).Errorf("Failed to get URI from Destination: %w", err)
		sb.Status.MarkBindingUnavailable("NoURI", "URI could not be extracted from destination ")
		return err
	}
	sb.Status.MarkSink(uri)
	return nil
}

// I'm just here so I won't get fined
func (*SinkBindingSubResourcesReconciler) ReconcileDeletion(ctx context.Context, b psbinding.Bindable) error {
	return nil
}
<|endoftext|>