<commit_before>package terraform

import (
	"fmt"
	"sort"
)

// StateFilter is responsible for filtering and searching a state.
//
// This is a separate struct from State rather than a method on State
// because StateFilter might create sidecar data structures to optimize
// filtering on the state.
//
// If you change the State, the filter created is invalid and either
// Reset should be called or a new one should be allocated. StateFilter
// will not watch State for changes and do this for you. If you filter after
// changing the State without calling Reset, the behavior is not defined.
type StateFilter struct {
	State *State
}

// Filter takes the addresses specified by fs and finds all the matches.
// The values of fs are resource addressing syntax that can be parsed by
// ParseResourceAddress.
func (f *StateFilter) Filter(fs ...string) ([]*StateFilterResult, error) {
	// Parse all the addresses
	as := make([]*ResourceAddress, len(fs))
	for i, v := range fs {
		a, err := ParseResourceAddress(v)
		if err != nil {
			return nil, fmt.Errorf("Error parsing address '%s': %s", v, err)
		}

		as[i] = a
	}

	// If we werent given any filters, then we list all
	if len(fs) == 0 {
		as = append(as, &ResourceAddress{Index: -1})
	}

	// Filter each of the address. We keep track of this in a map to
	// strip duplicates.
	resultSet := make(map[string]*StateFilterResult)
	for _, a := range as {
		for _, r := range f.filterSingle(a) {
			resultSet[r.String()] = r
		}
	}

	// Make the result list
	results := make([]*StateFilterResult, 0, len(resultSet))
	for _, v := range resultSet {
		results = append(results, v)
	}

	// Sort them and return
	sort.Sort(StateFilterResultSlice(results))
	return results, nil
}

func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult {
	// The slice to keep track of results
	var results []*StateFilterResult

	// Go through modules first.
	modules := make([]*ModuleState, 0, len(f.State.Modules))
	for _, m := range f.State.Modules {
		if f.relevant(a, m) {
			modules = append(modules, m)

			// Only add the module to the results if we haven't specified a type.
			// We also ignore the root module.
			if a.Type == "" && len(m.Path) > 1 {
				results = append(results, &StateFilterResult{
					Path:    m.Path[1:],
					Address: (&ResourceAddress{Path: m.Path[1:]}).String(),
					Value:   m,
				})
			}
		}
	}

	// With the modules set, go through all the resources within
	// the modules to find relevant resources.
	for _, m := range modules {
		for n, r := range m.Resources {
			if f.relevant(a, r) {
				// The name in the state contains valuable information. Parse.
				key, err := ParseResourceStateKey(n)
				if err != nil {
					// If we get an error parsing, then just ignore it
					// out of the state.
					continue
				}

				if a.Index >= 0 && key.Index != a.Index {
					// Index doesn't match
					continue
				}

				if a.Name != "" && a.Name != key.Name {
					continue
				}

				// Build the address for this resource
				addr := &ResourceAddress{
					Path:  m.Path[1:],
					Name:  key.Name,
					Type:  key.Type,
					Index: key.Index,
				}

				// Add the resource level result
				resourceResult := &StateFilterResult{
					Path:    addr.Path,
					Address: addr.String(),
					Value:   r,
				}
				if !a.InstanceTypeSet {
					results = append(results, resourceResult)
				}

				// Add the instances
				if r.Primary != nil {
					addr.InstanceType = TypePrimary
					addr.InstanceTypeSet = true
					results = append(results, &StateFilterResult{
						Path:    addr.Path,
						Address: addr.String(),
						Parent:  resourceResult,
						Value:   r.Primary,
					})
				}

				for _, instance := range r.Tainted {
					if f.relevant(a, instance) {
						addr.InstanceType = TypeTainted
						addr.InstanceTypeSet = true
						results = append(results, &StateFilterResult{
							Path:    addr.Path,
							Address: addr.String(),
							Parent:  resourceResult,
							Value:   instance,
						})
					}
				}

				for _, instance := range r.Deposed {
					if f.relevant(a, instance) {
						addr.InstanceType = TypeDeposed
						addr.InstanceTypeSet = true
						results = append(results, &StateFilterResult{
							Path:    addr.Path,
							Address: addr.String(),
							Parent:  resourceResult,
							Value:   instance,
						})
					}
				}
			}
		}
	}

	return results
}

// relevant checks for relevance of this address against the given value.
func (f *StateFilter) relevant(addr *ResourceAddress, raw interface{}) bool {
	switch v := raw.(type) {
	case *ModuleState:
		path := v.Path[1:]

		if len(addr.Path) > len(path) {
			// Longer path in address means there is no way we match.
			return false
		}

		// Check for a prefix match
		for i, p := range addr.Path {
			if path[i] != p {
				// Any mismatches don't match.
				return false
			}
		}

		return true
	case *ResourceState:
		if addr.Type == "" {
			// If we have no resource type, then we're interested in all!
			return true
		}

		// If the type doesn't match we fail immediately
		if v.Type != addr.Type {
			return false
		}

		return true
	default:
		// If we don't know about it, let's just say no
		return false
	}
}

// StateFilterResult is a single result from a filter operation. Filter
// can match multiple things within a state (module, resource, instance, etc.)
// and this unifies that.
type StateFilterResult struct {
	// Module path of the result
	Path []string

	// Address is the address that can be used to reference this exact result.
	Address string

	// Parent, if non-nil, is a parent of this result. For instances, the
	// parent would be a resource. For resources, the parent would be
	// a module. For modules, this is currently nil.
	Parent *StateFilterResult

	// Value is the actual value. This must be type switched on. It can be
	// any data structures that `State` can hold: `ModuleState`,
	// `ResourceState`, `InstanceState`.
	Value interface{}
}

func (r *StateFilterResult) String() string {
	return fmt.Sprintf("%T: %s", r.Value, r.Address)
}

func (r *StateFilterResult) sortedType() int {
	switch r.Value.(type) {
	case *ModuleState:
		return 0
	case *ResourceState:
		return 1
	case *InstanceState:
		return 2
	default:
		return 50
	}
}

// StateFilterResultSlice is a slice of results that implements
// sort.Interface. The sorting goal is what is most appealing to
// human output.
type StateFilterResultSlice []*StateFilterResult

func (s StateFilterResultSlice) Len() int      { return len(s) }
func (s StateFilterResultSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s StateFilterResultSlice) Less(i, j int) bool {
	a, b := s[i], s[j]

	// If the addresses are different it is just lexographic sorting
	if a.Address != b.Address {
		return a.Address < b.Address
	}

	// Addresses are the same, which means it matters on the type
	return a.sortedType() < b.sortedType()
}
<commit_msg>terraform: fix failing tests<commit_after>package terraform

import (
	"fmt"
	"sort"
)

// StateFilter is responsible for filtering and searching a state.
//
// This is a separate struct from State rather than a method on State
// because StateFilter might create sidecar data structures to optimize
// filtering on the state.
//
// If you change the State, the filter created is invalid and either
// Reset should be called or a new one should be allocated. StateFilter
// will not watch State for changes and do this for you. If you filter after
// changing the State without calling Reset, the behavior is not defined.
type StateFilter struct {
	State *State
}

// Filter takes the addresses specified by fs and finds all the matches.
// The values of fs are resource addressing syntax that can be parsed by
// ParseResourceAddress.
func (f *StateFilter) Filter(fs ...string) ([]*StateFilterResult, error) {
	// Parse all the addresses
	as := make([]*ResourceAddress, len(fs))
	for i, v := range fs {
		a, err := ParseResourceAddress(v)
		if err != nil {
			return nil, fmt.Errorf("Error parsing address '%s': %s", v, err)
		}

		as[i] = a
	}

	// If we werent given any filters, then we list all
	if len(fs) == 0 {
		as = append(as, &ResourceAddress{Index: -1})
	}

	// Filter each of the address. We keep track of this in a map to
	// strip duplicates.
	resultSet := make(map[string]*StateFilterResult)
	for _, a := range as {
		for _, r := range f.filterSingle(a) {
			resultSet[r.String()] = r
		}
	}

	// Make the result list
	results := make([]*StateFilterResult, 0, len(resultSet))
	for _, v := range resultSet {
		results = append(results, v)
	}

	// Sort them and return
	sort.Sort(StateFilterResultSlice(results))
	return results, nil
}

func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult {
	// The slice to keep track of results
	var results []*StateFilterResult

	// Go through modules first.
	modules := make([]*ModuleState, 0, len(f.State.Modules))
	for _, m := range f.State.Modules {
		if f.relevant(a, m) {
			modules = append(modules, m)

			// Only add the module to the results if we haven't specified a type.
			// We also ignore the root module.
			if a.Type == "" && len(m.Path) > 1 {
				results = append(results, &StateFilterResult{
					Path:    m.Path[1:],
					Address: (&ResourceAddress{Path: m.Path[1:]}).String(),
					Value:   m,
				})
			}
		}
	}

	// With the modules set, go through all the resources within
	// the modules to find relevant resources.
	for _, m := range modules {
		for n, r := range m.Resources {
			if f.relevant(a, r) {
				// The name in the state contains valuable information. Parse.
				key, err := ParseResourceStateKey(n)
				if err != nil {
					// If we get an error parsing, then just ignore it
					// out of the state.
					continue
				}

				if a.Index >= 0 && key.Index != a.Index {
					// Index doesn't match
					continue
				}

				if a.Name != "" && a.Name != key.Name {
					continue
				}

				// Build the address for this resource
				addr := &ResourceAddress{
					Path:  m.Path[1:],
					Name:  key.Name,
					Type:  key.Type,
					Index: key.Index,
				}

				// Add the resource level result
				resourceResult := &StateFilterResult{
					Path:    addr.Path,
					Address: addr.String(),
					Value:   r,
				}
				if !a.InstanceTypeSet {
					results = append(results, resourceResult)
				}

				// Add the instances
				if r.Primary != nil {
					addr.InstanceType = TypePrimary
					addr.InstanceTypeSet = false
					results = append(results, &StateFilterResult{
						Path:    addr.Path,
						Address: addr.String(),
						Parent:  resourceResult,
						Value:   r.Primary,
					})
				}

				for _, instance := range r.Tainted {
					if f.relevant(a, instance) {
						addr.InstanceType = TypeTainted
						addr.InstanceTypeSet = true
						results = append(results, &StateFilterResult{
							Path:    addr.Path,
							Address: addr.String(),
							Parent:  resourceResult,
							Value:   instance,
						})
					}
				}

				for _, instance := range r.Deposed {
					if f.relevant(a, instance) {
						addr.InstanceType = TypeDeposed
						addr.InstanceTypeSet = true
						results = append(results, &StateFilterResult{
							Path:    addr.Path,
							Address: addr.String(),
							Parent:  resourceResult,
							Value:   instance,
						})
					}
				}
			}
		}
	}

	return results
}

// relevant checks for relevance of this address against the given value.
func (f *StateFilter) relevant(addr *ResourceAddress, raw interface{}) bool {
	switch v := raw.(type) {
	case *ModuleState:
		path := v.Path[1:]

		if len(addr.Path) > len(path) {
			// Longer path in address means there is no way we match.
			return false
		}

		// Check for a prefix match
		for i, p := range addr.Path {
			if path[i] != p {
				// Any mismatches don't match.
				return false
			}
		}

		return true
	case *ResourceState:
		if addr.Type == "" {
			// If we have no resource type, then we're interested in all!
			return true
		}

		// If the type doesn't match we fail immediately
		if v.Type != addr.Type {
			return false
		}

		return true
	default:
		// If we don't know about it, let's just say no
		return false
	}
}

// StateFilterResult is a single result from a filter operation. Filter
// can match multiple things within a state (module, resource, instance, etc.)
// and this unifies that.
type StateFilterResult struct {
	// Module path of the result
	Path []string

	// Address is the address that can be used to reference this exact result.
	Address string

	// Parent, if non-nil, is a parent of this result. For instances, the
	// parent would be a resource. For resources, the parent would be
	// a module. For modules, this is currently nil.
	Parent *StateFilterResult

	// Value is the actual value. This must be type switched on. It can be
	// any data structures that `State` can hold: `ModuleState`,
	// `ResourceState`, `InstanceState`.
	Value interface{}
}

func (r *StateFilterResult) String() string {
	return fmt.Sprintf("%T: %s", r.Value, r.Address)
}

func (r *StateFilterResult) sortedType() int {
	switch r.Value.(type) {
	case *ModuleState:
		return 0
	case *ResourceState:
		return 1
	case *InstanceState:
		return 2
	default:
		return 50
	}
}

// StateFilterResultSlice is a slice of results that implements
// sort.Interface. The sorting goal is what is most appealing to
// human output.
type StateFilterResultSlice []*StateFilterResult

