<commit_before>package output

import (
	"encoding/json"
	"errors"
	"fmt"
	"sync/atomic"
	"time"

	"github.com/Jeffail/benthos/v3/lib/condition"
	"github.com/Jeffail/benthos/v3/lib/log"
	"github.com/Jeffail/benthos/v3/lib/metrics"
	"github.com/Jeffail/benthos/v3/lib/response"
	"github.com/Jeffail/benthos/v3/lib/types"
	"github.com/Jeffail/benthos/v3/lib/util/throttle"
)

//------------------------------------------------------------------------------

var (
	// ErrSwitchNoConditionMet is returned when a message does not match any
	// output conditions.
	ErrSwitchNoConditionMet = errors.New("no switch output conditions were met by message")
	// ErrSwitchNoOutputs is returned when creating a Switch type with less than
	// 2 outputs.
	ErrSwitchNoOutputs = errors.New("attempting to create switch with less than 2 outputs")
)

//------------------------------------------------------------------------------

func init() {
	Constructors[TypeSwitch] = TypeSpec{
		constructor: NewSwitch,
		Summary: `
The switch output type allows you to configure multiple conditional output
targets by listing child outputs paired with conditions.`,
		Description: `
When [batching messages at the input level](/docs/configuration/batching/)
conditional logic is applied across the entire batch. In order to multiplex per
message of a batch use the ` + "[`broker`](/docs/components/outputs/broker)" + `
output with the pattern ` + "`fan_out`" + `.

In the following example, messages containing "foo" will be sent to both the
` + "`foo`" + ` and ` + "`baz`" + ` outputs. Messages containing "bar" will be
sent to both the ` + "`bar`" + ` and ` + "`baz`" + ` outputs. Messages
containing both "foo" and "bar" will be sent to all three outputs. And finally,
messages that do not contain "foo" or "bar" will be sent to the ` + "`baz`" + `
output only.

` + "``` yaml" + `
output:
  switch:
    retry_until_success: true
    outputs:
    - output:
        foo:
          foo_field_1: value1
      condition:
        text:
          operator: contains
          arg: foo
      fallthrough: true
    - output:
        bar:
          bar_field_1: value2
          bar_field_2: value3
      condition:
        text:
          operator: contains
          arg: bar
      fallthrough: true
    - output:
        baz:
          baz_field_1: value4
        processors:
        - type: baz_processor
  processors:
  - type: some_processor
` + "```" + `

The switch output requires a minimum of two outputs. If no condition is defined
for an output, it behaves like a static ` + "`true`" + ` condition. If
` + "`fallthrough`" + ` is set to ` + "`true`" + `, the switch output will
continue evaluating additional outputs after finding a match.

Messages that do not match any outputs will be dropped. If an output applies
back pressure it will block all subsequent messages.

If an output fails to send a message it will be retried continuously until
completion or service shut down. You can change this behaviour so that when an
output returns an error the switch output also returns an error by setting
` + "`retry_until_success`" + ` to ` + "`false`" + `. This allows you to
wrap the switch with a ` + "`try`" + ` broker, but care must be taken to ensure
duplicate messages aren't introduced during error conditions.`,
		sanitiseConfigFunc: func(conf Config) (interface{}, error) {
			outSlice := []interface{}{}
			for _, out := range conf.Switch.Outputs {
				sanOutput, err := SanitiseConfig(out.Output)
				if err != nil {
					return nil, err
				}
				var sanCond interface{}
				if sanCond, err = condition.SanitiseConfig(out.Condition); err != nil {
					return nil, err
				}
				sanit := map[string]interface{}{
					"output":      sanOutput,
					"fallthrough": out.Fallthrough,
					"condition":   sanCond,
				}
				outSlice = append(outSlice, sanit)
			}
			return map[string]interface{}{
				"retry_until_success": conf.Switch.RetryUntilSuccess,
				"outputs":             outSlice,
			}, nil
		},
	}
}

//------------------------------------------------------------------------------

// SwitchConfig contains configuration fields for the Switch output type.
type SwitchConfig struct {
	RetryUntilSuccess bool                 `json:"retry_until_success" yaml:"retry_until_success"`
	Outputs           []SwitchConfigOutput `json:"outputs" yaml:"outputs"`
}

// NewSwitchConfig creates a new SwitchConfig with default values.
func NewSwitchConfig() SwitchConfig {
	return SwitchConfig{
		RetryUntilSuccess: true,
		Outputs:           []SwitchConfigOutput{},
	}
}

// SwitchConfigOutput contains configuration fields per output of a switch type.
type SwitchConfigOutput struct {
	Condition   condition.Config `json:"condition" yaml:"condition"`
	Fallthrough bool             `json:"fallthrough" yaml:"fallthrough"`
	Output      Config           `json:"output" yaml:"output"`
}

// NewSwitchConfigOutput creates a new switch output config with default values.
func NewSwitchConfigOutput() SwitchConfigOutput {
	cond := condition.NewConfig()
	cond.Type = condition.TypeStatic
	cond.Static = true

	return SwitchConfigOutput{
		Condition:   cond,
		Fallthrough: false,
		Output:      NewConfig(),
	}
}

//------------------------------------------------------------------------------

// UnmarshalJSON ensures that when parsing configs that are in a map or slice
// the default values are still applied.
func (s *SwitchConfigOutput) UnmarshalJSON(bytes []byte) error {
	type confAlias SwitchConfigOutput
	aliased := confAlias(NewSwitchConfigOutput())

	if err := json.Unmarshal(bytes, &aliased); err != nil {
		return err
	}

	*s = SwitchConfigOutput(aliased)
	return nil
}

// UnmarshalYAML ensures that when parsing configs that are in a map or slice
// the default values are still applied.
func (s *SwitchConfigOutput) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type confAlias SwitchConfigOutput
	aliased := confAlias(NewSwitchConfigOutput())

	if err := unmarshal(&aliased); err != nil {
		return err
	}

	*s = SwitchConfigOutput(aliased)
	return nil
}

//------------------------------------------------------------------------------

// Switch is a broker that implements types.Consumer and broadcasts each message
// out to an array of outputs.
type Switch struct {
	running int32

	logger log.Modular
	stats  metrics.Type

	throt *throttle.Type

	transactions <-chan types.Transaction

	outputTsChans  []chan types.Transaction
	outputResChans []chan types.Response

	retryUntilSuccess bool
	outputs           []types.Output
	conditions        []types.Condition
	fallthroughs      []bool

	closedChan chan struct{}
	closeChan  chan struct{}
}

// NewSwitch creates a new Switch type by providing outputs. Messages will be
// sent to a subset of outputs according to condition and fallthrough settings.
func NewSwitch(
	conf Config,
	mgr types.Manager,
	logger log.Modular,
	stats metrics.Type,
) (Type, error) {
	lOutputs := len(conf.Switch.Outputs)
	if lOutputs < 2 {
		return nil, ErrSwitchNoOutputs
	}

	o := &Switch{
		running:           1,
		stats:             stats,
		logger:            logger,
		transactions:      nil,
		outputs:           make([]types.Output, lOutputs),
		conditions:        make([]types.Condition, lOutputs),
		fallthroughs:      make([]bool, lOutputs),
		retryUntilSuccess: conf.Switch.RetryUntilSuccess,
		closedChan:        make(chan struct{}),
		closeChan:         make(chan struct{}),
	}

	var err error
	for i, oConf := range conf.Switch.Outputs {
		ns := fmt.Sprintf("switch.%v", i)
		if o.outputs[i], err = New(
			oConf.Output, mgr,
			logger.NewModule("."+ns+".output"),
			metrics.Combine(stats, metrics.Namespaced(stats, ns+".output")),
		); err != nil {
			return nil, fmt.Errorf("failed to create output '%v' type '%v': %v", i, oConf.Output.Type, err)
		}
		if o.conditions[i], err = condition.New(
			oConf.Condition, mgr,
			logger.NewModule("."+ns+".condition"),
			metrics.Namespaced(stats, ns+".condition"),
		); err != nil {
			return nil, fmt.Errorf("failed to create output '%v' condition '%v': %v", i, oConf.Condition.Type, err)
		}
		o.fallthroughs[i] = oConf.Fallthrough
	}

	o.throt = throttle.New(throttle.OptCloseChan(o.closeChan))

	o.outputTsChans = make([]chan types.Transaction, len(o.outputs))
	o.outputResChans = make([]chan types.Response, len(o.outputs))
	for i := range o.outputTsChans {
		o.outputTsChans[i] = make(chan types.Transaction)
		o.outputResChans[i] = make(chan types.Response)
		if err := o.outputs[i].Consume(o.outputTsChans[i]); err != nil {
			return nil, err
		}
	}
	return o, nil
}

//------------------------------------------------------------------------------

// Consume assigns a new transactions channel for the broker to read.
func (o *Switch) Consume(transactions <-chan types.Transaction) error {
	if o.transactions != nil {
		return types.ErrAlreadyStarted
	}
	o.transactions = transactions

	go o.loop()
	return nil
}

// Connected returns a boolean indicating whether this output is currently
// connected to its target.
func (o *Switch) Connected() bool {
	for _, out := range o.outputs {
		if !out.Connected() {
			return false
		}
	}
	return true
}

//------------------------------------------------------------------------------

// loop is an internal loop that brokers incoming messages to many outputs.
func (o *Switch) loop() {
	var (
		mMsgDrop   = o.stats.GetCounter("switch.messages.dropped")
		mMsgRcvd   = o.stats.GetCounter("switch.messages.received")
		mMsgSnt    = o.stats.GetCounter("switch.messages.sent")
		mOutputErr = o.stats.GetCounter("switch.output.error")
	)

	defer func() {
		for i, output := range o.outputs {
			output.CloseAsync()
			close(o.outputTsChans[i])
		}
		for _, output := range o.outputs {
			if err := output.WaitForClose(time.Second); err != nil {
				for err != nil {
					err = output.WaitForClose(time.Second)
				}
			}
		}
		close(o.closedChan)
	}()

	for atomic.LoadInt32(&o.running) == 1 {
		var ts types.Transaction
		var open bool

		select {
		case ts, open = <-o.transactions:
			if !open {
				return
			}
		case <-o.closeChan:
			return
		}
		mMsgRcvd.Incr(1)

		var outputTargets []int
		for i, oCond := range o.conditions {
			if oCond.Check(ts.Payload) {
				outputTargets = append(outputTargets, i)
				if !o.fallthroughs[i] {
					break
				}
			}
		}
		if len(outputTargets) == 0 {
			select {
			case ts.ResponseChan <- response.NewAck():
				mMsgDrop.Incr(1)
			case <-o.closeChan:
				return
			}
			continue
		}

		var oResponse types.Response

	outputsLoop:
		for len(outputTargets) > 0 {
			for _, i := range outputTargets {
				msgCopy := ts.Payload.Copy()
				select {
				case o.outputTsChans[i] <- types.NewTransaction(msgCopy, o.outputResChans[i]):
				case <-o.closeChan:
					return
				}
			}
			newTargets := []int{}
			for _, i := range outputTargets {
				select {
				case res := <-o.outputResChans[i]:
					if res.Error() != nil {
						if o.retryUntilSuccess {
							newTargets = append(newTargets, i)
							o.logger.Errorf("Failed to dispatch switch message: %v\n", res.Error())
							mOutputErr.Incr(1)
							if !o.throt.Retry() {
								return
							}
						} else {
							oResponse = res
						}
					} else {
						o.throt.Reset()
						mMsgSnt.Incr(1)
					}
				case <-o.closeChan:
					return
				}
			}
			outputTargets = newTargets
			if oResponse != nil {
				break outputsLoop
			}
		}
		if oResponse == nil {
			oResponse = response.NewAck()
		}
		select {
		case ts.ResponseChan <- oResponse:
		case <-o.closeChan:
			return
		}
	}
}

// CloseAsync shuts down the Switch broker and stops processing requests.
func (o *Switch) CloseAsync() {
	if atomic.CompareAndSwapInt32(&o.running, 1, 0) {
		close(o.closeChan)
	}
}

// WaitForClose blocks until the Switch broker has closed down.
func (o *Switch) WaitForClose(timeout time.Duration) error {
	select {
	case <-o.closedChan:
	case <-time.After(timeout):
		return types.ErrTimeout
	}
	return nil
}

//------------------------------------------------------------------------------
<commit_msg>Move switch implementation to use async<commit_after>package output

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/Jeffail/benthos/v3/lib/condition"
	"github.com/Jeffail/benthos/v3/lib/log"
	"github.com/Jeffail/benthos/v3/lib/metrics"
	"github.com/Jeffail/benthos/v3/lib/response"
	"github.com/Jeffail/benthos/v3/lib/types"
	"github.com/Jeffail/benthos/v3/lib/util/throttle"
	"golang.org/x/sync/errgroup"
)

//------------------------------------------------------------------------------

var (
	// ErrSwitchNoConditionMet is returned when a message does not match any
	// output conditions.
	ErrSwitchNoConditionMet = errors.New("no switch output conditions were met by message")
	// ErrSwitchNoOutputs is returned when creating a Switch type with less than
	// 2 outputs.
	ErrSwitchNoOutputs = errors.New("attempting to create switch with less than 2 outputs")
)

//------------------------------------------------------------------------------

