<commit_before>// Tideland Go Cells - Event
//
// Copyright (C) 2010-2017 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.

package cells

//--------------------
// IMPORTS
//--------------------

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/tideland/golib/errors"
)

//--------------------
// PAYLOAD
//--------------------

// PayloadValues is intended to set and get the information
// of a payload as bulk.
type PayloadValues map[string]interface{}

// Payload is a write-once/read-multiple container for the
// transport of additional information with events. In case
// one item is a reference type it's in the responsibility
// of the users to avoid concurrent changes of their values.
type Payload interface {
	fmt.Stringer

	// Len returns the number of values.
	Len() int

	// Get returns one of the payload values.
	Get(key string) (interface{}, bool)

	// GetBool returns one of the payload values
	// as bool. If it's no bool false is returned.
	GetBool(key string) (bool, bool)

	// GetInt returns one of the payload values
	// as int. If it's no int false is returned.
	GetInt(key string) (int, bool)

	// GetFloat64 returns one of the payload values
	// as float64. If it's no float64 false is returned.
	GetFloat64(key string) (float64, bool)

	// GetString returns one of the payload values
	// as string. If it's no string false is returned.
	GetString(key string) (string, bool)

	// GetTime returns one of the payload values
	// as time.Time. If it's no time false is returned.
	GetTime(key string) (time.Time, bool)

	// GetDuration returns one of the payload values as
	// time.Duration. If it's no duration false is returned.
	GetDuration(key string) (time.Duration, bool)

	// GetWaiter returns a payload waiter to be used
	// for answering with a payload.
	GetWaiter(key string) (PayloadWaiter, bool)

	// Keys return all keys of the payload.
	Keys() []string

	// Do iterates a function over all keys and values.
	Do(f func(key string, value interface{}) error) error

	// Apply creates a new payload containing the values
	// of this one and the passed values. Allowed are
	// PayloadValues, map[string]interface{}, and any
	// other single value. The latter will be stored
	// with the cells.DefaultPayload key. Values of this
	// payload are overwritten by those which are passed
	// if they share the key.
	Apply(values interface{}) Payload
}

// payload implements the Payload interface.
type payload struct {
	values PayloadValues
}

// NewPayload creates a new payload containing the passed
// values. In case of a Payload this is used directly, in
// case of a PayloadValues or a map[string]interface{} their
// content is used, and when passing any other type the
// value is stored with the key cells.DefaultPayload.
func NewPayload(values interface{}) Payload {
	if p, ok := values.(Payload); ok {
		return p
	}
	p := &payload{
		values: PayloadValues{},
	}
	switch vs := values.(type) {
	case PayloadValues:
		for key, value := range vs {
			p.values[key] = value
		}
	case map[string]interface{}:
		for key, value := range vs {
			p.values[key] = value
		}
	default:
		p.values[DefaultPayload] = values
	}
	return p
}

// Len implementes the Payload interface.
func (p *payload) Len() int {
	return len(p.values)
}

// Get implementes the Payload interface.
func (p *payload) Get(key string) (interface{}, bool) {
	value, ok := p.values[key]
	return value, ok
}

// GetBool implementes the Payload interface.
func (p *payload) GetBool(key string) (bool, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return false, ok
	}
	value, ok := raw.(bool)
	return value, ok
}

// GetInt implementes the Payload interface.
func (p *payload) GetInt(key string) (int, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return 0, ok
	}
	value, ok := raw.(int)
	return value, ok
}

// GetFloat64 implementes the Payload interface.
func (p *payload) GetFloat64(key string) (float64, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return 0.0, ok
	}
	value, ok := raw.(float64)
	return value, ok
}

// GetString implementes the Payload interface.
func (p *payload) GetString(key string) (string, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return "", ok
	}
	value, ok := raw.(string)
	return value, ok
}

// GetTime implementes the Payload interface.
func (p *payload) GetTime(key string) (time.Time, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return time.Time{}, ok
	}
	value, ok := raw.(time.Time)
	return value, ok
}

// GetDuration implementes the Payload interface.
func (p *payload) GetDuration(key string) (time.Duration, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return time.Duration(0), ok
	}
	value, ok := raw.(time.Duration)
	return value, ok
}

// GetWaiter implements the Payload interface.
func (p *payload) GetWaiter(key string) (PayloadWaiter, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return nil, ok
	}
	value, ok := raw.(PayloadWaiter)
	return value, ok
}

// Keys is specified on the Payload interface.
func (p *payload) Keys() []string {
	keys := []string{}
	for key := range p.values {
		keys = append(keys, key)
	}
	return keys
}

// Do implementes the Payload interface.
func (p *payload) Do(f func(key string, value interface{}) error) error {
	for key, value := range p.values {
		if err := f(key, value); err != nil {
			return err
		}
	}
	return nil
}

// Apply implementes the Payload interface.
func (p *payload) Apply(values interface{}) Payload {
	applied := &payload{
		values: PayloadValues{},
	}
	for key, value := range p.values {
		applied.values[key] = value
	}
	switch vs := values.(type) {
	case Payload:
		vs.Do(func(key string, value interface{}) error {
			applied.values[key] = value
			return nil
		})
	case PayloadValues:
		for key, value := range vs {
			applied.values[key] = value
		}
	case map[string]interface{}:
		for key, value := range vs {
			applied.values[key] = value
		}
	default:
		applied.values[DefaultPayload] = values
	}
	return applied
}

// String returns the payload represented as string.
func (p *payload) String() string {
	ps := []string{}
	for key, value := range p.values {
		ps = append(ps, fmt.Sprintf("<%q: %v>", key, value))
	}
	return strings.Join(ps, ", ")
}

// PayloadWaiter can be sent with an event as payload.
// Once a payload is set by a behavior Wait() continues
// and returns it.
type PayloadWaiter interface {
	// Set sets the payload somebody is waiting for.
	Set(p Payload)

	// Wait waits until the payload is set. A deadline
	// or timeout set by the context may cancel the
	// waiting.
	Wait(ctx context.Context) (Payload, error)
}

// payloadWaiter implements the PayloadWaiter interface.
type payloadWaiter struct {
	payloadc chan Payload
	once     sync.Once
}

// NewPayloadWaiter creates a new waiter for a payload
// returned by a behavior.
func NewPayloadWaiter() PayloadWaiter {
	return &payloadWaiter{
		payloadc: make(chan Payload, 1),
	}
}

// Set implements the PayloadWaiter interface.
func (w *payloadWaiter) Set(p Payload) {
	w.once.Do(func() {
		w.payloadc <- p
	})
}

// Wait implements the PayloadWaiter interface.
func (w *payloadWaiter) Wait(ctx context.Context) (Payload, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	for {
		select {
		case pl := <-w.payloadc:
			return pl, nil
		case <-ctx.Done():
			err := ctx.Err()
			return nil, err
		}
	}
}

//--------------------
// EVENT
//--------------------

// Event transports what to process.
type Event interface {
	fmt.Stringer

	// Context returns a Context that possibly has been
	// emitted with the event.
	Context() context.Context

	// Timestamp returns the UTC time the event has been created.
	Timestamp() time.Time

	// Topic returns the topic of the event.
	Topic() string

	// Payload returns the payload of the event.
	Payload() Payload
}

// event implements the Event interface.
type event struct {
	ctx       context.Context
	timestamp time.Time
	topic     string
	payload   Payload
}

// NewEvent creates a new event with the given topic and payload.
func NewEvent(ctx context.Context, topic string, payload interface{}) (Event, error) {
	if topic == "" {
		return nil, errors.New(ErrNoTopic, errorMessages)
	}
	p := NewPayload(payload)
	return &event{
		ctx:       ctx,
		timestamp: time.Now().UTC(),
		topic:     topic,
		payload:   p,
	}, nil
}

// Timestamp implements the Event interface.
func (e *event) Timestamp() time.Time {
	return e.timestamp
}

// Topic implements the Event interface.
func (e *event) Topic() string {
	return e.topic
}

// Payload implements the Event interface.
func (e *event) Payload() Payload {
	return e.payload
}

// Context implements the Event interface.
func (e *event) Context() context.Context {
	return e.ctx
}

// String implements the Stringer interface.
func (e *event) String() string {
	timeStr := e.timestamp.Format(time.RFC3339Nano)
	payloadStr := "none"
	if e.payload != nil {
		payloadStr = fmt.Sprintf("%v", e.payload)
	}
	return fmt.Sprintf("<timestamp: %s / topic: '%s' / payload: %s>", timeStr, e.topic, payloadStr)
}

//--------------------
// EVENT SINK
//--------------------

// EventSink stores a number of events ordered by adding. To be used
// in behaviors for collecting sets of events and operate on them.
type EventSink interface {
	// Add adds a new event data based on the passed event.
	Add(event Event) int

	// Len returns the number of stored events.
	Len() int

	// First returns the first of the collected events.
	First() (Event, bool)

	// Last returns the last of the collected event datas.
	Last() (Event, bool)

	// At returns an event at a given index and true if it
	// exists, otherwise nil and false.
	At(index int) (Event, bool)

	// Do iterates over all collected events.
	Do(doer func(index int, event Event) error) error

	// Match checks if all events match the passed criterion.
	Match(matcher func(index int, event Event) (bool, error)) (bool, error)

	// Clear removes all collected events.
	Clear()
}

// eventSink implements the EventSink interface.
type eventSink struct {
	mutex  sync.RWMutex
	max    int
	events []Event
}

// NewEventSink creates a sink for events.
func NewEventSink(max int) EventSink {
	return &eventSink{
		max: max,
	}
}

// Add implements the EventSink interface.
func (s *eventSink) Add(event Event) int {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.events = append(s.events, event)
	if s.max > 0 && len(s.events) > s.max {
		s.events = s.events[1:]
	}
	return len(s.events)
}

// Len implements the EventSink interface.
func (s *eventSink) Len() int {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	return len(s.events)
}

// First implements the EventSink interface.
func (s *eventSink) First() (Event, bool) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	if len(s.events) < 1 {
		return nil, false
	}
	return s.events[0], true
}

// Last implements the EventSink interface.
func (s *eventSink) Last() (Event, bool) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	if len(s.events) < 1 {
		return nil, false
	}
	return s.events[len(s.events)-1], true
}

// At implements the EventSink interface.
func (s *eventSink) At(index int) (Event, bool) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	if index < 0 || index > len(s.events)-1 {
		return nil, false
	}
	return s.events[index], true
}

// Do implements the EventSink interface.
func (s *eventSink) Do(doer func(index int, event Event) error) error {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	for index, event := range s.events {
		if err := doer(index, event); err != nil {
			return err
		}
	}
	return nil
}

// Match implements the EventSink interface.
func (s *eventSink) Match(matcher func(index int, event Event) (bool, error)) (bool, error) {
	match := true
	doer := func(mindex int, mevent Event) error {
		ok, err := matcher(mindex, mevent)
		if err != nil {
			match = false
			return err
		}
		match = match && ok
		return nil
	}
	err := s.Do(doer)
	return match, err
}

// Clear implements tne EventSink interface.
func (s *eventSink) Clear() {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.events = nil
}

// EOF
<commit_msg>Started adding waiter to a sink<commit_after>// Tideland Go Cells - Event
//
// Copyright (C) 2010-2017 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.

package cells

//--------------------
// IMPORTS
//--------------------

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/tideland/golib/errors"
)

//--------------------
// PAYLOAD
//--------------------

// PayloadValues is intended to set and get the information
// of a payload as bulk.
type PayloadValues map[string]interface{}