func (s StateFilterResultSlice) Len() int      { return len(s) }
func (s StateFilterResultSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s StateFilterResultSlice) Less(i, j int) bool {
	a, b := s[i], s[j]

	// If the addresses are different it is just lexographic sorting
	if a.Address != b.Address {
		return a.Address < b.Address
	}

	// Addresses are the same, which means it matters on the type
	return a.sortedType() < b.sortedType()
}
<|endoftext|>


<commit_before>package fsmonitor

import (
	"code.google.com/p/go.exp/fsnotify"
	"os"
	"path/filepath"
)

func NewWatcher() (*Watcher, error) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	monitorWatcher := initWatcher(watcher, []string{})
	return monitorWatcher, nil
}

func NewWatcherWithSkipFolders(skipFolders []string) (*Watcher, error) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	monitorWatcher := initWatcher(watcher, skipFolders)
	return monitorWatcher, nil
}

func initWatcher(watcher *fsnotify.Watcher, skipFolders []string) *Watcher {
	event := make(chan *fsnotify.FileEvent)
	watcherError := make(chan error)
	monitorWatcher := &Watcher{Event: event, Error: watcherError, watcher: watcher, SkipFolders: skipFolders}
	go func() {
		for {
			select {
			case ev := <-watcher.Event:
				event <- ev
				if ev.IsCreate() {
					go func() {
						if f, err := os.Stat(ev.Name); err == nil {
							if f.IsDir() {
								monitorWatcher.watchAllFolders(ev.Name)
							}
						}

					}()
				}
				if ev.IsDelete() {
					go func() {
						watcher.RemoveWatch(ev.Name)
					}()
				}
			case e := <-watcher.Error:
				watcherError <- e
			}
		}
	}()
	return monitorWatcher
}

type Watcher struct {
	Event       chan *fsnotify.FileEvent
	Error       chan error
	SkipFolders []string
	watcher     *fsnotify.Watcher
}

func (self *Watcher) Watch(path string) error {
	err := self.watchAllFolders(path)
	if err != nil {
		return err
	}
	return nil
}

func (self *Watcher) watchAllFolders(path string) (err error) {
	err = filepath.Walk(path, func(path string, f os.FileInfo, err error) error {
		if f != nil && f.IsDir() {
			filename := f.Name()
			for _, skipFolder := range self.SkipFolders {
				match, err := filepath.Match(skipFolder, filename)
				if err != nil {
					return err
				}
				if match {
					return filepath.SkipDir
				}
			}
			err := self.addWatcher(path)
			if err != nil {
				return err
			}
		}
		return nil
	})
	return
}

func (self *Watcher) addWatcher(path string) (err error) {
	err = self.watcher.Watch(path)
	return
}
<commit_msg>Updated fsmonitor to use github.com/fsnotify/fsnotify<commit_after>package fsmonitor

import (
	"github.com/fsnotify/fsnotify"
	"os"
	"path/filepath"
)

func NewWatcher() (*Watcher, error) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	monitorWatcher := initWatcher(watcher, []string{})
	return monitorWatcher, nil
}

func NewWatcherWithSkipFolders(skipFolders []string) (*Watcher, error) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	monitorWatcher := initWatcher(watcher, skipFolders)
	return monitorWatcher, nil
}

func initWatcher(watcher *fsnotify.Watcher, skipFolders []string) *Watcher {
	event := make(chan fsnotify.Event)
	watcherError := make(chan error)
	monitorWatcher := &Watcher{
		Event:       event,
		Error:       watcherError,
		watcher:     watcher,
		SkipFolders: skipFolders,
	}
	go func() {
		for {
			select {
			case ev, ok := <-watcher.Events:
				if !ok {
					return
				}
				if ev.Op&fsnotify.Write == fsnotify.Write ||
					ev.Op&fsnotify.Create == fsnotify.Create ||
					ev.Op&fsnotify.Remove == fsnotify.Remove {
					event <- ev
				}
				if ev.Op&fsnotify.Create == fsnotify.Create {
					go func() {
						if f, err := os.Stat(ev.Name); err == nil {
							if f.IsDir() {
								monitorWatcher.watchAllFolders(ev.Name)
							}
						}

					}()
				}
			case err, ok := <-watcher.Errors:
				if !ok {
					return
				}
				watcherError <- err
			}
		}
	}()
	return monitorWatcher
}

type Watcher struct {
	Event       chan fsnotify.Event
	Error       chan error
	SkipFolders []string
	watcher     *fsnotify.Watcher
}

func (self *Watcher) Watch(path string) error {
	err := self.watchAllFolders(path)
	if err != nil {
		return err
	}
	return nil
}

func (self *Watcher) watchAllFolders(path string) (err error) {
	err = filepath.Walk(path, func(path string, f os.FileInfo, err error) error {
		if f != nil && f.IsDir() {
			filename := f.Name()
			for _, skipFolder := range self.SkipFolders {
				match, err := filepath.Match(skipFolder, filename)
				if err != nil {
					return err
				}
				if match {
					return filepath.SkipDir
				}
			}
			err := self.addWatcher(path)
			if err != nil {
				return err
			}
		}
		return nil
	})
	return
}

func (self *Watcher) addWatcher(path string) (err error) {
	return self.watcher.Add(path)
}
<|endoftext|>


<commit_before>package funcs

import (
	"context"
	"sync"

	"github.com/hairyhenderson/gomplate/v3/aws"
	"github.com/hairyhenderson/gomplate/v3/conv"
)

var (
	af     *Funcs
	afInit sync.Once
)

// AWSNS - the aws namespace
func AWSNS() *Funcs {
	afInit.Do(func() {
		af = &Funcs{
			awsopts: aws.GetClientOptions(),
		}
	})
	return af
}

// AWSFuncs -
func AWSFuncs(f map[string]interface{}) {
	f2 := CreateAWSFuncs(context.Background())
	for k, v := range f2 {
		f[k] = v
	}
}

// CreateAWSFuncs -
func CreateAWSFuncs(ctx context.Context) map[string]interface{} {
	f := map[string]interface{}{}
	ns := AWSNS()
	ns.ctx = ctx

	f["aws"] = AWSNS

	// global aliases - for backwards compatibility
	f["ec2meta"] = ns.EC2Meta
	f["ec2dynamic"] = ns.EC2Dynamic
	f["ec2tag"] = ns.EC2Tag
	f["ec2region"] = ns.EC2Region
	return f
}

// Funcs -
type Funcs struct {
	ctx context.Context

	meta     *aws.Ec2Meta
	info     *aws.Ec2Info
	kms      *aws.KMS
	sts      *aws.STS
	metaInit sync.Once
	infoInit sync.Once
	kmsInit  sync.Once
	stsInit  sync.Once
	awsopts  aws.ClientOptions
}

// EC2Region -
func (a *Funcs) EC2Region(def ...string) (string, error) {
	a.metaInit.Do(a.initMeta)
	return a.meta.Region(def...)
}

// EC2Meta -
func (a *Funcs) EC2Meta(key string, def ...string) (string, error) {
	a.metaInit.Do(a.initMeta)
	return a.meta.Meta(key, def...)
}

// EC2Dynamic -
func (a *Funcs) EC2Dynamic(key string, def ...string) (string, error) {
	a.metaInit.Do(a.initMeta)
	return a.meta.Dynamic(key, def...)
}

// EC2Tag -
func (a *Funcs) EC2Tag(tag string, def ...string) (string, error) {
	a.infoInit.Do(a.initInfo)
	return a.info.Tag(tag, def...)
}

// KMSEncrypt -
func (a *Funcs) KMSEncrypt(keyID, plaintext interface{}) (string, error) {
	a.kmsInit.Do(a.initKMS)
	return a.kms.Encrypt(conv.ToString(keyID), conv.ToString(plaintext))
}

// KMSDecrypt -
func (a *Funcs) KMSDecrypt(ciphertext interface{}) (string, error) {
	a.kmsInit.Do(a.initKMS)
	return a.kms.Decrypt(conv.ToString(ciphertext))
}

// UserID - Gets the unique identifier of the calling entity. The exact value
// depends on the type of entity making the call. The values returned are those
// listed in the aws:userid column in the Principal table
// (http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_variables.html#principaltable)
// found on the Policy Variables reference page in the IAM User Guide.
func (a *Funcs) UserID() (string, error) {
	a.stsInit.Do(a.initSTS)
	return a.sts.UserID()
}

// Account - Gets the AWS account ID number of the account that owns or
// contains the calling entity.
func (a *Funcs) Account() (string, error) {
	a.stsInit.Do(a.initSTS)
	return a.sts.Account()
}

// ARN - Gets the AWS ARN associated with the calling entity
func (a *Funcs) ARN() (string, error) {
	a.stsInit.Do(a.initSTS)
	return a.sts.Arn()
}

func (a *Funcs) initMeta() {
	if a.meta == nil {
		a.meta = aws.NewEc2Meta(a.awsopts)
	}
}

func (a *Funcs) initInfo() {
	if a.info == nil {
		a.info = aws.NewEc2Info(a.awsopts)
	}
}

func (a *Funcs) initKMS() {
	if a.kms == nil {
		a.kms = aws.NewKMS(a.awsopts)
	}
}

func (a *Funcs) initSTS() {
	if a.sts == nil {
		a.sts = aws.NewSTS(a.awsopts)
	}
}
<commit_msg>Add ec2tags function<commit_after>package funcs

import (
	"context"
	"sync"

	"github.com/hairyhenderson/gomplate/v3/aws"
	"github.com/hairyhenderson/gomplate/v3/conv"
)

var (
	af     *Funcs
	afInit sync.Once
)

// AWSNS - the aws namespace
func AWSNS() *Funcs {
	afInit.Do(func() {
		af = &Funcs{
			awsopts: aws.GetClientOptions(),
		}
	})
	return af
}

// AWSFuncs -
func AWSFuncs(f map[string]interface{}) {
	f2 := CreateAWSFuncs(context.Background())
	for k, v := range f2 {
		f[k] = v
	}
}

// CreateAWSFuncs -
func CreateAWSFuncs(ctx context.Context) map[string]interface{} {
	f := map[string]interface{}{}
	ns := AWSNS()
	ns.ctx = ctx

	f["aws"] = AWSNS

	// global aliases - for backwards compatibility
	f["ec2meta"] = ns.EC2Meta
	f["ec2dynamic"] = ns.EC2Dynamic
	f["ec2tag"] = ns.EC2Tag
	f["ec2tags"] = ns.EC2Tags
	f["ec2region"] = ns.EC2Region
	return f
}

// Funcs -
type Funcs struct {
	ctx context.Context

	meta     *aws.Ec2Meta
	info     *aws.Ec2Info
	kms      *aws.KMS
	sts      *aws.STS
	metaInit sync.Once
	infoInit sync.Once
	kmsInit  sync.Once
	stsInit  sync.Once
	awsopts  aws.ClientOptions
}

// EC2Region -
func (a *Funcs) EC2Region(def ...string) (string, error) {
	a.metaInit.Do(a.initMeta)
	return a.meta.Region(def...)
}

// EC2Meta -
func (a *Funcs) EC2Meta(key string, def ...string) (string, error) {
	a.metaInit.Do(a.initMeta)
	return a.meta.Meta(key, def...)
}

// EC2Dynamic -
func (a *Funcs) EC2Dynamic(key string, def ...string) (string, error) {
	a.metaInit.Do(a.initMeta)
	return a.meta.Dynamic(key, def...)
}

// EC2Tag -
func (a *Funcs) EC2Tag(tag string, def ...string) (string, error) {
	a.infoInit.Do(a.initInfo)
	return a.info.Tag(tag, def...)
}

// EC2Tag -
func (a *Funcs) EC2Tags() (map[string]string, error) {
	a.infoInit.Do(a.initInfo)
	return a.info.Tags()
}

// KMSEncrypt -
func (a *Funcs) KMSEncrypt(keyID, plaintext interface{}) (string, error) {
	a.kmsInit.Do(a.initKMS)
	return a.kms.Encrypt(conv.ToString(keyID), conv.ToString(plaintext))
}

// KMSDecrypt -
func (a *Funcs) KMSDecrypt(ciphertext interface{}) (string, error) {
	a.kmsInit.Do(a.initKMS)
	return a.kms.Decrypt(conv.ToString(ciphertext))
}

// UserID - Gets the unique identifier of the calling entity. The exact value
// depends on the type of entity making the call. The values returned are those
// listed in the aws:userid column in the Principal table
// (http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_variables.html#principaltable)
// found on the Policy Variables reference page in the IAM User Guide.
func (a *Funcs) UserID() (string, error) {
	a.stsInit.Do(a.initSTS)
	return a.sts.UserID()
}

// Account - Gets the AWS account ID number of the account that owns or
// contains the calling entity.
func (a *Funcs) Account() (string, error) {
	a.stsInit.Do(a.initSTS)
	return a.sts.Account()
}

// ARN - Gets the AWS ARN associated with the calling entity
func (a *Funcs) ARN() (string, error) {
	a.stsInit.Do(a.initSTS)
	return a.sts.Arn()
}