func init() {
	Constructors[TypeSwitch] = TypeSpec{
		constructor: NewSwitch,
		Summary: `
The switch output type allows you to configure multiple conditional output
targets by listing child outputs paired with conditions.`,
		Description: `
When [batching messages at the input level](/docs/configuration/batching/)
conditional logic is applied across the entire batch. In order to multiplex per
message of a batch use the ` + "[`broker`](/docs/components/outputs/broker)" + `
output with the pattern ` + "`fan_out`" + `.

In the following example, messages containing "foo" will be sent to both the
` + "`foo`" + ` and ` + "`baz`" + ` outputs. Messages containing "bar" will be
sent to both the ` + "`bar`" + ` and ` + "`baz`" + ` outputs. Messages
containing both "foo" and "bar" will be sent to all three outputs. And finally,
messages that do not contain "foo" or "bar" will be sent to the ` + "`baz`" + `
output only.

` + "``` yaml" + `
output:
  switch:
    retry_until_success: true
    outputs:
    - output:
        foo:
          foo_field_1: value1
      condition:
        text:
          operator: contains
          arg: foo
      fallthrough: true
    - output:
        bar:
          bar_field_1: value2
          bar_field_2: value3
      condition:
        text:
          operator: contains
          arg: bar
      fallthrough: true
    - output:
        baz:
          baz_field_1: value4
        processors:
        - type: baz_processor
  processors:
  - type: some_processor
` + "```" + `

The switch output requires a minimum of two outputs. If no condition is defined
for an output, it behaves like a static ` + "`true`" + ` condition. If
` + "`fallthrough`" + ` is set to ` + "`true`" + `, the switch output will
continue evaluating additional outputs after finding a match.

Messages that do not match any outputs will be dropped. If an output applies
back pressure it will block all subsequent messages.

If an output fails to send a message it will be retried continuously until
completion or service shut down. You can change this behaviour so that when an
output returns an error the switch output also returns an error by setting
` + "`retry_until_success`" + ` to ` + "`false`" + `. This allows you to
wrap the switch with a ` + "`try`" + ` broker, but care must be taken to ensure
duplicate messages aren't introduced during error conditions.`,
		sanitiseConfigFunc: func(conf Config) (interface{}, error) {
			outSlice := []interface{}{}
			for _, out := range conf.Switch.Outputs {
				sanOutput, err := SanitiseConfig(out.Output)
				if err != nil {
					return nil, err
				}
				var sanCond interface{}
				if sanCond, err = condition.SanitiseConfig(out.Condition); err != nil {
					return nil, err
				}
				sanit := map[string]interface{}{
					"output":      sanOutput,
					"fallthrough": out.Fallthrough,
					"condition":   sanCond,
				}
				outSlice = append(outSlice, sanit)
			}
			return map[string]interface{}{
				"retry_until_success": conf.Switch.RetryUntilSuccess,
				"outputs":             outSlice,
			}, nil
		},
	}
}

//------------------------------------------------------------------------------

// SwitchConfig contains configuration fields for the Switch output type.
type SwitchConfig struct {
	RetryUntilSuccess bool                 `json:"retry_until_success" yaml:"retry_until_success"`
	Outputs           []SwitchConfigOutput `json:"outputs" yaml:"outputs"`
}

// NewSwitchConfig creates a new SwitchConfig with default values.
func NewSwitchConfig() SwitchConfig {
	return SwitchConfig{
		RetryUntilSuccess: true,
		Outputs:           []SwitchConfigOutput{},
	}
}

// SwitchConfigOutput contains configuration fields per output of a switch type.
type SwitchConfigOutput struct {
	Condition   condition.Config `json:"condition" yaml:"condition"`
	Fallthrough bool             `json:"fallthrough" yaml:"fallthrough"`
	Output      Config           `json:"output" yaml:"output"`
}

// NewSwitchConfigOutput creates a new switch output config with default values.
func NewSwitchConfigOutput() SwitchConfigOutput {
	cond := condition.NewConfig()
	cond.Type = condition.TypeStatic
	cond.Static = true

	return SwitchConfigOutput{
		Condition:   cond,
		Fallthrough: false,
		Output:      NewConfig(),
	}
}

//------------------------------------------------------------------------------

// UnmarshalJSON ensures that when parsing configs that are in a map or slice
// the default values are still applied.
func (s *SwitchConfigOutput) UnmarshalJSON(bytes []byte) error {
	type confAlias SwitchConfigOutput
	aliased := confAlias(NewSwitchConfigOutput())

	if err := json.Unmarshal(bytes, &aliased); err != nil {
		return err
	}

	*s = SwitchConfigOutput(aliased)
	return nil
}

// UnmarshalYAML ensures that when parsing configs that are in a map or slice
// the default values are still applied.
func (s *SwitchConfigOutput) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type confAlias SwitchConfigOutput
	aliased := confAlias(NewSwitchConfigOutput())

	if err := unmarshal(&aliased); err != nil {
		return err
	}

	*s = SwitchConfigOutput(aliased)
	return nil
}

//------------------------------------------------------------------------------

// Switch is a broker that implements types.Consumer and broadcasts each message
// out to an array of outputs.
type Switch struct {
	logger log.Modular
	stats  metrics.Type

	transactions <-chan types.Transaction

	retryUntilSuccess bool
	outputTsChans     []chan types.Transaction
	outputs           []types.Output
	conditions        []types.Condition
	fallthroughs      []bool

	ctx        context.Context
	close      func()
	closedChan chan struct{}
}

// NewSwitch creates a new Switch type by providing outputs. Messages will be
// sent to a subset of outputs according to condition and fallthrough settings.
func NewSwitch(
	conf Config,
	mgr types.Manager,
	logger log.Modular,
	stats metrics.Type,
) (Type, error) {
	lOutputs := len(conf.Switch.Outputs)
	if lOutputs < 2 {
		return nil, ErrSwitchNoOutputs
	}

	ctx, done := context.WithCancel(context.Background())
	o := &Switch{
		stats:             stats,
		logger:            logger,
		transactions:      nil,
		outputs:           make([]types.Output, lOutputs),
		conditions:        make([]types.Condition, lOutputs),
		fallthroughs:      make([]bool, lOutputs),
		retryUntilSuccess: conf.Switch.RetryUntilSuccess,
		closedChan:        make(chan struct{}),
		ctx:               ctx,
		close:             done,
	}

	var err error
	for i, oConf := range conf.Switch.Outputs {
		ns := fmt.Sprintf("switch.%v", i)
		if o.outputs[i], err = New(
			oConf.Output, mgr,
			logger.NewModule("."+ns+".output"),
			metrics.Combine(stats, metrics.Namespaced(stats, ns+".output")),
		); err != nil {
			return nil, fmt.Errorf("failed to create output '%v' type '%v': %v", i, oConf.Output.Type, err)
		}
		if o.conditions[i], err = condition.New(
			oConf.Condition, mgr,
			logger.NewModule("."+ns+".condition"),
			metrics.Namespaced(stats, ns+".condition"),
		); err != nil {
			return nil, fmt.Errorf("failed to create output '%v' condition '%v': %v", i, oConf.Condition.Type, err)
		}
		o.fallthroughs[i] = oConf.Fallthrough
	}

	o.outputTsChans = make([]chan types.Transaction, len(o.outputs))
	for i := range o.outputTsChans {
		o.outputTsChans[i] = make(chan types.Transaction)
		if err := o.outputs[i].Consume(o.outputTsChans[i]); err != nil {
			return nil, err
		}
	}
	return o, nil
}

//------------------------------------------------------------------------------

// Consume assigns a new transactions channel for the broker to read.
func (o *Switch) Consume(transactions <-chan types.Transaction) error {
	if o.transactions != nil {
		return types.ErrAlreadyStarted
	}
	o.transactions = transactions

	go o.loop()
	return nil
}

// Connected returns a boolean indicating whether this output is currently
// connected to its target.
func (o *Switch) Connected() bool {
	for _, out := range o.outputs {
		if !out.Connected() {
			return false
		}
	}
	return true
}

//------------------------------------------------------------------------------

// loop is an internal loop that brokers incoming messages to many outputs.
func (o *Switch) loop() {
	var (
		wg         = sync.WaitGroup{}
		mMsgRcvd   = o.stats.GetCounter("switch.messages.received")
		mMsgSnt    = o.stats.GetCounter("switch.messages.sent")
		mOutputErr = o.stats.GetCounter("switch.output.error")
	)

	defer func() {
		wg.Wait()
		for i, output := range o.outputs {
			output.CloseAsync()
			close(o.outputTsChans[i])
		}
		for _, output := range o.outputs {
			if err := output.WaitForClose(time.Second); err != nil {
				for err != nil {
					err = output.WaitForClose(time.Second)
				}
			}
		}
		close(o.closedChan)
	}()

	// This gets locked if an outbound message is running in an error loop for
	// an output. It prevents consuming new messages until the error is
	// resolved.
	var errMut sync.RWMutex
	for {
		var ts types.Transaction
		var open bool

		errMut.Lock()
		errMut.Unlock()
		select {
		case ts, open = <-o.transactions:
			if !open {
				return
			}
		case <-o.ctx.Done():
			return
		}
		mMsgRcvd.Incr(1)

		var outputTargets []int
		for i, oCond := range o.conditions {
			if oCond.Check(ts.Payload) {
				outputTargets = append(outputTargets, i)
				if !o.fallthroughs[i] {
					break
				}
			}
		}

		wg.Add(1)
		bp, received := context.WithCancel(context.Background())
		go func() {
			defer wg.Done()

			var owg errgroup.Group
			for _, target := range outputTargets {
				msgCopy, i := ts.Payload.Copy(), target
				owg.Go(func() error {
					throt := throttle.New(throttle.OptCloseChan(o.ctx.Done()))
					resChan := make(chan types.Response)
					failed := false

					// Try until success or shutdown.
					for {
						select {
						case o.outputTsChans[i] <- types.NewTransaction(msgCopy, resChan):
							received()
						case <-o.ctx.Done():
							return types.ErrTypeClosed
						}
						select {
						case res := <-resChan:
							if res.Error() != nil {
								if o.retryUntilSuccess {
									// Once an output returns an error we block
									// incoming messages until the problem is
									// resolved.
									//
									// We claim a read lock so that other outbound
									// messages are also able to retry.
									if !failed {
										errMut.RLock()
										defer errMut.RUnlock()
										failed = true
									}
									o.logger.Errorf("Failed to dispatch switch message: %v\n", res.Error())
									mOutputErr.Incr(1)
									if !throt.Retry() {
										return types.ErrTypeClosed
									}
								} else {
									return res.Error()
								}
							} else {
								mMsgSnt.Incr(1)
								return nil
							}
						case <-o.ctx.Done():
							return types.ErrTypeClosed
						}
					}
				})
			}

			var oResponse types.Response = response.NewAck()
			if resErr := owg.Wait(); resErr != nil {
				oResponse = response.NewError(resErr)
			}
			select {
			case ts.ResponseChan <- oResponse:
			case <-o.ctx.Done():
				return
			}
		}()

		// Block until one output has accepted our message or the service is
		// closing. This ensures we preserve back pressure when outputs are
		// saturated.
		select {
		case <-bp.Done():
		case <-o.ctx.Done():
			return
		}
	}
}

// CloseAsync shuts down the Switch broker and stops processing requests.
func (o *Switch) CloseAsync() {
	o.close()
}

// WaitForClose blocks until the Switch broker has closed down.
func (o *Switch) WaitForClose(timeout time.Duration) error {
	select {
	case <-o.closedChan:
	case <-time.After(timeout):
		return types.ErrTimeout
	}
	return nil
}

//------------------------------------------------------------------------------
<|endoftext|>


<commit_before>// Package history implements functionality for fetching/adding to play history.
package history

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"sync"
	"time"

	"tchaik.com/index"
)

// Store is an interface which defines methods necessary for fetching/adding to play history for
// index paths.  All times are stored in UTC.
type Store interface {
	// Add a play event to the store.
	Add(index.Path) error
	// Get the play events associated to a path.
	Get(index.Path) []time.Time
}

// NewStore creates a basic implementation of a play history store, using the given path as the
// source of data. If the file does not exist it will be created.
func NewStore(path string) (Store, error) {
	f, err := os.Open(path)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}
		f, err = os.Create(path)
		if err != nil {
			return nil, err
		}
	}
	defer f.Close()

	m := make(map[string][]time.Time)
	dec := json.NewDecoder(f)
	err = dec.Decode(&m)
	if err != nil && err != io.EOF {
		return nil, err
	}

	return &basicStore{
		m:    m,
		path: path,
	}, nil
}

type basicStore struct {
	sync.RWMutex

	m    map[string][]time.Time
	path string
}

func (s *basicStore) persist() error {
	f, err := os.Create(s.path)
	if err != nil {
		return err
	}
	defer f.Close()

	b, err := json.Marshal(s.m)
	if err != nil {
		return err
	}

	_, err = f.Write(b)
	return err
}

// Add implements Store.
func (s *basicStore) Add(p index.Path) error {
	s.Lock()
	defer s.Unlock()

	k := fmt.Sprintf("%v", p)
	s.m[k] = append(s.m[k], time.Now().UTC())
	return s.persist()
}