// Payload is a write-once/read-multiple container for the
// transport of additional information with events. In case
// one item is a reference type it's in the responsibility
// of the users to avoid concurrent changes of their values.
type Payload interface {
	fmt.Stringer

	// Len returns the number of values.
	Len() int

	// Get returns one of the payload values.
	Get(key string) (interface{}, bool)

	// GetBool returns one of the payload values
	// as bool. If it's no bool false is returned.
	GetBool(key string) (bool, bool)

	// GetInt returns one of the payload values
	// as int. If it's no int false is returned.
	GetInt(key string) (int, bool)

	// GetFloat64 returns one of the payload values
	// as float64. If it's no float64 false is returned.
	GetFloat64(key string) (float64, bool)

	// GetString returns one of the payload values
	// as string. If it's no string false is returned.
	GetString(key string) (string, bool)

	// GetTime returns one of the payload values
	// as time.Time. If it's no time false is returned.
	GetTime(key string) (time.Time, bool)

	// GetDuration returns one of the payload values as
	// time.Duration. If it's no duration false is returned.
	GetDuration(key string) (time.Duration, bool)

	// GetWaiter returns a payload waiter to be used
	// for answering with a payload.
	GetWaiter(key string) (PayloadWaiter, bool)

	// Keys return all keys of the payload.
	Keys() []string

	// Do iterates a function over all keys and values.
	Do(f func(key string, value interface{}) error) error

	// Apply creates a new payload containing the values
	// of this one and the passed values. Allowed are
	// PayloadValues, map[string]interface{}, and any
	// other single value. The latter will be stored
	// with the cells.DefaultPayload key. Values of this
	// payload are overwritten by those which are passed
	// if they share the key.
	Apply(values interface{}) Payload
}

// payload implements the Payload interface.
type payload struct {
	values PayloadValues
}

// NewPayload creates a new payload containing the passed
// values. In case of a Payload this is used directly, in
// case of a PayloadValues or a map[string]interface{} their
// content is used, and when passing any other type the
// value is stored with the key cells.DefaultPayload.
func NewPayload(values interface{}) Payload {
	if p, ok := values.(Payload); ok {
		return p
	}
	p := &payload{
		values: PayloadValues{},
	}
	switch vs := values.(type) {
	case PayloadValues:
		for key, value := range vs {
			p.values[key] = value
		}
	case map[string]interface{}:
		for key, value := range vs {
			p.values[key] = value
		}
	default:
		p.values[DefaultPayload] = values
	}
	return p
}

// Len implementes the Payload interface.
func (p *payload) Len() int {
	return len(p.values)
}

// Get implementes the Payload interface.
func (p *payload) Get(key string) (interface{}, bool) {
	value, ok := p.values[key]
	return value, ok
}

// GetBool implementes the Payload interface.
func (p *payload) GetBool(key string) (bool, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return false, ok
	}
	value, ok := raw.(bool)
	return value, ok
}

// GetInt implementes the Payload interface.
func (p *payload) GetInt(key string) (int, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return 0, ok
	}
	value, ok := raw.(int)
	return value, ok
}

// GetFloat64 implementes the Payload interface.
func (p *payload) GetFloat64(key string) (float64, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return 0.0, ok
	}
	value, ok := raw.(float64)
	return value, ok
}

// GetString implementes the Payload interface.
func (p *payload) GetString(key string) (string, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return "", ok
	}
	value, ok := raw.(string)
	return value, ok
}

// GetTime implementes the Payload interface.
func (p *payload) GetTime(key string) (time.Time, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return time.Time{}, ok
	}
	value, ok := raw.(time.Time)
	return value, ok
}

// GetDuration implementes the Payload interface.
func (p *payload) GetDuration(key string) (time.Duration, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return time.Duration(0), ok
	}
	value, ok := raw.(time.Duration)
	return value, ok
}

// GetWaiter implements the Payload interface.
func (p *payload) GetWaiter(key string) (PayloadWaiter, bool) {
	raw, ok := p.Get(key)
	if !ok {
		return nil, ok
	}
	value, ok := raw.(PayloadWaiter)
	return value, ok
}

// Keys is specified on the Payload interface.
func (p *payload) Keys() []string {
	keys := []string{}
	for key := range p.values {
		keys = append(keys, key)
	}
	return keys
}

// Do implementes the Payload interface.
func (p *payload) Do(f func(key string, value interface{}) error) error {
	for key, value := range p.values {
		if err := f(key, value); err != nil {
			return err
		}
	}
	return nil
}

// Apply implementes the Payload interface.
func (p *payload) Apply(values interface{}) Payload {
	applied := &payload{
		values: PayloadValues{},
	}
	for key, value := range p.values {
		applied.values[key] = value
	}
	switch vs := values.(type) {
	case Payload:
		vs.Do(func(key string, value interface{}) error {
			applied.values[key] = value
			return nil
		})
	case PayloadValues:
		for key, value := range vs {
			applied.values[key] = value
		}
	case map[string]interface{}:
		for key, value := range vs {
			applied.values[key] = value
		}
	default:
		applied.values[DefaultPayload] = values
	}
	return applied
}

// String returns the payload represented as string.
func (p *payload) String() string {
	ps := []string{}
	for key, value := range p.values {
		ps = append(ps, fmt.Sprintf("<%q: %v>", key, value))
	}
	return strings.Join(ps, ", ")
}

// PayloadWaiter can be sent with an event as payload.
// Once a payload is set by a behavior Wait() continues
// and returns it.
type PayloadWaiter interface {
	// Set sets the payload somebody is waiting for.
	Set(p Payload)

	// Wait waits until the payload is set. A deadline
	// or timeout set by the context may cancel the
	// waiting.
	Wait(ctx context.Context) (Payload, error)
}

// payloadWaiter implements the PayloadWaiter interface.
type payloadWaiter struct {
	payloadc chan Payload
	once     sync.Once
}

// NewPayloadWaiter creates a new waiter for a payload
// returned by a behavior.
func NewPayloadWaiter() PayloadWaiter {
	return &payloadWaiter{
		payloadc: make(chan Payload, 1),
	}
}

// Set implements the PayloadWaiter interface.
func (w *payloadWaiter) Set(p Payload) {
	w.once.Do(func() {
		w.payloadc <- p
	})
}

// Wait implements the PayloadWaiter interface.
func (w *payloadWaiter) Wait(ctx context.Context) (Payload, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	for {
		select {
		case pl := <-w.payloadc:
			return pl, nil
		case <-ctx.Done():
			err := ctx.Err()
			return nil, err
		}
	}
}

//--------------------
// EVENT
//--------------------

// Event transports what to process.
type Event interface {
	fmt.Stringer

	// Context returns a Context that possibly has been
	// emitted with the event.
	Context() context.Context

	// Timestamp returns the UTC time the event has been created.
	Timestamp() time.Time

	// Topic returns the topic of the event.
	Topic() string

	// Payload returns the payload of the event.
	Payload() Payload
}

// event implements the Event interface.
type event struct {
	ctx       context.Context
	timestamp time.Time
	topic     string
	payload   Payload
}

// NewEvent creates a new event with the given topic and payload.
func NewEvent(ctx context.Context, topic string, payload interface{}) (Event, error) {
	if topic == "" {
		return nil, errors.New(ErrNoTopic, errorMessages)
	}
	p := NewPayload(payload)
	return &event{
		ctx:       ctx,
		timestamp: time.Now().UTC(),
		topic:     topic,
		payload:   p,
	}, nil
}

// Timestamp implements the Event interface.
func (e *event) Timestamp() time.Time {
	return e.timestamp
}

// Topic implements the Event interface.
func (e *event) Topic() string {
	return e.topic
}

// Payload implements the Event interface.
func (e *event) Payload() Payload {
	return e.payload
}

// Context implements the Event interface.
func (e *event) Context() context.Context {
	return e.ctx
}

// String implements the Stringer interface.
func (e *event) String() string {
	timeStr := e.timestamp.Format(time.RFC3339Nano)
	payloadStr := "none"
	if e.payload != nil {
		payloadStr = fmt.Sprintf("%v", e.payload)
	}
	return fmt.Sprintf("<timestamp: %s / topic: '%s' / payload: %s>", timeStr, e.topic, payloadStr)
}

//--------------------
// EVENT SINK
//--------------------

// EventSinkIterator can be used to check the events in a sink.
type EventSinkIterator interface {
	// Do iterates over all collected events.
	Do(doer func(index int, event Event) error) error

	// Match checks if all events match the passed criterion.
	Match(matcher func(index int, event Event) (bool, error)) (bool, error)
}

// EventSinkChecker can be used to check sinks for a criterion.
type EventSinkChecker func(events EventSinkIterator) (bool, error)

// EventSink stores a number of events ordered by adding. To be used
// in behaviors for collecting sets of events and operate on them.
type EventSink interface {
	// Add adds a new event data based on the passed event.
	Add(event Event) int

	// Len returns the number of stored events.
	Len() int

	// First returns the first of the collected events.
	First() (Event, bool)

	// Last returns the last of the collected event datas.
	Last() (Event, bool)

	// At returns an event at a given index and true if it
	// exists, otherwise nil and false.
	At(index int) (Event, bool)

	// Clear removes all collected events.
	Clear()

	EventSinkIterator
}

// eventSink implements the EventSink interface.
type eventSink struct {
	mutex   sync.RWMutex
	max     int
	events  []Event
	checker EventSinkChecker
	waiter  PayloadWaiter
}

// NewEventSink creates a sink for events.
func NewEventSink(max int) EventSink {
	return &eventSink{
		max: max,
	}
}

// NewCheckedEventSink creates a sink running a checker
// after each change.
func NewCheckedEventSink(max int, checker EventSinkChecker) (EventSink, PayloadWaiter) {
	waiter := NewPayloadWaiter()
	return &eventSink{
		max:     max,
		checker: checker,
		waiter:  waiter,
	}, waiter
}

// Add implements the EventSink interface.
func (s *eventSink) Add(event Event) int {
	s.mutex.Lock()
	s.events = append(s.events, event)
	if s.max > 0 && len(s.events) > s.max {
		s.events = s.events[1:]
	}
	s.mutex.Unlock()
	if s.checker != nil {
		ok, err := s.checker(s)
		if err != nil {
			// TODO
			return 0
		}
		if ok {
			s.waiter.Set(NewPayload(s))
		}
	}
	return len(s.events)
}

// Len implements the EventSink interface.
func (s *eventSink) Len() int {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	return len(s.events)
}

// First implements the EventSink interface.
func (s *eventSink) First() (Event, bool) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	if len(s.events) < 1 {
		return nil, false
	}
	return s.events[0], true
}

// Last implements the EventSink interface.
func (s *eventSink) Last() (Event, bool) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	if len(s.events) < 1 {
		return nil, false
	}
	return s.events[len(s.events)-1], true
}

// At implements the EventSink interface.
func (s *eventSink) At(index int) (Event, bool) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	if index < 0 || index > len(s.events)-1 {
		return nil, false
	}
	return s.events[index], true
}

// Clear implements tne EventSink interface.
func (s *eventSink) Clear() {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.events = nil
}

// Do implements the EventSinkIterator interface.
func (s *eventSink) Do(doer func(index int, event Event) error) error {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	for index, event := range s.events {
		if err := doer(index, event); err != nil {
			return err
		}
	}
	return nil
}

// Match implements the EventSinkIterator interface.
func (s *eventSink) Match(matcher func(index int, event Event) (bool, error)) (bool, error) {
	match := true
	doer := func(mindex int, mevent Event) error {
		ok, err := matcher(mindex, mevent)
		if err != nil {
			match = false
			return err
		}
		match = match && ok
		return nil
	}
	err := s.Do(doer)
	return match, err
}

// EOF
<|endoftext|>


<commit_before>package cfcurl_test