func (a *Funcs) initMeta() {
	if a.meta == nil {
		a.meta = aws.NewEc2Meta(a.awsopts)
	}
}

func (a *Funcs) initInfo() {
	if a.info == nil {
		a.info = aws.NewEc2Info(a.awsopts)
	}
}

func (a *Funcs) initKMS() {
	if a.kms == nil {
		a.kms = aws.NewKMS(a.awsopts)
	}
}

func (a *Funcs) initSTS() {
	if a.sts == nil {
		a.sts = aws.NewSTS(a.awsopts)
	}
}
<|endoftext|>


<commit_before>package shared

import (
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"io/ioutil"
	"os"
	"os/user"
	"strings"
)

/*
IsTinzenite checks whether a given path is indeed a valid directory
*/
// TODO detect incomplete dir (no connected peers, etc) or write a validate method
func IsTinzenite(dirpath string) bool {
	_, err := os.Stat(dirpath + "/" + TINZENITEDIR)
	if err == nil {
		return true
	}
	// NOTE: object may exist but we may not have permission to access it: in that case
	//       we consider it unaccessible and thus return false
	return false
}

/*
PrettifyDirectoryList reads the directory.list file from the user's tinzenite
config directory and removes all invalid entries.
*/
// TODO rewrite this so that it accepts a string and then applies it if valid
//		while ensuring that the rest is valid
func PrettifyDirectoryList() error {
	user, err := user.Current()
	if err != nil {
		return err
	}
	path := user.HomeDir + "/.config/tinzenite/" + DIRECTORYLIST
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	lines := strings.Split(string(bytes), "\n")
	writeList := map[string]bool{}
	for _, line := range lines {
		if IsTinzenite(line) {
			writeList[line] = true
		}
	}
	var newContents string
	for key := range writeList {
		newContents += key + "\n"
	}
	return ioutil.WriteFile(path, []byte(newContents), FILEPERMISSIONMODE)
}

/*
RandomHash genereates one long random hash.
*/
func RandomHash() (string, error) {
	data := make([]byte, RANDOMSEEDLENGTH)
	_, err := rand.Read(data)
	if err != nil {
		return "", err
	}
	hash := sha256.New()
	_, err = hash.Write(data)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(hash.Sum(nil)), nil
}

/*
NewIdentifier creates a new random hash that is intended as identification
strings for all manner of different objects. Length is IDMAXLENGTH.
*/
func NewIdentifier() (string, error) {
	data := make([]byte, RANDOMSEEDLENGTH)
	_, err := rand.Read(data)
	if err != nil {
		return "", err
	}
	hash := md5.New()
	_, err = hash.Write(data)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(hash.Sum(nil))[:IDMAXLENGTH], nil
}

/*
ContentHash generates the hash of the content of the given file at path.
*/
func ContentHash(path string) (string, error) {
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()
	hash := md5.New()
	buf := make([]byte, CHUNKSIZE)
	// create hash
	for amount := CHUNKSIZE; amount == CHUNKSIZE; {
		amount, _ = file.Read(buf)
		// log.Printf("Read %d bytes", amount)
		hash.Write(buf)
	}
	// return hex representation
	return hex.EncodeToString(hash.Sum(nil)), nil
}

/*
MakeDirectory creates the path.
*/
func MakeDirectory(path string) error {
	err := os.MkdirAll(path, FILEPERMISSIONMODE)
	// TODO this doesn't seem to work... why not?
	if err == os.ErrExist {
		return nil
	}
	// either successful or true error
	return err
}

/*
MakeDirectories creates a number of directories in the given root path. Useful
if a complete directory tree has to be built at once.
*/
func MakeDirectories(root string, subdirs ...string) error {
	for _, path := range subdirs {
		err := MakeDirectory(root + "/" + path)
		if err != nil {
			return err
		}
	}
	return nil
}

/*
FileExists checks whether a file at that location exists.
*/
func FileExists(path string) bool {
	/*TODO differentiate between dir and file?*/
	_, err := os.Lstat(path)
	return err == nil
}

/*
Contains check whether the string slice contains the given string value.
*/
func Contains(s []string, value string) bool {
	for _, entry := range s {
		if entry == value {
			return true
		}
	}
	return false
}

/*
CountFiles within a directory given by path.
*/
func CountFiles(path string) (int, error) {
	files, err := ioutil.ReadDir(path)
	if err != nil {
		return -1, err
	}
	return len(files), nil
}
<commit_msg>moved MakeDotTinzenite to shared<commit_after>package shared

import (
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"io/ioutil"
	"os"
	"os/user"
	"strings"

	"github.com/tinzenite/shared"
)

/*
IsTinzenite checks whether a given path is indeed a valid directory
*/
// TODO detect incomplete dir (no connected peers, etc) or write a validate method
func IsTinzenite(dirpath string) bool {
	_, err := os.Stat(dirpath + "/" + TINZENITEDIR)
	if err == nil {
		return true
	}
	// NOTE: object may exist but we may not have permission to access it: in that case
	//       we consider it unaccessible and thus return false
	return false
}

/*
MakeDotTinzenite creates the directory structure for the .tinzenite directory
including the .tinignore file required for it. The given path is the path to the
directory (NOT the .TINZENITEDIR!).
*/
func MakeDotTinzenite(root string) error {
	root := root + "/" + shared.TINZENITEDIR
	// build directory structure
	err := shared.MakeDirectories(root, shared.ORGDIR+"/"+shared.PEERSDIR, shared.TEMPDIR, shared.REMOVEDIR, shared.LOCALDIR, shared.RECEIVINGDIR)
	if err != nil {
		return err
	}
	// write required .tinignore file
	return ioutil.WriteFile(root+"/"+shared.TINIGNORE, []byte(TINDIRIGNORE), shared.FILEPERMISSIONMODE)
}

/*
PrettifyDirectoryList reads the directory.list file from the user's tinzenite
config directory and removes all invalid entries.
*/
// TODO rewrite this so that it accepts a string and then applies it if valid
//		while ensuring that the rest is valid
func PrettifyDirectoryList() error {
	user, err := user.Current()
	if err != nil {
		return err
	}
	path := user.HomeDir + "/.config/tinzenite/" + DIRECTORYLIST
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	lines := strings.Split(string(bytes), "\n")
	writeList := map[string]bool{}
	for _, line := range lines {
		if IsTinzenite(line) {
			writeList[line] = true
		}
	}
	var newContents string
	for key := range writeList {
		newContents += key + "\n"
	}
	return ioutil.WriteFile(path, []byte(newContents), FILEPERMISSIONMODE)
}

/*
RandomHash genereates one long random hash.
*/
func RandomHash() (string, error) {
	data := make([]byte, RANDOMSEEDLENGTH)
	_, err := rand.Read(data)
	if err != nil {
		return "", err
	}
	hash := sha256.New()
	_, err = hash.Write(data)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(hash.Sum(nil)), nil
}

/*
NewIdentifier creates a new random hash that is intended as identification
strings for all manner of different objects. Length is IDMAXLENGTH.
*/
func NewIdentifier() (string, error) {
	data := make([]byte, RANDOMSEEDLENGTH)
	_, err := rand.Read(data)
	if err != nil {
		return "", err
	}
	hash := md5.New()
	_, err = hash.Write(data)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(hash.Sum(nil))[:IDMAXLENGTH], nil
}

/*
ContentHash generates the hash of the content of the given file at path.
*/
func ContentHash(path string) (string, error) {
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()
	hash := md5.New()
	buf := make([]byte, CHUNKSIZE)
	// create hash
	for amount := CHUNKSIZE; amount == CHUNKSIZE; {
		amount, _ = file.Read(buf)
		// log.Printf("Read %d bytes", amount)
		hash.Write(buf)
	}
	// return hex representation
	return hex.EncodeToString(hash.Sum(nil)), nil
}

/*
MakeDirectory creates the path.
*/
func MakeDirectory(path string) error {
	err := os.MkdirAll(path, FILEPERMISSIONMODE)
	// TODO this doesn't seem to work... why not?
	if err == os.ErrExist {
		return nil
	}
	// either successful or true error
	return err
}

/*
MakeDirectories creates a number of directories in the given root path. Useful
if a complete directory tree has to be built at once.
*/
func MakeDirectories(root string, subdirs ...string) error {
	for _, path := range subdirs {
		err := MakeDirectory(root + "/" + path)
		if err != nil {
			return err
		}
	}
	return nil
}

/*
FileExists checks whether a file at that location exists.
*/
func FileExists(path string) bool {
	/*TODO differentiate between dir and file?*/
	_, err := os.Lstat(path)
	return err == nil
}

/*
Contains check whether the string slice contains the given string value.
*/
func Contains(s []string, value string) bool {
	for _, entry := range s {
		if entry == value {
			return true
		}
	}
	return false
}

/*
CountFiles within a directory given by path.
*/
func CountFiles(path string) (int, error) {
	files, err := ioutil.ReadDir(path)
	if err != nil {
		return -1, err
	}
	return len(files), nil
}
<|endoftext|>


<commit_before>package main

import "fmt"

type Caller interface {
	// Call this function in it's written form, i.e. with a list of expressions, where the first one is the function name
	Call(args List, context *Context) Data
}

type NativeFunction struct {
	// Native functions receive a list of arguments, the first being the name under
	// which the function itself was called
	Function func(List, *Context) Data
}

type NativeFunctionB struct {
	// Same as NativeFunction, except it doesnt expect evaluated arguments
	Function func(List, *Context) Data
}

// data type for user-defined gamelisp functions
type Function struct {
	Name        string
	Dispatchers []DispatchPattern
	Code        Data
}

func (f Function) String() string {
	return fmt.Sprintf("Function<%s>", f.Name)
}

type ParameterDeclaration interface {
	ParameterName() string
	Match(arg Data) bool
	Bind(args List, index int, context *Context)
}

type DispatchPattern struct {
	Parameters []ParameterDeclaration
}

// Attempts to match a list of arguments to this dispatch pattern
// ignores the first list item, assuming it's the function name
func (dp DispatchPattern) Match(args List) bool {
	if len(dp.Parameters) > args.Len() {
		return false
	}

	i := 0
	for e := args.Front(); e != nil && i < len(dp.Parameters); e = e.Next() {
		arg := e.Value.(Data)
		if !dp.Parameters[i].Match(arg) {
			return false
		}
		i++
	}

	// Are there still arguments left that were not matched?
	if i < args.Len() {
		// Check if the last parameter is a sink
		if _, isSink := dp.Parameters[i-1].(ArgumentSink); isSink {
			return true
		} else {
			return false
		}
	}

	return true
}

// pattern for a regular function argument
type ArgumentPattern struct {
	// Name of the parameter which will be created as a symbol in the function
	Name string

	// The expected type or nil if any type is accepted
	ExpectedType *DataType

	// The expected value or nil if any value is accepted
	ExpectedValue *Data
}

func (ap ArgumentPattern) Bind(args List, index int, context *Context) {
	if ap.Name != "" {
		context.Define(Symbol{ap.Name}, args.Get(index))
	} else {
		// nothing to bind since the expected value is known to the user
	}
}

func (ap ArgumentPattern) Match(param Data) bool {
	// check for a required value
	if ap.ExpectedValue != nil {
		return param.Equals(*ap.ExpectedValue)
	}

	// check for a required type
	if ap.ExpectedType != nil {
		return param.GetType().Equals(ap.ExpectedType)
	}

	// if no type or value has been specified anything is accepted
	return true
}

func (ap ArgumentPattern) ParameterName() string {
	return ap.Name
}

// placeholder for a argument sink that consumes all following arguments passed to the function
type ArgumentSink struct {
	Name string
}

func (as ArgumentSink) Bind(args List, index int, context *Context) {
	// binds arguments i and all following as a list
	context.Define(Symbol{as.Name}, args.SliceFrom(index))
}

func (as ArgumentSink) Match(param Data) bool {
	return true
}

func (as ArgumentSink) ParameterName() string {
	return as.Name
}

func (fn NativeFunction) Call(args List, context *Context) Data {
	args = args.Map(__evalArgs(context))
	return fn.Function(args, context)
}

func (fn NativeFunction) String() string {
	return "native function"
}

func (fn NativeFunctionB) Call(args List, context *Context) Data {
	return fn.Function(args, context)
}

func (fn NativeFunctionB) String() string {
	return "native function"
}

// Expects args for a function definition, e.g.
// (defn myfunction [p1 p2 ...] (stmts*))
func CreateFunction(args List, context *Context) *Function {
	ValidateArgs(args, []string{"Symbol", "List", "Data"})
	fn := new(Function)

	// function name
	fn.Name = args.First().(Symbol).Value

	// create dispatch pattern
	fnArgs := args.Second().(List)
	if first, isSymbol := fnArgs.Get(0).(Symbol); isSymbol {
		if first.Value == "list" {
			fnArgs = fnArgs.SliceFrom(1)
		}
	}

	count := fnArgs.Len()
	dispatcher := new(DispatchPattern)
	dispatcher.Parameters = make([]ParameterDeclaration, count)
	for i := 0; i < count; i++ {
		dispatcher.Parameters[i] = CreateParameter(fnArgs.Get(i), context)
	}

	fn.Dispatchers = make([]DispatchPattern, 1)
	fn.Dispatchers[0] = *dispatcher

	// Save the code for later execution
	fn.Code = args.Third()

	return fn
}