// Get implements Store.
func (s *basicStore) Get(p index.Path) []time.Time {
	s.RLock()
	defer s.RUnlock()

	return s.m[fmt.Sprintf("%v", p)]
}
<commit_msg>Use PersistStore in index/history.<commit_after>// Package history implements functionality for fetching/adding to play history.
package history

import (
	"fmt"
	"sync"
	"time"

	"tchaik.com/index"
)

// Store is an interface which defines methods necessary for fetching/adding to play history for
// index paths.  All times are stored in UTC.
type Store interface {
	// Add a play event to the store.
	Add(index.Path) error
	// Get the play events associated to a path.
	Get(index.Path) []time.Time
}

// NewStore creates a basic implementation of a play history store, using the given path as the
// source of data. If the file does not exist it will be created.
func NewStore(path string) (Store, error) {
	m := make(map[string][]time.Time)
	s, err := index.NewPersistStore(path, &m)
	if err != nil {
		return nil, err
	}

	return &basicStore{
		m:     m,
		store: s,
	}, nil
}

type basicStore struct {
	sync.RWMutex

	m     map[string][]time.Time
	store index.PersistStore
}

// Add implements Store.
func (s *basicStore) Add(p index.Path) error {
	s.Lock()
	defer s.Unlock()

	k := fmt.Sprintf("%v", p)
	s.m[k] = append(s.m[k], time.Now().UTC())
	return s.store.Persist(&s.m)
}

// Get implements Store.
func (s *basicStore) Get(p index.Path) []time.Time {
	s.RLock()
	defer s.RUnlock()

	return s.m[fmt.Sprintf("%v", p)]
}
<|endoftext|>


<commit_before>package sous

import (
	"fmt"
	"sync"
	"time"

	"github.com/opentable/sous/util/logging"
	"github.com/opentable/sous/util/logging/messages"
)

type (
	// ResolveStatus captures the status of a Resolve
	ResolveStatus struct {
		// Started collects the time that the Status began being collected
		Started,
		// Finished collects the time that the Status was completed - or the zero
		// time if the status is still live.
		Finished time.Time
		// Phase reports the current phase of resolution
		Phase string
		// Intended are the deployments that are the target of this resolution
		Intended []*Deployment
		// logging.Log collects the resolution steps that have been performed
		Log []DiffResolution
		// Errs collects errors during resolution
		Errs ResolveErrors
	}

	// ResolveRecorder represents the status of a resolve run.
	ResolveRecorder struct {
		status *ResolveStatus
		// Log is a channel of statuses of individual diff resolutions.
		Log chan DiffResolution
		// finished may be closed with no error, or closed after a single
		// error is emitted to the channel.
		finished chan struct{}
		// err is the final error returned from a phase that ends the resolution.
		err error
		sync.RWMutex
		logSink logging.LogSink
	}

	// DiffResolution is the result of applying a single diff.
	DiffResolution struct {
		// DeployID is the ID of the deployment being resolved
		DeploymentID
		// Desc describes the difference and its resolution
		Desc ResolutionType
		// Error captures the error (if any) encountered during diff resolution
		Error *ErrorWrapper

		// DeployState is the state of this deployment as running.
		DeployState *DeployState

		// SchedulerURL is a URL where this deployment can be seen.
		SchedulerURL string
	}

	// ResolutionType marks the kind of a DiffResolution
	// XXX should be made an int and generate with gostringer
	ResolutionType string
)

const (
	// StableDiff - the active deployment is the intended deployment
	StableDiff = ResolutionType("unchanged")
	// ComingDiff - the intended deployment is pending, assumed will be come active
	ComingDiff = ResolutionType("coming")
	// CreateDiff - the intended deployment was missing and had to be created.
	CreateDiff = ResolutionType("created")
	// ModifyDiff - there was a deployment that differed from the intended was changed.
	ModifyDiff = ResolutionType("updated")
	// DeleteDiff - a deployment was active that wasn't intended at all, and was deleted.
	DeleteDiff = ResolutionType("deleted")
)

func (rez DiffResolution) String() string {
	return fmt.Sprintf("%s %s %v", rez.DeploymentID, rez.Desc, rez.Error)
}

// EachField implements EachFielder on DiffResolution.
func (rez DiffResolution) EachField(f logging.FieldReportFn) {
	f(logging.SousDeploymentId, rez.DeploymentID.String())
	f(logging.SousManifestId, rez.ManifestID.String())
	f(logging.SousResolutionDescription, string(rez.Desc))
	if rez.Error == nil {
		return
	}
	marshallable := buildMarshableError(rez.Error.error)
	f(logging.SousResolutionErrortype, marshallable.Type)
	f(logging.SousResolutionErrormessage, marshallable.String)
}

// NewResolveRecorder creates a new ResolveRecorder and calls f with it as its
// argument. It then returns that ResolveRecorder immediately.
func NewResolveRecorder(intended Deployments, ls logging.LogSink, f func(*ResolveRecorder)) *ResolveRecorder {
	rr := &ResolveRecorder{
		status: &ResolveStatus{
			Started:  time.Now(),
			Intended: []*Deployment{},
			Log:      []DiffResolution{},
			Errs:     ResolveErrors{Causes: []ErrorWrapper{}},
		},
		Log:      make(chan DiffResolution, 10),
		finished: make(chan struct{}),
		logSink:  ls,
	}

	for _, d := range intended.Snapshot() {
		rr.status.Intended = append(rr.status.Intended, d)
	}

	// Update status incrementally.
	go func() {
		for rez := range rr.Log {
			rr.write(func() {
				rr.status.Log = append(rr.status.Log, rez)
				if rez.Error != nil {
					rr.status.Errs.Causes = append(rr.status.Errs.Causes, ErrorWrapper{error: rez.Error})
					messages.ReportLogFieldsMessage("resolve error", logging.DebugLevel, ls, rez.Error)
				}
			})
		}
	}()

	// Execute the main function (f) over this resolve recorder.
	go func() {
		f(rr)
		close(rr.Log)
		rr.write(func() {
			rr.status.Finished = time.Now()
			if rr.err == nil {
				rr.status.Phase = "finished"
			}
			close(rr.finished)
		})
	}()
	return rr
}

// Err returns any collected error from the course of resolution
func (rs *ResolveStatus) Err() error {
	if len(rs.Errs.Causes) > 0 {
		return &rs.Errs
	}
	return nil
}

// CurrentStatus returns a copy of the current status of the resolve
func (rr *ResolveRecorder) CurrentStatus() (rs ResolveStatus) {
	rr.read(func() {
		rs = *rr.status
		rs.Log = make([]DiffResolution, len(rr.status.Log))
		copy(rs.Log, rr.status.Log)
		rs.Errs.Causes = make([]ErrorWrapper, len(rr.status.Errs.Causes))
		copy(rs.Errs.Causes, rr.status.Errs.Causes)
	})
	return
}

// Done returns true if the resolution has finished. Otherwise it returns false.
func (rr *ResolveRecorder) Done() bool {
	select {
	case <-rr.finished:
		return true
	default:
		return false
	}
}

// Wait blocks until the resolution is finished.
func (rr *ResolveRecorder) Wait() error {
	<-rr.finished
	var err error
	rr.read(func() {
		err = rr.err
		if err == nil {
			err = rr.status.Err()
		}
	})
	return err
}

func (rr *ResolveRecorder) earlyExit() (yes bool) {
	rr.read(func() {
		yes = (rr.err != nil)
	})
	return
}

// performPhase performs the requested phase, only if nothing has cancelled the
// resolve.
func (rr *ResolveRecorder) performPhase(name string, f func() error) {
	if rr.earlyExit() {
		logging.Info(rr.logSink, "Skipping phase", name, rr.err)
		return
	}
	logging.Debug(rr.logSink, "Performing phase", name)
	rr.setPhase(name)
	if err := f(); err != nil {
		rr.doneWithError(err)
	}
}

// setPhase sets the phase of this resolve status.
func (rr *ResolveRecorder) setPhase(phase string) {
	rr.write(func() {
		rr.status.Phase = phase
	})
}

// Phase returns the name of the current phase.
func (rr *ResolveRecorder) Phase() string {
	var phase string
	rr.read(func() { phase = rr.status.Phase })
	return phase
}

// write encapsulates locking this ResolveRecorder for writing using f.
func (rr *ResolveRecorder) write(f func()) {
	rr.Lock()
	defer rr.Unlock()
	f()
}

// read encapsulates locking this ResolveRecorder for reading using f.
func (rr *ResolveRecorder) read(f func()) {
	rr.RLock()
	defer rr.RUnlock()
	f()
}

// doneWithError marks the resolution as finished with an error.
func (rr *ResolveRecorder) doneWithError(err error) {
	logging.Warn(rr.logSink, "Error during resolve", rr.Phase(), err)
	rr.write(func() {
		rr.err = err
	})
}
<commit_msg>lib: fix race in ResolveRecorder<commit_after>package sous

import (
	"fmt"
	"sync"
	"time"

	"github.com/opentable/sous/util/logging"
	"github.com/opentable/sous/util/logging/messages"
)

type (
	// ResolveStatus captures the status of a Resolve
	ResolveStatus struct {
		// Started collects the time that the Status began being collected
		Started,
		// Finished collects the time that the Status was completed - or the zero
		// time if the status is still live.
		Finished time.Time
		// Phase reports the current phase of resolution
		Phase string
		// Intended are the deployments that are the target of this resolution
		Intended []*Deployment
		// logging.Log collects the resolution steps that have been performed
		Log []DiffResolution
		// Errs collects errors during resolution
		Errs ResolveErrors
	}

	// ResolveRecorder represents the status of a resolve run.
	ResolveRecorder struct {
		status *ResolveStatus
		// Log is a channel of statuses of individual diff resolutions.
		Log chan DiffResolution
		// finished may be closed with no error, or closed after a single
		// error is emitted to the channel.
		finished chan struct{}
		// err is the final error returned from a phase that ends the resolution.
		err error
		sync.RWMutex
		logSink logging.LogSink
	}

	// DiffResolution is the result of applying a single diff.
	DiffResolution struct {
		// DeployID is the ID of the deployment being resolved
		DeploymentID
		// Desc describes the difference and its resolution
		Desc ResolutionType
		// Error captures the error (if any) encountered during diff resolution
		Error *ErrorWrapper

		// DeployState is the state of this deployment as running.
		DeployState *DeployState

		// SchedulerURL is a URL where this deployment can be seen.
		SchedulerURL string
	}

	// ResolutionType marks the kind of a DiffResolution
	// XXX should be made an int and generate with gostringer
	ResolutionType string
)

const (
	// StableDiff - the active deployment is the intended deployment
	StableDiff = ResolutionType("unchanged")
	// ComingDiff - the intended deployment is pending, assumed will be come active
	ComingDiff = ResolutionType("coming")
	// CreateDiff - the intended deployment was missing and had to be created.
	CreateDiff = ResolutionType("created")
	// ModifyDiff - there was a deployment that differed from the intended was changed.
	ModifyDiff = ResolutionType("updated")
	// DeleteDiff - a deployment was active that wasn't intended at all, and was deleted.
	DeleteDiff = ResolutionType("deleted")
)

func (rez DiffResolution) String() string {
	return fmt.Sprintf("%s %s %v", rez.DeploymentID, rez.Desc, rez.Error)
}

// EachField implements EachFielder on DiffResolution.
func (rez DiffResolution) EachField(f logging.FieldReportFn) {
	f(logging.SousDeploymentId, rez.DeploymentID.String())
	f(logging.SousManifestId, rez.ManifestID.String())
	f(logging.SousResolutionDescription, string(rez.Desc))
	if rez.Error == nil {
		return
	}
	marshallable := buildMarshableError(rez.Error.error)
	f(logging.SousResolutionErrortype, marshallable.Type)
	f(logging.SousResolutionErrormessage, marshallable.String)
}

// NewResolveRecorder creates a new ResolveRecorder and calls f with it as its
// argument. It then returns that ResolveRecorder immediately.
func NewResolveRecorder(intended Deployments, ls logging.LogSink, f func(*ResolveRecorder)) *ResolveRecorder {
	rr := &ResolveRecorder{
		status: &ResolveStatus{
			Started:  time.Now(),
			Intended: []*Deployment{},
			Log:      []DiffResolution{},
			Errs:     ResolveErrors{Causes: []ErrorWrapper{}},
		},
		Log:      make(chan DiffResolution, 10),
		finished: make(chan struct{}),
		logSink:  ls,
	}

	for _, d := range intended.Snapshot() {
		rr.status.Intended = append(rr.status.Intended, d)
	}

	// Update status incrementally.
	go func() {
		for rez := range rr.Log {
			rr.write(func() {
				rr.status.Log = append(rr.status.Log, rez)
				if rez.Error != nil {
					rr.status.Errs.Causes = append(rr.status.Errs.Causes, ErrorWrapper{error: rez.Error})
					messages.ReportLogFieldsMessage("resolve error", logging.DebugLevel, ls, rez.Error)
				}
			})
		}
	}()

	// Execute the main function (f) over this resolve recorder.
	go func() {
		f(rr)
		rr.write(func() {
			defer close(rr.finished)
			defer close(rr.Log)
			rr.status.Finished = time.Now()
			if rr.err == nil {
				rr.status.Phase = "finished"
			}
		})
	}()
	return rr
}

// Err returns any collected error from the course of resolution
func (rs *ResolveStatus) Err() error {
	if len(rs.Errs.Causes) > 0 {
		return &rs.Errs
	}
	return nil
}