import (
	"bufio"
	"errors"
	"os"

	. "github.com/krujos/cfcurl"

	"github.com/cloudfoundry/cli/plugin/fakes"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Cfcurl", func() {
	var fakeCliConnection *fakes.FakeCliConnection
	var v2apps []string

	Describe("an api that is not depricated", func() {
		BeforeEach(func() {
			fakeCliConnection = &fakes.FakeCliConnection{}
			file, err := os.Open("apps.json")
			defer file.Close()
			if err != nil {
				Fail("Could not open apps.json")
			}

			scanner := bufio.NewScanner(file)
			for scanner.Scan() {
				v2apps = append(v2apps, scanner.Text())
			}

			if scanner.Err() != nil {
				Fail("Failed to read lines from file")
			}

			if 0 == len(v2apps) {
				Fail("you didn't read anything in")
			}
		})

		AfterEach(func() {
			v2apps = nil
		})

		Describe("cf cli results validation", func() {
			It("returns an error when there is no output", func() {
				fakeCliConnection.CliCommandWithoutTerminalOutputReturns(nil, nil)
				appsJSON, err := Curl(fakeCliConnection, "/v2/apps")
				Expect(err).ToNot(BeNil())
				Expect(appsJSON).To(BeNil())
			})

			It("returns an error with zero length output", func() {

				fakeCliConnection.CliCommandWithoutTerminalOutputReturns([]string{""}, nil)
				appsJSON, err := Curl(fakeCliConnection, "/v2/apps")
				Expect(err).ToNot(BeNil())
				Expect(appsJSON).To(BeNil())
			})

			It("should call the path specified", func() {
				fakeCliConnection.CliCommandWithoutTerminalOutputReturns(v2apps, nil)
				Curl(fakeCliConnection, "/v2/an_unpredictable_path")
				args := fakeCliConnection.CliCommandWithoutTerminalOutputArgsForCall(0)
				Expect("curl").To(Equal(args[0]))
				Expect("/v2/an_unpredictable_path").To(Equal(args[1]))
			})

			It("returns an error when the cli fails", func() {
				fakeCliConnection.CliCommandWithoutTerminalOutputReturns(nil, errors.New("Something bad"))
				appsJSON, err := Curl(fakeCliConnection, "/v2/an_unpredictable_path")
				Expect(appsJSON).To(BeNil())
				Expect(err).NotTo(BeNil())
			})
		})
		It("should return the output for apps", func() {
			fakeCliConnection.CliCommandWithoutTerminalOutputReturns(v2apps, nil)
			appsJSON, err := Curl(fakeCliConnection, "/v2/apps")
			Expect(err).To(BeNil())
			Expect(appsJSON).ToNot(BeNil())
		})
	})
})
<commit_msg>validate json<commit_after>package cfcurl_test

import (
	"bufio"
	"errors"
	"os"

	. "github.com/krujos/cfcurl"

	"github.com/cloudfoundry/cli/plugin/fakes"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Cfcurl", func() {
	var fakeCliConnection *fakes.FakeCliConnection
	var v2apps []string

	Describe("an api that is not depricated", func() {
		BeforeEach(func() {
			fakeCliConnection = &fakes.FakeCliConnection{}
			file, err := os.Open("apps.json")
			defer file.Close()
			if err != nil {
				Fail("Could not open apps.json")
			}

			scanner := bufio.NewScanner(file)
			for scanner.Scan() {
				v2apps = append(v2apps, scanner.Text())
			}

			if scanner.Err() != nil {
				Fail("Failed to read lines from file")
			}

			if 0 == len(v2apps) {
				Fail("you didn't read anything in")
			}
		})

		AfterEach(func() {
			v2apps = nil
		})

		Describe("cf cli results validation", func() {
			It("returns an error when there is no output", func() {
				fakeCliConnection.CliCommandWithoutTerminalOutputReturns(nil, nil)
				appsJSON, err := Curl(fakeCliConnection, "/v2/apps")
				Expect(err).ToNot(BeNil())
				Expect(appsJSON).To(BeNil())
			})

			It("returns an error with zero length output", func() {

				fakeCliConnection.CliCommandWithoutTerminalOutputReturns([]string{""}, nil)
				appsJSON, err := Curl(fakeCliConnection, "/v2/apps")
				Expect(err).ToNot(BeNil())
				Expect(appsJSON).To(BeNil())
			})

			It("should call the path specified", func() {
				fakeCliConnection.CliCommandWithoutTerminalOutputReturns(v2apps, nil)
				Curl(fakeCliConnection, "/v2/an_unpredictable_path")
				args := fakeCliConnection.CliCommandWithoutTerminalOutputArgsForCall(0)
				Expect("curl").To(Equal(args[0]))
				Expect("/v2/an_unpredictable_path").To(Equal(args[1]))
			})

			It("returns an error when the cli fails", func() {
				fakeCliConnection.CliCommandWithoutTerminalOutputReturns(nil, errors.New("Something bad"))
				appsJSON, err := Curl(fakeCliConnection, "/v2/an_unpredictable_path")
				Expect(appsJSON).To(BeNil())
				Expect(err).NotTo(BeNil())
			})
		})

		Describe("we get legit json for apps", func() {

			It("should return the output for apps", func() {
				fakeCliConnection.CliCommandWithoutTerminalOutputReturns(v2apps, nil)
				appsJSON, err := Curl(fakeCliConnection, "/v2/apps")
				Expect(err).To(BeNil())
				Expect(appsJSON).ToNot(BeNil())
			})

			It("has a 2 results", func() {
				fakeCliConnection.CliCommandWithoutTerminalOutputReturns(v2apps, nil)
				appsJSON, _ := Curl(fakeCliConnection, "/v2/apps")
				Expect(appsJSON["total_results"]).To(Equal(float64(2)))
			})
		})
	})
})
<|endoftext|>


<commit_before>package main

import "fmt"
import (
	"flag"
	"os"
	"strings"
	"regexp"
	"path/filepath"
	"bytes"
	"io"
	"github.com/urfave/cli"
)

type TransferSource struct {
	path string
}

const ERR_MISSING_PARAMS = 1
const ERR_COULD_NOT_COMPILE_SOURCE_PATTERN = 2

//var wordPtr = flag.String("word", "foo", "a string")
//var numbPtr = flag.Int("numb", 42, "an int")
//var svar string
var debug = flag.Bool("debug", false, "enable debug messages")
var help = flag.Bool("help", false, "show help")
var useRegex = flag.Bool("use-regex", false, "use real regex instead of glob patterns")
var simulate = flag.Bool("simulate", false, "simulation - just show preview, do not really transfer")
var times = flag.Bool("times", false, "keep times")
var move = flag.Bool("move", false, "move files instead of copying")

func dbg(a ...interface{}) {
	if (*debug) {
		fmt.Println(a...)
	}
}

func printlnWrapper(a ...interface{}) {
	fmt.Println(a...)
}

func exitWithError(message string, code int) {
	fmt.Println(message)
	os.Exit(code)
}

func exitWithHelp(message string) {
	fmt.Println(message)
	flag.Usage();
	os.Exit(ERR_MISSING_PARAMS)
}

func showFindResults(paths []string, sourcePattern *regexp.Regexp) {
	for i := 0; i < len(paths); i++ {
		printlnWrapper(paths[i])
		normalizedPath := normalizeDirSep(paths[i])
		sourcePattern.ReplaceAllStringFunc(normalizedPath, func(m string) string {
			parts := sourcePattern.FindStringSubmatch(m)
			i := 1
			for range parts[1:] {
				printlnWrapper("  $1: " + parts[i])
				i++

			}
			return m
		})

	}
}

// https://blog.gopheracademy.com/advent-2014/parsers-lexers/
// https://gist.github.com/yangls06/5464683

//var debug = flag.Bool("debug", false, "enable debug messages")
//var help = flag.Bool("help", false, "show help")
//var useRegex = flag.Bool("use-regex", false, "use real regex instead of glob patterns")
//var simulate = flag.Bool("simulate", false, "simulation - just show preview, do not really transfer")
//var times = flag.Bool("times", false, "keep times")
//var move = flag.Bool("move", false, "move files instead of copying")
//

func main() {

	// default values for args
	app := cli.NewApp()
	app.Name = "graft"
	app.Usage = "find and copy files via command line"
	app.Version = "0.0.1"
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "dry-run",
			Usage: "perform a dry-run without transferring files",
		},
	}
	//app.Flags = []cli.Flag{
	//	cli.StringFlag{
	//		Name:  "p, project-name",
	//		Usage: "Specify an alternate project name (default: directory name)",
	//	},
	//}

	//app.PositionalArgs = []cli.StringArg{
	//	Name: "branch",
	//	Optional: true,
	//	Value: &x
	//}
	// global level flags
	//app.Flags = []gangstaCli.Flag{
	//	gangstaCli.BoolFlag{
	//		Name:  "verbose",
	//		Usage: "Show more output",
	//	},
	//	gangstaCli.StringFlag{
	//		Name:  "f, file",
	//		Usage: "Specify an alternate fig file (default: fig.yml)",
	//	},
	//	gangstaCli.StringFlag{
	//		Name:  "p, project-name",
	//		Usage: "Specify an alternate project name (default: directory name)",
	//	},
	//}
	//
	//// Commands
	//app.Commands = []gangstaCli.Command{
	//	{
	//		Name: "build",
	//		Flags: []gangstaCli.Flag{
	//			gangstaCli.BoolFlag{
	//				Name:  "no-cache",
	//				Usage: "Do not use cache when building the image.",
	//			},
	//		},
	//		Usage:  "Build or rebuild services",
	//		Action: CmdBuild,
	//	},
	//	// etc...
	//	{
	//		Name: "run",
	//		Flags: []gangstaCli.Flag{
	//			gangstaCli.BoolFlag{
	//				Name:  "d",
	//				Usage: "Detached mode: Run container in the background, print new container name.",
	//			},
	//			gangstaCli.BoolFlag{
	//				Name:  "T",
	//				Usage: "Disables psuedo-tty allocation. By default `fig run` allocates a TTY.",
	//			},
	//			gangstaCli.BoolFlag{
	//				Name:  "rm",
	//				Usage: "Remove container after run.  Ignored in detached mode.",
	//			},
	//			gangstaCli.BoolFlag{
	//				Name:  "no-deps",
	//				Usage: "Don't start linked services.",
	//			},
	//		},
	//		Usage:  "Run a one-off command",
	//		Action: CmdRm,
	//	},
	//
	//	{
	//		Name: "up",
	//		Flags: []gangstaCli.Flag{
	//			gangstaCli.BoolFlag{
	//				Name:  "watch",
	//				Usage: "Watch build directory for changes and auto-rebuild/restart",
	//			},
	//			gangstaCli.BoolFlag{
	//				Name:  "d",
	//				Usage: "Detached mode: Run containers in the background, print new container names.",
	//			},
	//			gangstaCli.BoolFlag{
	//				Name:  "k,kill",
	//				Usage: "Kill instead of stop on terminal stignal",
	//			},
	//			gangstaCli.BoolFlag{
	//				Name:  "no-clean",
	//				Usage: "Don't remove containers after termination signal interrupt (CTRL+C)",
	//			},
	//			gangstaCli.BoolFlag{
	//				Name:  "no-deps",
	//				Usage: "Don't start linked services.",
	//			},
	//			gangstaCli.BoolFlag{
	//				Name:  "no-recreate",
	//				Usage: "If containers already exist, don't recreate them.",
	//			},
	//		},
	//		Usage:  "Create and start containers",
	//		Action: CmdUp,
	//	},
	//}

	app.Action = func(c *cli.Context) error {

		appCommand(c)

		//fmt.Println("src: ", sourcePattern)
		//fmt.Println("dst: ", destinationPattern)

		//if language == "spanish" {
		//	fmt.Println("Hola", name)
		//} else {
		//	fmt.Println("Hello", name)
		//}
		return nil
	}

	app.Run(os.Args)
	os.Exit(0)

}