func CreateParameter(args Data, context *Context) ParameterDeclaration {
	switch t := args.(type) {
	case Symbol:
		return ArgumentPattern{Name: t.Value}
	case Int, Float, Bool, String, Keyword, Nothing:
		return ArgumentPattern{ExpectedValue: &t}
	}

	panic(fmt.Sprintf("Couldn't create parameter from %s", args.String()))
}

func (fn Function) selectDispatch(args List) *DispatchPattern {
	for _, dispatch := range fn.Dispatchers {
		if dispatch.Match(args) {
			return &dispatch
		}
	}

	return nil
}

func (dp DispatchPattern) bindParameters(args List, context *Context) {
	for i, decl := range dp.Parameters {
		decl.Bind(args, i, context)
	}
}

// ($name args...)
func (fn Function) Call(args List, env *Context) Data {
	// evaluate the arguments
	args = args.Map(__evalArgs(env))

	// create the function context for this call
	context := NewContext()
	context.parent = env

	// choose dispatcher
	dispatch := fn.selectDispatch(args)
	if dispatch == nil {
		panic("No dispatch pattern matches the given function arguments")
	}
	dispatch.bindParameters(args, context)

	// execute the args in the temporary context
	result, err := Evaluate(fn.Code, context)
	if err != nil {
		panic(fmt.Sprintf("Failed to call %s: %s", fn.Name, err))
	}

	return result
}
<commit_msg>argument matching by value or type<commit_after>package main

import "fmt"

type Caller interface {
	// Call this function in it's written form, i.e. with a list of expressions, where the first one is the function name
	Call(args List, context *Context) Data
}

type NativeFunction struct {
	// Native functions receive a list of arguments, the first being the name under
	// which the function itself was called
	Function func(List, *Context) Data
}

type NativeFunctionB struct {
	// Same as NativeFunction, except it doesnt expect evaluated arguments
	Function func(List, *Context) Data
}

// data type for user-defined gamelisp functions
type Function struct {
	Name        string
	Dispatchers []DispatchPattern
	Code        Data
}

func (f Function) String() string {
	return fmt.Sprintf("Function<%s>", f.Name)
}

type ParameterDeclaration interface {
	ParameterName() string
	Match(arg Data) bool
	Bind(args List, index int, context *Context)
}

type DispatchPattern struct {
	Parameters []ParameterDeclaration
}

// Attempts to match a list of arguments to this dispatch pattern
// ignores the first list item, assuming it's the function name
func (dp DispatchPattern) Match(args List) bool {
	if len(dp.Parameters) > args.Len() {
		return false
	}

	i := 0
	for e := args.Front(); e != nil && i < len(dp.Parameters); e = e.Next() {
		arg := e.Value.(Data)
		if !dp.Parameters[i].Match(arg) {
			return false
		}
		i++
	}

	// Are there still arguments left that were not matched?
	if i < args.Len() {
		// Check if the last parameter is a sink
		if _, isSink := dp.Parameters[i-1].(ArgumentSink); isSink {
			return true
		} else {
			return false
		}
	}

	return true
}

// pattern for a regular function argument
type ArgumentPattern struct {
	// Name of the parameter which will be created as a symbol in the function
	Name string

	// The expected type or nil if any type is accepted
	ExpectedType *DataType

	// The expected value or nil if any value is accepted
	ExpectedValue *Data
}

func (ap ArgumentPattern) Bind(args List, index int, context *Context) {
	if ap.Name != "" {
		context.Define(Symbol{ap.Name}, args.Get(index))
	} else {
		// nothing to bind since the expected value is known to the user
	}
}

func (ap ArgumentPattern) Match(param Data) bool {
	// check for a required value
	if ap.ExpectedValue != nil {
		return param.Equals(*ap.ExpectedValue)
	}

	// check for a required type
	if ap.ExpectedType != nil {
		paramType := param.GetType()
		equal := paramType.Equals(*ap.ExpectedType)
		return equal
	}

	// if no type or value has been specified anything is accepted
	return true
}

func (ap ArgumentPattern) ParameterName() string {
	return ap.Name
}

// placeholder for a argument sink that consumes all following arguments passed to the function
type ArgumentSink struct {
	Name string
}

func (as ArgumentSink) Bind(args List, index int, context *Context) {
	// binds arguments i and all following as a list
	context.Define(Symbol{as.Name}, args.SliceFrom(index))
}

func (as ArgumentSink) Match(param Data) bool {
	return true
}

func (as ArgumentSink) ParameterName() string {
	return as.Name
}

func (fn NativeFunction) Call(args List, context *Context) Data {
	args = args.Map(__evalArgs(context))
	return fn.Function(args, context)
}

func (fn NativeFunction) String() string {
	return "native function"
}

func (fn NativeFunctionB) Call(args List, context *Context) Data {
	return fn.Function(args, context)
}

func (fn NativeFunctionB) String() string {
	return "native function"
}

// Expects args for a function definition, e.g.
// (defn myfunction [p1 p2 ...] (stmts*))
func CreateFunction(args List, context *Context) *Function {
	ValidateArgs(args, []string{"Symbol", "List", "Data"})
	fn := new(Function)

	// function name
	fn.Name = args.First().(Symbol).Value

	// create dispatch pattern
	fnArgs := args.Second().(List)
	if first, isSymbol := fnArgs.Get(0).(Symbol); isSymbol {
		if first.Value == "list" {
			fnArgs = fnArgs.SliceFrom(1)
		}
	}

	count := fnArgs.Len()
	dispatcher := new(DispatchPattern)
	dispatcher.Parameters = make([]ParameterDeclaration, count)
	for i := 0; i < count; i++ {
		dispatcher.Parameters[i] = CreateParameter(fnArgs.Get(i), context)
	}

	fn.Dispatchers = make([]DispatchPattern, 1)
	fn.Dispatchers[0] = *dispatcher

	// Save the code for later execution
	fn.Code = args.Third()

	return fn
}

func CreateParameter(args Data, context *Context) ParameterDeclaration {
	switch t := args.(type) {
	case Symbol:
		// check if the symbol refers to a datatype
		def := context.LookUp(t)
		if def != nil {
			if datatype, ok := def.(DataType); ok {
				return ArgumentPattern{ExpectedType: &datatype}
			}
		}

		return ArgumentPattern{Name: t.Value}
	case Int, Float, Bool, String, Keyword, Nothing:
		return ArgumentPattern{ExpectedValue: &t}
	}

	panic(fmt.Sprintf("Couldn't create parameter from %s", args.String()))
}

func (fn Function) selectDispatch(args List) *DispatchPattern {
	for _, dispatch := range fn.Dispatchers {
		if dispatch.Match(args) {
			return &dispatch
		}
	}

	return nil
}

func (dp DispatchPattern) bindParameters(args List, context *Context) {
	for i, decl := range dp.Parameters {
		decl.Bind(args, i, context)
	}
}

// ($name args...)
func (fn Function) Call(args List, env *Context) Data {
	// evaluate the arguments
	args = args.Map(__evalArgs(env))

	// create the function context for this call
	context := NewContext()
	context.parent = env

	// choose dispatcher
	dispatch := fn.selectDispatch(args)
	if dispatch == nil {
		panic("No dispatch pattern matches the given function arguments")
	}
	dispatch.bindParameters(args, context)

	// execute the args in the temporary context
	result, err := Evaluate(fn.Code, context)
	if err != nil {
		panic(fmt.Sprintf("Failed to call %s: %s", fn.Name, err))
	}

	return result
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 controller

import (
	"context"
	"fmt"
	"reflect"
	"strconv"

	corev1 "k8s.io/api/core/v1"
	extv1beta1 "k8s.io/api/extensions/v1beta1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/klog"

	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack/cert-manager/pkg/util"
)

const (
	// editInPlaceAnnotation is used to toggle the use of ingressClass instead
	// of ingress on the created Certificate resource
	editInPlaceAnnotation = "certmanager.k8s.io/acme-http01-edit-in-place"
	// issuerNameAnnotation can be used to override the issuer specified on the
	// created Certificate resource.
	issuerNameAnnotation = "certmanager.k8s.io/issuer"
	// clusterIssuerNameAnnotation can be used to override the issuer specified on the
	// created Certificate resource. The Certificate will reference the
	// specified *ClusterIssuer* instead of normal issuer.
	clusterIssuerNameAnnotation = "certmanager.k8s.io/cluster-issuer"
	// acmeIssuerChallengeTypeAnnotation can be used to override the default ACME challenge
	// type to be used when the specified issuer is an ACME issuer
	acmeIssuerChallengeTypeAnnotation = "certmanager.k8s.io/acme-challenge-type"
	// acmeIssuerDNS01ProviderNameAnnotation can be used to override the default dns01 provider
	// configured on the issuer if the challenge type is set to dns01
	acmeIssuerDNS01ProviderNameAnnotation = "certmanager.k8s.io/acme-dns01-provider"
	// acmeIssuerHTTP01IngressClassAnnotation can be used to override the http01 ingressClass
	// if the challenge type is set to http01
	acmeIssuerHTTP01IngressClassAnnotation = "certmanager.k8s.io/acme-http01-ingress-class"

	ingressClassAnnotation = util.IngressKey
)

var ingressGVK = extv1beta1.SchemeGroupVersion.WithKind("Ingress")

func (c *Controller) Sync(ctx context.Context, ing *extv1beta1.Ingress) error {
	if !shouldSync(ing, c.defaults.autoCertificateAnnotations) {
		klog.Infof("Not syncing ingress %s/%s as it does not contain necessary annotations", ing.Namespace, ing.Name)
		return nil
	}

	issuerName, issuerKind := c.issuerForIngress(ing)
	if issuerName == "" {
		c.Recorder.Eventf(ing, corev1.EventTypeWarning, "BadConfig", "Issuer name annotation is not set and a default issuer has not been configured")
		return nil
	}

	issuer, err := c.helper.GetGenericIssuer(v1alpha1.ObjectReference{
		Name: issuerName,
		Kind: issuerKind,
	}, ing.Namespace)
	if apierrors.IsNotFound(err) {
		c.Recorder.Eventf(ing, corev1.EventTypeWarning, "BadConfig", "%s resource %q not found", issuerKind, issuerName)
		return nil
	}
	if err != nil {
		return err
	}

	errs := c.validateIngress(ing)
	if len(errs) > 0 {
		errMsg := errs[0].Error()
		if len(errs) > 1 {
			errMsg = utilerrors.NewAggregate(errs).Error()
		}
		c.Recorder.Eventf(ing, corev1.EventTypeWarning, "BadConfig", errMsg)
		return nil
	}

	newCrts, updateCrts, err := c.buildCertificates(ing, issuer, issuerKind)
	if err != nil {
		return err
	}

	for _, crt := range newCrts {
		_, err := c.CMClient.CertmanagerV1alpha1().Certificates(crt.Namespace).Create(crt)
		if err != nil {
			return err
		}
		c.Recorder.Eventf(ing, corev1.EventTypeNormal, "CreateCertificate", "Successfully created Certificate %q", crt.Name)
	}

	for _, crt := range updateCrts {
		_, err := c.CMClient.CertmanagerV1alpha1().Certificates(crt.Namespace).Update(crt)
		if err != nil {
			return err
		}
		c.Recorder.Eventf(ing, corev1.EventTypeNormal, "UpdateCertificate", "Successfully updated Certificate %q", crt.Name)
	}

	return nil
}

func (c *Controller) validateIngress(ing *extv1beta1.Ingress) []error {
	var errs []error
	if ing.Annotations != nil {
		challengeType := ing.Annotations[acmeIssuerChallengeTypeAnnotation]
		switch challengeType {
		case "", "http01":
		case "dns01":
			providerName := ing.Annotations[acmeIssuerDNS01ProviderNameAnnotation]
			if providerName == "" {
				errs = append(errs, fmt.Errorf("No acme dns01 challenge provider specified"))
			}
		default:
			errs = append(errs, fmt.Errorf("Invalid acme challenge type specified %q", challengeType))
		}
	}
	for i, tls := range ing.Spec.TLS {
		// validate the ingress TLS block
		if len(tls.Hosts) == 0 {
			errs = append(errs, fmt.Errorf("Secret %q for ingress TLS has no hosts specified", tls.SecretName))
		}
		if tls.SecretName == "" {
			errs = append(errs, fmt.Errorf("TLS entry %d for hosts %v must specify a secretName", i, tls.Hosts))
		}
	}
	return errs
}

func (c *Controller) buildCertificates(ing *extv1beta1.Ingress, issuer v1alpha1.GenericIssuer, issuerKind string) (new, update []*v1alpha1.Certificate, _ error) {
	var newCrts []*v1alpha1.Certificate
	var updateCrts []*v1alpha1.Certificate
	for _, tls := range ing.Spec.TLS {
		existingCrt, err := c.certificateLister.Certificates(ing.Namespace).Get(tls.SecretName)
		if !apierrors.IsNotFound(err) && err != nil {
			return nil, nil, err
		}

		crt := &v1alpha1.Certificate{
			ObjectMeta: metav1.ObjectMeta{
				Name:            tls.SecretName,
				Namespace:       ing.Namespace,
				Labels:          ing.Labels,
				OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(ing, ingressGVK)},
			},
			Spec: v1alpha1.CertificateSpec{
				DNSNames:   tls.Hosts,
				SecretName: tls.SecretName,
				IssuerRef: v1alpha1.ObjectReference{
					Name: issuer.GetObjectMeta().Name,
					Kind: issuerKind,
				},
			},
		}

		err = c.setIssuerSpecificConfig(crt, issuer, ing, tls)
		if err != nil {
			return nil, nil, err
		}

		// check if a Certificate for this TLS entry already exists, and if it
		// does then skip this entry
		if existingCrt != nil {
			klog.Infof("Certificate %q for ingress %q already exists", tls.SecretName, ing.Name)

			if !certNeedsUpdate(existingCrt, crt) {
				klog.Infof("Certificate %q for ingress %q is up to date", tls.SecretName, ing.Name)
				continue
			}

			updateCrt := existingCrt.DeepCopy()

			updateCrt.Spec.DNSNames = tls.Hosts
			updateCrt.Spec.SecretName = tls.SecretName
			updateCrt.Spec.IssuerRef.Name = issuer.GetObjectMeta().Name
			updateCrt.Spec.IssuerRef.Kind = issuerKind
			err = c.setIssuerSpecificConfig(updateCrt, issuer, ing, tls)
			if err != nil {
				return nil, nil, err
			}
			updateCrts = append(updateCrts, updateCrt)
		} else {
			newCrts = append(newCrts, crt)
		}
	}
	return newCrts, updateCrts, nil
}