// CurrentStatus returns a copy of the current status of the resolve
func (rr *ResolveRecorder) CurrentStatus() (rs ResolveStatus) {
	rr.read(func() {
		rs = *rr.status
		rs.Log = make([]DiffResolution, len(rr.status.Log))
		copy(rs.Log, rr.status.Log)
		rs.Errs.Causes = make([]ErrorWrapper, len(rr.status.Errs.Causes))
		copy(rs.Errs.Causes, rr.status.Errs.Causes)
	})
	return
}

// Done returns true if the resolution has finished. Otherwise it returns false.
func (rr *ResolveRecorder) Done() bool {
	select {
	case <-rr.finished:
		return true
	default:
		return false
	}
}

// Wait blocks until the resolution is finished.
func (rr *ResolveRecorder) Wait() error {
	<-rr.finished
	var err error
	rr.read(func() {
		err = rr.err
		if err == nil {
			err = rr.status.Err()
		}
	})
	return err
}

func (rr *ResolveRecorder) earlyExit() (yes bool) {
	rr.read(func() {
		yes = (rr.err != nil)
	})
	return
}

// performPhase performs the requested phase, only if nothing has cancelled the
// resolve.
func (rr *ResolveRecorder) performPhase(name string, f func() error) {
	if rr.earlyExit() {
		logging.Info(rr.logSink, "Skipping phase", name, rr.err)
		return
	}
	logging.Debug(rr.logSink, "Performing phase", name)
	rr.setPhase(name)
	if err := f(); err != nil {
		rr.doneWithError(err)
	}
}

// setPhase sets the phase of this resolve status.
func (rr *ResolveRecorder) setPhase(phase string) {
	rr.write(func() {
		rr.status.Phase = phase
	})
}

// Phase returns the name of the current phase.
func (rr *ResolveRecorder) Phase() string {
	var phase string
	rr.read(func() { phase = rr.status.Phase })
	return phase
}

// write encapsulates locking this ResolveRecorder for writing using f.
func (rr *ResolveRecorder) write(f func()) {
	rr.Lock()
	defer rr.Unlock()
	f()
}

// read encapsulates locking this ResolveRecorder for reading using f.
func (rr *ResolveRecorder) read(f func()) {
	rr.RLock()
	defer rr.RUnlock()
	f()
}

// doneWithError marks the resolution as finished with an error.
func (rr *ResolveRecorder) doneWithError(err error) {
	logging.Warn(rr.logSink, "Error during resolve", rr.Phase(), err)
	rr.write(func() {
		rr.err = err
	})
}
<|endoftext|>


<commit_before>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package check

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/url"

	"github.com/circonus-labs/circonus-agent/internal/config"
	"github.com/spf13/viper"
)

var (
	errBrokerNotInitialized  = fmt.Errorf("broker not initialized")
	errBrokerAddCACertToPool = fmt.Errorf("unable to add Broker CA Certificate to x509 cert pool")
	errBrokerMatchRevURLHost = fmt.Errorf("unable to match reverse URL host to broker")
)

// brokerTLSConfig returns the correct TLS configuration for the broker.
func (c *Check) brokerTLSConfig(reverseURL *url.URL) (*tls.Config, string, error) {
	if c.broker == nil {
		return nil, "", errBrokerNotInitialized
	}

	cn, err := c.getBrokerCN(reverseURL)
	if err != nil {
		return nil, "", err
	}
	cert, err := c.fetchBrokerCA()
	if err != nil {
		return nil, "", err
	}
	cp := x509.NewCertPool()
	if !cp.AppendCertsFromPEM(cert) {
		return nil, "", errBrokerAddCACertToPool
	}

	tlsConfig := &tls.Config{
		// RootCAs:    cp, // go1.15 see VerifyConnection below - until CN added to SAN in broker certs
		ServerName: cn,
		MinVersion: tls.VersionTLS12,
		// NOTE: This does NOT disable VerifyConnection()
		InsecureSkipVerify: true, //nolint:gosec
		VerifyConnection: func(cs tls.ConnectionState) error {
			commonName := cs.PeerCertificates[0].Subject.CommonName
			if commonName != cs.ServerName {
				return fmt.Errorf("invalid certificate name %q, expected %q", commonName, cs.ServerName) //nolint:goerr113
			}
			opts := x509.VerifyOptions{
				Roots:         cp,
				Intermediates: x509.NewCertPool(),
			}
			for _, cert := range cs.PeerCertificates[1:] {
				opts.Intermediates.AddCert(cert)
			}
			_, err := cs.PeerCertificates[0].Verify(opts)
			return fmt.Errorf("verify peer cert: %w", err)
		},
	}

	c.logger.Debug().Str("CN", cn).Msg("setting tls CN")

	return tlsConfig, cn, nil
}

func (c *Check) getBrokerCN(reverseURL *url.URL) (string, error) {
	host := reverseURL.Hostname()

	// OK...
	//
	// mtev_reverse can have an IP or an FQDN for the host portion
	// it used to be that when it was an IP, the CN was needed in order to verify TLS connections
	// otherwise, the FQDN was valid. now, the FQDN may be valid for the cert or it may not be...

	cn := ""

	for _, detail := range c.broker.Details {
		// certs are generated against the CN (in theory)
		// 1. find the right broker instance with matching IP or external hostname
		// 2. set the tls.Config.ServerName to whatever that instance's CN is currently
		// 3. cert will be valid for TLS conns (in theory)
		if detail.IP != nil && *detail.IP == host {
			cn = detail.CN
			break
		}
		if detail.ExternalHost != nil && *detail.ExternalHost == host {
			cn = detail.CN
			break
		}
	}

	if cn == "" {
		return "", fmt.Errorf("%s: %w", host, errBrokerMatchRevURLHost)
	}

	return cn, nil
}

func (c *Check) fetchBrokerCA() ([]byte, error) {
	// use local file if specified
	file := viper.GetString(config.KeyReverseBrokerCAFile)
	if file != "" {
		cert, err := ioutil.ReadFile(file)
		if err != nil {
			return nil, fmt.Errorf("read file: %w", err)
		}
		return cert, nil
	}

	// otherwise, try the api
	data, err := c.client.Get("/pki/ca.crt")
	if err != nil {
		return nil, fmt.Errorf("fetching Broker CA certificate: %w", err)
	}

	type cacert struct {
		Contents string `json:"contents"`
	}

	var cadata cacert

	if err := json.Unmarshal(data, &cadata); err != nil {
		return nil, fmt.Errorf("json parse - Broker CA certificate: %w", err)
	}

	if cadata.Contents == "" {
		return nil, fmt.Errorf("no Broker CA certificate in response (%#v)", string(data)) //nolint:goerr113
	}

	return []byte(cadata.Contents), nil
}
<commit_msg>fix: error wrap when nil on verify<commit_after>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package check

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/url"

	"github.com/circonus-labs/circonus-agent/internal/config"
	"github.com/spf13/viper"
)

var (
	errBrokerNotInitialized  = fmt.Errorf("broker not initialized")
	errBrokerAddCACertToPool = fmt.Errorf("unable to add Broker CA Certificate to x509 cert pool")
	errBrokerMatchRevURLHost = fmt.Errorf("unable to match reverse URL host to broker")
)

// brokerTLSConfig returns the correct TLS configuration for the broker.
func (c *Check) brokerTLSConfig(reverseURL *url.URL) (*tls.Config, string, error) {
	if c.broker == nil {
		return nil, "", errBrokerNotInitialized
	}

	cn, err := c.getBrokerCN(reverseURL)
	if err != nil {
		return nil, "", err
	}
	cert, err := c.fetchBrokerCA()
	if err != nil {
		return nil, "", err
	}
	cp := x509.NewCertPool()
	if !cp.AppendCertsFromPEM(cert) {
		return nil, "", errBrokerAddCACertToPool
	}

	tlsConfig := &tls.Config{
		// RootCAs:    cp, // go1.15 see VerifyConnection below - until CN added to SAN in broker certs
		ServerName: cn,
		MinVersion: tls.VersionTLS12,
		// NOTE: This does NOT disable VerifyConnection()
		InsecureSkipVerify: true, //nolint:gosec
		VerifyConnection: func(cs tls.ConnectionState) error {
			commonName := cs.PeerCertificates[0].Subject.CommonName
			if commonName != cs.ServerName {
				return fmt.Errorf("invalid certificate name %q, expected %q", commonName, cs.ServerName) //nolint:goerr113
			}
			opts := x509.VerifyOptions{
				Roots:         cp,
				Intermediates: x509.NewCertPool(),
			}
			for _, cert := range cs.PeerCertificates[1:] {
				opts.Intermediates.AddCert(cert)
			}
			_, err := cs.PeerCertificates[0].Verify(opts)
			if err != nil {
				return fmt.Errorf("verify peer cert: %w", err)
			}
			return nil
		},
	}

	c.logger.Debug().Str("CN", cn).Msg("setting tls CN")

	return tlsConfig, cn, nil
}

func (c *Check) getBrokerCN(reverseURL *url.URL) (string, error) {
	host := reverseURL.Hostname()

	// OK...
	//
	// mtev_reverse can have an IP or an FQDN for the host portion
	// it used to be that when it was an IP, the CN was needed in order to verify TLS connections
	// otherwise, the FQDN was valid. now, the FQDN may be valid for the cert or it may not be...

	cn := ""

	for _, detail := range c.broker.Details {
		if detail.Status != StatusActive {
			continue
		}
		// certs are generated against the CN (in theory)
		// 1. find the right broker instance with matching IP or external hostname
		// 2. set the tls.Config.ServerName to whatever that instance's CN is currently
		// 3. cert will be valid for TLS conns (in theory)
		if detail.IP != nil && *detail.IP == host {
			cn = detail.CN
			break
		}
		if detail.ExternalHost != nil && *detail.ExternalHost == host {
			cn = detail.CN
			break
		}
	}

	if cn == "" {
		return "", fmt.Errorf("%s: %w", host, errBrokerMatchRevURLHost)
	}

	return cn, nil
}

func (c *Check) fetchBrokerCA() ([]byte, error) {
	// use local file if specified
	file := viper.GetString(config.KeyReverseBrokerCAFile)
	if file != "" {
		cert, err := ioutil.ReadFile(file)
		if err != nil {
			return nil, fmt.Errorf("read file: %w", err)
		}
		return cert, nil
	}

	// otherwise, try the api
	data, err := c.client.Get("/pki/ca.crt")
	if err != nil {
		return nil, fmt.Errorf("fetching Broker CA certificate: %w", err)
	}

	type cacert struct {
		Contents string `json:"contents"`
	}

	var cadata cacert

	if err := json.Unmarshal(data, &cadata); err != nil {
		return nil, fmt.Errorf("json parse - Broker CA certificate: %w", err)
	}

	if cadata.Contents == "" {
		return nil, fmt.Errorf("no Broker CA certificate in response (%#v)", string(data)) //nolint:goerr113
	}

	return []byte(cadata.Contents), nil
}
<|endoftext|>


<commit_before>package handler

import (
	"context"
	"encoding/json"
	"net/http"
	"strings"

	"github.com/nicolasparada/nakama/internal/service"
)

type loginInput struct {
	Email string
}

type sendMagicLinkInput struct {
	Email       string
	RedirectURI string
}