func appCommand(c *cli.Context) {
	//patt, err := compilePattern("fixtures", "(?i)(.*)")
	//x := patt.ReplaceAllString("fixtures/global/textfile.txt", "test/$1")
	//dbg(x)
	//os.Exit(0)
	sourcePattern := ""
	if c.NArg() < 1 {
		fmt.Println("missing required parameter source-pattern, use --help parameter for usage instructions")
		return nil
	}

	sourcePattern = c.Args().Get(0)
	destinationPattern := ""
	if c.NArg() > 1{
		destinationPattern = c.Args().Get(1)
	}

	path, pattern := parseSourcePattern(sourcePattern)

	dbg("src - parameter:", sourcePattern)
	dbg("src - parsedPath: ", path)
	dbg("src - pattern: ", pattern)
	dbg("dst - parameter:", destinationPattern)

	dbg("regex preparation - before: " + pattern)
	var replacedPattern string
	if (*useRegex) {
		replacedPattern = pattern
	} else {
		replacedPattern = GlobToRegex(pattern)
	}
	dbg("regex preparation - after: " + replacedPattern)

	compiledPattern, err := compilePattern(path, "(?i)" + replacedPattern)
	if compiledPattern.NumSubexp() < 1 {
		compiledPattern, err = compilePattern(path, "(?i)(" + replacedPattern + ")")
	}

	if (err != nil) {
		exitWithError("could not compile source pattern: " + err.Error(), ERR_COULD_NOT_COMPILE_SOURCE_PATTERN)
	}

	dbg("=============================================");
	if destinationPattern == "" {
		dbg("search in path " + path + ", pattern: " + pattern)
	} else {
		dbg("replace in path " + path + ", pattern: " + pattern + ", replacement: " + destinationPattern)
	}
	dbg("=============================================");

	list := make([]string, 0)
	err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		dbg("===================================")
		dbg("path: " + path)

		normalizedPath := normalizeDirSep(path)
		// normalizedPath := strings.Replace(path, "\\", "/", -1)

		dbg("normalized: " + normalizedPath)

		if ! compiledPattern.MatchString(normalizedPath) {
			dbg("match: no")
			return nil
		}
		dbg("match: yes, appending to list")

		//compiledPattern.ReplaceAllStringFunc(normalizedPath, func(m string) string {
		//	parts := compiledPattern.FindStringSubmatch(m)
		//	i:=1
		//	for range parts[1:] {
		//		dbg("    match: " + parts[i])
		//		i++
		//
		//	}
		//	return m
		//})
		list = append(list, path)

		//if info.IsDir() {
		//	return nil
		//}
		//if filepath.Ext(path) == ".sh" {
		//	list = append(list, path)
		//}
		return nil
	})

	if destinationPattern == "" {
		showFindResults(list, compiledPattern)
		os.Exit(0)
	}

	printlnWrapper("===================================")
	if *move {
		printlnWrapper("move files: " + sourcePattern + " => " + destinationPattern)
	} else {
		printlnWrapper("copy files: " + sourcePattern + " => " + destinationPattern)
	}

	printlnWrapper("===================================")
	transferFiles(list, compiledPattern, destinationPattern)

	//if err != nil {
	//	exitWithError("walk failed: " + err.Error(), ERR_WALK_FAILED)
	//}

	//if compiledPattern.MatchString(foundFile) {
	//	fmt.Println("match: " + foundFile + " => " + preparedPatternToCompile)
	//} else {
	//	fmt.Println("no match: " + foundFile + " => " + preparedPatternToCompile)
	//}

	//res := compiledPattern.FindStringSubmatch(foundFile)
	//fmt.Printf("%v", res)

	// input := `bla bla b:foo="hop" blablabla b:bar="hu?"`
	//r := regexp.MustCompile(`(\bb:\w+=")([^"]+)`)
	//fmt.Println(compiledPattern.ReplaceAllStringFunc(foundFile, func(m string) string {
	//	parts := compiledPattern.FindStringSubmatch(m)
	//	// return parts[1] + complexFunc(parts[2])
	//	// fmt.Println(m)
	//	fmt.Println("0: " + parts[0])
	//	fmt.Println("1: " + parts[1])
	//	//fmt.Println("2: " + parts[2])
	//	return m
	//}))

	// var sourcePattern = [0]
	// var sourcePattern = flag.Args()[0]

	//err := filepath.Walk(sourcePattern, fileWalkCallback)
	//
	//for e := mapping.Front(); e != nil; e = e.Next() {
	//	fmt.Println(e.Value)
	//}

	//fmt.Printf("filepath.Walk() returned %v\n", err)
	//
	//fmt.Println("sourcePattern:", sourcePattern)
	//fmt.Println("destinationPattern:", destinationPattern)
	//if len(flag.Args()) == 0 {
	//	flag.Usage()
	//	os.Exit(1)
	//}
	//fmt.Println(*sourcePattern);

	// reader := io.ReaderAt(sourcePattern)
	// reader.ReadAt(),
}
func transferFiles(paths []string, sourcePattern *regexp.Regexp, replacement string) {
	for i := 0; i < len(paths); i++ {
		dbg("path: " + paths[i])
		dbg("patt: ", sourcePattern)
		dbg("repl: " + replacement)
		transferFile(paths[i], sourcePattern.ReplaceAllString(paths[i], replacement))
	}
}
func transferFile(src string, dst string) {
	printlnWrapper(src + " => " + dst)
	if *simulate {
		return
	}

	var inDirStats os.FileInfo
	inStats, err := os.Stat(src)
	inDirStats = inStats
	var srcSize int64 = 0

	var srcDir string
	if !inStats.IsDir() {
		srcDir = filepath.Dir(src)
		inDirStats, err = os.Stat(srcDir)
		srcSize = inStats.Size()
	}

	dbg("srcSize: ", srcSize)

	if err != nil {
		printlnWrapper("could not determine attributes for " + src + ": " + err.Error())
		return
	}

	var dstStats os.FileInfo
	dstStats, err = os.Stat(dst)
	dstExists := false
	var dstSize int64 = 0
	if !os.IsNotExist(err) {
		dstExists = true
		dstSize = dstStats.Size()
	}

	dbg("dstSize: ", dstSize)
	dbg("dstExists: ", dstExists)

	if inStats.IsDir() {
		if os.IsNotExist(err) {
			err = os.MkdirAll(dst, inDirStats.Mode())
		}

		if err != nil {
			printlnWrapper("could not create destination directory " + dst + ": " + err.Error())
		}
		return
	}

	if *move && !dstExists {
		renameErr := os.Rename(src, dst)
		if renameErr != nil {
			printlnWrapper("could not rename " + src + ": " + renameErr.Error())
		} else {
			os.Remove(srcDir)
			return
		}
	}

	fi, inError := os.Open(src)
	defer fi.Close()
	if inError != nil {
		printlnWrapper("could not open source file " + src + ": " + err.Error())
		return
	}

	//flags := os.O_RDWR | os.O_CREATE
	//if dstExists {
	//	flags = os.O_RDWR | os.O_APPEND
	//}
	fo, outError := os.OpenFile(dst, os.O_RDWR | os.O_CREATE | os.O_APPEND, inStats.Mode())
	//var fo os.File
	//var outError error
	//if dstExists {
	//	fo, outError := os.Open(dst)
	//} else {
	//	fo, outError := os.Create(dst)
	//}

	defer fo.Close()
	if outError != nil {
		printlnWrapper("could not open destination file " + dst + ": " + err.Error())
		return
	}

	if srcSize == 0 {
		return
	}

	if dstExists {

		if (!areFilesEqual(fi, fo, srcSize, dstSize)) {
			printlnWrapper("source and destination are not equal " + src + " != " + dst)
			return
		}

		if *move {
			removeErr := os.Remove(dst)
			if removeErr != nil {
				printlnWrapper("Could not remove existing file before moving")
				return
			}

			renameErr := os.Rename(src, dst)
			if renameErr != nil {
				printlnWrapper("could not rename " + src + ": " + renameErr.Error())
			} else {
				os.Remove(srcDir)
				return
			}
		}

		_, fiErr := fi.Seek(dstSize, 0)
		if fiErr != nil {
			printlnWrapper("could not seek source file " + src + ": " + fiErr.Error())
			return
		}

		_, foErr := fo.Seek(dstSize, 0)
		if foErr != nil {
			printlnWrapper("could not seek destination file " + dst + ": " + foErr.Error())
			return
		}
	}

	buf := make([]byte, 1024)
	for {
		// read a chunk
		n, err := fi.Read(buf)
		if err != nil && err != io.EOF {
			printlnWrapper("reading file chunk failed: " + err.Error())
			return
		}
		if n == 0 {
			break
		}

		// write a chunk
		if _, err := fo.Write(buf[:n]); err != nil {
			printlnWrapper("writing file chunk failed: " + err.Error())
		}
	}

	if *times {
		os.Chtimes(dst, inStats.ModTime(), inStats.ModTime())
	}

	if (*move) {
		os.Remove(src)
		os.Remove(srcDir)
	}

	//var fo os.File
	//
	//if os.IsNotExist(err) {
	//	fo, err = os.CreateFile(dst)
	//} else {
	//	fo, err = os.OpenFile()
	//}

	//fi, inErr := os.Open(src)
	//if inErr != nil {
	//	printlnWrapper("could not open source file " + src + ": " + inErr.Error())
	//	return
	//}
	//fo, outErr := createOrOpenFile(dst)
	//if outErr != nil {
	//	printlnWrapper("could not open destination file " + dst + ": " + outErr.Error())
	//	return
	//}

	// os.Chtimes()
	//os.Chown()
	//os.Chmod()
}

func areFilesEqual(fi *os.File, fo *os.File, inSize int64, outSize int64) (bool) {

	if (outSize > inSize) {
		return false
	}

	var bufSize int64
	bufSize = 1024 * 1024 * 1024
	backBufSize := bufSize
	if bufSize > outSize {
		bufSize = outSize
		backBufSize = 0
	} else if outSize < bufSize * 2 {
		backBufSize = outSize - bufSize
	}

	fiBuf := make([]byte, bufSize)
	_, err := fi.ReadAt(fiBuf, 0)

	if err != nil {
		printlnWrapper("comparing files failed reading in buffer: " + err.Error())
	}

	foBuf := make([]byte, bufSize)
	_, err = fo.ReadAt(foBuf, 0)

	if err != nil {
		printlnWrapper("comparing files failed reading in out buffer: " + err.Error())
	}

	if ! bytes.Equal(fiBuf, foBuf) {
		return false
	}

	if backBufSize > 0 {
		backOffset := outSize - backBufSize
		fiBuf = make([]byte, backBufSize)
		_, err = fi.ReadAt(fiBuf, backOffset)
		if err != nil {
			printlnWrapper("comparing files failed reading in back buffer: " + err.Error())
		}
		foBuf = make([]byte, backBufSize)
		_, err = fo.ReadAt(foBuf, backOffset)
		if err != nil {
			printlnWrapper("comparing files failed reading out back buffer: " + err.Error())
		}
		if ! bytes.Equal(fiBuf, foBuf) {
			return false
		}
	}

	// buf := make([]byte, 1024)
	return true
}
<commit_msg>removed old code<commit_after><|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	_ "github.com/lib/pq" // sql database
	"github.com/pkg/errors"
)

// Queryer database/sql compatible query interface
type Queryer interface {
	Exec(string, ...interface{}) (sql.Result, error)
	Query(string, ...interface{}) (*sql.Rows, error)
	QueryRow(string, ...interface{}) *sql.Row
}

// Txer database/sql transaction interface
type Txer interface {
	Queryer
	Commit() error
	Rollback() error
}

// DBer database/sql
type DBer interface {
	Queryer
	Begin() (*sql.Tx, error)
	Close() error
	Ping() error
}

// DB database
type DB struct {
	*sql.DB
}

// DBConfig config
type DBConfig struct {
	Host     string
	User     string
	UserPass string
	Port     string
	DBName   string
	SSLMode  string
}

// App custom context
type App struct {
	DB     DBer
	Logger *log.Logger
}

// NewDB creates DB
func NewDB(c *DBConfig) (DBer, error) {
	conStr := fmt.Sprintf("user=%s dbname=%s sslmode=%s", c.User, c.DBName, c.SSLMode)
	db, err := sql.Open("postgres", conStr)
	if err != nil {
		return nil, errors.Wrap(err, "failed to create db")
	}
	return &DB{db}, nil
}

// NewApp new echo
func NewApp(dbCfg *DBConfig) (*App, error) {
	db, err := NewDB(dbCfg)
	if err != nil {
		return nil, err
	}
	e := &App{
		DB:     db,
		Logger: log.New(os.Stdout, "[echo app] ", log.LstdFlags),
	}
	return e, nil
}

// Message message
type Message struct {
	Text string    `json:"text"`
	Time time.Time `json:"time"`
}