// certNeedsUpdate checks and returns true if two Certificates differ
func certNeedsUpdate(a, b *v1alpha1.Certificate) bool {
	if a.Name != b.Name {
		return true
	}

	if len(a.Spec.DNSNames) != len(b.Spec.DNSNames) {
		return true
	}

	for i := range a.Spec.DNSNames {
		if a.Spec.DNSNames[i] != b.Spec.DNSNames[i] {
			return true
		}
	}

	if a.Spec.SecretName != b.Spec.SecretName {
		return true
	}

	if a.Spec.IssuerRef.Name != b.Spec.IssuerRef.Name {
		return true
	}

	if a.Spec.IssuerRef.Kind != b.Spec.IssuerRef.Kind {
		return true
	}

	var configA, configB []v1alpha1.DomainSolverConfig

	if a.Spec.ACME != nil {
		configA = a.Spec.ACME.Config
	}

	if b.Spec.ACME != nil {
		configB = b.Spec.ACME.Config
	}

	if !reflect.DeepEqual(configA, configB) {
		return true
	}

	return false
}

func (c *Controller) setIssuerSpecificConfig(crt *v1alpha1.Certificate, issuer v1alpha1.GenericIssuer, ing *extv1beta1.Ingress, tls extv1beta1.IngressTLS) error {
	ingAnnotations := ing.Annotations
	if ingAnnotations == nil {
		ingAnnotations = map[string]string{}
	}
	// for ACME issuers
	if issuer.GetSpec().ACME != nil {
		challengeType, ok := ingAnnotations[acmeIssuerChallengeTypeAnnotation]
		if !ok {
			challengeType = c.defaults.acmeIssuerChallengeType
		}
		domainCfg := v1alpha1.DomainSolverConfig{
			Domains: tls.Hosts,
		}
		switch challengeType {
		case "http01":
			// If the HTTP01 issuer is not enabled, skip setting the ACME field
			// on the Certificate resource.
			if issuer.GetSpec().ACME.HTTP01 == nil {
				crt.Spec.ACME = nil
				return nil
			}
			domainCfg.HTTP01 = &v1alpha1.HTTP01SolverConfig{}
			editInPlace, ok := ingAnnotations[editInPlaceAnnotation]
			// If annotation isn't present, or it's set to true, edit the existing ingress
			if ok && editInPlace == "true" {
				domainCfg.HTTP01.Ingress = ing.Name
			} else {
				ingressClass, ok := ingAnnotations[acmeIssuerHTTP01IngressClassAnnotation]
				if ok {
					domainCfg.HTTP01.IngressClass = &ingressClass
				} else {
					ingressClass, ok := ingAnnotations[ingressClassAnnotation]
					if ok {
						domainCfg.HTTP01.IngressClass = &ingressClass
					}
				}
			}
		case "dns01":
			// If the DNS01 issuer is not enabled, skip setting the ACME field
			// on the Certificate resource.
			if issuer.GetSpec().ACME.DNS01 == nil {
				crt.Spec.ACME = nil
				return nil
			}
			dnsProvider, ok := ingAnnotations[acmeIssuerDNS01ProviderNameAnnotation]
			if !ok {
				dnsProvider = c.defaults.acmeIssuerDNS01ProviderName
			}
			if dnsProvider == "" {
				return fmt.Errorf("no acme issuer dns01 challenge provider specified")
			}
			domainCfg.DNS01 = &v1alpha1.DNS01SolverConfig{Provider: dnsProvider}
		// If no challenge type is specified, don't set the ACME field at all
		// and instead rely on the 'new API format' to provide solver config.
		case "":
			crt.Spec.ACME = nil
			return nil
		default:
			return fmt.Errorf("invalid acme issuer challenge type specified %q", challengeType)
		}
		crt.Spec.ACME = &v1alpha1.ACMECertificateConfig{Config: []v1alpha1.DomainSolverConfig{domainCfg}}
	}
	return nil
}

// shouldSync returns true if this ingress should have a Certificate resource
// created for it
func shouldSync(ing *extv1beta1.Ingress, autoCertificateAnnotations []string) bool {
	annotations := ing.Annotations
	if annotations == nil {
		annotations = map[string]string{}
	}
	if _, ok := annotations[issuerNameAnnotation]; ok {
		return true
	}
	if _, ok := annotations[clusterIssuerNameAnnotation]; ok {
		return true
	}
	for _, x := range autoCertificateAnnotations {
		if s, ok := annotations[x]; ok {
			if b, _ := strconv.ParseBool(s); b {
				return true
			}
		}
	}
	if _, ok := annotations[acmeIssuerChallengeTypeAnnotation]; ok {
		return true
	}
	if _, ok := annotations[acmeIssuerDNS01ProviderNameAnnotation]; ok {
		return true
	}
	return false
}

// issuerForIngress will determine the issuer that should be specified on a
// Certificate created for the given Ingress resource. If one is not set, the
// default issuer given to the controller will be used.
func (c *Controller) issuerForIngress(ing *extv1beta1.Ingress) (name string, kind string) {
	name = c.defaults.issuerName
	kind = c.defaults.issuerKind
	annotations := ing.Annotations
	if annotations == nil {
		annotations = map[string]string{}
	}
	if issuerName, ok := annotations[issuerNameAnnotation]; ok {
		name = issuerName
		kind = v1alpha1.IssuerKind
	}
	if issuerName, ok := annotations[clusterIssuerNameAnnotation]; ok {
		name = issuerName
		kind = v1alpha1.ClusterIssuerKind
	}
	return name, kind
}
<commit_msg>Log warning Event if edit-in-place is enabled with new solver format<commit_after>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 controller

import (
	"context"
	"fmt"
	"reflect"
	"strconv"

	corev1 "k8s.io/api/core/v1"
	extv1beta1 "k8s.io/api/extensions/v1beta1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/klog"

	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack/cert-manager/pkg/util"
)

const (
	// editInPlaceAnnotation is used to toggle the use of ingressClass instead
	// of ingress on the created Certificate resource
	editInPlaceAnnotation = "certmanager.k8s.io/acme-http01-edit-in-place"
	// issuerNameAnnotation can be used to override the issuer specified on the
	// created Certificate resource.
	issuerNameAnnotation = "certmanager.k8s.io/issuer"
	// clusterIssuerNameAnnotation can be used to override the issuer specified on the
	// created Certificate resource. The Certificate will reference the
	// specified *ClusterIssuer* instead of normal issuer.
	clusterIssuerNameAnnotation = "certmanager.k8s.io/cluster-issuer"
	// acmeIssuerChallengeTypeAnnotation can be used to override the default ACME challenge
	// type to be used when the specified issuer is an ACME issuer
	acmeIssuerChallengeTypeAnnotation = "certmanager.k8s.io/acme-challenge-type"
	// acmeIssuerDNS01ProviderNameAnnotation can be used to override the default dns01 provider
	// configured on the issuer if the challenge type is set to dns01
	acmeIssuerDNS01ProviderNameAnnotation = "certmanager.k8s.io/acme-dns01-provider"
	// acmeIssuerHTTP01IngressClassAnnotation can be used to override the http01 ingressClass
	// if the challenge type is set to http01
	acmeIssuerHTTP01IngressClassAnnotation = "certmanager.k8s.io/acme-http01-ingress-class"

	ingressClassAnnotation = util.IngressKey
)

var ingressGVK = extv1beta1.SchemeGroupVersion.WithKind("Ingress")

func (c *Controller) Sync(ctx context.Context, ing *extv1beta1.Ingress) error {
	if !shouldSync(ing, c.defaults.autoCertificateAnnotations) {
		klog.Infof("Not syncing ingress %s/%s as it does not contain necessary annotations", ing.Namespace, ing.Name)
		return nil
	}

	issuerName, issuerKind := c.issuerForIngress(ing)
	if issuerName == "" {
		c.Recorder.Eventf(ing, corev1.EventTypeWarning, "BadConfig", "Issuer name annotation is not set and a default issuer has not been configured")
		return nil
	}

	issuer, err := c.helper.GetGenericIssuer(v1alpha1.ObjectReference{
		Name: issuerName,
		Kind: issuerKind,
	}, ing.Namespace)
	if apierrors.IsNotFound(err) {
		c.Recorder.Eventf(ing, corev1.EventTypeWarning, "BadConfig", "%s resource %q not found", issuerKind, issuerName)
		return nil
	}
	if err != nil {
		return err
	}

	errs := c.validateIngress(ing)
	if len(errs) > 0 {
		errMsg := errs[0].Error()
		if len(errs) > 1 {
			errMsg = utilerrors.NewAggregate(errs).Error()
		}
		c.Recorder.Eventf(ing, corev1.EventTypeWarning, "BadConfig", errMsg)
		return nil
	}

	newCrts, updateCrts, err := c.buildCertificates(ing, issuer, issuerKind)
	if err != nil {
		return err
	}

	for _, crt := range newCrts {
		_, err := c.CMClient.CertmanagerV1alpha1().Certificates(crt.Namespace).Create(crt)
		if err != nil {
			return err
		}
		c.Recorder.Eventf(ing, corev1.EventTypeNormal, "CreateCertificate", "Successfully created Certificate %q", crt.Name)
	}

	for _, crt := range updateCrts {
		_, err := c.CMClient.CertmanagerV1alpha1().Certificates(crt.Namespace).Update(crt)
		if err != nil {
			return err
		}
		c.Recorder.Eventf(ing, corev1.EventTypeNormal, "UpdateCertificate", "Successfully updated Certificate %q", crt.Name)
	}

	return nil
}

func (c *Controller) validateIngress(ing *extv1beta1.Ingress) []error {
	var errs []error
	if ing.Annotations != nil {
		challengeType := ing.Annotations[acmeIssuerChallengeTypeAnnotation]
		switch challengeType {
		case "", "http01":
		case "dns01":
			providerName := ing.Annotations[acmeIssuerDNS01ProviderNameAnnotation]
			if providerName == "" {
				errs = append(errs, fmt.Errorf("No acme dns01 challenge provider specified"))
			}
		default:
			errs = append(errs, fmt.Errorf("Invalid acme challenge type specified %q", challengeType))
		}
	}
	for i, tls := range ing.Spec.TLS {
		// validate the ingress TLS block
		if len(tls.Hosts) == 0 {
			errs = append(errs, fmt.Errorf("Secret %q for ingress TLS has no hosts specified", tls.SecretName))
		}
		if tls.SecretName == "" {
			errs = append(errs, fmt.Errorf("TLS entry %d for hosts %v must specify a secretName", i, tls.Hosts))
		}
	}
	return errs
}