func (h *handler) sendMagicLink(w http.ResponseWriter, r *http.Request) {
	var in sendMagicLinkInput
	defer r.Body.Close()
	if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	err := h.SendMagicLink(r.Context(), in.Email, in.RedirectURI)
	if err == service.ErrInvalidEmail || err == service.ErrInvalidRedirectURI {
		http.Error(w, err.Error(), http.StatusUnprocessableEntity)
		return
	}

	if err == service.ErrUserNotFound {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	if err != nil {
		respondErr(w, err)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (h *handler) authRedirect(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	uri, err := h.AuthURI(r.Context(), q.Get("verification_code"), q.Get("redirect_uri"))
	if err == service.ErrInvalidVerificationCode || err == service.ErrInvalidRedirectURI {
		http.Error(w, err.Error(), http.StatusUnprocessableEntity)
		return
	}

	if err == service.ErrVerificationCodeNotFound {
		http.Error(w, err.Error(), http.StatusGone)
		return
	}

	if err == service.ErrExpiredToken {
		http.Error(w, err.Error(), http.StatusGone)
		return
	}

	if err != nil {
		respondErr(w, err)
		return
	}

	http.Redirect(w, r, uri, http.StatusFound)
}

func (h *handler) devLogin(w http.ResponseWriter, r *http.Request) {
	var in loginInput
	defer r.Body.Close()
	if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	out, err := h.DevLogin(r.Context(), in.Email)
	if err == service.ErrUnimplemented {
		http.Error(w, err.Error(), http.StatusNotImplemented)
		return
	}

	if err == service.ErrInvalidEmail {
		http.Error(w, err.Error(), http.StatusUnprocessableEntity)
		return
	}

	if err == service.ErrUserNotFound {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	if err != nil {
		respondErr(w, err)
		return
	}

	respond(w, out, http.StatusOK)
}

func (h *handler) authUser(w http.ResponseWriter, r *http.Request) {
	u, err := h.AuthUser(r.Context())
	if err == service.ErrUnauthenticated {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}

	if err == service.ErrUserNotFound {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	if err != nil {
		respondErr(w, err)
		return
	}

	respond(w, u, http.StatusOK)
}

func (h *handler) token(w http.ResponseWriter, r *http.Request) {
	out, err := h.Token(r.Context())
	if err == service.ErrUnauthenticated {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}

	if err != nil {
		respondErr(w, err)
		return
	}

	respond(w, out, http.StatusOK)
}

func (h *handler) withAuth(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := strings.TrimSpace(r.URL.Query().Get("auth_token"))

		if token == "" {
			if a := r.Header.Get("Authorization"); strings.HasPrefix(a, "Bearer ") {
				token = a[7:]
			}
		}

		if token == "" || token == "null" || token == "undefined" {
			next.ServeHTTP(w, r)
			return
		}

		uid, err := h.AuthUserIDFromToken(token)
		if err != nil {
			http.Error(w, err.Error(), http.StatusUnauthorized)
			return
		}

		ctx := r.Context()
		ctx = context.WithValue(ctx, service.KeyAuthUserID, uid)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}
<commit_msg>internal/handler: remove js checks<commit_after>package handler

import (
	"context"
	"encoding/json"
	"net/http"
	"strings"

	"github.com/nicolasparada/nakama/internal/service"
)

type loginInput struct {
	Email string
}

type sendMagicLinkInput struct {
	Email       string
	RedirectURI string
}

func (h *handler) sendMagicLink(w http.ResponseWriter, r *http.Request) {
	var in sendMagicLinkInput
	defer r.Body.Close()
	if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	err := h.SendMagicLink(r.Context(), in.Email, in.RedirectURI)
	if err == service.ErrInvalidEmail || err == service.ErrInvalidRedirectURI {
		http.Error(w, err.Error(), http.StatusUnprocessableEntity)
		return
	}

	if err == service.ErrUserNotFound {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	if err != nil {
		respondErr(w, err)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (h *handler) authRedirect(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	uri, err := h.AuthURI(r.Context(), q.Get("verification_code"), q.Get("redirect_uri"))
	if err == service.ErrInvalidVerificationCode || err == service.ErrInvalidRedirectURI {
		http.Error(w, err.Error(), http.StatusUnprocessableEntity)
		return
	}

	if err == service.ErrVerificationCodeNotFound {
		http.Error(w, err.Error(), http.StatusGone)
		return
	}

	if err == service.ErrExpiredToken {
		http.Error(w, err.Error(), http.StatusGone)
		return
	}

	if err != nil {
		respondErr(w, err)
		return
	}

	http.Redirect(w, r, uri, http.StatusFound)
}

func (h *handler) devLogin(w http.ResponseWriter, r *http.Request) {
	var in loginInput
	defer r.Body.Close()
	if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	out, err := h.DevLogin(r.Context(), in.Email)
	if err == service.ErrUnimplemented {
		http.Error(w, err.Error(), http.StatusNotImplemented)
		return
	}

	if err == service.ErrInvalidEmail {
		http.Error(w, err.Error(), http.StatusUnprocessableEntity)
		return
	}

	if err == service.ErrUserNotFound {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	if err != nil {
		respondErr(w, err)
		return
	}

	respond(w, out, http.StatusOK)
}

func (h *handler) authUser(w http.ResponseWriter, r *http.Request) {
	u, err := h.AuthUser(r.Context())
	if err == service.ErrUnauthenticated {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}

	if err == service.ErrUserNotFound {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	if err != nil {
		respondErr(w, err)
		return
	}

	respond(w, u, http.StatusOK)
}

func (h *handler) token(w http.ResponseWriter, r *http.Request) {
	out, err := h.Token(r.Context())
	if err == service.ErrUnauthenticated {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}

	if err != nil {
		respondErr(w, err)
		return
	}

	respond(w, out, http.StatusOK)
}

func (h *handler) withAuth(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := strings.TrimSpace(r.URL.Query().Get("auth_token"))

		if token == "" {
			if a := r.Header.Get("Authorization"); strings.HasPrefix(a, "Bearer ") {
				token = a[7:]
			}
		}

		if token == "" {
			next.ServeHTTP(w, r)
			return
		}

		uid, err := h.AuthUserIDFromToken(token)
		if err != nil {
			http.Error(w, err.Error(), http.StatusUnauthorized)
			return
		}

		ctx := r.Context()
		ctx = context.WithValue(ctx, service.KeyAuthUserID, uid)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}
<|endoftext|>


<commit_before>package log

import (
	"fmt"
	"io"
	goLog "log"
	"os"
	"runtime"
)

var standard Standard

type Standard struct {
	Interface
}

func SetOutput(w io.Writer) {
	goLog.SetOutput(w)
}

func Output(level string, format string, args ...interface{}) {
	logMsg := fmt.Sprintf(format, args...)
	_, file, line, _ := runtime.Caller(4)
	// timestamp will be provided by goLog
	goLog.Printf("•%v•%v•%v•%v", level, file, line, logMsg)
}

func (_ Standard) Fatal(format string, args ...interface{}) {
	Output("FATAL", format, args...)
	os.Exit(1)
}

func (_ Standard) Error(format string, args ...interface{}) {
	Output("ERROR", format, args...)
}

func (_ Standard) Warn(format string, args ...interface{}) {
	Output("WARN", format, args...)
}

func (_ Standard) Info(format string, args ...interface{}) {
	Output("INFO", format, args...)
}

func (_ Standard) Debug(format string, args ...interface{}) {
	Output("DEBUG", format, args...)
}

func (_ Standard) Trace(format string, args ...interface{}) {
	Output("TRACE", format, args...)
}
<commit_msg>@gdey requested change.  (#147)<commit_after>package log

import (
	"fmt"
	"io"
	goLog "log"
	"os"
	"runtime"
)

var standard Standard

type Standard struct{}

func SetOutput(w io.Writer) {
	goLog.SetOutput(w)
}

func Output(level string, format string, args ...interface{}) {
	logMsg := fmt.Sprintf(format, args...)
	_, file, line, _ := runtime.Caller(4)
	// timestamp will be provided by goLog
	goLog.Printf("•%v•%v•%v•%v", level, file, line, logMsg)
}

func (_ Standard) Fatal(format string, args ...interface{}) {
	Output("FATAL", format, args...)
	os.Exit(1)
}

func (_ Standard) Error(format string, args ...interface{}) {
	Output("ERROR", format, args...)
}

func (_ Standard) Warn(format string, args ...interface{}) {
	Output("WARN", format, args...)
}

func (_ Standard) Info(format string, args ...interface{}) {
	Output("INFO", format, args...)
}

func (_ Standard) Debug(format string, args ...interface{}) {
	Output("DEBUG", format, args...)
}

func (_ Standard) Trace(format string, args ...interface{}) {
	Output("TRACE", format, args...)
}
<|endoftext|>


<commit_before>package ndpcmd

import (
	"fmt"
	"log"
	"net"
	"strings"

	"github.com/mdlayher/ndp"
)

func printMessage(ll *log.Logger, m ndp.Message, from net.IP) {
	switch m := m.(type) {
	case *ndp.NeighborAdvertisement:
		printNA(ll, m, from)
	case *ndp.RouterAdvertisement:
		printRA(ll, m, from)
	default:
		ll.Printf("%s %#v", from, m)
	}
}

func printRA(ll *log.Logger, ra *ndp.RouterAdvertisement, from net.IP) {
	var opts string
	for _, o := range ra.Options {
		opts += fmt.Sprintf("        - %s\n", optStr(o))
	}

	var flags string
	if ra.ManagedConfiguration {
		flags += "M"
	}
	if ra.OtherConfiguration {
		flags += "O"
	}
	if ra.MobileIPv6HomeAgent {
		flags += "H"
	}
	if ra.NeighborDiscoveryProxy {
		flags += "P"
	}

	ll.Printf(
		raFormat,
		from.String(),
		ra.CurrentHopLimit,
		flags,
		ra.RouterSelectionPreference,
		ra.RouterLifetime,
		ra.ReachableTime,
		ra.RetransmitTimer,
		opts,
	)
}

const raFormat = `router advertisement from: %s:
    - hop limit:        %d
    - flags:            [%s]
    - preference:       %d
    - router lifetime:  %s
    - reachable time:   %s
    - retransmit timer: %s
    - options:
%s`

func printNA(ll *log.Logger, na *ndp.NeighborAdvertisement, from net.IP) {
	var opts string
	for _, o := range na.Options {
		opts += fmt.Sprintf("        - %s\n", optStr(o))
	}

	ll.Printf(
		naFormat,
		from.String(),
		na.Router,
		na.Solicited,
		na.Override,
		na.TargetAddress.String(),
		opts,
	)
}

const naFormat = `neighbor advertisement from %s:
    - router:         %t
    - solicited:      %t
    - override:       %t
    - target address: %s
    - options:
%s`

func optStr(o ndp.Option) string {
	switch o := o.(type) {
	case *ndp.LinkLayerAddress:
		dir := "source"
		if o.Direction == ndp.Target {
			dir = "target"
		}

		return fmt.Sprintf("%s link-layer address: %s", dir, o.Addr.String())
	case *ndp.MTU:
		return fmt.Sprintf("MTU: %d", *o)
	case *ndp.PrefixInformation:
		var flags string
		if o.OnLink {
			flags += "O"
		}
		if o.AutonomousAddressConfiguration {
			flags += "A"
		}

		return fmt.Sprintf("prefix information: %s/%d, flags: [%s], valid: %s, preferred: %s",
			o.Prefix.String(),
			o.PrefixLength,
			flags,
			o.ValidLifetime,
			o.PreferredLifetime,
		)
	case *ndp.RawOption:
		return fmt.Sprintf("type: %03d, value: %v", o.Type, o.Value)
	case *ndp.RecursiveDNSServer:
		var ss []string
		for _, s := range o.Servers {
			ss = append(ss, s.String())
		}
		servers := strings.Join(ss, ", ")

		return fmt.Sprintf("recursive DNS servers: lifetime: %s, servers: %s", o.Lifetime, servers)
	default:
		panic(fmt.Sprintf("unrecognized option: %v", o))
	}
}
<commit_msg>internal/ndpcmd: add printing for neighbor solicitations<commit_after>package ndpcmd

import (
	"fmt"
	"log"
	"net"
	"strings"

	"github.com/mdlayher/ndp"
)

func printMessage(ll *log.Logger, m ndp.Message, from net.IP) {
	switch m := m.(type) {
	case *ndp.NeighborAdvertisement:
		printNA(ll, m, from)
	case *ndp.NeighborSolicitation:
		printNS(ll, m, from)
	case *ndp.RouterAdvertisement:
		printRA(ll, m, from)
	default:
		ll.Printf("%s %#v", from, m)
	}
}

func printRA(ll *log.Logger, ra *ndp.RouterAdvertisement, from net.IP) {
	var opts string
	for _, o := range ra.Options {
		opts += fmt.Sprintf("        - %s\n", optStr(o))
	}

	var flags string
	if ra.ManagedConfiguration {
		flags += "M"
	}
	if ra.OtherConfiguration {
		flags += "O"
	}
	if ra.MobileIPv6HomeAgent {
		flags += "H"
	}
	if ra.NeighborDiscoveryProxy {
		flags += "P"
	}

	ll.Printf(
		raFormat,
		from.String(),
		ra.CurrentHopLimit,
		flags,
		ra.RouterSelectionPreference,
		ra.RouterLifetime,
		ra.ReachableTime,
		ra.RetransmitTimer,
		opts,
	)
}

const raFormat = `router advertisement from: %s:
    - hop limit:        %d
    - flags:            [%s]
    - preference:       %d
    - router lifetime:  %s
    - reachable time:   %s
    - retransmit timer: %s
    - options:
%s`

func printNA(ll *log.Logger, na *ndp.NeighborAdvertisement, from net.IP) {
	var opts string
	for _, o := range na.Options {
		opts += fmt.Sprintf("        - %s\n", optStr(o))
	}

	ll.Printf(
		naFormat,
		from.String(),
		na.Router,
		na.Solicited,
		na.Override,
		na.TargetAddress.String(),
		opts,
	)
}

const naFormat = `neighbor advertisement from %s:
    - router:         %t
    - solicited:      %t
    - override:       %t
    - target address: %s
    - options:
%s`

func printNS(ll *log.Logger, ns *ndp.NeighborSolicitation, from net.IP) {
	var opts string
	for _, o := range ns.Options {
		opts += fmt.Sprintf("        - %s\n", optStr(o))
	}

	ll.Printf(
		nsFormat,
		from.String(),
		ns.TargetAddress.String(),
		opts,
	)
}

const nsFormat = `neighbor solicitation from %s:
    - target address: %s
    - options:
%s`

func optStr(o ndp.Option) string {
	switch o := o.(type) {
	case *ndp.LinkLayerAddress:
		dir := "source"
		if o.Direction == ndp.Target {
			dir = "target"
		}

		return fmt.Sprintf("%s link-layer address: %s", dir, o.Addr.String())
	case *ndp.MTU:
		return fmt.Sprintf("MTU: %d", *o)
	case *ndp.PrefixInformation:
		var flags string
		if o.OnLink {
			flags += "O"
		}
		if o.AutonomousAddressConfiguration {
			flags += "A"
		}

		return fmt.Sprintf("prefix information: %s/%d, flags: [%s], valid: %s, preferred: %s",
			o.Prefix.String(),
			o.PrefixLength,
			flags,
			o.ValidLifetime,
			o.PreferredLifetime,
		)
	case *ndp.RawOption:
		return fmt.Sprintf("type: %03d, value: %v", o.Type, o.Value)
	case *ndp.RecursiveDNSServer:
		var ss []string
		for _, s := range o.Servers {
			ss = append(ss, s.String())
		}
		servers := strings.Join(ss, ", ")

		return fmt.Sprintf("recursive DNS servers: lifetime: %s, servers: %s", o.Lifetime, servers)
	default:
		panic(fmt.Sprintf("unrecognized option: %v", o))
	}
}
<|endoftext|>


<commit_before>// 由工具自动生成，不能修改！

package version

// Version 版本号
//
// 版本号规则遵循 https://semver.org/lang/zh-CN/
const Version = "0.35.0"

// 编译日期，可以由编译器指定
var buildDate string

// 最后一次提交的 hash 值
var commitHash string

var fullVersion = Version

func init() {
	if buildDate != "" {
		fullVersion = Version + "+" + buildDate
	}

	if commitHash != "" {
		fullVersion += "." + commitHash
	}
}

// FullVersion 完整的版本号
//
// 可能包括了编译日期。
func FullVersion() string {
	return fullVersion
}
<commit_msg>build: 更新版本<commit_after>// 由工具自动生成，不能修改！

package version

// Version 版本号
//
// 版本号规则遵循 https://semver.org/lang/zh-CN/
const Version = "0.36.0"

// 编译日期，可以由编译器指定
var buildDate string

// 最后一次提交的 hash 值
var commitHash string

var fullVersion = Version

func init() {
	if buildDate != "" {
		fullVersion = Version + "+" + buildDate
	}

	if commitHash != "" {
		fullVersion += "." + commitHash
	}
}

// FullVersion 完整的版本号
//
// 可能包括了编译日期。
func FullVersion() string {
	return fullVersion
}
<|endoftext|>


<commit_before>package suite

import (
	"errors"
	"io/ioutil"
	"os"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// SuiteRequireTwice is intended to test the usage of suite.Require in two
// different tests
type SuiteRequireTwice struct{ Suite }

// TestSuiteRequireTwice checks for regressions of issue #149 where
// suite.requirements was not initialised in suite.SetT()
// A regression would result on these tests panicking rather than failing.
func TestSuiteRequireTwice(t *testing.T) {
	ok := testing.RunTests(
		allTestsFilter,
		[]testing.InternalTest{{
			Name: "TestSuiteRequireTwice",
			F: func(t *testing.T) {
				suite := new(SuiteRequireTwice)
				Run(t, suite)
			},
		}},
	)
	assert.Equal(t, false, ok)
}

func (s *SuiteRequireTwice) TestRequireOne() {
	r := s.Require()
	r.Equal(1, 2)
}

func (s *SuiteRequireTwice) TestRequireTwo() {
	r := s.Require()
	r.Equal(1, 2)
}

type panickingSuite struct {
	Suite
	panicInSetupSuite    bool
	panicInSetupTest     bool
	panicInBeforeTest    bool
	panicInTest          bool
	panicInAfterTest     bool
	panicInTearDownTest  bool
	panicInTearDownSuite bool
}

func (s *panickingSuite) SetupSuite() {
	if s.panicInSetupSuite {
		panic("oops in setup suite")
	}
}

func (s *panickingSuite) SetupTest() {
	if s.panicInSetupTest {
		panic("oops in setup test")
	}
}

func (s *panickingSuite) BeforeTest(_, _ string) {
	if s.panicInBeforeTest {
		panic("oops in before test")
	}
}

func (s *panickingSuite) Test() {
	if s.panicInTest {
		panic("oops in test")
	}
}

func (s *panickingSuite) AfterTest(_, _ string) {
	if s.panicInAfterTest {
		panic("oops in after test")
	}
}

func (s *panickingSuite) TearDownTest() {
	if s.panicInTearDownTest {
		panic("oops in tear down test")
	}
}

func (s *panickingSuite) TearDownSuite() {
	if s.panicInTearDownSuite {
		panic("oops in tear down suite")
	}
}

func TestSuiteRecoverPanic(t *testing.T) {
	ok := true
	panickingTests := []testing.InternalTest{
		{
			Name: "TestPanicInSetupSuite",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInSetupSuite: true}) },
		},
		{
			Name: "TestPanicInSetupTest",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInSetupTest: true}) },
		},
		{
			Name: "TestPanicInBeforeTest",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInBeforeTest: true}) },
		},
		{
			Name: "TestPanicInTest",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTest: true}) },
		},
		{
			Name: "TestPanicInAfterTest",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInAfterTest: true}) },
		},
		{
			Name: "TestPanicInTearDownTest",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownTest: true}) },
		},
		{
			Name: "TestPanicInTearDownSuite",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownSuite: true}) },
		},
	}

	require.NotPanics(t, func() {
		ok = testing.RunTests(allTestsFilter, panickingTests)
	})

	assert.False(t, ok)
}