// Hello say hello
func (app *App) Hello(ctx echo.Context) error {
	var t time.Time
	err := app.DB.QueryRow(`select now()`).Scan(&t)
	if err != nil {
		return err
	}
	msg := Message{
		Text: "hello!",
		Time: t,
	}
	app.Logger.Printf("sending back message: %v", msg)
	ctx.JSON(http.StatusOK, msg)
	return nil
}

func main() {

	e := echo.New()
	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	cfg := &DBConfig{
		Host:    "localhost",
		DBName:  "gotodoit",
		User:    "gotodoit_api",
		Port:    "5432",
		SSLMode: "disable",
	}
	app, err := NewApp(cfg)
	if err != nil {
		log.Fatal(err)
	}
	// Route => handler
	e.GET("/hello", app.Hello)

	// Start server
	e.Logger.Fatal(e.Start(":1323"))
}
<commit_msg>Update<commit_after>package main

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	_ "github.com/lib/pq" // sql database
	"github.com/pkg/errors"
)

// Queryer database/sql compatible query interface
type Queryer interface {
	Exec(string, ...interface{}) (sql.Result, error)
	Query(string, ...interface{}) (*sql.Rows, error)
	QueryRow(string, ...interface{}) *sql.Row
}

// Txer database/sql transaction interface
type Txer interface {
	Queryer
	Commit() error
	Rollback() error
}

// DBer database/sql
type DBer interface {
	Queryer
	Begin() (*sql.Tx, error)
	Close() error
	Ping() error
}

// DB database
type DB struct {
	*sql.DB
}

// DBConfig config
type DBConfig struct {
	Host     string
	User     string
	UserPass string
	Port     string
	DBName   string
	SSLMode  string
}

// App custom context
type App struct {
	DB     DBer
	Logger *log.Logger
}

// NewDB creates DB
func NewDB(c *DBConfig) (DBer, error) {
	conStr := fmt.Sprintf("user=%s dbname=%s sslmode=%s", c.User, c.DBName, c.SSLMode)
	db, err := sql.Open("postgres", conStr)
	if err != nil {
		return nil, errors.Wrap(err, "failed to create db")
	}
	return &DB{db}, nil
}

// NewApp new echo
func NewApp(dbCfg *DBConfig) (*App, error) {
	db, err := NewDB(dbCfg)
	if err != nil {
		return nil, err
	}
	e := &App{
		DB:     db,
		Logger: log.New(os.Stdout, "[echo app] ", log.LstdFlags),
	}
	return e, nil
}

// Message message
type Message struct {
	Text string    `json:"text"`
	Time time.Time `json:"time"`
}

// Hello say hello
func (app *App) Hello(ctx echo.Context) error {
	var t time.Time
	err := app.DB.QueryRow(`select now()`).Scan(&t)
	if err != nil {
		return err
	}
	msg := Message{
		Text: "hello!",
		Time: t,
	}
	app.Logger.Printf("sending back message: %v", msg)
	l := ctx.Logger()
	l.Debugf("this is echo logger: %v", msg)
	ctx.JSON(http.StatusOK, msg)
	return nil
}

func main() {

	e := echo.New()
	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	cfg := &DBConfig{
		Host:    "localhost",
		DBName:  "gotodoit",
		User:    "gotodoit_api",
		Port:    "5432",
		SSLMode: "disable",
	}
	app, err := NewApp(cfg)
	if err != nil {
		log.Fatal(err)
	}
	// Route => handler
	e.GET("/hello", app.Hello)
	e.GET("/logger", func(ctx echo.Context) error {
		ctx.Logger().Debug("no log")
		return nil
	})

	// Start server
	e.Logger.Fatal(e.Start(":1323"))
}
<|endoftext|>


<commit_before>/*
 Copyright 2020 The GoPlus Authors (goplus.org)

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

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

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

package spec

import (
	"reflect"
)

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

// A ConstKind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type ConstKind = reflect.Kind

const (
	// BigInt - bound type - bigint
	BigInt = ConstKind(reflect.UnsafePointer + 1)
	// BigRat - bound type - bigrat
	BigRat = ConstKind(reflect.UnsafePointer + 2)
	// BigFloat - bound type - bigfloat
	BigFloat = ConstKind(reflect.UnsafePointer + 3)
	// ConstBoundRune - bound type: rune
	ConstBoundRune = reflect.Int32
	// ConstBoundString - bound type: string
	ConstBoundString = reflect.String
	// ConstUnboundInt - unbound int type
	ConstUnboundInt = ConstKind(reflect.UnsafePointer + 4)
	// ConstUnboundFloat - unbound float type
	ConstUnboundFloat = ConstKind(reflect.UnsafePointer + 5)
	// ConstUnboundComplex - unbound complex type
	ConstUnboundComplex = ConstKind(reflect.UnsafePointer + 6)
	// ConstUnboundPtr - nil: unbound ptr
	ConstUnboundPtr = ConstKind(reflect.UnsafePointer + 7)
	// Slice - bound type: slice
	Slice = reflect.Slice
	// Map - bound type: map
	Map = reflect.Map
	// Chan - bound type: chan
	Chan = reflect.Chan
	// Ptr - bound type: ptr
	Ptr = reflect.Ptr
)

// IsConstBound checks a const is bound or not.
func IsConstBound(kind ConstKind) bool {
	return kind <= BigFloat
}

// -----------------------------------------------------------------------------
<commit_msg>ast/spec: add KindName for dump<commit_after>/*
 Copyright 2020 The GoPlus Authors (goplus.org)

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

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

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

package spec

import (
	"reflect"
)

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

// A ConstKind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type ConstKind = reflect.Kind

const (
	// BigInt - bound type - bigint
	BigInt = ConstKind(reflect.UnsafePointer + 1)
	// BigRat - bound type - bigrat
	BigRat = ConstKind(reflect.UnsafePointer + 2)
	// BigFloat - bound type - bigfloat
	BigFloat = ConstKind(reflect.UnsafePointer + 3)
	// ConstBoundRune - bound type: rune
	ConstBoundRune = reflect.Int32
	// ConstBoundString - bound type: string
	ConstBoundString = reflect.String
	// ConstUnboundInt - unbound int type
	ConstUnboundInt = ConstKind(reflect.UnsafePointer + 4)
	// ConstUnboundFloat - unbound float type
	ConstUnboundFloat = ConstKind(reflect.UnsafePointer + 5)
	// ConstUnboundComplex - unbound complex type
	ConstUnboundComplex = ConstKind(reflect.UnsafePointer + 6)
	// ConstUnboundPtr - nil: unbound ptr
	ConstUnboundPtr = ConstKind(reflect.UnsafePointer + 7)
	// Slice - bound type: slice
	Slice = reflect.Slice
	// Map - bound type: map
	Map = reflect.Map
	// Chan - bound type: chan
	Chan = reflect.Chan
	// Ptr - bound type: ptr
	Ptr = reflect.Ptr
)

// IsConstBound checks a const is bound or not.
func IsConstBound(kind ConstKind) bool {
	return kind <= BigFloat
}

func KindName(kind ConstKind) string {
	switch kind {
	case BigInt:
		return "bigint"
	case BigRat:
		return "bigrat"
	case BigFloat:
		return "bigfloat"
	case ConstUnboundInt:
		return "unbound int"
	case ConstUnboundFloat:
		return "unbound float"
	case ConstUnboundComplex:
		return "unbound complex"
	case ConstUnboundPtr:
		return "unbound ptr"
	}
	return kind.String()
}

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


<commit_before>package main

import (
	"crypto/rand"
	"flag"
	"github.com/schmichael/minnow"
	"log"
	"net"
)

var host = flag.String("host", "localhost:9876", "host and port of winnower")

func main() {
	flag.Parse()

	message := "Hello Kyle!"
	secret := []byte("goduckyourself")
	err := WriteMessage(message, secret)

	if err != nil {
		log.Fatalf("You had one job: %+v", err)
	} else {
		log.Printf("You won")
	}
}

func WriteMessage(message string, secret []byte) error {
	var err error
	raw := []byte(message)
	fakesecret := make([]byte, 64)
	fakedata := make([]byte, 1)

	rand.Read(fakesecret[0:64])

	conn, err := net.Dial("tcp", *host)
	defer conn.Close()

	if err != nil {
		log.Fatalf("Connection failed: %+v", err)
	}

	rs := minnow.NewStream(conn, secret)
	fs := minnow.NewStream(conn, fakesecret)

	for i, v := range raw {
		// Send the real packet
		bytev := []byte{v}

		err = rs.WritePacket(bytev, int32(i))
		if err != nil {
			return err
		}

		log.Printf("Sent good packet: %d %s", i, string(v))

		// Send the chaf packet
		//FIXME Obviously always sending the chaf second makes this trivial to break
		rand.Read(fakedata[0:1])
		err = fs.WritePacket(fakedata, int32(i))
		if err != nil {
			return err
		}
		log.Printf("Sent chaf packet: %d %v", i, fakedata)
	}

	return err
}
<commit_msg>Make secret configurable<commit_after>package main

import (
	"crypto/rand"
	"flag"
	"github.com/schmichael/minnow"
	"log"
	"net"
)

var host = flag.String("host", "localhost:9876", "host and port of winnower")
var secret = flag.String("secret", "goduckyourself", "shared secret")

func main() {
	flag.Parse()

	message := "Hello Kyle!"
	err := WriteMessage(message, []byte(*secret))

	if err != nil {
		log.Fatalf("You had one job: %+v", err)
	} else {
		log.Printf("You won")
	}
}

func WriteMessage(message string, secret []byte) error {
	var err error
	raw := []byte(message)
	fakesecret := make([]byte, 64)
	fakedata := make([]byte, 1)

	rand.Read(fakesecret[0:64])

	conn, err := net.Dial("tcp", *host)
	defer conn.Close()

	if err != nil {
		log.Fatalf("Connection failed: %+v", err)
	}

	rs := minnow.NewStream(conn, secret)
	fs := minnow.NewStream(conn, fakesecret)

	for i, v := range raw {
		// Send the real packet
		bytev := []byte{v}

		err = rs.WritePacket(bytev, int32(i))
		if err != nil {
			return err
		}

		log.Printf("Sent good packet: %d %s", i, string(v))

		// Send the chaf packet
		//FIXME Obviously always sending the chaf second makes this trivial to break
		rand.Read(fakedata[0:1])
		err = fs.WritePacket(fakedata, int32(i))
		if err != nil {
			return err
		}
		log.Printf("Sent chaf packet: %d %v", i, fakedata)
	}

	return err
}
<|endoftext|>


<commit_before>//The server package privdes the basic gofire server
package server

import (
	"encoding/json"
	"gofire/user"
	"net/http"
	"net/url"
	"strings"
)

//Rounting of the restful api
const (
	API      = "/api"
	CHAT     = "/api/c"
	CHATROOM = "/api/c/"
)

type FireServer struct {
	Addr                string `json:"-"`
	RegisteredChatRooms []string
	User                []user.User `json:"-"`
}

//a fireserver instance
var fireServer = new(FireServer)
var restCommands = make(map[string]string)

func init() {
	AddRestCommand(API, ApiHandler, "Get all commands")
	AddRestCommand(CHAT, ChatRoomHandler, "Get all chatrooms")
	AddRestCommand(CHATROOM, SpecificChatRoomHandler, "Get specific chatroom info")
	//initServer()
}

//adds a rest command
func AddRestCommand(pattern string, handler func(http.ResponseWriter, *http.Request), desc string) {
	restCommands[pattern] = desc
	http.HandleFunc(pattern, handler)
}

//A wrapper for the ListenAndServe of net/http
func ListenAndServe(addr string) error {
	fireServer.Addr = addr
	err := http.ListenAndServe(addr, nil)
	return err
}

func ApiHandler(w http.ResponseWriter, r *http.Request) {
	json, err := json.Marshal(restCommands)
	if err != nil {
		w.Write([]byte("404"))
	} else {
		w.Write(json)
	}
}

//ChatRoomHandler
func ChatRoomHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		getAllChatrooms(w, r)
	}

	if r.Method == "POST" {
		err := r.ParseForm()
		if err != nil {
			//TODO Write error better
			w.Write([]byte([]byte(string(http.StatusBadRequest))))
		} else {
			name := r.FormValue("name")
			fireServer.RegisteredChatRooms = append(fireServer.RegisteredChatRooms, name)
			w.Write([]byte(name))
		}
	}
}

func postChatRoom(form url.Values, w http.ResponseWriter) {
	name := form.Get("name")
	fireServer.RegisteredChatRooms = append(fireServer.RegisteredChatRooms, name)
	w.Write([]byte(string(http.StatusOK)))
}

// get /c
func getAllChatrooms(w http.ResponseWriter, r *http.Request) {
	if len(fireServer.RegisteredChatRooms) != 0 {
		json, err := json.Marshal(fireServer.RegisteredChatRooms)
		if err == nil {
			w.Write(json)
		} else {
			w.Write([]byte(string(http.StatusNotFound)))
		}

	} else {
		w.Write([]byte(string(http.StatusNotFound)))
	}
}

//get information about an specific chatroom
func SpecificChatRoomHandler(w http.ResponseWriter, r *http.Request) {
	name := getChatRoomName(r.URL.Path)
	if name != "" {
		roomcommand := strings.Split(name, "/")
		w.Write([]byte(roomcommand[1]))
	} else {
		w.Write([]byte(string(http.StatusBadRequest)))
	}
}

func isCommand(input string) bool {
	return false
}

//helper function for getting the chatroom
func getChatRoomName(link string) string {
	if len(link) == len(CHATROOM) {
		return ""
	}

	return link[len(CHATROOM):]
}
<commit_msg>added some comments for the FireServer struct<commit_after>//The server package privdes the basic gofire server
package server

import (
	"encoding/json"
	"gofire/user"
	"net/http"
	"net/url"
	"strings"
)

//Rounting of the restful api
const (
	API      = "/api"
	CHAT     = "/api/c"
	CHATROOM = "/api/c/"
)

type FireServer struct {
	Addr                string `json:"-"`//The Adress the server is running on
	RegisteredChatRooms []string//All registered chatrooms
	User                []user.User `json:"-"`//All user on the chatroom
}

//a fireserver instance
var fireServer = new(FireServer)
var restCommands = make(map[string]string)

func init() {
	AddRestCommand(API, ApiHandler, "Get all commands")
	AddRestCommand(CHAT, ChatRoomHandler, "Get all chatrooms")
	AddRestCommand(CHATROOM, SpecificChatRoomHandler, "Get specific chatroom info")
	//initServer()
}

//adds a rest command
func AddRestCommand(pattern string, handler func(http.ResponseWriter, *http.Request), desc string) {
	restCommands[pattern] = desc
	http.HandleFunc(pattern, handler)
}

//A wrapper for the ListenAndServe of net/http
func ListenAndServe(addr string) error {
	fireServer.Addr = addr
	err := http.ListenAndServe(addr, nil)
	return err
}

func ApiHandler(w http.ResponseWriter, r *http.Request) {
	json, err := json.Marshal(restCommands)
	if err != nil {
		w.Write([]byte("404"))
	} else {
		w.Write(json)
	}
}

//ChatRoomHandler
func ChatRoomHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		getAllChatrooms(w, r)
	}

	if r.Method == "POST" {
		err := r.ParseForm()
		if err != nil {
			//TODO Write error better
			w.Write([]byte([]byte(string(http.StatusBadRequest))))
		} else {
			name := r.FormValue("name")
			fireServer.RegisteredChatRooms = append(fireServer.RegisteredChatRooms, name)
			w.Write([]byte(name))
		}
	}
}

func postChatRoom(form url.Values, w http.ResponseWriter) {
	name := form.Get("name")
	fireServer.RegisteredChatRooms = append(fireServer.RegisteredChatRooms, name)
	w.Write([]byte(string(http.StatusOK)))
}

// get /c
func getAllChatrooms(w http.ResponseWriter, r *http.Request) {
	if len(fireServer.RegisteredChatRooms) != 0 {
		json, err := json.Marshal(fireServer.RegisteredChatRooms)
		if err == nil {
			w.Write(json)
		} else {
			w.Write([]byte(string(http.StatusNotFound)))
		}

	} else {
		w.Write([]byte(string(http.StatusNotFound)))
	}
}

//get information about an specific chatroom
func SpecificChatRoomHandler(w http.ResponseWriter, r *http.Request) {
	name := getChatRoomName(r.URL.Path)
	if name != "" {
		roomcommand := strings.Split(name, "/")
		w.Write([]byte(roomcommand[1]))
	} else {
		w.Write([]byte(string(http.StatusBadRequest)))
	}
}

func isCommand(input string) bool {
	return false
}

//helper function for getting the chatroom
func getChatRoomName(link string) string {
	if len(link) == len(CHATROOM) {
		return ""
	}

	return link[len(CHATROOM):]
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/md14454/gosensors"
)

func main() {
	gosensors.Init()
	defer gosensors.Cleanup()

	chips := gosensors.GetDetectedChips()

	for i := 0; i < len(chips); i++ {
		chip := chips[i]

		fmt.Printf("%v\n", chip)
		fmt.Printf("Adapter: %v\n", chip.AdapterName())

		features := chip.GetFeatures()

		for j := 0; j < len(features); j++ {
			feature := features[j]

			fmt.Printf("%v (%v): %.1f\n", feature.Name, feature.GetLabel(), feature.GetValue())

			subfeatures := feature.GetSubFeatures()

			for k := 0; k < len(subfeatures); k++ {
				subfeature := subfeatures[k]

				fmt.Printf("  %v: %.1f\n", subfeature.Name, subfeature.GetValue())
			}
		}

		fmt.Printf("\n")
	}
}
<commit_msg>Fix to generate identical output to PySensors<commit_after>package main

import (
	"fmt"

	"github.com/md14454/gosensors"
)

func main() {
	gosensors.Init()
	defer gosensors.Cleanup()

	chips := gosensors.GetDetectedChips()

	for i := 0; i < len(chips); i++ {
		chip := chips[i]

		fmt.Printf("%v\n", chip)
		fmt.Printf("Adapter: %v\n", chip.AdapterName())

		features := chip.GetFeatures()

		for j := 0; j < len(features); j++ {
			feature := features[j]

			fmt.Printf("%v ('%v'): %.1f\n", feature.Name, feature.GetLabel(), feature.GetValue())

			subfeatures := feature.GetSubFeatures()

			for k := 0; k < len(subfeatures); k++ {
				subfeature := subfeatures[k]

				fmt.Printf("  %v: %.1f\n", subfeature.Name, subfeature.GetValue())
			}
		}

		fmt.Printf("\n")
	}
}
<|endoftext|>


<commit_before>package aci

/*

Image Layout

The on-disk layout of an app container is straightforward.
It includes a rootfs with all of the files that will exist in the root of the app and a manifest describing the image.
The layout must contain an app image manifest.

/manifest
/rootfs/
/rootfs/usr/bin/mysql

*/