func (c *Controller) buildCertificates(ing *extv1beta1.Ingress, issuer v1alpha1.GenericIssuer, issuerKind string) (new, update []*v1alpha1.Certificate, _ error) {
	var newCrts []*v1alpha1.Certificate
	var updateCrts []*v1alpha1.Certificate
	for _, tls := range ing.Spec.TLS {
		existingCrt, err := c.certificateLister.Certificates(ing.Namespace).Get(tls.SecretName)
		if !apierrors.IsNotFound(err) && err != nil {
			return nil, nil, err
		}

		crt := &v1alpha1.Certificate{
			ObjectMeta: metav1.ObjectMeta{
				Name:            tls.SecretName,
				Namespace:       ing.Namespace,
				Labels:          ing.Labels,
				OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(ing, ingressGVK)},
			},
			Spec: v1alpha1.CertificateSpec{
				DNSNames:   tls.Hosts,
				SecretName: tls.SecretName,
				IssuerRef: v1alpha1.ObjectReference{
					Name: issuer.GetObjectMeta().Name,
					Kind: issuerKind,
				},
			},
		}

		err = c.setIssuerSpecificConfig(crt, issuer, ing, tls)
		if err != nil {
			return nil, nil, err
		}

		// check if a Certificate for this TLS entry already exists, and if it
		// does then skip this entry
		if existingCrt != nil {
			klog.Infof("Certificate %q for ingress %q already exists", tls.SecretName, ing.Name)

			if !certNeedsUpdate(existingCrt, crt) {
				klog.Infof("Certificate %q for ingress %q is up to date", tls.SecretName, ing.Name)
				continue
			}

			updateCrt := existingCrt.DeepCopy()

			updateCrt.Spec.DNSNames = tls.Hosts
			updateCrt.Spec.SecretName = tls.SecretName
			updateCrt.Spec.IssuerRef.Name = issuer.GetObjectMeta().Name
			updateCrt.Spec.IssuerRef.Kind = issuerKind
			err = c.setIssuerSpecificConfig(updateCrt, issuer, ing, tls)
			if err != nil {
				return nil, nil, err
			}
			updateCrts = append(updateCrts, updateCrt)
		} else {
			newCrts = append(newCrts, crt)
		}
	}
	return newCrts, updateCrts, nil
}

// certNeedsUpdate checks and returns true if two Certificates differ
func certNeedsUpdate(a, b *v1alpha1.Certificate) bool {
	if a.Name != b.Name {
		return true
	}

	if len(a.Spec.DNSNames) != len(b.Spec.DNSNames) {
		return true
	}

	for i := range a.Spec.DNSNames {
		if a.Spec.DNSNames[i] != b.Spec.DNSNames[i] {
			return true
		}
	}

	if a.Spec.SecretName != b.Spec.SecretName {
		return true
	}

	if a.Spec.IssuerRef.Name != b.Spec.IssuerRef.Name {
		return true
	}

	if a.Spec.IssuerRef.Kind != b.Spec.IssuerRef.Kind {
		return true
	}

	var configA, configB []v1alpha1.DomainSolverConfig

	if a.Spec.ACME != nil {
		configA = a.Spec.ACME.Config
	}

	if b.Spec.ACME != nil {
		configB = b.Spec.ACME.Config
	}

	if !reflect.DeepEqual(configA, configB) {
		return true
	}

	return false
}

func (c *Controller) setIssuerSpecificConfig(crt *v1alpha1.Certificate, issuer v1alpha1.GenericIssuer, ing *extv1beta1.Ingress, tls extv1beta1.IngressTLS) error {
	ingAnnotations := ing.Annotations
	if ingAnnotations == nil {
		ingAnnotations = map[string]string{}
	}
	// for ACME issuers
	if issuer.GetSpec().ACME != nil {
		challengeType, ok := ingAnnotations[acmeIssuerChallengeTypeAnnotation]
		if !ok {
			challengeType = c.defaults.acmeIssuerChallengeType
		}
		domainCfg := v1alpha1.DomainSolverConfig{
			Domains: tls.Hosts,
		}
		switch challengeType {
		case "http01":
			editInPlaceVal, ok := ingAnnotations[editInPlaceAnnotation]
			editInPlace := editInPlaceVal == "true"
			// If the HTTP01 issuer is not enabled, skip setting the ACME field
			// on the Certificate resource.
			if issuer.GetSpec().ACME.HTTP01 == nil {
				if editInPlace {
					c.Recorder.Eventf(ing, corev1.EventTypeWarning, "Unsupported", "%s annotation cannot be enabled when using new format solver type. "+
						"Re-enable the old format HTTP01 solver, or otherwise create a specific HTTP01 solver for this Ingress.", editInPlaceAnnotation)
				}
				crt.Spec.ACME = nil
				return nil
			}
			domainCfg.HTTP01 = &v1alpha1.HTTP01SolverConfig{}
			// If annotation isn't present, or it's set to true, edit the existing ingress
			if ok && editInPlace {
				domainCfg.HTTP01.Ingress = ing.Name
			} else {
				ingressClass, ok := ingAnnotations[acmeIssuerHTTP01IngressClassAnnotation]
				if ok {
					domainCfg.HTTP01.IngressClass = &ingressClass
				} else {
					ingressClass, ok := ingAnnotations[ingressClassAnnotation]
					if ok {
						domainCfg.HTTP01.IngressClass = &ingressClass
					}
				}
			}
		case "dns01":
			// If the DNS01 issuer is not enabled, skip setting the ACME field
			// on the Certificate resource.
			if issuer.GetSpec().ACME.DNS01 == nil {
				crt.Spec.ACME = nil
				return nil
			}
			dnsProvider, ok := ingAnnotations[acmeIssuerDNS01ProviderNameAnnotation]
			if !ok {
				dnsProvider = c.defaults.acmeIssuerDNS01ProviderName
			}
			if dnsProvider == "" {
				return fmt.Errorf("no acme issuer dns01 challenge provider specified")
			}
			domainCfg.DNS01 = &v1alpha1.DNS01SolverConfig{Provider: dnsProvider}
		// If no challenge type is specified, don't set the ACME field at all
		// and instead rely on the 'new API format' to provide solver config.
		case "":
			crt.Spec.ACME = nil
			return nil
		default:
			return fmt.Errorf("invalid acme issuer challenge type specified %q", challengeType)
		}
		crt.Spec.ACME = &v1alpha1.ACMECertificateConfig{Config: []v1alpha1.DomainSolverConfig{domainCfg}}
	}
	return nil
}

// shouldSync returns true if this ingress should have a Certificate resource
// created for it
func shouldSync(ing *extv1beta1.Ingress, autoCertificateAnnotations []string) bool {
	annotations := ing.Annotations
	if annotations == nil {
		annotations = map[string]string{}
	}
	if _, ok := annotations[issuerNameAnnotation]; ok {
		return true
	}
	if _, ok := annotations[clusterIssuerNameAnnotation]; ok {
		return true
	}
	for _, x := range autoCertificateAnnotations {
		if s, ok := annotations[x]; ok {
			if b, _ := strconv.ParseBool(s); b {
				return true
			}
		}
	}
	if _, ok := annotations[acmeIssuerChallengeTypeAnnotation]; ok {
		return true
	}
	if _, ok := annotations[acmeIssuerDNS01ProviderNameAnnotation]; ok {
		return true
	}
	return false
}

// issuerForIngress will determine the issuer that should be specified on a
// Certificate created for the given Ingress resource. If one is not set, the
// default issuer given to the controller will be used.
func (c *Controller) issuerForIngress(ing *extv1beta1.Ingress) (name string, kind string) {
	name = c.defaults.issuerName
	kind = c.defaults.issuerKind
	annotations := ing.Annotations
	if annotations == nil {
		annotations = map[string]string{}
	}
	if issuerName, ok := annotations[issuerNameAnnotation]; ok {
		name = issuerName
		kind = v1alpha1.IssuerKind
	}
	if issuerName, ok := annotations[clusterIssuerNameAnnotation]; ok {
		name = issuerName
		kind = v1alpha1.ClusterIssuerKind
	}
	return name, kind
}
<|endoftext|>


<commit_before>//
// Copyright 2021 The AFF Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package processor

import (
	"github.com/secure-systems-lab/go-securesystemslib/dsse"
)

type Processor interface{}

// ProcessorInput describes the input for a processor to run. This input can
// come from a collector or from the processor itself (run recursively).
type ProcessorInput struct {
	Blob              []byte
	Type              DocumentType
	Format            FormatType
	TrustInformation  TrustInformation
	SourceInformation SourceInformation
}

// DocumentType describes the type of the document contents for schema checks
type DocumentType string

// Document* is the enumerables of DocumentType
const (
	DocumentSLSA DocumentType = "SLSA"
	DocumentITE6              = "ITE6"
	DocumentDSSE              = "DSSE"
)

// FormatType describes the document format for malform checks
type FormatType string

// Format* is the enumerables of FormatType
const (
	FormatJSON FormatType = "JSON"
)

// TrustInformation provides additional information about how to verify the document
type TrustInformation struct {
	DSSE      *dsse.Envelope
	IssuerUri *string
	// TODO: Figure out how to handle log verification trust
	// LogVerification *rtype.LogEntryAnonVerification
}

// TrustInformation provides additional information about where the document comes from
type SourceInformation struct {
	// Collector describes the name of the collector providing this information
	Collector string
	// Source describes the source which the collector got this information
	Source string
}
<commit_msg>add DocumentProcessor interface<commit_after>//
// Copyright 2021 The AFF Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package processor

import (
	"fmt"

	"github.com/secure-systems-lab/go-securesystemslib/dsse"
)

type Processor interface {
	ProcessDocument(i ProcessorInput) ([]ProcessorOutput, error)
}

type DocumentProcessor interface {
	ValidateSchema(i *ProcessorInput) error
	ValidateTrustInformation(i *ProcessorInput) error

	// Unpack takes in the document and tries to unpack it
	// if there is a valid decomposition of sub-documents.
	//
	// For example, a DSSE envelope or a tarball
	// Returns empty list and nil error if nothing to unpack
	// Returns unpacked list and nil error if successfully unpacked
	Unpack(i *ProcessorInput) ([]ProcessorInput, error)
}

func (i *ProcessorInput) validateFormat() error {
	return fmt.Errorf("Unimplemented")
}

// ProcessorInput describes the input for a processor to run. This input can
// come from a collector or from the processor itself (run recursively).
type ProcessorInput struct {
	Blob              []byte
	Type              DocumentType
	Format            FormatType
	TrustInformation  TrustInformation
	SourceInformation SourceInformation
}

type ProcessorOutput ProcessorInput

// DocumentType describes the type of the document contents for schema checks
type DocumentType string

// Document* is the enumerables of DocumentType
const (
	DocumentSLSA DocumentType = "SLSA"
	DocumentITE6              = "ITE6"
	DocumentDSSE              = "DSSE"
)

// FormatType describes the document format for malform checks
type FormatType string

// Format* is the enumerables of FormatType
const (
	FormatJSON FormatType = "JSON"
)

// TrustInformation provides additional information about how to verify the document
type TrustInformation struct {
	DSSE      *dsse.Envelope
	IssuerUri *string
	// TODO: Figure out how to handle log verification trust
	// LogVerification *rtype.LogEntryAnonVerification
}

// TrustInformation provides additional information about where the document comes from
type SourceInformation struct {
	// Collector describes the name of the collector providing this information
	Collector string
	// Source describes the source which the collector got this information
	Source string
}
<|endoftext|>


<commit_before>package accesslog

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"sync"
	"sync/atomic"
	"time"

	"github.com/containous/alice"
	"github.com/containous/traefik/v2/pkg/log"
	"github.com/containous/traefik/v2/pkg/types"
	"github.com/sirupsen/logrus"
)

type key string

const (
	// DataTableKey is the key within the request context used to store the Log Data Table.
	DataTableKey key = "LogDataTable"

	// CommonFormat is the common logging format (CLF).
	CommonFormat string = "common"

	// JSONFormat is the JSON logging format.
	JSONFormat string = "json"
)

type handlerParams struct {
	logDataTable *LogData
	crr          *captureRequestReader
	crw          *captureResponseWriter
}

// Handler will write each request and its response to the access log.
type Handler struct {
	config         *types.AccessLog
	logger         *logrus.Logger
	file           *os.File
	mu             sync.Mutex
	httpCodeRanges types.HTTPCodeRanges
	logHandlerChan chan handlerParams
	wg             sync.WaitGroup
}

// WrapHandler Wraps access log handler into an Alice Constructor.
func WrapHandler(handler *Handler) alice.Constructor {
	return func(next http.Handler) (http.Handler, error) {
		return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
			handler.ServeHTTP(rw, req, next)
		}), nil
	}
}