// This suite is intended to store values to make sure that only
// testing-suite-related methods are run.  It's also a fully
// functional example of a testing suite, using setup/teardown methods
// and a helper method that is ignored by testify.  To make this look
// more like a real world example, all tests in the suite perform some
// type of assertion.
type SuiteTester struct {
	// Include our basic suite logic.
	Suite

	// Keep counts of how many times each method is run.
	SetupSuiteRunCount    int
	TearDownSuiteRunCount int
	SetupTestRunCount     int
	TearDownTestRunCount  int
	TestOneRunCount       int
	TestTwoRunCount       int
	TestSubtestRunCount   int
	NonTestMethodRunCount int

	SuiteNameBefore []string
	TestNameBefore  []string

	SuiteNameAfter []string
	TestNameAfter  []string

	TimeBefore []time.Time
	TimeAfter  []time.Time
}

type SuiteSkipTester struct {
	// Include our basic suite logic.
	Suite

	// Keep counts of how many times each method is run.
	SetupSuiteRunCount    int
	TearDownSuiteRunCount int
}

// The SetupSuite method will be run by testify once, at the very
// start of the testing suite, before any tests are run.
func (suite *SuiteTester) SetupSuite() {
	suite.SetupSuiteRunCount++
}

func (suite *SuiteTester) BeforeTest(suiteName, testName string) {
	suite.SuiteNameBefore = append(suite.SuiteNameBefore, suiteName)
	suite.TestNameBefore = append(suite.TestNameBefore, testName)
	suite.TimeBefore = append(suite.TimeBefore, time.Now())
}

func (suite *SuiteTester) AfterTest(suiteName, testName string) {
	suite.SuiteNameAfter = append(suite.SuiteNameAfter, suiteName)
	suite.TestNameAfter = append(suite.TestNameAfter, testName)
	suite.TimeAfter = append(suite.TimeAfter, time.Now())
}

func (suite *SuiteSkipTester) SetupSuite() {
	suite.SetupSuiteRunCount++
	suite.T().Skip()
}

// The TearDownSuite method will be run by testify once, at the very
// end of the testing suite, after all tests have been run.
func (suite *SuiteTester) TearDownSuite() {
	suite.TearDownSuiteRunCount++
}

func (suite *SuiteSkipTester) TearDownSuite() {
	suite.TearDownSuiteRunCount++
}

// The SetupTest method will be run before every test in the suite.
func (suite *SuiteTester) SetupTest() {
	suite.SetupTestRunCount++
}

// The TearDownTest method will be run after every test in the suite.
func (suite *SuiteTester) TearDownTest() {
	suite.TearDownTestRunCount++
}

// Every method in a testing suite that begins with "Test" will be run
// as a test.  TestOne is an example of a test.  For the purposes of
// this example, we've included assertions in the tests, since most
// tests will issue assertions.
func (suite *SuiteTester) TestOne() {
	beforeCount := suite.TestOneRunCount
	suite.TestOneRunCount++
	assert.Equal(suite.T(), suite.TestOneRunCount, beforeCount+1)
	suite.Equal(suite.TestOneRunCount, beforeCount+1)
}

// TestTwo is another example of a test.
func (suite *SuiteTester) TestTwo() {
	beforeCount := suite.TestTwoRunCount
	suite.TestTwoRunCount++
	assert.NotEqual(suite.T(), suite.TestTwoRunCount, beforeCount)
	suite.NotEqual(suite.TestTwoRunCount, beforeCount)
}

func (suite *SuiteTester) TestSkip() {
	suite.T().Skip()
}

// NonTestMethod does not begin with "Test", so it will not be run by
// testify as a test in the suite.  This is useful for creating helper
// methods for your tests.
func (suite *SuiteTester) NonTestMethod() {
	suite.NonTestMethodRunCount++
}

func (suite *SuiteTester) TestSubtest() {
	suite.TestSubtestRunCount++

	for _, t := range []struct {
		testName string
	}{
		{"first"},
		{"second"},
	} {
		suiteT := suite.T()
		suite.Run(t.testName, func() {
			// We should get a different *testing.T for subtests, so that
			// go test recognizes them as proper subtests for output formatting
			// and running individual subtests
			subTestT := suite.T()
			suite.NotEqual(subTestT, suiteT)
		})
		suite.Equal(suiteT, suite.T())
	}
}

// TestRunSuite will be run by the 'go test' command, so within it, we
// can run our suite using the Run(*testing.T, TestingSuite) function.
func TestRunSuite(t *testing.T) {
	suiteTester := new(SuiteTester)
	Run(t, suiteTester)

	// Normally, the test would end here.  The following are simply
	// some assertions to ensure that the Run function is working as
	// intended - they are not part of the example.

	// The suite was only run once, so the SetupSuite and TearDownSuite
	// methods should have each been run only once.
	assert.Equal(t, suiteTester.SetupSuiteRunCount, 1)
	assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1)

	assert.Equal(t, len(suiteTester.SuiteNameAfter), 4)
	assert.Equal(t, len(suiteTester.SuiteNameBefore), 4)
	assert.Equal(t, len(suiteTester.TestNameAfter), 4)
	assert.Equal(t, len(suiteTester.TestNameBefore), 4)

	assert.Contains(t, suiteTester.TestNameAfter, "TestOne")
	assert.Contains(t, suiteTester.TestNameAfter, "TestTwo")
	assert.Contains(t, suiteTester.TestNameAfter, "TestSkip")
	assert.Contains(t, suiteTester.TestNameAfter, "TestSubtest")

	assert.Contains(t, suiteTester.TestNameBefore, "TestOne")
	assert.Contains(t, suiteTester.TestNameBefore, "TestTwo")
	assert.Contains(t, suiteTester.TestNameBefore, "TestSkip")
	assert.Contains(t, suiteTester.TestNameBefore, "TestSubtest")

	for _, suiteName := range suiteTester.SuiteNameAfter {
		assert.Equal(t, "SuiteTester", suiteName)
	}

	for _, suiteName := range suiteTester.SuiteNameBefore {
		assert.Equal(t, "SuiteTester", suiteName)
	}

	for _, when := range suiteTester.TimeAfter {
		assert.False(t, when.IsZero())
	}

	for _, when := range suiteTester.TimeBefore {
		assert.False(t, when.IsZero())
	}

	// There are four test methods (TestOne, TestTwo, TestSkip, and TestSubtest), so
	// the SetupTest and TearDownTest methods (which should be run once for
	// each test) should have been run four times.
	assert.Equal(t, suiteTester.SetupTestRunCount, 4)
	assert.Equal(t, suiteTester.TearDownTestRunCount, 4)

	// Each test should have been run once.
	assert.Equal(t, suiteTester.TestOneRunCount, 1)
	assert.Equal(t, suiteTester.TestTwoRunCount, 1)
	assert.Equal(t, suiteTester.TestSubtestRunCount, 1)

	// Methods that don't match the test method identifier shouldn't
	// have been run at all.
	assert.Equal(t, suiteTester.NonTestMethodRunCount, 0)

	suiteSkipTester := new(SuiteSkipTester)
	Run(t, suiteSkipTester)

	// The suite was only run once, so the SetupSuite and TearDownSuite
	// methods should have each been run only once, even though SetupSuite
	// called Skip()
	assert.Equal(t, suiteSkipTester.SetupSuiteRunCount, 1)
	assert.Equal(t, suiteSkipTester.TearDownSuiteRunCount, 1)

}

func TestSuiteGetters(t *testing.T) {
	suite := new(SuiteTester)
	suite.SetT(t)
	assert.NotNil(t, suite.Assert())
	assert.Equal(t, suite.Assertions, suite.Assert())
	assert.NotNil(t, suite.Require())
	assert.Equal(t, suite.require, suite.Require())
}

type SuiteLoggingTester struct {
	Suite
}

func (s *SuiteLoggingTester) TestLoggingPass() {
	s.T().Log("TESTLOGPASS")
}

func (s *SuiteLoggingTester) TestLoggingFail() {
	s.T().Log("TESTLOGFAIL")
	assert.NotNil(s.T(), nil) // expected to fail
}

type StdoutCapture struct {
	oldStdout *os.File
	readPipe  *os.File
}

func (sc *StdoutCapture) StartCapture() {
	sc.oldStdout = os.Stdout
	sc.readPipe, os.Stdout, _ = os.Pipe()
}