import (
	"archive/tar"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/coreos/rocket/app-container/schema"
)

var (
	ErrNoRootFS   = errors.New("no rootfs found in layout")
	ErrNoManifest = errors.New("no app image manifest found in layout")
)

// ValidateLayout takes a directory and validates that the layout of the directory
// matches that expected by the Application Container Image format.
// If any errors are encountered during the validation, it will abort and
// return the first one.
func ValidateLayout(dir string) error {
	fi, err := os.Stat(dir)
	if err != nil {
		return fmt.Errorf("error accessing layout: %v", err)
	}
	if !fi.IsDir() {
		return fmt.Errorf("given path %q is not a directory", dir)
	}
	var flist []string
	var imOK, rfsOK bool
	var im io.Reader
	walkLayout := func(fpath string, fi os.FileInfo, err error) error {
		rpath, err := filepath.Rel(dir, fpath)
		if err != nil {
			return err
		}
		name := filepath.Base(rpath)
		switch name {
		case ".":
		case "app":
			im, err = os.Open(fpath)
			if err != nil {
				return err
			}
			imOK = true
		case "rootfs":
			if !fi.IsDir() {
				return errors.New("rootfs is not a directory")
			}
			rfsOK = true
		default:
			flist = append(flist, rpath)
		}
		return nil
	}
	if err := filepath.Walk(dir, walkLayout); err != nil {
		return err
	}
	return validate(imOK, im, rfsOK, flist)
}

// ValidateLayout takes a *tar.Reader and validates that the layout of the
// filesystem the reader encapsulates matches that expected by the
// Application Container Image format.  If any errors are encountered during
// the validation, it will abort and return the first one.
func ValidateArchive(tr *tar.Reader) error {
	var flist []string
	var imOK, rfsOK bool
	var im bytes.Buffer
Tar:
	for {
		hdr, err := tr.Next()
		switch {
		case err == nil:
		case err == io.EOF:
			break Tar
		default:
			return err
		}
		switch hdr.Name {
		case "app":
			_, err := io.Copy(&im, tr)
			if err != nil {
				return err
			}
			imOK = true
		case "rootfs/":
			if !hdr.FileInfo().IsDir() {
				return fmt.Errorf("rootfs is not a directory")
			}
			rfsOK = true
		default:
			flist = append(flist, hdr.Name)
		}
	}
	return validate(imOK, &im, rfsOK, flist)
}

func validate(imOK bool, im io.Reader, rfsOK bool, files []string) error {
	if !imOK {
		return ErrNoManifest
	}
	if !rfsOK {
		return ErrNoRootFS
	}
	b, err := ioutil.ReadAll(im)
	if err != nil {
		return fmt.Errorf("error reading app manifest: %v", err)
	}
	var a schema.ImageManifest
	if err := a.UnmarshalJSON(b); err != nil {
		return fmt.Errorf("app manifest validation failed: %v", err)
	}
	for _, f := range files {
		if !strings.HasPrefix(f, "rootfs") {
			return fmt.Errorf("unrecognized file path in layout: %q", f)
		}
	}
	return nil
}

// validateImageManifest ensures that the given io.Reader represents a valid
// ImageManifest.
func validateImageManifest(r io.Reader) error {
	b, err := ioutil.ReadAll(r)
	if err != nil {
		return fmt.Errorf("error reading app manifest: %v", err)
	}
	var im schema.ImageManifest
	if err = json.Unmarshal(b, &im); err != nil {
		return fmt.Errorf("error unmarshaling app manifest: %v", err)
	}
	return nil
}
<commit_msg>The tar Header.Name (for example with gnu tar) can start with ./ (for example ./app and ./rootfs/). Use filepath.Clean and use the cleaned up name. This also makes the logic similar to ValidateLayout().<commit_after>package aci

/*

Image Layout

The on-disk layout of an app container is straightforward.
It includes a rootfs with all of the files that will exist in the root of the app and a manifest describing the image.
The layout must contain an app image manifest.

/manifest
/rootfs/
/rootfs/usr/bin/mysql

*/

import (
	"archive/tar"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/coreos/rocket/app-container/schema"
)

var (
	ErrNoRootFS   = errors.New("no rootfs found in layout")
	ErrNoManifest = errors.New("no app image manifest found in layout")
)

// ValidateLayout takes a directory and validates that the layout of the directory
// matches that expected by the Application Container Image format.
// If any errors are encountered during the validation, it will abort and
// return the first one.
func ValidateLayout(dir string) error {
	fi, err := os.Stat(dir)
	if err != nil {
		return fmt.Errorf("error accessing layout: %v", err)
	}
	if !fi.IsDir() {
		return fmt.Errorf("given path %q is not a directory", dir)
	}
	var flist []string
	var imOK, rfsOK bool
	var im io.Reader
	walkLayout := func(fpath string, fi os.FileInfo, err error) error {
		rpath, err := filepath.Rel(dir, fpath)
		if err != nil {
			return err
		}
		name := filepath.Base(rpath)
		switch name {
		case ".":
		case "app":
			im, err = os.Open(fpath)
			if err != nil {
				return err
			}
			imOK = true
		case "rootfs":
			if !fi.IsDir() {
				return errors.New("rootfs is not a directory")
			}
			rfsOK = true
		default:
			flist = append(flist, rpath)
		}
		return nil
	}
	if err := filepath.Walk(dir, walkLayout); err != nil {
		return err
	}
	return validate(imOK, im, rfsOK, flist)
}