// NewHandler creates a new Handler.
func NewHandler(config *types.AccessLog) (*Handler, error) {
	file := os.Stdout
	if len(config.FilePath) > 0 {
		f, err := openAccessLogFile(config.FilePath)
		if err != nil {
			return nil, fmt.Errorf("error opening access log file: %s", err)
		}
		file = f
	}
	logHandlerChan := make(chan handlerParams, config.BufferingSize)

	var formatter logrus.Formatter

	switch config.Format {
	case CommonFormat:
		formatter = new(CommonLogFormatter)
	case JSONFormat:
		formatter = new(logrus.JSONFormatter)
	default:
		log.WithoutContext().Errorf("unsupported access log format: %q, defaulting to common format instead.", config.Format)
		formatter = new(CommonLogFormatter)
	}

	logger := &logrus.Logger{
		Out:       file,
		Formatter: formatter,
		Hooks:     make(logrus.LevelHooks),
		Level:     logrus.InfoLevel,
	}

	logHandler := &Handler{
		config:         config,
		logger:         logger,
		file:           file,
		logHandlerChan: logHandlerChan,
	}

	if config.Filters != nil {
		if httpCodeRanges, err := types.NewHTTPCodeRanges(config.Filters.StatusCodes); err != nil {
			log.WithoutContext().Errorf("Failed to create new HTTP code ranges: %s", err)
		} else {
			logHandler.httpCodeRanges = httpCodeRanges
		}
	}

	if config.BufferingSize > 0 {
		logHandler.wg.Add(1)
		go func() {
			defer logHandler.wg.Done()
			for handlerParams := range logHandler.logHandlerChan {
				logHandler.logTheRoundTrip(handlerParams.logDataTable, handlerParams.crr, handlerParams.crw)
			}
		}()
	}

	return logHandler, nil
}

func openAccessLogFile(filePath string) (*os.File, error) {
	dir := filepath.Dir(filePath)

	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, fmt.Errorf("failed to create log path %s: %s", dir, err)
	}

	file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664)
	if err != nil {
		return nil, fmt.Errorf("error opening file %s: %s", filePath, err)
	}

	return file, nil
}

// GetLogData gets the request context object that contains logging data.
// This creates data as the request passes through the middleware chain.
func GetLogData(req *http.Request) *LogData {
	if ld, ok := req.Context().Value(DataTableKey).(*LogData); ok {
		return ld
	}
	return nil
}

func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.Handler) {
	now := time.Now().UTC()

	core := CoreLogData{
		StartUTC:   now,
		StartLocal: now.Local(),
	}

	logDataTable := &LogData{Core: core, Request: req.Header}

	reqWithDataTable := req.WithContext(context.WithValue(req.Context(), DataTableKey, logDataTable))

	var crr *captureRequestReader
	if req.Body != nil {
		crr = &captureRequestReader{source: req.Body, count: 0}
		reqWithDataTable.Body = crr
	}

	core[RequestCount] = nextRequestCount()
	if req.Host != "" {
		core[RequestAddr] = req.Host
		core[RequestHost], core[RequestPort] = silentSplitHostPort(req.Host)
	}
	// copy the URL without the scheme, hostname etc
	urlCopy := &url.URL{
		Path:       req.URL.Path,
		RawPath:    req.URL.RawPath,
		RawQuery:   req.URL.RawQuery,
		ForceQuery: req.URL.ForceQuery,
		Fragment:   req.URL.Fragment,
	}
	urlCopyString := urlCopy.String()
	core[RequestMethod] = req.Method
	core[RequestPath] = urlCopyString
	core[RequestProtocol] = req.Proto

	core[ClientAddr] = req.RemoteAddr
	core[ClientHost], core[ClientPort] = silentSplitHostPort(req.RemoteAddr)

	if forwardedFor := req.Header.Get("X-Forwarded-For"); forwardedFor != "" {
		core[ClientHost] = forwardedFor
	}

	crw := &captureResponseWriter{rw: rw}

	next.ServeHTTP(crw, reqWithDataTable)

	if _, ok := core[ClientUsername]; !ok {
		core[ClientUsername] = usernameIfPresent(reqWithDataTable.URL)
	}

	logDataTable.DownstreamResponse = crw.Header()

	if h.config.BufferingSize > 0 {
		h.logHandlerChan <- handlerParams{
			logDataTable: logDataTable,
			crr:          crr,
			crw:          crw,
		}
	} else {
		h.logTheRoundTrip(logDataTable, crr, crw)
	}
}

// Close closes the Logger (i.e. the file, drain logHandlerChan, etc).
func (h *Handler) Close() error {
	close(h.logHandlerChan)
	h.wg.Wait()
	return h.file.Close()
}

// Rotate closes and reopens the log file to allow for rotation by an external source.
func (h *Handler) Rotate() error {
	var err error

	if h.file != nil {
		defer func(f *os.File) {
			f.Close()
		}(h.file)
	}

	h.file, err = os.OpenFile(h.config.FilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664)
	if err != nil {
		return err
	}
	h.mu.Lock()
	defer h.mu.Unlock()
	h.logger.Out = h.file
	return nil
}

func silentSplitHostPort(value string) (host string, port string) {
	host, port, err := net.SplitHostPort(value)
	if err != nil {
		return value, "-"
	}
	return host, port
}

func usernameIfPresent(theURL *url.URL) string {
	if theURL.User != nil {
		if name := theURL.User.Username(); name != "" {
			return name
		}
	}
	return "-"
}

// Logging handler to log frontend name, backend name, and elapsed time.
func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestReader, crw *captureResponseWriter) {
	core := logDataTable.Core

	retryAttempts, ok := core[RetryAttempts].(int)
	if !ok {
		retryAttempts = 0
	}
	core[RetryAttempts] = retryAttempts

	if crr != nil {
		core[RequestContentSize] = crr.count
	}

	core[DownstreamStatus] = crw.Status()

	// n.b. take care to perform time arithmetic using UTC to avoid errors at DST boundaries.
	totalDuration := time.Now().UTC().Sub(core[StartUTC].(time.Time))
	core[Duration] = totalDuration

	if h.keepAccessLog(crw.Status(), retryAttempts, totalDuration) {
		core[DownstreamContentSize] = crw.Size()
		if original, ok := core[OriginContentSize]; ok {
			o64 := original.(int64)
			if crw.Size() != o64 && crw.Size() != 0 {
				core[GzipRatio] = float64(o64) / float64(crw.Size())
			}
		}

		core[Overhead] = totalDuration
		if origin, ok := core[OriginDuration]; ok {
			core[Overhead] = totalDuration - origin.(time.Duration)
		}

		fields := logrus.Fields{}

		for k, v := range logDataTable.Core {
			if h.config.Fields.Keep(k) {
				fields[k] = v
			}
		}

		h.redactHeaders(logDataTable.Request, fields, "request_")
		h.redactHeaders(logDataTable.OriginResponse, fields, "origin_")
		h.redactHeaders(logDataTable.DownstreamResponse, fields, "downstream_")

		h.mu.Lock()
		defer h.mu.Unlock()
		h.logger.WithFields(fields).Println()
	}
}

func (h *Handler) redactHeaders(headers http.Header, fields logrus.Fields, prefix string) {
	for k := range headers {
		v := h.config.Fields.KeepHeader(k)
		if v == types.AccessLogKeep {
			fields[prefix+k] = headers.Get(k)
		} else if v == types.AccessLogRedact {
			fields[prefix+k] = "REDACTED"
		}
	}
}

func (h *Handler) keepAccessLog(statusCode, retryAttempts int, duration time.Duration) bool {
	if h.config.Filters == nil {
		// no filters were specified
		return true
	}

	if len(h.httpCodeRanges) == 0 && !h.config.Filters.RetryAttempts && h.config.Filters.MinDuration == 0 {
		// empty filters were specified, e.g. by passing --accessLog.filters only (without other filter options)
		return true
	}

	if h.httpCodeRanges.Contains(statusCode) {
		return true
	}

	if h.config.Filters.RetryAttempts && retryAttempts > 0 {
		return true
	}

	if h.config.Filters.MinDuration > 0 && (types.Duration(duration) > h.config.Filters.MinDuration) {
		return true
	}

	return false
}

var requestCounter uint64 // Request ID

func nextRequestCount() uint64 {
	return atomic.AddUint64(&requestCounter, 1)
}
<commit_msg>Avoid closing stdout when the accesslog handler is closed<commit_after>package accesslog

import (
	"context"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"sync"
	"sync/atomic"
	"time"

	"github.com/containous/alice"
	"github.com/containous/traefik/v2/pkg/log"
	"github.com/containous/traefik/v2/pkg/types"
	"github.com/sirupsen/logrus"
)

type key string

const (
	// DataTableKey is the key within the request context used to store the Log Data Table.
	DataTableKey key = "LogDataTable"

	// CommonFormat is the common logging format (CLF).
	CommonFormat string = "common"

	// JSONFormat is the JSON logging format.
	JSONFormat string = "json"
)

type noopCloser struct {
	*os.File
}

func (n noopCloser) Write(p []byte) (int, error) {
	return n.File.Write(p)
}

func (n noopCloser) Close() error {
	// noop
	return nil
}

type handlerParams struct {
	logDataTable *LogData
	crr          *captureRequestReader
	crw          *captureResponseWriter
}

// Handler will write each request and its response to the access log.
type Handler struct {
	config         *types.AccessLog
	logger         *logrus.Logger
	file           io.WriteCloser
	mu             sync.Mutex
	httpCodeRanges types.HTTPCodeRanges
	logHandlerChan chan handlerParams
	wg             sync.WaitGroup
}

// WrapHandler Wraps access log handler into an Alice Constructor.
func WrapHandler(handler *Handler) alice.Constructor {
	return func(next http.Handler) (http.Handler, error) {
		return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
			handler.ServeHTTP(rw, req, next)
		}), nil
	}
}

// NewHandler creates a new Handler.
func NewHandler(config *types.AccessLog) (*Handler, error) {
	var file io.WriteCloser = noopCloser{os.Stdout}
	if len(config.FilePath) > 0 {
		f, err := openAccessLogFile(config.FilePath)
		if err != nil {
			return nil, fmt.Errorf("error opening access log file: %s", err)
		}
		file = f
	}
	logHandlerChan := make(chan handlerParams, config.BufferingSize)

	var formatter logrus.Formatter

	switch config.Format {
	case CommonFormat:
		formatter = new(CommonLogFormatter)
	case JSONFormat:
		formatter = new(logrus.JSONFormatter)
	default:
		log.WithoutContext().Errorf("unsupported access log format: %q, defaulting to common format instead.", config.Format)
		formatter = new(CommonLogFormatter)
	}

	logger := &logrus.Logger{
		Out:       file,
		Formatter: formatter,
		Hooks:     make(logrus.LevelHooks),
		Level:     logrus.InfoLevel,
	}

	logHandler := &Handler{
		config:         config,
		logger:         logger,
		file:           file,
		logHandlerChan: logHandlerChan,
	}

	if config.Filters != nil {
		if httpCodeRanges, err := types.NewHTTPCodeRanges(config.Filters.StatusCodes); err != nil {
			log.WithoutContext().Errorf("Failed to create new HTTP code ranges: %s", err)
		} else {
			logHandler.httpCodeRanges = httpCodeRanges
		}
	}

	if config.BufferingSize > 0 {
		logHandler.wg.Add(1)
		go func() {
			defer logHandler.wg.Done()
			for handlerParams := range logHandler.logHandlerChan {
				logHandler.logTheRoundTrip(handlerParams.logDataTable, handlerParams.crr, handlerParams.crw)
			}
		}()
	}

	return logHandler, nil
}

func openAccessLogFile(filePath string) (*os.File, error) {
	dir := filepath.Dir(filePath)

	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, fmt.Errorf("failed to create log path %s: %s", dir, err)
	}

	file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664)
	if err != nil {
		return nil, fmt.Errorf("error opening file %s: %s", filePath, err)
	}

	return file, nil
}

// GetLogData gets the request context object that contains logging data.
// This creates data as the request passes through the middleware chain.
func GetLogData(req *http.Request) *LogData {
	if ld, ok := req.Context().Value(DataTableKey).(*LogData); ok {
		return ld
	}
	return nil
}

func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.Handler) {
	now := time.Now().UTC()

	core := CoreLogData{
		StartUTC:   now,
		StartLocal: now.Local(),
	}

	logDataTable := &LogData{Core: core, Request: req.Header}

	reqWithDataTable := req.WithContext(context.WithValue(req.Context(), DataTableKey, logDataTable))

	var crr *captureRequestReader
	if req.Body != nil {
		crr = &captureRequestReader{source: req.Body, count: 0}
		reqWithDataTable.Body = crr
	}

	core[RequestCount] = nextRequestCount()
	if req.Host != "" {
		core[RequestAddr] = req.Host
		core[RequestHost], core[RequestPort] = silentSplitHostPort(req.Host)
	}
	// copy the URL without the scheme, hostname etc
	urlCopy := &url.URL{
		Path:       req.URL.Path,
		RawPath:    req.URL.RawPath,
		RawQuery:   req.URL.RawQuery,
		ForceQuery: req.URL.ForceQuery,
		Fragment:   req.URL.Fragment,
	}
	urlCopyString := urlCopy.String()
	core[RequestMethod] = req.Method
	core[RequestPath] = urlCopyString
	core[RequestProtocol] = req.Proto

	core[ClientAddr] = req.RemoteAddr
	core[ClientHost], core[ClientPort] = silentSplitHostPort(req.RemoteAddr)

	if forwardedFor := req.Header.Get("X-Forwarded-For"); forwardedFor != "" {
		core[ClientHost] = forwardedFor
	}

	crw := &captureResponseWriter{rw: rw}

	next.ServeHTTP(crw, reqWithDataTable)

	if _, ok := core[ClientUsername]; !ok {
		core[ClientUsername] = usernameIfPresent(reqWithDataTable.URL)
	}

	logDataTable.DownstreamResponse = crw.Header()

	if h.config.BufferingSize > 0 {
		h.logHandlerChan <- handlerParams{
			logDataTable: logDataTable,
			crr:          crr,
			crw:          crw,
		}
	} else {
		h.logTheRoundTrip(logDataTable, crr, crw)
	}
}