func (sc *StdoutCapture) StopCapture() (string, error) {
	if sc.oldStdout == nil || sc.readPipe == nil {
		return "", errors.New("StartCapture not called before StopCapture")
	}
	os.Stdout.Close()
	os.Stdout = sc.oldStdout
	bytes, err := ioutil.ReadAll(sc.readPipe)
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}

func TestSuiteLogging(t *testing.T) {
	suiteLoggingTester := new(SuiteLoggingTester)
	capture := StdoutCapture{}
	internalTest := testing.InternalTest{
		Name: "SomeTest",
		F: func(subT *testing.T) {
			Run(subT, suiteLoggingTester)
		},
	}
	capture.StartCapture()
	testing.RunTests(allTestsFilter, []testing.InternalTest{internalTest})
	output, err := capture.StopCapture()
	require.NoError(t, err, "Got an error trying to capture stdout and stderr!")
	require.NotEmpty(t, output, "output content must not be empty")

	// Failed tests' output is always printed
	assert.Contains(t, output, "TESTLOGFAIL")

	if testing.Verbose() {
		// In verbose mode, output from successful tests is also printed
		assert.Contains(t, output, "TESTLOGPASS")
	} else {
		assert.NotContains(t, output, "TESTLOGPASS")
	}
}
<commit_msg>Fix tests<commit_after>package suite

import (
	"errors"
	"io/ioutil"
	"os"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// SuiteRequireTwice is intended to test the usage of suite.Require in two
// different tests
type SuiteRequireTwice struct{ Suite }

// TestSuiteRequireTwice checks for regressions of issue #149 where
// suite.requirements was not initialised in suite.SetT()
// A regression would result on these tests panicking rather than failing.
func TestSuiteRequireTwice(t *testing.T) {
	ok := testing.RunTests(
		allTestsFilter,
		[]testing.InternalTest{{
			Name: "TestSuiteRequireTwice",
			F: func(t *testing.T) {
				suite := new(SuiteRequireTwice)
				Run(t, suite)
			},
		}},
	)
	assert.Equal(t, false, ok)
}

func (s *SuiteRequireTwice) TestRequireOne() {
	r := s.Require()
	r.Equal(1, 2)
}

func (s *SuiteRequireTwice) TestRequireTwo() {
	r := s.Require()
	r.Equal(1, 2)
}

type panickingSuite struct {
	Suite
	panicInSetupSuite    bool
	panicInSetupTest     bool
	panicInBeforeTest    bool
	panicInTest          bool
	panicInAfterTest     bool
	panicInTearDownTest  bool
	panicInTearDownSuite bool
}

func (s *panickingSuite) SetupSuite() {
	if s.panicInSetupSuite {
		panic("oops in setup suite")
	}
}

func (s *panickingSuite) SetupTest() {
	if s.panicInSetupTest {
		panic("oops in setup test")
	}
}

func (s *panickingSuite) BeforeTest(_, _ string) {
	if s.panicInBeforeTest {
		panic("oops in before test")
	}
}

func (s *panickingSuite) Test() {
	if s.panicInTest {
		panic("oops in test")
	}
}

func (s *panickingSuite) AfterTest(_, _ string) {
	if s.panicInAfterTest {
		panic("oops in after test")
	}
}

func (s *panickingSuite) TearDownTest() {
	if s.panicInTearDownTest {
		panic("oops in tear down test")
	}
}

func (s *panickingSuite) TearDownSuite() {
	if s.panicInTearDownSuite {
		panic("oops in tear down suite")
	}
}

func TestSuiteRecoverPanic(t *testing.T) {
	ok := true
	panickingTests := []testing.InternalTest{
		{
			Name: "TestPanicInSetupSuite",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInSetupSuite: true}) },
		},
		{
			Name: "TestPanicInSetupTest",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInSetupTest: true}) },
		},
		{
			Name: "TestPanicInBeforeTest",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInBeforeTest: true}) },
		},
		{
			Name: "TestPanicInTest",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTest: true}) },
		},
		{
			Name: "TestPanicInAfterTest",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInAfterTest: true}) },
		},
		{
			Name: "TestPanicInTearDownTest",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownTest: true}) },
		},
		{
			Name: "TestPanicInTearDownSuite",
			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownSuite: true}) },
		},
	}

	require.NotPanics(t, func() {
		ok = testing.RunTests(allTestsFilter, panickingTests)
	})

	assert.False(t, ok)
}

// This suite is intended to store values to make sure that only
// testing-suite-related methods are run.  It's also a fully
// functional example of a testing suite, using setup/teardown methods
// and a helper method that is ignored by testify.  To make this look
// more like a real world example, all tests in the suite perform some
// type of assertion.
type SuiteTester struct {
	// Include our basic suite logic.
	Suite

	// Keep counts of how many times each method is run.
	SetupSuiteRunCount    int
	TearDownSuiteRunCount int
	SetupTestRunCount     int
	TearDownTestRunCount  int
	TestOneRunCount       int
	TestTwoRunCount       int
	TestSubtestRunCount   int
	NonTestMethodRunCount int

	SuiteNameBefore []string
	TestNameBefore  []string

	SuiteNameAfter []string
	TestNameAfter  []string

	TimeBefore []time.Time
	TimeAfter  []time.Time
}

// The SetupSuite method will be run by testify once, at the very
// start of the testing suite, before any tests are run.
func (suite *SuiteTester) SetupSuite() {
	suite.SetupSuiteRunCount++
}

func (suite *SuiteTester) BeforeTest(suiteName, testName string) {
	suite.SuiteNameBefore = append(suite.SuiteNameBefore, suiteName)
	suite.TestNameBefore = append(suite.TestNameBefore, testName)
	suite.TimeBefore = append(suite.TimeBefore, time.Now())
}

func (suite *SuiteTester) AfterTest(suiteName, testName string) {
	suite.SuiteNameAfter = append(suite.SuiteNameAfter, suiteName)
	suite.TestNameAfter = append(suite.TestNameAfter, testName)
	suite.TimeAfter = append(suite.TimeAfter, time.Now())
}

// The TearDownSuite method will be run by testify once, at the very
// end of the testing suite, after all tests have been run.
func (suite *SuiteTester) TearDownSuite() {
	suite.TearDownSuiteRunCount++
}

// The SetupTest method will be run before every test in the suite.
func (suite *SuiteTester) SetupTest() {
	suite.SetupTestRunCount++
}

// The TearDownTest method will be run after every test in the suite.
func (suite *SuiteTester) TearDownTest() {
	suite.TearDownTestRunCount++
}

// Every method in a testing suite that begins with "Test" will be run
// as a test.  TestOne is an example of a test.  For the purposes of
// this example, we've included assertions in the tests, since most
// tests will issue assertions.
func (suite *SuiteTester) TestOne() {
	beforeCount := suite.TestOneRunCount
	suite.TestOneRunCount++
	assert.Equal(suite.T(), suite.TestOneRunCount, beforeCount+1)
	suite.Equal(suite.TestOneRunCount, beforeCount+1)
}

// TestTwo is another example of a test.
func (suite *SuiteTester) TestTwo() {
	beforeCount := suite.TestTwoRunCount
	suite.TestTwoRunCount++
	assert.NotEqual(suite.T(), suite.TestTwoRunCount, beforeCount)
	suite.NotEqual(suite.TestTwoRunCount, beforeCount)
}

func (suite *SuiteTester) TestSkip() {
	suite.T().Skip()
}

// NonTestMethod does not begin with "Test", so it will not be run by
// testify as a test in the suite.  This is useful for creating helper
// methods for your tests.
func (suite *SuiteTester) NonTestMethod() {
	suite.NonTestMethodRunCount++
}

func (suite *SuiteTester) TestSubtest() {
	suite.TestSubtestRunCount++

	for _, t := range []struct {
		testName string
	}{
		{"first"},
		{"second"},
	} {
		suiteT := suite.T()
		suite.Run(t.testName, func() {
			// We should get a different *testing.T for subtests, so that
			// go test recognizes them as proper subtests for output formatting
			// and running individual subtests
			subTestT := suite.T()
			suite.NotEqual(subTestT, suiteT)
		})
		suite.Equal(suiteT, suite.T())
	}
}

type SuiteSkipTester struct {
	// Include our basic suite logic.
	Suite

	// Keep counts of how many times each method is run.
	SetupSuiteRunCount    int
	TearDownSuiteRunCount int
}

func (suite *SuiteSkipTester) SetupSuite() {
	suite.SetupSuiteRunCount++
	suite.T().Skip()
}

func (suite *SuiteSkipTester) TestNothing() {
	// SetupSuite is only called when at least one test satisfies
	// test filter. For this suite to be set up (and then tore down)
	// it is necessary to add at least one test method.
}

func (suite *SuiteSkipTester) TearDownSuite() {
	suite.TearDownSuiteRunCount++
}

// TestRunSuite will be run by the 'go test' command, so within it, we
// can run our suite using the Run(*testing.T, TestingSuite) function.
func TestRunSuite(t *testing.T) {
	suiteTester := new(SuiteTester)
	Run(t, suiteTester)

	// Normally, the test would end here.  The following are simply
	// some assertions to ensure that the Run function is working as
	// intended - they are not part of the example.

	// The suite was only run once, so the SetupSuite and TearDownSuite
	// methods should have each been run only once.
	assert.Equal(t, suiteTester.SetupSuiteRunCount, 1)
	assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1)

	assert.Equal(t, len(suiteTester.SuiteNameAfter), 4)
	assert.Equal(t, len(suiteTester.SuiteNameBefore), 4)
	assert.Equal(t, len(suiteTester.TestNameAfter), 4)
	assert.Equal(t, len(suiteTester.TestNameBefore), 4)

	assert.Contains(t, suiteTester.TestNameAfter, "TestOne")
	assert.Contains(t, suiteTester.TestNameAfter, "TestTwo")
	assert.Contains(t, suiteTester.TestNameAfter, "TestSkip")
	assert.Contains(t, suiteTester.TestNameAfter, "TestSubtest")

	assert.Contains(t, suiteTester.TestNameBefore, "TestOne")
	assert.Contains(t, suiteTester.TestNameBefore, "TestTwo")
	assert.Contains(t, suiteTester.TestNameBefore, "TestSkip")
	assert.Contains(t, suiteTester.TestNameBefore, "TestSubtest")

	for _, suiteName := range suiteTester.SuiteNameAfter {
		assert.Equal(t, "SuiteTester", suiteName)
	}

	for _, suiteName := range suiteTester.SuiteNameBefore {
		assert.Equal(t, "SuiteTester", suiteName)
	}

	for _, when := range suiteTester.TimeAfter {
		assert.False(t, when.IsZero())
	}

	for _, when := range suiteTester.TimeBefore {
		assert.False(t, when.IsZero())
	}

	// There are four test methods (TestOne, TestTwo, TestSkip, and TestSubtest), so
	// the SetupTest and TearDownTest methods (which should be run once for
	// each test) should have been run four times.
	assert.Equal(t, suiteTester.SetupTestRunCount, 4)
	assert.Equal(t, suiteTester.TearDownTestRunCount, 4)

	// Each test should have been run once.
	assert.Equal(t, suiteTester.TestOneRunCount, 1)
	assert.Equal(t, suiteTester.TestTwoRunCount, 1)
	assert.Equal(t, suiteTester.TestSubtestRunCount, 1)

	// Methods that don't match the test method identifier shouldn't
	// have been run at all.
	assert.Equal(t, suiteTester.NonTestMethodRunCount, 0)

	suiteSkipTester := new(SuiteSkipTester)
	Run(t, suiteSkipTester)

	// The suite was only run once, so the SetupSuite and TearDownSuite
	// methods should have each been run only once, even though SetupSuite
	// called Skip()
	assert.Equal(t, suiteSkipTester.SetupSuiteRunCount, 1)
	assert.Equal(t, suiteSkipTester.TearDownSuiteRunCount, 1)

}

func TestSuiteGetters(t *testing.T) {
	suite := new(SuiteTester)
	suite.SetT(t)
	assert.NotNil(t, suite.Assert())
	assert.Equal(t, suite.Assertions, suite.Assert())
	assert.NotNil(t, suite.Require())
	assert.Equal(t, suite.require, suite.Require())
}

type SuiteLoggingTester struct {
	Suite
}

func (s *SuiteLoggingTester) TestLoggingPass() {
	s.T().Log("TESTLOGPASS")
}

func (s *SuiteLoggingTester) TestLoggingFail() {
	s.T().Log("TESTLOGFAIL")
	assert.NotNil(s.T(), nil) // expected to fail
}

type StdoutCapture struct {
	oldStdout *os.File
	readPipe  *os.File
}

func (sc *StdoutCapture) StartCapture() {
	sc.oldStdout = os.Stdout
	sc.readPipe, os.Stdout, _ = os.Pipe()
}

func (sc *StdoutCapture) StopCapture() (string, error) {
	if sc.oldStdout == nil || sc.readPipe == nil {
		return "", errors.New("StartCapture not called before StopCapture")
	}
	os.Stdout.Close()
	os.Stdout = sc.oldStdout
	bytes, err := ioutil.ReadAll(sc.readPipe)
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}