// ValidateLayout takes a *tar.Reader and validates that the layout of the
// filesystem the reader encapsulates matches that expected by the
// Application Container Image format.  If any errors are encountered during
// the validation, it will abort and return the first one.
func ValidateArchive(tr *tar.Reader) error {
	var flist []string
	var imOK, rfsOK bool
	var im bytes.Buffer
Tar:
	for {
		hdr, err := tr.Next()
		switch {
		case err == nil:
		case err == io.EOF:
			break Tar
		default:
			return err
		}
		name := filepath.Clean(hdr.Name)
		switch name {
		case ".":
		case "app":
			_, err := io.Copy(&im, tr)
			if err != nil {
				return err
			}
			imOK = true
		case "rootfs":
			if !hdr.FileInfo().IsDir() {
				return fmt.Errorf("rootfs is not a directory")
			}
			rfsOK = true
		default:
			flist = append(flist, name)
		}
	}
	return validate(imOK, &im, rfsOK, flist)
}

func validate(imOK bool, im io.Reader, rfsOK bool, files []string) error {
	if !imOK {
		return ErrNoManifest
	}
	if !rfsOK {
		return ErrNoRootFS
	}
	b, err := ioutil.ReadAll(im)
	if err != nil {
		return fmt.Errorf("error reading app manifest: %v", err)
	}
	var a schema.ImageManifest
	if err := a.UnmarshalJSON(b); err != nil {
		return fmt.Errorf("app manifest validation failed: %v", err)
	}
	for _, f := range files {
		if !strings.HasPrefix(f, "rootfs") {
			return fmt.Errorf("unrecognized file path in layout: %q", f)
		}
	}
	return nil
}

// validateImageManifest ensures that the given io.Reader represents a valid
// ImageManifest.
func validateImageManifest(r io.Reader) error {
	b, err := ioutil.ReadAll(r)
	if err != nil {
		return fmt.Errorf("error reading app manifest: %v", err)
	}
	var im schema.ImageManifest
	if err = json.Unmarshal(b, &im); err != nil {
		return fmt.Errorf("error unmarshaling app manifest: %v", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package lb

import (
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/remind101/empire/empire/pkg/awsutil"
	"golang.org/x/net/context"
)

func TestELB_CreateLoadBalancer(t *testing.T) {
	h := awsutil.NewHandler([]awsutil.Cycle{
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=CreateLoadBalancer&Listeners.member.1.InstancePort=9000&Listeners.member.1.InstanceProtocol=http&Listeners.member.1.LoadBalancerPort=80&Listeners.member.1.Protocol=http&LoadBalancerName=acme-inc&Scheme=internet-facing&SecurityGroups.member.1=&Subnets.member.1=public-subnet&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<?xml version="1.0"?>
<CreateLoadBalancerResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
	<DNSName>acme-inc.us-east-1.elb.amazonaws.com</DNSName>
</CreateLoadBalancerResponse>`,
			},
		},
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=ModifyLoadBalancerAttributes&LoadBalancerAttributes.ConnectionDraining.Enabled=true&LoadBalancerAttributes.ConnectionDraining.Timeout=30&LoadBalancerName=acme-inc&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<?xml version="1.0"?>
<ModifyLoadBalancerAttributesResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
</ModifyLoadBalancerAttributesResponse>`,
			},
		},
	})
	m, s := newTestELBManager(h)
	defer s.Close()

	lb, err := m.CreateLoadBalancer(context.Background(), CreateLoadBalancerOpts{
		InstancePort: 9000,
		External:     true,
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := &LoadBalancer{
		Name:         "acme-inc",
		DNSName:      "acme-inc.us-east-1.elb.amazonaws.com",
		InstancePort: 9000,
		External:     true,
	}

	if got, want := lb, expected; !reflect.DeepEqual(got, want) {
		t.Fatalf("LoadBalancer => %v; want %v", got, want)
	}
}

func TestELB_DestroyLoadBalancer(t *testing.T) {
	h := awsutil.NewHandler([]awsutil.Cycle{
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=DeleteLoadBalancer&LoadBalancerName=acme-inc&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<?xml version="1.0"?>
<DeleteLoadBalancerResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
</DeleteLoadBalancerResponse>`,
			},
		},
	})
	m, s := newTestELBManager(h)
	defer s.Close()

	lb := &LoadBalancer{
		Name:         "acme-inc",
		DNSName:      "acme-inc.us-east-1.elb.amazonaws.com",
		InstancePort: 9000,
		External:     true,
	}

	if err := m.DestroyLoadBalancer(context.Background(), lb); err != nil {
		t.Fatal(err)
	}
}

func TestELB_LoadBalancers(t *testing.T) {
	h := awsutil.NewHandler([]awsutil.Cycle{
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=DescribeLoadBalancers&PageSize=20&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<DescribeLoadBalancersResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
	  <DescribeLoadBalancersResult>
	    <NextMarker>
	      abcd
	    </NextMarker>
	    <LoadBalancerDescriptions>
	      <member>
	        <SecurityGroups>
	          <member>sg-1</member>
	        </SecurityGroups>
	        <LoadBalancerName>foo</LoadBalancerName>
		<DNSName>foo.us-east-1.elb.amazonaws.com</DNSName>
	        <VPCId>vpc-1</VPCId>
	        <ListenerDescriptions>
	          <member>
	            <PolicyNames/>
	            <Listener>
	              <Protocol>HTTP</Protocol>
	              <LoadBalancerPort>80</LoadBalancerPort>
	              <InstanceProtocol>HTTP</InstanceProtocol>
	              <InstancePort>9000</InstancePort>
	            </Listener>
	          </member>
	        </ListenerDescriptions>
	        <AvailabilityZones>
	          <member>us-east-1a</member>
	        </AvailabilityZones>
	        <Scheme>internal</Scheme>
	        <Subnets>
	          <member>subnet-1a</member>
	        </Subnets>
	      </member>
	    </LoadBalancerDescriptions>
	  </DescribeLoadBalancersResult>
	</DescribeLoadBalancersResponse>`,
			},
		},
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=DescribeTags&LoadBalancerNames.member.1=foo&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<DescribeTagsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
	  <DescribeTagsResult>
	    <TagDescriptions>
	      <member>
	        <Tags>
	          <member>
	            <Key>AppName</Key>
	            <Value>foo</Value>
	          </member>
	          <member>
	            <Key>ProcessType</Key>
	            <Value>web</Value>
	          </member>
	        </Tags>
	        <LoadBalancerName>foo</LoadBalancerName>
	      </member>
	    </TagDescriptions>
	  </DescribeTagsResult>
	</DescribeTagsResponse>`,
			},
		},
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=DescribeLoadBalancers&Marker=%0A%09++++++abcd%0A%09++++&PageSize=20&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<DescribeLoadBalancersResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
	  <DescribeLoadBalancersResult>
	    <NextMarker></NextMarker>
	    <LoadBalancerDescriptions>
	      <member>
	        <SecurityGroups>
	          <member>sg-1</member>
	        </SecurityGroups>
	        <LoadBalancerName>bar</LoadBalancerName>
		<DNSName>bar.us-east-1.elb.amazonaws.com</DNSName>
	        <VPCId>vpc-1</VPCId>
	        <ListenerDescriptions>
	          <member>
	            <PolicyNames/>
	            <Listener>
	              <Protocol>HTTP</Protocol>
	              <LoadBalancerPort>80</LoadBalancerPort>
	              <InstanceProtocol>HTTP</InstanceProtocol>
	              <InstancePort>9001</InstancePort>
	            </Listener>
	          </member>
	        </ListenerDescriptions>
	        <AvailabilityZones>
	          <member>us-east-1a</member>
	        </AvailabilityZones>
	        <Scheme>internet-facing</Scheme>
	        <Subnets>
	          <member>subnet-1a</member>
	        </Subnets>
	      </member>
	    </LoadBalancerDescriptions>
	  </DescribeLoadBalancersResult>
	</DescribeLoadBalancersResponse>`,
			},
		},
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=DescribeTags&LoadBalancerNames.member.1=bar&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<DescribeTagsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
	  <DescribeTagsResult>
	    <TagDescriptions>
	      <member>
	        <Tags>
	          <member>
	            <Key>AppName</Key>
	            <Value>bar</Value>
	          </member>
	          <member>
	            <Key>ProcessType</Key>
	            <Value>web</Value>
	          </member>
	        </Tags>
	        <LoadBalancerName>bar</LoadBalancerName>
	      </member>
	    </TagDescriptions>
	  </DescribeTagsResult>
	</DescribeTagsResponse>`,
			},
		},
	})
	m, s := newTestELBManager(h)
	defer s.Close()

	lbs, err := m.LoadBalancers(context.Background(), nil)
	if err != nil {
		t.Fatal(err)
	}

	if got, want := len(lbs), 2; got != want {
		t.Fatalf("%v load balancers; want %v", got, want)
	}

	expected := []*LoadBalancer{
		{Name: "foo", DNSName: "foo.us-east-1.elb.amazonaws.com", InstancePort: 9000, Tags: map[string]string{"AppName": "foo", "ProcessType": "web"}},
		{Name: "bar", DNSName: "bar.us-east-1.elb.amazonaws.com", External: true, InstancePort: 9001, Tags: map[string]string{"AppName": "bar", "ProcessType": "web"}},
	}

	if got, want := lbs, expected; !reflect.DeepEqual(got, want) {
		t.Fatalf("LoadBalancers => %v; want %v", got, want)
	}
}

func newTestELBManager(h http.Handler) (*ELBManager, *httptest.Server) {
	s := httptest.NewServer(h)

	m := NewELBManager(
		aws.DefaultConfig.Merge(&aws.Config{
			Credentials: credentials.NewStaticCredentials(" ", " ", " "),
			Endpoint:    s.URL,
			Region:      "localhost",
			LogLevel:    0,
		}),
	)
	m.newName = func() string {
		return "acme-inc"
	}
	m.InternalSubnetIDs = []string{"private-subnet"}
	m.ExternalSubnetIDs = []string{"public-subnet"}

	return m, s
}

// fakeNameserver is a fake implementation of the Nameserver interface.
type fakeNameserver struct{}

func (n *fakeNameserver) CNAME(cname, record string) error {
	return nil
}
<commit_msg>Add test for DestroyLoadBalancer with CNAME<commit_after>package lb

import (
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/remind101/empire/empire/pkg/awsutil"
	"golang.org/x/net/context"
)

func TestELB_CreateLoadBalancer(t *testing.T) {
	h := awsutil.NewHandler([]awsutil.Cycle{
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=CreateLoadBalancer&Listeners.member.1.InstancePort=9000&Listeners.member.1.InstanceProtocol=http&Listeners.member.1.LoadBalancerPort=80&Listeners.member.1.Protocol=http&LoadBalancerName=acme-inc&Scheme=internet-facing&SecurityGroups.member.1=&Subnets.member.1=public-subnet&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<?xml version="1.0"?>
<CreateLoadBalancerResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
	<DNSName>acme-inc.us-east-1.elb.amazonaws.com</DNSName>
</CreateLoadBalancerResponse>`,
			},
		},
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=ModifyLoadBalancerAttributes&LoadBalancerAttributes.ConnectionDraining.Enabled=true&LoadBalancerAttributes.ConnectionDraining.Timeout=30&LoadBalancerName=acme-inc&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<?xml version="1.0"?>
<ModifyLoadBalancerAttributesResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
</ModifyLoadBalancerAttributesResponse>`,
			},
		},
	})
	m, s := newTestELBManager(h)
	defer s.Close()

	lb, err := m.CreateLoadBalancer(context.Background(), CreateLoadBalancerOpts{
		InstancePort: 9000,
		External:     true,
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := &LoadBalancer{
		Name:         "acme-inc",
		DNSName:      "acme-inc.us-east-1.elb.amazonaws.com",
		InstancePort: 9000,
		External:     true,
	}

	if got, want := lb, expected; !reflect.DeepEqual(got, want) {
		t.Fatalf("LoadBalancer => %v; want %v", got, want)
	}
}

func TestELB_DestroyLoadBalancer(t *testing.T) {
	h := awsutil.NewHandler([]awsutil.Cycle{
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=DeleteLoadBalancer&LoadBalancerName=acme-inc&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<?xml version="1.0"?>
<DeleteLoadBalancerResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
</DeleteLoadBalancerResponse>`,
			},
		},
	})
	m, s := newTestELBManager(h)
	defer s.Close()

	lb := &LoadBalancer{
		Name:         "acme-inc",
		DNSName:      "acme-inc.us-east-1.elb.amazonaws.com",
		InstancePort: 9000,
		External:     true,
		Tags:         map[string]string{AppTag: "acme-inc"},
	}

	if err := m.DestroyLoadBalancer(context.Background(), lb); err != nil {
		t.Fatal(err)
	}
}