// Close closes the Logger (i.e. the file, drain logHandlerChan, etc).
func (h *Handler) Close() error {
	close(h.logHandlerChan)
	h.wg.Wait()
	return h.file.Close()
}

// Rotate closes and reopens the log file to allow for rotation by an external source.
func (h *Handler) Rotate() error {
	if h.config.FilePath == "" {
		return nil
	}

	if h.file != nil {
		defer func(f io.Closer) { _ = f.Close() }(h.file)
	}

	var err error
	h.file, err = os.OpenFile(h.config.FilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664)
	if err != nil {
		return err
	}
	h.mu.Lock()
	defer h.mu.Unlock()
	h.logger.Out = h.file
	return nil
}

func silentSplitHostPort(value string) (host string, port string) {
	host, port, err := net.SplitHostPort(value)
	if err != nil {
		return value, "-"
	}
	return host, port
}

func usernameIfPresent(theURL *url.URL) string {
	if theURL.User != nil {
		if name := theURL.User.Username(); name != "" {
			return name
		}
	}
	return "-"
}

// Logging handler to log frontend name, backend name, and elapsed time.
func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestReader, crw *captureResponseWriter) {
	core := logDataTable.Core

	retryAttempts, ok := core[RetryAttempts].(int)
	if !ok {
		retryAttempts = 0
	}
	core[RetryAttempts] = retryAttempts

	if crr != nil {
		core[RequestContentSize] = crr.count
	}

	core[DownstreamStatus] = crw.Status()

	// n.b. take care to perform time arithmetic using UTC to avoid errors at DST boundaries.
	totalDuration := time.Now().UTC().Sub(core[StartUTC].(time.Time))
	core[Duration] = totalDuration

	if h.keepAccessLog(crw.Status(), retryAttempts, totalDuration) {
		core[DownstreamContentSize] = crw.Size()
		if original, ok := core[OriginContentSize]; ok {
			o64 := original.(int64)
			if crw.Size() != o64 && crw.Size() != 0 {
				core[GzipRatio] = float64(o64) / float64(crw.Size())
			}
		}

		core[Overhead] = totalDuration
		if origin, ok := core[OriginDuration]; ok {
			core[Overhead] = totalDuration - origin.(time.Duration)
		}

		fields := logrus.Fields{}

		for k, v := range logDataTable.Core {
			if h.config.Fields.Keep(k) {
				fields[k] = v
			}
		}

		h.redactHeaders(logDataTable.Request, fields, "request_")
		h.redactHeaders(logDataTable.OriginResponse, fields, "origin_")
		h.redactHeaders(logDataTable.DownstreamResponse, fields, "downstream_")

		h.mu.Lock()
		defer h.mu.Unlock()
		h.logger.WithFields(fields).Println()
	}
}

func (h *Handler) redactHeaders(headers http.Header, fields logrus.Fields, prefix string) {
	for k := range headers {
		v := h.config.Fields.KeepHeader(k)
		if v == types.AccessLogKeep {
			fields[prefix+k] = headers.Get(k)
		} else if v == types.AccessLogRedact {
			fields[prefix+k] = "REDACTED"
		}
	}
}

func (h *Handler) keepAccessLog(statusCode, retryAttempts int, duration time.Duration) bool {
	if h.config.Filters == nil {
		// no filters were specified
		return true
	}

	if len(h.httpCodeRanges) == 0 && !h.config.Filters.RetryAttempts && h.config.Filters.MinDuration == 0 {
		// empty filters were specified, e.g. by passing --accessLog.filters only (without other filter options)
		return true
	}

	if h.httpCodeRanges.Contains(statusCode) {
		return true
	}

	if h.config.Filters.RetryAttempts && retryAttempts > 0 {
		return true
	}

	if h.config.Filters.MinDuration > 0 && (types.Duration(duration) > h.config.Filters.MinDuration) {
		return true
	}

	return false
}

var requestCounter uint64 // Request ID

func nextRequestCount() uint64 {
	return atomic.AddUint64(&requestCounter, 1)
}
<|endoftext|>


<commit_before>package eureka

import (
	"log"
	"net/url"
	"github.com/gliderlabs/registrator/bridge"
	eureka "github.com/hudl/fargo"
	"strconv"
)
const DefaultInterval = "10s"

func init() {
	bridge.Register(new(Factory), "eureka")
}

type Factory struct{}

func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter {
	client := eureka.EurekaConnection{}
	if uri.Host != "" {
		client = eureka.NewConn("http://"+uri.Host+uri.Path)
	}else {
		client = eureka.NewConn("http://eureka:8761")
	}

	return &EurekaAdapter{client: client}
}

type EurekaAdapter struct {
	client eureka.EurekaConnection
}

// Ping will try to connect to consul by attempting to retrieve the current leader.
func (r *EurekaAdapter) Ping() error {

	eurekaApps, err := r.client.GetApps()
	if err != nil {
		return err
	}
	log.Println("eureka: current apps ", len(eurekaApps))

	return nil
}

func instanceInformation(service *bridge.Service) *eureka.Instance {

	registration := new(eureka.Instance)

	registration.HostName   = service.IP + ":" + service.Port
	registration.App        = service.Name
	registration.Port       = service.Port
	registration.VipAddress = ShortHandTernary(service.Attrs["eureka_vip"], service.Name)

	if(service.Attrs["eureka_status"] == string(eureka.DOWN)) {
		registration.Status = eureka.DOWN
	} else {
		registration.Status = eureka.UP
	}

	if(service.Attrs["eureka_leaseinfo_renewalintervalinsecs"] != "") {
		v, err := strconv.Atoi(service.Attrs["eureka_leaseinfo_renewalintervalinsecs"])
		if(err != nil) {
			log.Println("eureka: Renewal interval must be valid int", err)
		} else {
			registration.LeaseInfo.RenewalIntervalInSecs = int32(v)
		}
	} else {
		registration.LeaseInfo.RenewalIntervalInSecs = 30
	}

	if(service.Attrs["eureka_leaseinfo_durationinsecs"] != "") {
		v, err := strconv.Atoi(service.Attrs["eureka_leaseinfo_durationinsecs"])
		if(err != nil) {
			log.Println("eureka: Lease duration must be valid int", err)
		} else {
			registration.LeaseInfo.DurationInSecs = int32(v)
		}
	} else {
		registration.LeaseInfo.DurationInSecs = 90
	}

	if service.Attrs["eureka_datacenterinfo_name"] != eureka.MyOwn {
		registration.DataCenterInfo.Name = eureka.Amazon
		registration.DataCenterInfo.Metadata = eureka.AmazonMetadataType {
			PublicHostname: ShortHandTernary(service.Attrs["eureka_datacenterinfo_publichostname"], service.Origin.HostIP),
			PublicIpv4:     ShortHandTernary(service.Attrs["eureka_datacenterinfo_publicipv4"], service.Origin.HostIP),
			LocalHostname:  ShortHandTernary(service.Attrs["eureka_datacenterinfo_localipv4"], service.IP),
			LocalIpv4:      ShortHandTernary(service.Attrs["eureka_datacenterinfo_localhostname"], service.IP),
		}
	} else {
		registration.DataCenterInfo.Name = eureka.MyOwn
	}

	return registration
}

func (r *EurekaAdapter) Register(service *bridge.Service) error {
	registration := instanceInformation(service)
	return r.client.RegisterInstance(registration)
}

func (r *EurekaAdapter) Deregister(service *bridge.Service) error {
	registration := new(eureka.Instance)
	registration.HostName = service.ID
	return r.client.DeregisterInstance(registration)
}

func (r *EurekaAdapter) Refresh(service *bridge.Service) error {
	registration := instanceInformation(service)
	return r.client.HeartBeatInstance(registration)
}

func (r *EurekaAdapter) Services() ([]*bridge.Service, error) {
	return []*bridge.Service{}, nil
}

func ShortHandTernary(string1 string, string2 string) string {
	if(string1 != "") {
		return string1
	} else {
		return string2
	}
}
<commit_msg>converting int to str<commit_after>package eureka

import (
	"log"
	"net/url"
	"github.com/gliderlabs/registrator/bridge"
	eureka "github.com/hudl/fargo"
	"strconv"
)
const DefaultInterval = "10s"

func init() {
	bridge.Register(new(Factory), "eureka")
}

type Factory struct{}

func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter {
	client := eureka.EurekaConnection{}
	if uri.Host != "" {
		client = eureka.NewConn("http://"+uri.Host+uri.Path)
	}else {
		client = eureka.NewConn("http://eureka:8761")
	}

	return &EurekaAdapter{client: client}
}

type EurekaAdapter struct {
	client eureka.EurekaConnection
}

// Ping will try to connect to consul by attempting to retrieve the current leader.
func (r *EurekaAdapter) Ping() error {

	eurekaApps, err := r.client.GetApps()
	if err != nil {
		return err
	}
	log.Println("eureka: current apps ", len(eurekaApps))

	return nil
}

func instanceInformation(service *bridge.Service) *eureka.Instance {

	registration := new(eureka.Instance)

	registration.HostName   = service.IP + ":" + strconv.Itoa(service.Port)
	registration.App        = service.Name
	registration.Port       = service.Port
	registration.VipAddress = ShortHandTernary(service.Attrs["eureka_vip"], service.Name)

	if(service.Attrs["eureka_status"] == string(eureka.DOWN)) {
		registration.Status = eureka.DOWN
	} else {
		registration.Status = eureka.UP
	}

	if(service.Attrs["eureka_leaseinfo_renewalintervalinsecs"] != "") {
		v, err := strconv.Atoi(service.Attrs["eureka_leaseinfo_renewalintervalinsecs"])
		if(err != nil) {
			log.Println("eureka: Renewal interval must be valid int", err)
		} else {
			registration.LeaseInfo.RenewalIntervalInSecs = int32(v)
		}
	} else {
		registration.LeaseInfo.RenewalIntervalInSecs = 30
	}

	if(service.Attrs["eureka_leaseinfo_durationinsecs"] != "") {
		v, err := strconv.Atoi(service.Attrs["eureka_leaseinfo_durationinsecs"])
		if(err != nil) {
			log.Println("eureka: Lease duration must be valid int", err)
		} else {
			registration.LeaseInfo.DurationInSecs = int32(v)
		}
	} else {
		registration.LeaseInfo.DurationInSecs = 90
	}

	if service.Attrs["eureka_datacenterinfo_name"] != eureka.MyOwn {
		registration.DataCenterInfo.Name = eureka.Amazon
		registration.DataCenterInfo.Metadata = eureka.AmazonMetadataType {
			PublicHostname: ShortHandTernary(service.Attrs["eureka_datacenterinfo_publichostname"], service.Origin.HostIP),
			PublicIpv4:     ShortHandTernary(service.Attrs["eureka_datacenterinfo_publicipv4"], service.Origin.HostIP),
			LocalHostname:  ShortHandTernary(service.Attrs["eureka_datacenterinfo_localipv4"], service.IP),
			LocalIpv4:      ShortHandTernary(service.Attrs["eureka_datacenterinfo_localhostname"], service.IP),
		}
	} else {
		registration.DataCenterInfo.Name = eureka.MyOwn
	}

	return registration
}

func (r *EurekaAdapter) Register(service *bridge.Service) error {
	registration := instanceInformation(service)
	return r.client.RegisterInstance(registration)
}

func (r *EurekaAdapter) Deregister(service *bridge.Service) error {
	registration := new(eureka.Instance)
	registration.HostName = service.ID
	return r.client.DeregisterInstance(registration)
}

func (r *EurekaAdapter) Refresh(service *bridge.Service) error {
	registration := instanceInformation(service)
	return r.client.HeartBeatInstance(registration)
}

func (r *EurekaAdapter) Services() ([]*bridge.Service, error) {
	return []*bridge.Service{}, nil
}

func ShortHandTernary(string1 string, string2 string) string {
	if(string1 != "") {
		return string1
	} else {
		return string2
	}
}
<|endoftext|>