func TestSuiteLogging(t *testing.T) {
	suiteLoggingTester := new(SuiteLoggingTester)
	capture := StdoutCapture{}
	internalTest := testing.InternalTest{
		Name: "SomeTest",
		F: func(subT *testing.T) {
			Run(subT, suiteLoggingTester)
		},
	}
	capture.StartCapture()
	testing.RunTests(allTestsFilter, []testing.InternalTest{internalTest})
	output, err := capture.StopCapture()
	require.NoError(t, err, "Got an error trying to capture stdout and stderr!")
	require.NotEmpty(t, output, "output content must not be empty")

	// Failed tests' output is always printed
	assert.Contains(t, output, "TESTLOGFAIL")

	if testing.Verbose() {
		// In verbose mode, output from successful tests is also printed
		assert.Contains(t, output, "TESTLOGPASS")
	} else {
		assert.NotContains(t, output, "TESTLOGPASS")
	}
}
<|endoftext|>


<commit_before>package template_metrics

import (
	"fmt"
	metrics "github.com/yvasiyarov/go-metrics" // max,mean,min,stddev,percentile
	"html/template"                            // ToDo: how can I change this from outside?
	"io"
	"time"
)

// print infomation on each request
var Verbose = false

// Set Enable = false if you want to turn off the instrumentation
var Enable = true

// a set of proxies
var proxyRegistry = make(map[string](*proxyTemplate))

type proxyTemplate struct {
	name     string
	original *template.Template
	timers   map[string]metrics.Timer
}

func newProxyTemplate(name string, template *template.Template) *proxyTemplate {
	return &proxyTemplate{
		name:     name,
		original: template,
		timers:   map[string]metrics.Timer{},
	}
}

//print the elapsed time on each request if Verbose flag is true
func (proxy *proxyTemplate) printVerbose(elapsedTime time.Duration, base string) {
	fmt.Printf("time:%v\ttemplate:%s\tbase:%s\telapsed:%f\n",
		time.Now(),
		proxy.name,
		base,
		elapsedTime.Seconds(),
	)
}

//measure the time
func (proxy *proxyTemplate) measure(startTime time.Time, base string) {
	elapsedTime := time.Now().Sub(startTime)
	if proxy.timers[base] == nil {
		proxy.timers[base] = metrics.NewTimer()
	}
	proxy.timers[base].Update(elapsedTime)
	if Enable && Verbose {
		proxy.printVerbose(elapsedTime, base)
	}
}

////// instrument functions

// instrument template.Execute
func (proxy *proxyTemplate) Execute(wr io.Writer, data interface{}) error {
	var startTime time.Time
	if Enable {
		startTime = time.Now()
	}
	error := proxy.original.Execute(wr, data)
	if Enable {
		// treat as no base name
		defer proxy.measure(startTime, "")
	}
	return error
}

// instrucment template.ExecuteTemplate
func (proxy *proxyTemplate) ExecuteTemplate(wr io.Writer, base string, data interface{}) error {
	var startTime time.Time
	if Enable {
		startTime = time.Now()
	}
	error := proxy.original.ExecuteTemplate(wr, base, data)
	if Enable {
		defer proxy.measure(startTime, base)
	}
	return error
}

///// package functions

//Wrap  instrument template
func WrapTemplate(name string, template *template.Template) *proxyTemplate {
	proxy := newProxyTemplate(name, template)
	proxyRegistry[name] = proxy
	return proxy
}

//Print  print the metrics in each second
func Print(duration int) {
	timeDuration := time.Duration(duration)
	go func() {
		time.Sleep(timeDuration * time.Second)
		for {
			startTime := time.Now()
			for name, proxy := range proxyRegistry {
				for base, timer := range proxy.timers {
					count := timer.Count()
					if count > 0 {
						fmt.Printf(
							"time:%v\ttemplate:%s\tbase:%s\tcount:%d\tmax:%f\tmean:%f\tmin:%f\tpercentile95:%f\tduration:%d\n",
							time.Now(),
							name,
							base,
							timer.Count(),
							float64(timer.Max())/float64(time.Second),
							timer.Mean()/float64(time.Second),
							float64(timer.Min())/float64(time.Second),
							timer.Percentile(0.95)/float64(time.Second),
							duration,
						)
						proxy.timers[base] = metrics.NewTimer()
					}
				}
			}
			elapsedTime := time.Now().Sub(startTime)
			time.Sleep(timeDuration*time.Second - elapsedTime)
		}
	}()
}
<commit_msg>Expose Original<commit_after>package template_metrics

import (
	"fmt"
	metrics "github.com/yvasiyarov/go-metrics" // max,mean,min,stddev,percentile
	"html/template"                            // ToDo: how can I change this from outside?
	"io"
	"time"
)

// print infomation on each request
var Verbose = false

// Set Enable = false if you want to turn off the instrumentation
var Enable = true

// a set of proxies
var proxyRegistry = make(map[string](*proxyTemplate))

type proxyTemplate struct {
	name     string
	Original *template.Template
	timers   map[string]metrics.Timer
}

func newProxyTemplate(name string, template *template.Template) *proxyTemplate {
	return &proxyTemplate{
		name:     name,
		Original: template,
		timers:   map[string]metrics.Timer{},
	}
}

//print the elapsed time on each request if Verbose flag is true
func (proxy *proxyTemplate) printVerbose(elapsedTime time.Duration, base string) {
	fmt.Printf("time:%v\ttemplate:%s\tbase:%s\telapsed:%f\n",
		time.Now(),
		proxy.name,
		base,
		elapsedTime.Seconds(),
	)
}

//measure the time
func (proxy *proxyTemplate) measure(startTime time.Time, base string) {
	elapsedTime := time.Now().Sub(startTime)
	if proxy.timers[base] == nil {
		proxy.timers[base] = metrics.NewTimer()
	}
	proxy.timers[base].Update(elapsedTime)
	if Enable && Verbose {
		proxy.printVerbose(elapsedTime, base)
	}
}

////// instrument functions

// instrument template.Execute
func (proxy *proxyTemplate) Execute(wr io.Writer, data interface{}) error {
	var startTime time.Time
	if Enable {
		startTime = time.Now()
	}
	error := proxy.Original.Execute(wr, data)
	if Enable {
		// treat as no base name
		defer proxy.measure(startTime, "")
	}
	return error
}

// instrucment template.ExecuteTemplate
func (proxy *proxyTemplate) ExecuteTemplate(wr io.Writer, base string, data interface{}) error {
	var startTime time.Time
	if Enable {
		startTime = time.Now()
	}
	error := proxy.Original.ExecuteTemplate(wr, base, data)
	if Enable {
		defer proxy.measure(startTime, base)
	}
	return error
}

///// package functions

//Wrap  instrument template
func WrapTemplate(name string, template *template.Template) *proxyTemplate {
	proxy := newProxyTemplate(name, template)
	proxyRegistry[name] = proxy
	return proxy
}

//Print  print the metrics in each second
func Print(duration int) {
	timeDuration := time.Duration(duration)
	go func() {
		time.Sleep(timeDuration * time.Second)
		for {
			startTime := time.Now()
			for name, proxy := range proxyRegistry {
				for base, timer := range proxy.timers {
					count := timer.Count()
					if count > 0 {
						fmt.Printf(
							"time:%v\ttemplate:%s\tbase:%s\tcount:%d\tmax:%f\tmean:%f\tmin:%f\tpercentile95:%f\tduration:%d\n",
							time.Now(),
							name,
							base,
							timer.Count(),
							float64(timer.Max())/float64(time.Second),
							timer.Mean()/float64(time.Second),
							float64(timer.Min())/float64(time.Second),
							timer.Percentile(0.95)/float64(time.Second),
							duration,
						)
						proxy.timers[base] = metrics.NewTimer()
					}
				}
			}
			elapsedTime := time.Now().Sub(startTime)
			time.Sleep(timeDuration*time.Second - elapsedTime)
		}
	}()
}
<|endoftext|>


<commit_before>package actions

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/matthistuff/shelf/colors"
	"github.com/matthistuff/shelf/data"
	"gopkg.in/mgo.v2/bson"
	"strconv"
)

func Search(c *cli.Context) {
	colors.Allow(c)

	page := c.Int("page")
	perPage := 10

	searchQuery := data.ParseQuery(c.Args())
	search := []bson.M{}
	if searchQuery.Text != "" {
		search = append(search, bson.M{
			"$text": bson.M{
				"$search": searchQuery.Text,
			},
		})
	}

	for _, attrQuery := range searchQuery.AttributeQuery {
		search = append(search, bson.M{
			"attributes": bson.M{
				"$elemMatch": bson.M{
					"name": attrQuery.Name,
					"value": bson.M{
						"$regex": attrQuery.Value,
					},
				},
			},
		})
	}

	query := data.Objects().Find(bson.M{
		"$and": search,
	}).Select(bson.M{
		"score": bson.M{
			"$meta": "textScore",
		},
	}).Sort("$textScore:score", "-_id")

	total, _ := query.Count()
	result := []data.SearchObject{}
	query.Skip((page - 1) * perPage).Limit(perPage).All(&result)

	if total > 0 {
		for index, object := range result {
			fmt.Printf("(%s) %s \"%s\" %s\n", colors.ShortId(index+1),
				colors.ObjectId(object.Id.Hex()),
				object.Title,
				colors.Faint(fmt.Sprintf("[%.2f]", object.Score)))
		}
		fmt.Printf("Page %s of %s\n", colors.Bold(strconv.Itoa(page)), colors.Bold(strconv.Itoa(int(total/perPage)+1)))
	}
}
<commit_msg>write short ids for search results<commit_after>package actions

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/matthistuff/shelf/colors"
	"github.com/matthistuff/shelf/data"
	"gopkg.in/mgo.v2/bson"
	"strconv"
)

func Search(c *cli.Context) {
	colors.Allow(c)

	page := c.Int("page")
	perPage := 10

	searchQuery := data.ParseQuery(c.Args())
	search := []bson.M{}
	if searchQuery.Text != "" {
		search = append(search, bson.M{
			"$text": bson.M{
				"$search": searchQuery.Text,
			},
		})
	}

	for _, attrQuery := range searchQuery.AttributeQuery {
		search = append(search, bson.M{
			"attributes": bson.M{
				"$elemMatch": bson.M{
					"name": attrQuery.Name,
					"value": bson.M{
						"$regex": attrQuery.Value,
					},
				},
			},
		})
	}

	query := data.Objects().Find(bson.M{
		"$and": search,
	}).Select(bson.M{
		"score": bson.M{
			"$meta": "textScore",
		},
	}).Sort("$textScore:score", "-_id")

	total, _ := query.Count()
	result := []data.SearchObject{}
	query.Skip((page - 1) * perPage).Limit(perPage).All(&result)

	if total > 0 {
		data.ClearCache()
		defer data.FlushCache()

		for index, object := range result {
			fmt.Printf("(%s) %s \"%s\" %s\n", colors.ShortId(index+1),
				colors.ObjectId(object.Id.Hex()),
				object.Title,
				colors.Faint(fmt.Sprintf("[%.2f]", object.Score)))
			data.SetCache(strconv.Itoa(index+1), object.Id.Hex())
		}
		fmt.Printf("Page %s of %s\n", colors.Bold(strconv.Itoa(page)), colors.Bold(strconv.Itoa(int(total/perPage)+1)))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"./lib"
	"flag"
	"io/ioutil"
	"net/http"
)

func registerMc(alias, hostName string, portNumber int, accessKey, secretKey string) {
}

func main() {
	config := lib.ReadConfig()
	alias := *flag.String("alias", "akashic-storage", "name to access the server")
	hostName := *flag.String("hostname", "localhost", "hostname")
	portNumber := *flag.Int("portnumber", 10946, "port number")
	flag.Parse()

	args := flag.Args()
	userId := args[0]

	url := lib.AdminURL(config.HostName, config.PortNumber) + "/" + userId

	req, _ := http.NewRequest("GET", url, lib.EmptyReader)
	req.SetBasicAuth("admin", config.Passwd)

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

	bytes, _ := ioutil.ReadAll(res.Body)
	user := lib.NewUserFromXML(bytes)

	registerMc(alias, hostName, portNumber, user.AccessKey, user.SecretKey)
}
<commit_msg>admin: setup-mc<commit_after>package main

import (
	"./lib"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
)

func registerMc(alias, hostName string, portNumber int, accessKey, secretKey string) {
	command := exec.Command("mc", "config", "host", alias, "add",
		fmt.Sprintf("http://%s:%d", hostName, portNumber),
		accessKey, secretKey,
		"S3v2")
	if err := command.Run(); err != nil {
		os.Exit(1)
	}
}

func main() {
	config := lib.ReadConfig()
	alias := *flag.String("alias", "akashic-storage", "name to access the server")
	hostName := *flag.String("hostname", "localhost", "hostname")
	portNumber := *flag.Int("portnumber", 10946, "port number")
	flag.Parse()

	args := flag.Args()
	userId := args[0]

	url := lib.AdminURL(config.HostName, config.PortNumber) + "/" + userId

	req, _ := http.NewRequest("GET", url, lib.EmptyReader)
	req.SetBasicAuth("admin", config.Passwd)

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

	bytes, _ := ioutil.ReadAll(res.Body)
	user := lib.NewUserFromXML(bytes)

	registerMc(alias, hostName, portNumber, user.AccessKey, user.SecretKey)
}
<|endoftext|>