func TestELB_LoadBalancers(t *testing.T) {
	h := awsutil.NewHandler([]awsutil.Cycle{
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=DescribeLoadBalancers&PageSize=20&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<DescribeLoadBalancersResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
	  <DescribeLoadBalancersResult>
	    <NextMarker>
	      abcd
	    </NextMarker>
	    <LoadBalancerDescriptions>
	      <member>
	        <SecurityGroups>
	          <member>sg-1</member>
	        </SecurityGroups>
	        <LoadBalancerName>foo</LoadBalancerName>
		<DNSName>foo.us-east-1.elb.amazonaws.com</DNSName>
	        <VPCId>vpc-1</VPCId>
	        <ListenerDescriptions>
	          <member>
	            <PolicyNames/>
	            <Listener>
	              <Protocol>HTTP</Protocol>
	              <LoadBalancerPort>80</LoadBalancerPort>
	              <InstanceProtocol>HTTP</InstanceProtocol>
	              <InstancePort>9000</InstancePort>
	            </Listener>
	          </member>
	        </ListenerDescriptions>
	        <AvailabilityZones>
	          <member>us-east-1a</member>
	        </AvailabilityZones>
	        <Scheme>internal</Scheme>
	        <Subnets>
	          <member>subnet-1a</member>
	        </Subnets>
	      </member>
	    </LoadBalancerDescriptions>
	  </DescribeLoadBalancersResult>
	</DescribeLoadBalancersResponse>`,
			},
		},
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=DescribeTags&LoadBalancerNames.member.1=foo&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<DescribeTagsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
	  <DescribeTagsResult>
	    <TagDescriptions>
	      <member>
	        <Tags>
	          <member>
	            <Key>AppName</Key>
	            <Value>foo</Value>
	          </member>
	          <member>
	            <Key>ProcessType</Key>
	            <Value>web</Value>
	          </member>
	        </Tags>
	        <LoadBalancerName>foo</LoadBalancerName>
	      </member>
	    </TagDescriptions>
	  </DescribeTagsResult>
	</DescribeTagsResponse>`,
			},
		},
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=DescribeLoadBalancers&Marker=%0A%09++++++abcd%0A%09++++&PageSize=20&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<DescribeLoadBalancersResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
	  <DescribeLoadBalancersResult>
	    <NextMarker></NextMarker>
	    <LoadBalancerDescriptions>
	      <member>
	        <SecurityGroups>
	          <member>sg-1</member>
	        </SecurityGroups>
	        <LoadBalancerName>bar</LoadBalancerName>
		<DNSName>bar.us-east-1.elb.amazonaws.com</DNSName>
	        <VPCId>vpc-1</VPCId>
	        <ListenerDescriptions>
	          <member>
	            <PolicyNames/>
	            <Listener>
	              <Protocol>HTTP</Protocol>
	              <LoadBalancerPort>80</LoadBalancerPort>
	              <InstanceProtocol>HTTP</InstanceProtocol>
	              <InstancePort>9001</InstancePort>
	            </Listener>
	          </member>
	        </ListenerDescriptions>
	        <AvailabilityZones>
	          <member>us-east-1a</member>
	        </AvailabilityZones>
	        <Scheme>internet-facing</Scheme>
	        <Subnets>
	          <member>subnet-1a</member>
	        </Subnets>
	      </member>
	    </LoadBalancerDescriptions>
	  </DescribeLoadBalancersResult>
	</DescribeLoadBalancersResponse>`,
			},
		},
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=DescribeTags&LoadBalancerNames.member.1=bar&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<DescribeTagsResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
	  <DescribeTagsResult>
	    <TagDescriptions>
	      <member>
	        <Tags>
	          <member>
	            <Key>AppName</Key>
	            <Value>bar</Value>
	          </member>
	          <member>
	            <Key>ProcessType</Key>
	            <Value>web</Value>
	          </member>
	        </Tags>
	        <LoadBalancerName>bar</LoadBalancerName>
	      </member>
	    </TagDescriptions>
	  </DescribeTagsResult>
	</DescribeTagsResponse>`,
			},
		},
	})
	m, s := newTestELBManager(h)
	defer s.Close()

	lbs, err := m.LoadBalancers(context.Background(), nil)
	if err != nil {
		t.Fatal(err)
	}

	if got, want := len(lbs), 2; got != want {
		t.Fatalf("%v load balancers; want %v", got, want)
	}

	expected := []*LoadBalancer{
		{Name: "foo", DNSName: "foo.us-east-1.elb.amazonaws.com", InstancePort: 9000, Tags: map[string]string{"AppName": "foo", "ProcessType": "web"}},
		{Name: "bar", DNSName: "bar.us-east-1.elb.amazonaws.com", External: true, InstancePort: 9001, Tags: map[string]string{"AppName": "bar", "ProcessType": "web"}},
	}

	if got, want := lbs, expected; !reflect.DeepEqual(got, want) {
		t.Fatalf("LoadBalancers => %v; want %v", got, want)
	}
}

func TestELBwDNS_DestroyLoadBalancer(t *testing.T) {
	h := awsutil.NewHandler([]awsutil.Cycle{
		{
			Request: awsutil.Request{
				RequestURI: "/",
				Body:       `Action=DeleteLoadBalancer&LoadBalancerName=acme-inc&Version=2012-06-01`,
			},
			Response: awsutil.Response{
				StatusCode: 200,
				Body: `<?xml version="1.0"?>
<DeleteLoadBalancerResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
</DeleteLoadBalancerResponse>`,
			},
		},
	})
	m, s := newTestELBManager(h)
	defer s.Close()
	ns := newTestNameserver("FAKEZONE")

	lb := &LoadBalancer{
		Name:         "acme-inc",
		DNSName:      "acme-inc.us-east-1.elb.amazonaws.com",
		InstancePort: 9000,
		External:     true,
		Tags:         map[string]string{AppTag: "acme-inc"},
	}

	m2 := WithCNAME(m, ns)

	if err := m2.DestroyLoadBalancer(context.Background(), lb); err != nil {
		t.Fatal(err)
	}

	if ok := ns.DeleteCNAMECalled; !ok {
		t.Fatal("DeleteCNAME was not called.")
	}

}

func newTestELBManager(h http.Handler) (*ELBManager, *httptest.Server) {
	s := httptest.NewServer(h)

	m := NewELBManager(
		aws.DefaultConfig.Merge(&aws.Config{
			Credentials: credentials.NewStaticCredentials(" ", " ", " "),
			Endpoint:    s.URL,
			Region:      "localhost",
			LogLevel:    0,
		}),
	)
	m.newName = func() string {
		return "acme-inc"
	}
	m.InternalSubnetIDs = []string{"private-subnet"}
	m.ExternalSubnetIDs = []string{"public-subnet"}

	return m, s
}

// fakeNameserver is a fake implementation of the Nameserver interface.
type fakeNameserver struct {
	ZoneID string

	CNAMECalled       bool
	DeleteCNAMECalled bool
}

func (n *fakeNameserver) CNAME(cname, record string) error {
	n.CNAMECalled = true
	return nil
}

func (n *fakeNameserver) DeleteCNAME(cname, record string) error {
	n.DeleteCNAMECalled = true
	return nil
}

func newTestNameserver(zoneID string) *fakeNameserver {
	return &fakeNameserver{
		ZoneID:            zoneID,
		CNAMECalled:       false,
		DeleteCNAMECalled: false,
	}
}
<|endoftext|>


<commit_before>package adder

import (
	"testing"
)

func TestAdd(t *testing.T) {
	tests := []struct {
		n1   int
		n2   int
		want int
	}{
		{
			n1:   0,
			n2:   0,
			want: 0,
		},
		{
			n1:   1,
			n2:   0,
			want: 1,
		},
		{
			n1:   -1,
			n2:   0,
			want: -1,
		},
		{
			n1:   3,
			n2:   -3,
			want: 0,
		},
		{
			n1:   5,
			n2:   16,
			want: 21,
		},
		{
			n1:   694,
			n2:   1,
			want: 695,
		},
	}

	for i, tt := range tests {
		t.Logf("[%02d] test %d + %d", i, tt.n1, tt.n2)
		sum := Add(tt.n1, tt.n2)
		if sum != tt.want {
			t.Errorf("unexpected value:\n- want: %v\n- got: %v", tt.want, sum)
		}
	}
}

// func TestSubtract(t *testing.T) {
// 	tests := []struct {
// 		n1   int
// 		n2   int
// 		want int
// 	}{
// 		{
// 			n1:   0,
// 			n2:   0,
// 			want: 0,
// 		},
// 		{
// 			n1:   1,
// 			n2:   0,
// 			want: 1,
// 		},
// 		{
// 			n1:   -1,
// 			n2:   0,
// 			want: -1,
// 		},
// 		{
// 			n1:   3,
// 			n2:   -3,
// 			want: 6,
// 		},
// 		{
// 			n1:   5,
// 			n2:   16,
// 			want: -11,
// 		},
// 		{
// 			n1:   694,
// 			n2:   1,
// 			want: 693,
// 		},
// 	}

// 	for i, tt := range tests {
// 		t.Logf("[%02d] test %d - %d", i, tt.n1, tt.n2)
// 		difference := Subtract(tt.n1, tt.n2)
// 		if difference != tt.want {
// 			t.Errorf("unexpected value:\n- want: %v\n- got: %v", tt.want, difference)
// 		}
// 	}
// }
<commit_msg>Add tests for Subtract<commit_after>package adder

import (
	"testing"
)

func TestAdd(t *testing.T) {
	tests := []struct {
		n1   int
		n2   int
		want int
	}{
		{
			n1:   0,
			n2:   0,
			want: 0,
		},
		{
			n1:   1,
			n2:   0,
			want: 1,
		},
		{
			n1:   -1,
			n2:   0,
			want: -1,
		},
		{
			n1:   3,
			n2:   -3,
			want: 0,
		},
		{
			n1:   5,
			n2:   16,
			want: 21,
		},
		{
			n1:   694,
			n2:   1,
			want: 695,
		},
	}

	for i, tt := range tests {
		t.Logf("[%02d] test %d + %d", i, tt.n1, tt.n2)
		sum := Add(tt.n1, tt.n2)
		if sum != tt.want {
			t.Errorf("unexpected value:\n- want: %v\n- got: %v", tt.want, sum)
		}
	}
}

func TestSubtract(t *testing.T) {
	tests := []struct {
		n1   int
		n2   int
		want int
	}{
		{
			n1:   0,
			n2:   0,
			want: 0,
		},
		{
			n1:   1,
			n2:   0,
			want: 1,
		},
		{
			n1:   -1,
			n2:   0,
			want: -1,
		},
		{
			n1:   3,
			n2:   -3,
			want: 6,
		},
		{
			n1:   5,
			n2:   16,
			want: -11,
		},
		{
			n1:   694,
			n2:   1,
			want: 693,
		},
	}

	for i, tt := range tests {
		t.Logf("[%02d] test %d - %d", i, tt.n1, tt.n2)
		difference := Subtract(tt.n1, tt.n2)
		if difference != tt.want {
			t.Errorf("unexpected value:\n- want: %v\n- got: %v", tt.want, difference)
		}
	}
}
<|endoftext|>