<commit_before>package signals

import (
	"math/rand"
	"encoding/gob"
)

func init() {
	gob.Register(Noise{})
}

// Noise is a deterministic random level, white.
// deterministic becasue has the same value at the same time for the same Noise, but random otherwise.
type Noise struct {
	generator rand.Rand
}

func NewNoise() Noise {
	return Noise{*rand.New(rand.NewSource(rand.Int63()))}  // give each noise, very probably, a different generator source
}

func (s Noise) Level(t interval) (l level) {
	rand.Seed(int64(t))  // default generator set to the same seed for the same time
	s.generator.Seed(int64(rand.Int63()))   // Noise sets its generator's seed to a random number from default generator, which is the same at a given t, so the same random numbers generated from it, for the same t, but different for different Noises.
	l+=level(s.generator.Int63())
	l-=level(s.generator.Int63())
	return
}

<commit_msg>comment update<commit_after>package signals

import (
	"math/rand"
	"encoding/gob"
)

func init() {
	gob.Register(Noise{})
}

// Noise is a deterministic random level Signal, white noise.
// it has the same value at the same time, but random otherwise.
type Noise struct {
	generator rand.Rand
}

func NewNoise() Noise {
	return Noise{*rand.New(rand.NewSource(rand.Int63()))}  // give each noise, very probably, a different generator source
}

func (s Noise) Level(t interval) (l level) {
	rand.Seed(int64(t))  // default generator set to the same seed for the same time
	s.generator.Seed(int64(rand.Int63()))   // Noise sets its generator's seed to a random number from default generator, which is the same at a given t, so the same random numbers generated from it, for the same t, but different for different Noises.
	l+=level(s.generator.Int63())
	l-=level(s.generator.Int63())
	return
}

<|endoftext|>


<commit_before>// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package expand

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"

	"mvdan.cc/sh/v3/syntax"
)

func nodeLit(node syntax.Node) string {
	if word, ok := node.(*syntax.Word); ok {
		return word.Lit()
	}
	return ""
}

type UnsetParameterError struct {
	Node    *syntax.ParamExp
	Message string
}

func (u UnsetParameterError) Error() string {
	return u.Message
}

func (cfg *Config) paramExp(pe *syntax.ParamExp) (string, error) {
	oldParam := cfg.curParam
	cfg.curParam = pe
	defer func() { cfg.curParam = oldParam }()

	name := pe.Param.Value
	index := pe.Index
	switch name {
	case "@", "*":
		index = &syntax.Word{Parts: []syntax.WordPart{
			&syntax.Lit{Value: name},
		}}
	}
	var vr Variable
	switch name {
	case "LINENO":
		// This is the only parameter expansion that the environment
		// interface cannot satisfy.
		line := uint64(cfg.curParam.Pos().Line())
		vr = Variable{Kind: String, Str: strconv.FormatUint(line, 10)}
	default:
		vr = cfg.Env.Get(name)
	}
	orig := vr
	_, vr = vr.Resolve(cfg.Env)
	str, err := cfg.varInd(vr, index)
	if err != nil {
		return "", err
	}
	slicePos := func(n int) int {
		if n < 0 {
			n = len(str) + n
			if n < 0 {
				n = len(str)
			}
		} else if n > len(str) {
			n = len(str)
		}
		return n
	}
	elems := []string{str}
	switch nodeLit(index) {
	case "@", "*":
		switch vr.Kind {
		case Unset:
			elems = nil
		case Indexed:
			elems = vr.List
		}
	}
	switch {
	case pe.Length:
		n := len(elems)
		switch nodeLit(index) {
		case "@", "*":
		default:
			n = utf8.RuneCountInString(str)
		}
		str = strconv.Itoa(n)
	case pe.Excl:
		var strs []string
		if pe.Names != 0 {
			strs = cfg.namesByPrefix(pe.Param.Value)
		} else if orig.Kind == NameRef {
			strs = append(strs, orig.Str)
		} else if vr.Kind == Indexed {
			for i, e := range vr.List {
				if e != "" {
					strs = append(strs, strconv.Itoa(i))
				}
			}
		} else if vr.Kind == Associative {
			for k := range vr.Map {
				strs = append(strs, k)
			}
		} else if !syntax.ValidName(str) {
			return "", fmt.Errorf("invalid indirect expansion")
		} else {
			vr = cfg.Env.Get(str)
			strs = append(strs, vr.String())
		}
		sort.Strings(strs)
		str = strings.Join(strs, " ")
	case pe.Slice != nil:
		if pe.Slice.Offset != nil {
			n, err := Arithm(cfg, pe.Slice.Offset)
			if err != nil {
				return "", err
			}
			str = str[slicePos(n):]
		}
		if pe.Slice.Length != nil {
			n, err := Arithm(cfg, pe.Slice.Length)
			if err != nil {
				return "", err
			}
			str = str[:slicePos(n)]
		}
	case pe.Repl != nil:
		orig, err := Pattern(cfg, pe.Repl.Orig)
		if err != nil {
			return "", err
		}
		with, err := Literal(cfg, pe.Repl.With)
		if err != nil {
			return "", err
		}
		n := 1
		if pe.Repl.All {
			n = -1
		}
		locs := findAllIndex(orig, str, n)
		buf := cfg.strBuilder()
		last := 0
		for _, loc := range locs {
			buf.WriteString(str[last:loc[0]])
			buf.WriteString(with)
			last = loc[1]
		}
		buf.WriteString(str[last:])
		str = buf.String()
	case pe.Exp != nil:
		arg, err := Literal(cfg, pe.Exp.Word)
		if err != nil {
			return "", err
		}
		switch op := pe.Exp.Op; op {
		case syntax.SubstColPlus:
			if str == "" {
				break
			}
			fallthrough
		case syntax.SubstPlus:
			if vr.IsSet() {
				str = arg
			}
		case syntax.SubstMinus:
			if vr.IsSet() {
				break
			}
			fallthrough
		case syntax.SubstColMinus:
			if str == "" {
				str = arg
			}
		case syntax.SubstQuest:
			if vr.IsSet() {
				break
			}
			fallthrough
		case syntax.SubstColQuest:
			if str == "" {
				return "", UnsetParameterError{
					Node:    pe,
					Message: arg,
				}
			}
		case syntax.SubstAssgn:
			if vr.IsSet() {
				break
			}
			fallthrough
		case syntax.SubstColAssgn:
			if str == "" {
				if err := cfg.envSet(name, arg); err != nil {
					return "", err
				}
				str = arg
			}
		case syntax.RemSmallPrefix, syntax.RemLargePrefix,
			syntax.RemSmallSuffix, syntax.RemLargeSuffix:
			suffix := op == syntax.RemSmallSuffix ||
				op == syntax.RemLargeSuffix
			large := op == syntax.RemLargePrefix ||
				op == syntax.RemLargeSuffix
			for i, elem := range elems {
				elems[i] = removePattern(elem, arg, suffix, large)
			}
			str = strings.Join(elems, " ")
		case syntax.UpperFirst, syntax.UpperAll,
			syntax.LowerFirst, syntax.LowerAll:

			caseFunc := unicode.ToLower
			if op == syntax.UpperFirst || op == syntax.UpperAll {
				caseFunc = unicode.ToUpper
			}
			all := op == syntax.UpperAll || op == syntax.LowerAll

			// empty string means '?'; nothing to do there
			expr, err := syntax.TranslatePattern(arg, false)
			if err != nil {
				return str, nil
			}
			rx := regexp.MustCompile(expr)

			for i, elem := range elems {
				rs := []rune(elem)
				for ri, r := range rs {
					if rx.MatchString(string(r)) {
						rs[ri] = caseFunc(r)
						if !all {
							break
						}
					}
				}
				elems[i] = string(rs)
			}
			str = strings.Join(elems, " ")
		case syntax.OtherParamOps:
			switch arg {
			case "Q":
				str = strconv.Quote(str)
			case "E":
				tail := str
				var rns []rune
				for tail != "" {
					var rn rune
					rn, _, tail, _ = strconv.UnquoteChar(tail, 0)
					rns = append(rns, rn)
				}
				str = string(rns)
			case "P", "A", "a":
				panic(fmt.Sprintf("unhandled @%s param expansion", arg))
			default:
				panic(fmt.Sprintf("unexpected @%s param expansion", arg))
			}
		}
	}
	return str, nil
}

func removePattern(str, pattern string, fromEnd, greedy bool) string {
	expr, err := syntax.TranslatePattern(pattern, greedy)
	if err != nil {
		return str
	}
	switch {
	case fromEnd && !greedy:
		// use .* to get the right-most (shortest) match
		expr = ".*(" + expr + ")$"
	case fromEnd:
		// simple suffix
		expr = "(" + expr + ")$"
	default:
		// simple prefix
		expr = "^(" + expr + ")"
	}
	// no need to check error as TranslatePattern returns one
	rx := regexp.MustCompile(expr)
	if loc := rx.FindStringSubmatchIndex(str); loc != nil {
		// remove the original pattern (the submatch)
		str = str[:loc[2]] + str[loc[3]:]
	}
	return str
}

func (cfg *Config) varInd(vr Variable, idx syntax.ArithmExpr) (string, error) {
	if idx == nil {
		return vr.String(), nil
	}
	switch vr.Kind {
	case String:
		n, err := Arithm(cfg, idx)
		if err != nil {
			return "", err
		}
		if n == 0 {
			return vr.Str, nil
		}
	case Indexed:
		switch nodeLit(idx) {
		case "@":
			return strings.Join(vr.List, " "), nil
		case "*":
			return cfg.ifsJoin(vr.List), nil
		}
		i, err := Arithm(cfg, idx)
		if err != nil {
			return "", err
		}
		if len(vr.List) > 0 {
			return vr.List[i], nil
		}
	case Associative:
		switch lit := nodeLit(idx); lit {
		case "@", "*":
			strs := make([]string, 0, len(vr.Map))
			for _, val := range vr.Map {
				strs = append(strs, val)
			}
			sort.Strings(strs)
			if lit == "*" {
				return cfg.ifsJoin(strs), nil
			}
			return strings.Join(strs, " "), nil
		}
		val, err := Literal(cfg, idx.(*syntax.Word))
		if err != nil {
			return "", err
		}
		return vr.Map[val], nil
	}
	return "", nil
}

func (cfg *Config) namesByPrefix(prefix string) []string {
	var names []string
	cfg.Env.Each(func(name string, vr Variable) bool {
		if strings.HasPrefix(name, prefix) {
			names = append(names, name)
		}
		return true
	})
	return names
}
<commit_msg>expand: rewrite if-else-if-else chain as a switch<commit_after>// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package expand

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"

	"mvdan.cc/sh/v3/syntax"
)

func nodeLit(node syntax.Node) string {
	if word, ok := node.(*syntax.Word); ok {
		return word.Lit()
	}
	return ""
}

type UnsetParameterError struct {
	Node    *syntax.ParamExp
	Message string
}

func (u UnsetParameterError) Error() string {
	return u.Message
}

func (cfg *Config) paramExp(pe *syntax.ParamExp) (string, error) {
	oldParam := cfg.curParam
	cfg.curParam = pe
	defer func() { cfg.curParam = oldParam }()

	name := pe.Param.Value
	index := pe.Index
	switch name {
	case "@", "*":
		index = &syntax.Word{Parts: []syntax.WordPart{
			&syntax.Lit{Value: name},
		}}
	}
	var vr Variable
	switch name {
	case "LINENO":
		// This is the only parameter expansion that the environment
		// interface cannot satisfy.
		line := uint64(cfg.curParam.Pos().Line())
		vr = Variable{Kind: String, Str: strconv.FormatUint(line, 10)}
	default:
		vr = cfg.Env.Get(name)
	}
	orig := vr
	_, vr = vr.Resolve(cfg.Env)
	str, err := cfg.varInd(vr, index)
	if err != nil {
		return "", err
	}
	slicePos := func(n int) int {
		if n < 0 {
			n = len(str) + n
			if n < 0 {
				n = len(str)
			}
		} else if n > len(str) {
			n = len(str)
		}
		return n
	}
	elems := []string{str}
	switch nodeLit(index) {
	case "@", "*":
		switch vr.Kind {
		case Unset:
			elems = nil
		case Indexed:
			elems = vr.List
		}
	}
	switch {
	case pe.Length:
		n := len(elems)
		switch nodeLit(index) {
		case "@", "*":
		default:
			n = utf8.RuneCountInString(str)
		}
		str = strconv.Itoa(n)
	case pe.Excl:
		var strs []string
		switch {
		case pe.Names != 0:
			strs = cfg.namesByPrefix(pe.Param.Value)
		case orig.Kind == NameRef:
			strs = append(strs, orig.Str)
		case vr.Kind == Indexed:
			for i, e := range vr.List {
				if e != "" {
					strs = append(strs, strconv.Itoa(i))
				}
			}
		case vr.Kind == Associative:
			for k := range vr.Map {
				strs = append(strs, k)
			}
		case !syntax.ValidName(str):
			return "", fmt.Errorf("invalid indirect expansion")
		default:
			vr = cfg.Env.Get(str)
			strs = append(strs, vr.String())
		}
		sort.Strings(strs)
		str = strings.Join(strs, " ")
	case pe.Slice != nil:
		if pe.Slice.Offset != nil {
			n, err := Arithm(cfg, pe.Slice.Offset)
			if err != nil {
				return "", err
			}
			str = str[slicePos(n):]
		}
		if pe.Slice.Length != nil {
			n, err := Arithm(cfg, pe.Slice.Length)
			if err != nil {
				return "", err
			}
			str = str[:slicePos(n)]
		}
	case pe.Repl != nil:
		orig, err := Pattern(cfg, pe.Repl.Orig)
		if err != nil {
			return "", err
		}
		with, err := Literal(cfg, pe.Repl.With)
		if err != nil {
			return "", err
		}
		n := 1
		if pe.Repl.All {
			n = -1
		}
		locs := findAllIndex(orig, str, n)
		buf := cfg.strBuilder()
		last := 0
		for _, loc := range locs {
			buf.WriteString(str[last:loc[0]])
			buf.WriteString(with)
			last = loc[1]
		}
		buf.WriteString(str[last:])
		str = buf.String()
	case pe.Exp != nil:
		arg, err := Literal(cfg, pe.Exp.Word)
		if err != nil {
			return "", err
		}
		switch op := pe.Exp.Op; op {
		case syntax.SubstColPlus:
			if str == "" {
				break
			}
			fallthrough
		case syntax.SubstPlus:
			if vr.IsSet() {
				str = arg
			}
		case syntax.SubstMinus:
			if vr.IsSet() {
				break
			}
			fallthrough
		case syntax.SubstColMinus:
			if str == "" {
				str = arg
			}
		case syntax.SubstQuest:
			if vr.IsSet() {
				break
			}
			fallthrough
		case syntax.SubstColQuest:
			if str == "" {
				return "", UnsetParameterError{
					Node:    pe,
					Message: arg,
				}
			}
		case syntax.SubstAssgn:
			if vr.IsSet() {
				break
			}
			fallthrough
		case syntax.SubstColAssgn:
			if str == "" {
				if err := cfg.envSet(name, arg); err != nil {
					return "", err
				}
				str = arg
			}
		case syntax.RemSmallPrefix, syntax.RemLargePrefix,
			syntax.RemSmallSuffix, syntax.RemLargeSuffix:
			suffix := op == syntax.RemSmallSuffix ||
				op == syntax.RemLargeSuffix
			large := op == syntax.RemLargePrefix ||
				op == syntax.RemLargeSuffix
			for i, elem := range elems {
				elems[i] = removePattern(elem, arg, suffix, large)
			}
			str = strings.Join(elems, " ")
		case syntax.UpperFirst, syntax.UpperAll,
			syntax.LowerFirst, syntax.LowerAll:

			caseFunc := unicode.ToLower
			if op == syntax.UpperFirst || op == syntax.UpperAll {
				caseFunc = unicode.ToUpper
			}
			all := op == syntax.UpperAll || op == syntax.LowerAll

			// empty string means '?'; nothing to do there
			expr, err := syntax.TranslatePattern(arg, false)
			if err != nil {
				return str, nil
			}
			rx := regexp.MustCompile(expr)

			for i, elem := range elems {
				rs := []rune(elem)
				for ri, r := range rs {
					if rx.MatchString(string(r)) {
						rs[ri] = caseFunc(r)
						if !all {
							break
						}
					}
				}
				elems[i] = string(rs)
			}
			str = strings.Join(elems, " ")
		case syntax.OtherParamOps:
			switch arg {
			case "Q":
				str = strconv.Quote(str)
			case "E":
				tail := str
				var rns []rune
				for tail != "" {
					var rn rune
					rn, _, tail, _ = strconv.UnquoteChar(tail, 0)
					rns = append(rns, rn)
				}
				str = string(rns)
			case "P", "A", "a":
				panic(fmt.Sprintf("unhandled @%s param expansion", arg))
			default:
				panic(fmt.Sprintf("unexpected @%s param expansion", arg))
			}
		}
	}
	return str, nil
}

func removePattern(str, pattern string, fromEnd, greedy bool) string {
	expr, err := syntax.TranslatePattern(pattern, greedy)
	if err != nil {
		return str
	}
	switch {
	case fromEnd && !greedy:
		// use .* to get the right-most (shortest) match
		expr = ".*(" + expr + ")$"
	case fromEnd:
		// simple suffix
		expr = "(" + expr + ")$"
	default:
		// simple prefix
		expr = "^(" + expr + ")"
	}
	// no need to check error as TranslatePattern returns one
	rx := regexp.MustCompile(expr)
	if loc := rx.FindStringSubmatchIndex(str); loc != nil {
		// remove the original pattern (the submatch)
		str = str[:loc[2]] + str[loc[3]:]
	}
	return str
}

func (cfg *Config) varInd(vr Variable, idx syntax.ArithmExpr) (string, error) {
	if idx == nil {
		return vr.String(), nil
	}
	switch vr.Kind {
	case String:
		n, err := Arithm(cfg, idx)
		if err != nil {
			return "", err
		}
		if n == 0 {
			return vr.Str, nil
		}
	case Indexed:
		switch nodeLit(idx) {
		case "@":
			return strings.Join(vr.List, " "), nil
		case "*":
			return cfg.ifsJoin(vr.List), nil
		}
		i, err := Arithm(cfg, idx)
		if err != nil {
			return "", err
		}
		if len(vr.List) > 0 {
			return vr.List[i], nil
		}
	case Associative:
		switch lit := nodeLit(idx); lit {
		case "@", "*":
			strs := make([]string, 0, len(vr.Map))
			for _, val := range vr.Map {
				strs = append(strs, val)
			}
			sort.Strings(strs)
			if lit == "*" {
				return cfg.ifsJoin(strs), nil
			}
			return strings.Join(strs, " "), nil
		}
		val, err := Literal(cfg, idx.(*syntax.Word))
		if err != nil {
			return "", err
		}
		return vr.Map[val], nil
	}
	return "", nil
}

func (cfg *Config) namesByPrefix(prefix string) []string {
	var names []string
	cfg.Env.Each(func(name string, vr Variable) bool {
		if strings.HasPrefix(name, prefix) {
			names = append(names, name)
		}
		return true
	})
	return names
}
<|endoftext|>


<commit_before>/*
Package exportedservice provides exported named etcd ports.
This binds to an anonymous port, exports the host:port pair through etcd
and returns the port to the caller.

There are convenience methods for exporting a TLS port and an HTTP service.
*/
package exportedservice

import (
	"crypto/tls"
	"fmt"
	"net"

	etcd "github.com/coreos/etcd/clientv3"
	"golang.org/x/net/context"
)

// ServiceExporter exists because we need to initialize our etcd client
// beforehand and keep it somewhere.
type ServiceExporter struct {
	conn               *etcd.Client
	path               string
	leaseID            etcd.LeaseID
	keepaliveResponses <-chan *etcd.LeaseKeepAliveResponse
}

func consumeKeepaliveResponses(ch <-chan *etcd.LeaseKeepAliveResponse) {
	for _ = range ch {
	}
}

/*
NewExporter creates a new exporter object which can later be used to create
exported ports and services. This will create a client connection to etcd.
If the connection is severed, once the etcd lease is going to expire the
port will stop being exported.
The specified ttl (which must be at least 5 (seconds)) determines how frequently
the lease will be renewed.
*/
func NewExporter(ctx context.Context, etcdURL string, ttl int64) (
	*ServiceExporter, error) {
	var self *ServiceExporter
	var client *etcd.Client
	var err error

	client, err = etcd.NewFromURL(etcdURL)
	if err != nil {
		return nil, err
	}

	self = &ServiceExporter{
		conn: client,
	}

	return self, self.initLease(ctx, ttl)
}

/*
NewExporterFromConfigFile creates a new exporter by reading etcd flags from the
specified configuration file. This will create a client connection to etcd.
If the connection is severed, once the etcd lease is going to expire the
port will stop being exported.
The specified ttl (which must be at least 5 (seconds)) determines how frequently
the lease will be renewed.
*/
func NewExporterFromConfigFile(
	ctx context.Context, config string, ttl int64) (*ServiceExporter, error) {
	var self *ServiceExporter
	var client *etcd.Client
	var err error

	client, err = etcd.NewFromConfigFile(config)
	if err != nil {
		return nil, err
	}

	self = &ServiceExporter{
		conn: client,
	}

	return self, self.initLease(ctx, ttl)
}

/*
NewExporterFromClient creates a new exporter by reading etcd flags from the
specified configuration file.
*/
func NewExporterFromClient(
	ctx context.Context, client *etcd.Client, ttl int64) (
	*ServiceExporter, error) {
	var rv = &ServiceExporter{
		conn: client,
	}

	return rv, rv.initLease(ctx, ttl)
}

/*
initLease initializes the lease on the etcd service which will be used to export
ports in the future.
*/
func (e *ServiceExporter) initLease(ctx context.Context, ttl int64) error {
	var lease *etcd.LeaseGrantResponse
	var err error

	lease, err = e.conn.Grant(ctx, ttl)
	if err != nil {
		return err
	}

	e.keepaliveResponses, err = e.conn.KeepAlive(context.Background(), lease.ID)
	if err != nil {
		return err
	}

	e.leaseID = lease.ID

	go consumeKeepaliveResponses(e.keepaliveResponses)

	return nil
}

/*
NewExportedPort opens a new anonymous port on "ip" and export it through etcd
as "servicename". If "ip" is a host:port pair, the port will be overridden.
*/
func (e *ServiceExporter) NewExportedPort(
	ctx context.Context, network, ip, service string) (net.Listener, error) {
	var path string
	var host, hostport string
	var l net.Listener
	var err error

	if host, _, err = net.SplitHostPort(ip); err != nil {
		// Apparently, it's not in host:port format.
		host = ip
		hostport = net.JoinHostPort(host, "0")
	}

	if l, err = net.Listen(network, hostport); err != nil {
		return nil, err
	}

	// Use the lease ID as part of the path; it would be reasonable to expect
	// it to be unique.
	path = fmt.Sprintf("/ns/service/%s/%16x", service, e.leaseID)

	// Now write our host:port pair to etcd. Let etcd choose the file name.
	_, err = e.conn.Put(ctx, path, l.Addr().String(), etcd.WithLease(e.leaseID))
	if err != nil {
		return nil, err
	}

	e.path = path

	return l, nil
}

/*
NewExportedTLSPort opens a new anonymous port on "ip" and export it through
etcd as "servicename" (see NewExportedPort). Associates the TLS configuration
"config". If "ip" is a host:port pair, the port will be overridden.
*/
func (e *ServiceExporter) NewExportedTLSPort(
	ctx context.Context, network, ip, servicename string,
	config *tls.Config) (net.Listener, error) {
	var l net.Listener
	var err error

	// We can just create a new port as above...
	l, err = e.NewExportedPort(ctx, network, ip, servicename)
	if err != nil {
		return nil, err
	}

	// ... and inject a TLS context.
	return tls.NewListener(l, config), nil
}

/*
UnexportPort removes the associated exported port. This will only delete the
most recently exported port. Exported ports will disappear by themselves once
the process dies, but this will expedite the process.
*/
func (e *ServiceExporter) UnexportPort(ctx context.Context) error {
	var err error

	if len(e.path) == 0 {
		return nil
	}

	if _, err = e.conn.Delete(ctx, e.path); err != nil {
		return err
	}

	return nil
}
<commit_msg>Fix support for host:port specified addresses and update documentation.<commit_after>/*
Package exportedservice provides exported named etcd ports.
This binds to an anonymous port, exports the host:port pair through etcd
and returns the port to the caller.

There are convenience methods for exporting a TLS port and an HTTP service.
*/
package exportedservice

import (
	"crypto/tls"
	"fmt"
	"net"

	etcd "github.com/coreos/etcd/clientv3"
	"golang.org/x/net/context"
)

// ServiceExporter exists because we need to initialize our etcd client
// beforehand and keep it somewhere.
type ServiceExporter struct {
	conn               *etcd.Client
	path               string
	leaseID            etcd.LeaseID
	keepaliveResponses <-chan *etcd.LeaseKeepAliveResponse
}

func consumeKeepaliveResponses(ch <-chan *etcd.LeaseKeepAliveResponse) {
	for _ = range ch {
	}
}

/*
NewExporter creates a new exporter object which can later be used to create
exported ports and services. This will create a client connection to etcd.
If the connection is severed, once the etcd lease is going to expire the
port will stop being exported.
The specified ttl (which must be at least 5 (seconds)) determines how frequently
the lease will be renewed.
*/
func NewExporter(ctx context.Context, etcdURL string, ttl int64) (
	*ServiceExporter, error) {
	var self *ServiceExporter
	var client *etcd.Client
	var err error

	client, err = etcd.NewFromURL(etcdURL)
	if err != nil {
		return nil, err
	}

	self = &ServiceExporter{
		conn: client,
	}

	return self, self.initLease(ctx, ttl)
}

/*
NewExporterFromConfigFile creates a new exporter by reading etcd flags from the
specified configuration file. This will create a client connection to etcd.
If the connection is severed, once the etcd lease is going to expire the
port will stop being exported.
The specified ttl (which must be at least 5 (seconds)) determines how frequently
the lease will be renewed.
*/
func NewExporterFromConfigFile(
	ctx context.Context, config string, ttl int64) (*ServiceExporter, error) {
	var self *ServiceExporter
	var client *etcd.Client
	var err error

	client, err = etcd.NewFromConfigFile(config)
	if err != nil {
		return nil, err
	}

	self = &ServiceExporter{
		conn: client,
	}

	return self, self.initLease(ctx, ttl)
}

/*
NewExporterFromClient creates a new exporter by reading etcd flags from the
specified configuration file.
*/
func NewExporterFromClient(
	ctx context.Context, client *etcd.Client, ttl int64) (
	*ServiceExporter, error) {
	var rv = &ServiceExporter{
		conn: client,
	}

	return rv, rv.initLease(ctx, ttl)
}

/*
initLease initializes the lease on the etcd service which will be used to export
ports in the future.
*/
func (e *ServiceExporter) initLease(ctx context.Context, ttl int64) error {
	var lease *etcd.LeaseGrantResponse
	var err error

	lease, err = e.conn.Grant(ctx, ttl)
	if err != nil {
		return err
	}

	e.keepaliveResponses, err = e.conn.KeepAlive(context.Background(), lease.ID)
	if err != nil {
		return err
	}

	e.leaseID = lease.ID

	go consumeKeepaliveResponses(e.keepaliveResponses)

	return nil
}

/*
NewExportedPort opens a new anonymous port on "ip" and export it through etcd
as "servicename". If "ip" is not a host:port pair, the port will be chosen at
random.
*/
func (e *ServiceExporter) NewExportedPort(
	ctx context.Context, network, ip, service string) (net.Listener, error) {
	var path string
	var host, hostport string
	var l net.Listener
	var err error

	if host, _, err = net.SplitHostPort(ip); err != nil {
		// Apparently, it's not in host:port format.
		host = ip
		hostport = net.JoinHostPort(host, "0")
	} else {
		hostport = ip
	}

	if l, err = net.Listen(network, hostport); err != nil {
		return nil, err
	}

	// Use the lease ID as part of the path; it would be reasonable to expect
	// it to be unique.
	path = fmt.Sprintf("/ns/service/%s/%16x", service, e.leaseID)

	// Now write our host:port pair to etcd. Let etcd choose the file name.
	_, err = e.conn.Put(ctx, path, l.Addr().String(), etcd.WithLease(e.leaseID))
	if err != nil {
		return nil, err
	}

	e.path = path

	return l, nil
}

/*
NewExportedTLSPort opens a new anonymous port on "ip" and export it through
etcd as "servicename" (see NewExportedPort). Associates the TLS configuration
"config". If "ip" is a host:port pair, the port will be overridden.
*/
func (e *ServiceExporter) NewExportedTLSPort(
	ctx context.Context, network, ip, servicename string,
	config *tls.Config) (net.Listener, error) {
	var l net.Listener
	var err error

	// We can just create a new port as above...
	l, err = e.NewExportedPort(ctx, network, ip, servicename)
	if err != nil {
		return nil, err
	}

	// ... and inject a TLS context.
	return tls.NewListener(l, config), nil
}

/*
UnexportPort removes the associated exported port. This will only delete the
most recently exported port. Exported ports will disappear by themselves once
the process dies, but this will expedite the process.
*/
func (e *ServiceExporter) UnexportPort(ctx context.Context) error {
	var err error

	if len(e.path) == 0 {
		return nil
	}

	if _, err = e.conn.Delete(ctx, e.path); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2020 Anapaya Systems
//
// 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 certs

import (
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"time"

	"github.com/spf13/cobra"

	"github.com/scionproto/scion/go/lib/scrypto/cppki"
	"github.com/scionproto/scion/go/lib/serrors"
	"github.com/scionproto/scion/go/pkg/command"
)

func newVerifyCmd(pather command.Pather) *cobra.Command {
	var flags struct {
		trcFile  string
		unixTime int64
	}

	cmd := &cobra.Command{
		Use:   "verify",
		Short: "Verify a certificate chain",
		Long: `'verify' verifies the certificate chains based on a trusted TRC.

The chain must be a PEM bundle with the AS certificate first, and the CA
certificate second.
`,
		Example: fmt.Sprintf(`  %[1]s verify --trc ISD1-B1-S1.trc ISD1-ASff00_0_110.pem`,
			pather.CommandPath()),
		Args: cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			cmd.SilenceUsage = true
			chain, err := cppki.ReadPEMCerts(args[0])
			if err != nil {
				return serrors.WrapStr("reading chain", err, "file", args[0])
			}
			trc, err := loadTRC(flags.trcFile)
			if err != nil {
				return err
			}

			opts := cppki.VerifyOptions{TRC: &trc.TRC}
			if flags.unixTime != 0 {
				opts.CurrentTime = time.Unix(flags.unixTime, 0)
			}

			if err := cppki.VerifyChain(chain, opts); err != nil {
				return serrors.WrapStr("verification failed", err)
			}

			fmt.Printf("Successfully verified certificate chain: %q\n", args[0])
			return nil
		},
	}

	cmd.Flags().StringVar(&flags.trcFile, "trc", "", "trusted TRC (required)")
	cmd.Flags().Int64Var(&flags.unixTime, "currenttime", 0,
		"Optional unix timestamp that sets the current time")
	cmd.MarkFlagRequired("trc")

	return cmd
}

func loadTRC(trcFile string) (cppki.SignedTRC, error) {
	raw, err := ioutil.ReadFile(trcFile)
	block, _ := pem.Decode(raw)
	if block != nil && block.Type == "TRC" {
		raw = block.Bytes
	}
	if err != nil {
		return cppki.SignedTRC{}, serrors.WrapStr("reading TRC", err, "file", trcFile)
	}
	return cppki.DecodeSignedTRC(raw)
}
<commit_msg>scion-pki: add command for CA certificate verification<commit_after>// Copyright 2020 Anapaya Systems
//
// 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 certs

import (
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"time"

	"github.com/spf13/cobra"

	"github.com/scionproto/scion/go/lib/scrypto/cppki"
	"github.com/scionproto/scion/go/lib/serrors"
	"github.com/scionproto/scion/go/pkg/command"
)

func newVerifyCmd(pather command.Pather) *cobra.Command {
	var flags struct {
		trcFile  string
		unixTime int64
	}

	cmd := &cobra.Command{
		Use:   "verify",
		Short: "Verify a certificate chain",
		Long: `'verify' verifies the certificate chains based on a trusted TRC.

The chain must be a PEM bundle with the AS certificate first, and the CA
certificate second.
`,
		Example: fmt.Sprintf(`  %[1]s verify --trc ISD1-B1-S1.trc ISD1-ASff00_0_110.pem`,
			pather.CommandPath()),
		Args: cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			cmd.SilenceUsage = true
			chain, err := cppki.ReadPEMCerts(args[0])
			if err != nil {
				return serrors.WrapStr("reading chain", err, "file", args[0])
			}
			trc, err := loadTRC(flags.trcFile)
			if err != nil {
				return err
			}

			opts := cppki.VerifyOptions{TRC: &trc.TRC}
			if flags.unixTime != 0 {
				opts.CurrentTime = time.Unix(flags.unixTime, 0)
			}

			if err := cppki.VerifyChain(chain, opts); err != nil {
				return serrors.WrapStr("verification failed", err)
			}

			fmt.Printf("Successfully verified certificate chain: %q\n", args[0])
			return nil
		},
	}

	cmd.Flags().StringVar(&flags.trcFile, "trc", "", "trusted TRC (required)")
	cmd.Flags().Int64Var(&flags.unixTime, "currenttime", 0,
		"Optional unix timestamp that sets the current time")
	cmd.MarkFlagRequired("trc")

	joined := command.Join(pather, cmd)
	cmd.AddCommand(newVerifyCACmd(joined))

	return cmd
}

func newVerifyCACmd(pather command.Pather) *cobra.Command {
	var flags struct {
		trcFile  string
		unixTime int64
	}

	cmd := &cobra.Command{
		Use:   "ca",
		Short: "Verify a CA certificate",
		Long: `'ca' verifies the CA certificate based on a trusted TRC.

The CA certificate must be a PEM encoded.
`,
		Example: fmt.Sprintf(`  %[1]s --trc ISD1-B1-S1.trc ISD1-ASff00_0_110.ca.crt`,
			pather.CommandPath()),
		Args: cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			cmd.SilenceUsage = true
			certs, err := cppki.ReadPEMCerts(args[0])
			if err != nil {
				return serrors.WrapStr("reading certificate", err, "file", args[0])
			}
			if len(certs) != 1 {
				return serrors.New("file contains multiple certificates", "count", len(certs))
			}
			ct, err := cppki.ValidateCert(certs[0])
			if err != nil {
				return serrors.WrapStr("validating CA certificate", err)
			}
			if ct != cppki.CA {
				return serrors.New("certificate of wrong type", "type", ct)
			}

			trc, err := loadTRC(flags.trcFile)
			if err != nil {
				return err
			}
			rootPool, err := trc.TRC.RootPool()
			if err != nil {
				return serrors.WrapStr("failed to extract root certificates from TRC", err)
			}
			var currTime time.Time
			if flags.unixTime != 0 {
				currTime = time.Unix(flags.unixTime, 0)
			}
			_, err = certs[0].Verify(x509.VerifyOptions{
				Roots:       rootPool,
				KeyUsages:   []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
				CurrentTime: currTime,
			})
			if err != nil {
				return serrors.WrapStr("verification failed", err)
			}

			fmt.Printf("Successfully verified CA certificate: %q\n", args[0])
			return nil
		},
	}

	cmd.Flags().StringVar(&flags.trcFile, "trc", "", "trusted TRC (required)")
	cmd.Flags().Int64Var(&flags.unixTime, "currenttime", 0,
		"Optional unix timestamp that sets the current time")
	cmd.MarkFlagRequired("trc")

	return cmd
}

func loadTRC(trcFile string) (cppki.SignedTRC, error) {
	raw, err := ioutil.ReadFile(trcFile)
	block, _ := pem.Decode(raw)
	if block != nil && block.Type == "TRC" {
		raw = block.Bytes
	}
	if err != nil {
		return cppki.SignedTRC{}, serrors.WrapStr("reading TRC", err, "file", trcFile)
	}
	return cppki.DecodeSignedTRC(raw)
}
<|endoftext|>


<commit_before>package blockchain

import (
	crand "crypto/rand"
	"fmt"
	"testing"
)

func TestValidTransactionNoInputTransaction(t *testing.T) {
	tr, _ := newTransactionValue(newWallet(), newWallet(), 1, 0)
	bc, _ := newValidBlockChainFixture()

	valid, code := bc.ValidTransaction(tr)

	if valid {
		t.Fail()
	}
	if code != NoInputTransaction {
		t.Fail()
	}
}

func TestValidTransactionOverspend(t *testing.T) {
	// 2 + 2 = 5 ?
	bc, _ := newValidBlockChainFixture()
	tr := bc.Blocks[1].Transactions[0]
	tr.Outputs[0].Amount = 5

	valid, code := bc.ValidTransaction(tr)

	if valid {
		t.Fail()
	}
	if code != Overspend {
		fmt.Println(code)
		t.Fail()
	}
}

func TestValidTransactionSignatureFail(t *testing.T) {
	bc, _ := newValidBlockChainFixture()
	tr := bc.Blocks[1].Transactions[0]

	fakeSender := newWallet()
	tr, _ = tr.TxBody.Sign(fakeSender, crand.Reader)
	bc.Blocks[1].Transactions[0] = tr

	valid, code := bc.ValidTransaction(tr)
	if valid {
		t.Fail()
	}
	if code != BadSig {
		t.Fail()
	}
}

func TestValidTransactionPass(t *testing.T) {
	bc, b := newValidChainAndBlock()
	tr := b.Transactions[0]

	valid, code := bc.ValidTransaction(tr)

	if !valid {
		t.Fail()
	}
	if code != ValidTransaction {
		t.Fail()
	}
}

func TestTransactionRespend(t *testing.T) {
	bc, _ := newValidBlockChainFixture()
	trC := bc.Blocks[1].Transactions[0]
	b := newOutputBlock([]*Transaction{trC}, bc.Blocks[1])
	bc.AppendBlock(b, newWallet().Public())

	valid, code := bc.ValidTransaction(trC)

	if valid {
		t.Fail()
	}
	if code != Respend {
		t.Fail()
	}
}

func TestValidBlockBadTransactoion(t *testing.T) {
	bc, _ := newValidBlockChainFixture()
	tr := bc.Blocks[1].Transactions[0]
	tr.Outputs[0].Amount = 5

	valid, code := bc.ValidBlock(bc.Blocks[1])

	if valid {
		t.Fail()
	}
	if code != BadTransaction {
		t.Fail()
	}
}

func TestValidBlocBadBlockNumber(t *testing.T) {
	bc, _ := newValidBlockChainFixture()
	bc.Blocks[1].BlockNumber = 2

	valid, code := bc.ValidBlock(bc.Blocks[1])

	if valid {
		t.Fail()
	}
	if code != BadBlockNumber {
		t.Fail()
	}
}

func TestValidBlockBadHash(t *testing.T) {
	bc, b := newValidChainAndBlock()
	b.BlockHeader.LastBlock = newHash()

	valid, code := bc.ValidBlock(b)

	if valid {
		t.Fail()
	}
	if code != BadHash {
		t.Fail()
	}
}

func TestValidBlock(t *testing.T) {
	bc, b := newValidChainAndBlock()

	valid, code := bc.ValidBlock(b)

	if !valid {
		t.Fail()
	}
	if code != ValidBlock {
		t.Fail()
	}
}

func TestBlockDoubleSpend(t *testing.T) {
	// block should fail to be valid if there exists two transactions
	// referencing the same input, but output > input (double spend attack)
	bc, b := newValidChainAndBlock()
	b.Transactions = append(b.Transactions, b.Transactions[0])

	valid, code := bc.ValidBlock(b)

	if valid {
		t.Fail()
	}
	if code != DoubleSpend {
		t.Fail()
	}
}
<commit_msg>crand<commit_after>package blockchain

import (
	"crypto/rand"
	"fmt"
	"testing"
)

func TestValidTransactionNoInputTransaction(t *testing.T) {
	tr, _ := newTransactionValue(newWallet(), newWallet(), 1, 0)
	bc, _ := newValidBlockChainFixture()

	valid, code := bc.ValidTransaction(tr)

	if valid {
		t.Fail()
	}
	if code != NoInputTransaction {
		t.Fail()
	}
}

func TestValidTransactionOverspend(t *testing.T) {
	// 2 + 2 = 5 ?
	bc, _ := newValidBlockChainFixture()
	tr := bc.Blocks[1].Transactions[0]
	tr.Outputs[0].Amount = 5

	valid, code := bc.ValidTransaction(tr)

	if valid {
		t.Fail()
	}
	if code != Overspend {
		fmt.Println(code)
		t.Fail()
	}
}

func TestValidTransactionSignatureFail(t *testing.T) {
	bc, _ := newValidBlockChainFixture()
	tr := bc.Blocks[1].Transactions[0]

	fakeSender := newWallet()
	tr, _ = tr.TxBody.Sign(fakeSender, rand.Reader)
	bc.Blocks[1].Transactions[0] = tr

	valid, code := bc.ValidTransaction(tr)
	if valid {
		t.Fail()
	}
	if code != BadSig {
		t.Fail()
	}
}

func TestValidTransactionPass(t *testing.T) {
	bc, b := newValidChainAndBlock()
	tr := b.Transactions[0]

	valid, code := bc.ValidTransaction(tr)

	if !valid {
		t.Fail()
	}
	if code != ValidTransaction {
		t.Fail()
	}
}

func TestTransactionRespend(t *testing.T) {
	bc, _ := newValidBlockChainFixture()
	trC := bc.Blocks[1].Transactions[0]
	b := newOutputBlock([]*Transaction{trC}, bc.Blocks[1])
	bc.AppendBlock(b, newWallet().Public())

	valid, code := bc.ValidTransaction(trC)

	if valid {
		t.Fail()
	}
	if code != Respend {
		t.Fail()
	}
}

func TestValidBlockBadTransactoion(t *testing.T) {
	bc, _ := newValidBlockChainFixture()
	tr := bc.Blocks[1].Transactions[0]
	tr.Outputs[0].Amount = 5

	valid, code := bc.ValidBlock(bc.Blocks[1])

	if valid {
		t.Fail()
	}
	if code != BadTransaction {
		t.Fail()
	}
}

func TestValidBlocBadBlockNumber(t *testing.T) {
	bc, _ := newValidBlockChainFixture()
	bc.Blocks[1].BlockNumber = 2

	valid, code := bc.ValidBlock(bc.Blocks[1])

	if valid {
		t.Fail()
	}
	if code != BadBlockNumber {
		t.Fail()
	}
}

func TestValidBlockBadHash(t *testing.T) {
	bc, b := newValidChainAndBlock()
	b.BlockHeader.LastBlock = newHash()

	valid, code := bc.ValidBlock(b)

	if valid {
		t.Fail()
	}
	if code != BadHash {
		t.Fail()
	}
}

func TestValidBlock(t *testing.T) {
	bc, b := newValidChainAndBlock()

	valid, code := bc.ValidBlock(b)

	if !valid {
		t.Fail()
	}
	if code != ValidBlock {
		t.Fail()
	}
}

func TestBlockDoubleSpend(t *testing.T) {
	// block should fail to be valid if there exists two transactions
	// referencing the same input, but output > input (double spend attack)
	bc, b := newValidChainAndBlock()
	b.Transactions = append(b.Transactions, b.Transactions[0])

	valid, code := bc.ValidBlock(b)

	if valid {
		t.Fail()
	}
	if code != DoubleSpend {
		t.Fail()
	}
}
<|endoftext|>


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

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

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

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

package resttest

import (
	"strings"
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/coreos/go-etcd/etcd"
)

type Tester struct {
	*testing.T
	storage      rest.Storage
	storageError injectErrorFunc
	clusterScope bool
}

type injectErrorFunc func(err error)

func New(t *testing.T, storage rest.Storage, storageError injectErrorFunc) *Tester {
	return &Tester{
		T:            t,
		storage:      storage,
		storageError: storageError,
	}
}

func (t *Tester) withStorageError(err error, fn func()) {
	t.storageError(err)
	defer t.storageError(nil)
	fn()
}

func (t *Tester) ClusterScope() *Tester {
	t.clusterScope = true
	return t
}

func copyOrDie(obj runtime.Object) runtime.Object {
	out, err := api.Scheme.Copy(obj)
	if err != nil {
		panic(err)
	}
	return out
}

func (t *Tester) TestCreate(valid runtime.Object, invalid ...runtime.Object) {
	t.TestCreateHasMetadata(copyOrDie(valid))
	t.TestCreateGeneratesName(copyOrDie(valid))
	t.TestCreateGeneratesNameReturnsServerTimeout(copyOrDie(valid))
	if t.clusterScope {
		t.TestCreateRejectsNamespace(copyOrDie(valid))
	} else {
		t.TestCreateRejectsMismatchedNamespace(copyOrDie(valid))
	}
	t.TestCreateInvokesValidation(invalid...)
}

func (t *Tester) TestCreateResetsUserData(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	now := util.Now()
	objectMeta.UID = "bad-uid"
	objectMeta.CreationTimestamp = now

	obj, err := t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if obj == nil {
		t.Fatalf("Unexpected object from result: %#v", obj)
	}
	if objectMeta.UID == "bad-uid" || objectMeta.CreationTimestamp == now {
		t.Errorf("ObjectMeta did not reset basic fields: %#v", objectMeta)
	}
}

func (t *Tester) TestCreateHasMetadata(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	objectMeta.Name = "test"
	objectMeta.Namespace = api.NamespaceDefault
	context := api.NewDefaultContext()
	if t.clusterScope {
		objectMeta.Namespace = api.NamespaceNone
		context = api.NewContext()
	}

	obj, err := t.storage.(rest.Creater).Create(context, valid)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if obj == nil {
		t.Fatalf("Unexpected object from result: %#v", obj)
	}
	if !api.HasObjectMetaSystemFieldValues(objectMeta) {
		t.Errorf("storage did not populate object meta field values")
	}
}

func (t *Tester) TestCreateGeneratesName(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	objectMeta.GenerateName = "test-"

	_, err = t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if objectMeta.Name == "test-" || !strings.HasPrefix(objectMeta.Name, "test-") {
		t.Errorf("unexpected name: %#v", valid)
	}
}

func (t *Tester) TestCreateGeneratesNameReturnsServerTimeout(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	objectMeta.GenerateName = "test-"
	t.withStorageError(errors.NewAlreadyExists("kind", "thing"), func() {
		_, err := t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
		if err == nil || !errors.IsServerTimeout(err) {
			t.Fatalf("Unexpected error: %v", err)
		}
	})
}

func (t *Tester) TestCreateInvokesValidation(invalid ...runtime.Object) {
	for i, obj := range invalid {
		ctx := api.NewDefaultContext()
		_, err := t.storage.(rest.Creater).Create(ctx, obj)
		if !errors.IsInvalid(err) {
			t.Errorf("%d: Expected to get an invalid resource error, got %v", i, err)
		}
	}
}

func (t *Tester) TestCreateRejectsMismatchedNamespace(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	objectMeta.Namespace = "not-default"

	_, err = t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
	if err == nil {
		t.Errorf("Expected an error, but we didn't get one")
	} else if strings.Contains(err.Error(), "Controller.Namespace does not match the provided context") {
		t.Errorf("Expected 'Controller.Namespace does not match the provided context' error, got '%v'", err)
	}
}

func (t *Tester) TestCreateRejectsNamespace(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	objectMeta.Namespace = "not-default"

	_, err = t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
	if err == nil {
		t.Errorf("Expected an error, but we didn't get one")
	} else if strings.Contains(err.Error(), "Controller.Namespace does not match the provided context") {
		t.Errorf("Expected 'Controller.Namespace does not match the provided context' error, got '%v'", err)
	}
}

func (t *Tester) TestUpdate(valid runtime.Object, existing, older runtime.Object) {
	t.TestUpdateFailsOnNotFound(copyOrDie(valid))
	t.TestUpdateFailsOnVersion(copyOrDie(older))
}

func (t *Tester) TestUpdateFailsOnNotFound(valid runtime.Object) {
	_, _, err := t.storage.(rest.Updater).Update(api.NewDefaultContext(), valid)
	if err == nil {
		t.Errorf("Expected an error, but we didn't get one")
	} else if !errors.IsNotFound(err) {
		t.Errorf("Expected NotFound error, got '%v'", err)
	}
}

func (t *Tester) TestUpdateFailsOnVersion(older runtime.Object) {
	_, _, err := t.storage.(rest.Updater).Update(api.NewDefaultContext(), older)
	if err == nil {
		t.Errorf("Expected an error, but we didn't get one")
	} else if !errors.IsConflict(err) {
		t.Errorf("Expected Conflict error, got '%v'", err)
	}
}

func (t *Tester) TestDeleteInvokesValidation(invalid ...runtime.Object) {
	for i, obj := range invalid {
		objectMeta, err := api.ObjectMetaFor(obj)
		if err != nil {
			t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, obj)
		}
		ctx := api.NewDefaultContext()
		_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil)
		if !errors.IsInvalid(err) {
			t.Errorf("%d: Expected to get an invalid resource error, got %v", i, err)
		}
	}
}

func (t *Tester) TestDelete(createFn func() runtime.Object, wasGracefulFn func() bool, invalid ...runtime.Object) {
	t.TestDeleteNonExist(createFn)
	t.TestDeleteNoGraceful(createFn, wasGracefulFn)
	t.TestDeleteInvokesValidation(invalid...)
	// TODO: Test delete namespace mismatch rejection
	// once #5684 is fixed.
}

func (t *Tester) TestDeleteNonExist(createFn func() runtime.Object) {
	existing := createFn()
	objectMeta, err := api.ObjectMetaFor(existing)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
	}
	context := api.NewDefaultContext()

	t.withStorageError(&etcd.EtcdError{ErrorCode: tools.EtcdErrorCodeNotFound}, func() {
		_, err := t.storage.(rest.GracefulDeleter).Delete(context, objectMeta.Name, nil)
		if err == nil || !errors.IsNotFound(err) {
			t.Fatalf("Unexpected error: %v", err)
		}
	})
}

func (t *Tester) TestDeleteGraceful(createFn func() runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
	t.TestDeleteGracefulHasDefault(createFn(), expectedGrace, wasGracefulFn)
	t.TestDeleteGracefulUsesZeroOnNil(createFn(), 0)
}

func (t *Tester) TestDeleteNoGraceful(createFn func() runtime.Object, wasGracefulFn func() bool) {
	existing := createFn()
	objectMeta, err := api.ObjectMetaFor(existing)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
	}

	ctx := api.WithNamespace(api.NewContext(), objectMeta.Namespace)
	_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(10))
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) {
		t.Errorf("unexpected error, object should not exist: %v", err)
	}
	if wasGracefulFn() {
		t.Errorf("resource should not support graceful delete")
	}
}

func (t *Tester) TestDeleteGracefulHasDefault(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
	objectMeta, err := api.ObjectMetaFor(existing)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
	}

	ctx := api.WithNamespace(api.NewContext(), objectMeta.Namespace)
	_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{})
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); err != nil {
		t.Errorf("unexpected error, object should exist: %v", err)
	}
	if !wasGracefulFn() {
		t.Errorf("did not gracefully delete resource")
	}
}

func (t *Tester) TestDeleteGracefulUsesZeroOnNil(existing runtime.Object, expectedGrace int64) {
	objectMeta, err := api.ObjectMetaFor(existing)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
	}

	ctx := api.WithNamespace(api.NewContext(), objectMeta.Namespace)
	_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil)
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) {
		t.Errorf("unexpected error, object should exist: %v", err)
	}
}
<commit_msg>Adds ability to define a prefix for etcd paths<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package resttest

import (
	"strings"
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/coreos/go-etcd/etcd"
)

type Tester struct {
	*testing.T
	storage      rest.Storage
	storageError injectErrorFunc
	clusterScope bool
}

type injectErrorFunc func(err error)

func New(t *testing.T, storage rest.Storage, storageError injectErrorFunc) *Tester {
	return &Tester{
		T:            t,
		storage:      storage,
		storageError: storageError,
	}
}

func (t *Tester) withStorageError(err error, fn func()) {
	t.storageError(err)
	defer t.storageError(nil)
	fn()
}

func (t *Tester) ClusterScope() *Tester {
	t.clusterScope = true
	return t
}

func copyOrDie(obj runtime.Object) runtime.Object {
	out, err := api.Scheme.Copy(obj)
	if err != nil {
		panic(err)
	}
	return out
}

func (t *Tester) TestCreate(valid runtime.Object, invalid ...runtime.Object) {
	t.TestCreateHasMetadata(copyOrDie(valid))
	t.TestCreateGeneratesName(copyOrDie(valid))
	t.TestCreateGeneratesNameReturnsServerTimeout(copyOrDie(valid))
	if t.clusterScope {
		t.TestCreateRejectsNamespace(copyOrDie(valid))
	} else {
		t.TestCreateRejectsMismatchedNamespace(copyOrDie(valid))
	}
	t.TestCreateInvokesValidation(invalid...)
}

func (t *Tester) TestCreateResetsUserData(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	now := util.Now()
	objectMeta.UID = "bad-uid"
	objectMeta.CreationTimestamp = now

	obj, err := t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if obj == nil {
		t.Fatalf("Unexpected object from result: %#v", obj)
	}
	if objectMeta.UID == "bad-uid" || objectMeta.CreationTimestamp == now {
		t.Errorf("ObjectMeta did not reset basic fields: %#v", objectMeta)
	}
}

func (t *Tester) TestCreateHasMetadata(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	objectMeta.Name = "test"
	objectMeta.Namespace = api.NamespaceDefault
	context := api.NewDefaultContext()
	if t.clusterScope {
		objectMeta.Namespace = api.NamespaceNone
		context = api.NewContext()
	}

	obj, err := t.storage.(rest.Creater).Create(context, valid)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if obj == nil {
		t.Fatalf("Unexpected object from result: %#v", obj)
	}
	if !api.HasObjectMetaSystemFieldValues(objectMeta) {
		t.Errorf("storage did not populate object meta field values")
	}
}

func (t *Tester) TestCreateGeneratesName(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	objectMeta.GenerateName = "test-"

	_, err = t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if objectMeta.Name == "test-" || !strings.HasPrefix(objectMeta.Name, "test-") {
		t.Errorf("unexpected name: %#v", valid)
	}
}

func (t *Tester) TestCreateGeneratesNameReturnsServerTimeout(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	objectMeta.GenerateName = "test-"
	t.withStorageError(errors.NewAlreadyExists("kind", "thing"), func() {
		_, err := t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
		if err == nil || !errors.IsServerTimeout(err) {
			t.Fatalf("Unexpected error: %v", err)
		}
	})
}

func (t *Tester) TestCreateInvokesValidation(invalid ...runtime.Object) {
	for i, obj := range invalid {
		ctx := api.NewDefaultContext()
		_, err := t.storage.(rest.Creater).Create(ctx, obj)
		if !errors.IsInvalid(err) {
			t.Errorf("%d: Expected to get an invalid resource error, got %v", i, err)
		}
	}
}

func (t *Tester) TestCreateRejectsMismatchedNamespace(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	objectMeta.Namespace = "not-default"

	_, err = t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
	if err == nil {
		t.Errorf("Expected an error, but we didn't get one")
	} else if strings.Contains(err.Error(), "Controller.Namespace does not match the provided context") {
		t.Errorf("Expected 'Controller.Namespace does not match the provided context' error, got '%v'", err)
	}
}

func (t *Tester) TestCreateRejectsNamespace(valid runtime.Object) {
	objectMeta, err := api.ObjectMetaFor(valid)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, valid)
	}

	objectMeta.Namespace = "not-default"

	_, err = t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
	if err == nil {
		t.Errorf("Expected an error, but we didn't get one")
	} else if strings.Contains(err.Error(), "Controller.Namespace does not match the provided context") {
		t.Errorf("Expected 'Controller.Namespace does not match the provided context' error, got '%v'", err)
	}
}

func (t *Tester) TestUpdate(valid runtime.Object, existing, older runtime.Object) {
	t.TestUpdateFailsOnNotFound(copyOrDie(valid))
	t.TestUpdateFailsOnVersion(copyOrDie(older))
}

func (t *Tester) TestUpdateFailsOnNotFound(valid runtime.Object) {
	_, _, err := t.storage.(rest.Updater).Update(api.NewDefaultContext(), valid)
	if err == nil {
		t.Errorf("Expected an error, but we didn't get one")
	} else if !errors.IsNotFound(err) {
		t.Errorf("Expected NotFound error, got '%v'", err)
	}
}

func (t *Tester) TestUpdateFailsOnVersion(older runtime.Object) {
	_, _, err := t.storage.(rest.Updater).Update(api.NewDefaultContext(), older)
	if err == nil {
		t.Errorf("Expected an error, but we didn't get one")
	} else if !errors.IsConflict(err) {
		t.Errorf("Expected Conflict error, got '%v'", err)
	}
}

func (t *Tester) TestDeleteInvokesValidation(invalid ...runtime.Object) {
	for i, obj := range invalid {
		objectMeta, err := api.ObjectMetaFor(obj)
		if err != nil {
			t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, obj)
		}
		ctx := api.NewDefaultContext()
		_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil)
		if !errors.IsInvalid(err) {
			t.Errorf("%d: Expected to get an invalid resource error, got %v", i, err)
		}
	}
}

func (t *Tester) TestDelete(createFn func() runtime.Object, wasGracefulFn func() bool, invalid ...runtime.Object) {
	t.TestDeleteNonExist(createFn)
	t.TestDeleteNoGraceful(createFn, wasGracefulFn)
	t.TestDeleteInvokesValidation(invalid...)
	// TODO: Test delete namespace mismatch rejection
	// once #5684 is fixed.
}

func (t *Tester) TestDeleteNonExist(createFn func() runtime.Object) {
	existing := createFn()
	objectMeta, err := api.ObjectMetaFor(existing)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
	}
	context := api.NewDefaultContext()

	t.withStorageError(&etcd.EtcdError{ErrorCode: tools.EtcdErrorCodeNotFound}, func() {
		_, err := t.storage.(rest.GracefulDeleter).Delete(context, objectMeta.Name, nil)
		if err == nil || !errors.IsNotFound(err) {
			t.Fatalf("Unexpected error: %v", err)
		}
	})
}

func (t *Tester) TestDeleteGraceful(createFn func() runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
	t.TestDeleteGracefulHasDefault(createFn(), expectedGrace, wasGracefulFn)
	t.TestDeleteGracefulUsesZeroOnNil(createFn(), 0)
}

func (t *Tester) TestDeleteNoGraceful(createFn func() runtime.Object, wasGracefulFn func() bool) {
	existing := createFn()
	objectMeta, err := api.ObjectMetaFor(existing)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
	}
	ctx := api.WithNamespace(api.NewContext(), objectMeta.Namespace)
	_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(10))
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) {
		t.Errorf("unexpected error, object should not exist: %v", err)
	}
	if wasGracefulFn() {
		t.Errorf("resource should not support graceful delete")
	}
}

func (t *Tester) TestDeleteGracefulHasDefault(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
	objectMeta, err := api.ObjectMetaFor(existing)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
	}

	ctx := api.WithNamespace(api.NewContext(), objectMeta.Namespace)
	_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{})
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); err != nil {
		t.Errorf("unexpected error, object should exist: %v", err)
	}
	if !wasGracefulFn() {
		t.Errorf("did not gracefully delete resource")
	}
}

func (t *Tester) TestDeleteGracefulUsesZeroOnNil(existing runtime.Object, expectedGrace int64) {
	objectMeta, err := api.ObjectMetaFor(existing)
	if err != nil {
		t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
	}

	ctx := api.WithNamespace(api.NewContext(), objectMeta.Namespace)
	_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil)
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) {
		t.Errorf("unexpected error, object should exist: %v", err)
	}
}
<|endoftext|>


<commit_before>package container

import (
	"fmt"
	"path"
	"strings"

	"github.com/containerum/chkit/pkg/model/limits"
	"github.com/containerum/chkit/pkg/util/validation"
	"github.com/ninedraft/boxofstuff/str"
)

func (container Container) Validate() error {
	var errs []error
	if err := container.validateName(); err != nil {
		errs = append(errs, err)
	}
	if err := container.validateLimits(); err != nil {
		errs = append(errs, err)
	}
	if err := container.validateEnvs(); err != nil {
		errs = append(errs, err)
	}
	if err := container.validateVolumeMounts(); err != nil {
		errs = append(errs, err)
	}
	if err := container.validateConfigmaps(); err != nil {
		errs = append(errs, err)
	}
	if len(errs) > 0 {
		return fmt.Errorf("invalid container %q:\n%v\n",
			container.Name,
			str.FromErrs(errs...).Join("\n"))
	}
	return nil
}

func (container Container) validateEnvs() error {
	var errs = make([]error, len(container.Env))
	for _, env := range container.Env {
		if env.Name == "" || strings.Contains(env.Name, " ") {
			errs = append(errs, fmt.Errorf("invalid env name %q", env.Name))
		}
		if env.Value == "" {
			errs = append(errs, fmt.Errorf("invalud env %q value: empty values are not allowed", env.Name, env.Value))
		}
	}
	if len(errs) > 0 {
		return fmt.Errorf(" + invalid envs:\n",
			str.FromErrs(errs...).
				Map(str.Prefix(" ++ ")).
				Join("\n"))
	}
	return nil
}

func (container Container) validateName() error {
	return validation.ValidateContainerName(container.Name)
}

func (container Container) validateVolumeMounts() error {
	var errs []error
	for _, vol := range container.VolumeMounts {
		if err := validation.ValidateLabel(vol.Name); err != nil {
			errs = append(errs, fmt.Errorf("invalid name %q: %v", vol.Name, err))
		}
		if !path.IsAbs(vol.MountPath) {
			errs = append(errs, fmt.Errorf("invalid mount path %q: expected absolute path", vol.MountPath))
		}
	}
	if len(errs) > 0 {
		return fmt.Errorf(" + invalid volume mounts:\n%v", str.FromErrs(errs...).
			Map(str.Prefix(" ++ ")).Join("\n"))
	}
	return nil
}

func (container Container) validateConfigmaps() error {
	var errs []error
	for _, configmap := range container.ConfigMaps {
		if err := validation.ValidateLabel(configmap.Name); err != nil {
			errs = append(errs, fmt.Errorf("invalid name %q: %v", configmap.Name, err))
		}
		if !path.IsAbs(configmap.MountPath) {
			errs = append(errs, fmt.Errorf("invalid mount path %q: expected absolute path", configmap.MountPath))
		}
	}
	if len(errs) > 0 {
		return fmt.Errorf(" + invalid configmaps:\n%v", str.FromErrs(errs...).
			Map(str.Prefix(" ++ ")).
			Join("\n"))
	}
	return nil
}

func (container Container) validateLimits() error {
	var errs []error
	if !limits.CPULimit.Containing(int(container.Limits.CPU)) {
		errs = append(errs, fmt.Errorf("invalid CPU limit %d: expected %v mCPU",
			container.Limits.CPU, limits.CPULimit))
	}
	if !limits.MemLimit.Containing(int(container.Limits.Memory)) {
		errs = append(errs, fmt.Errorf("invalid memory limit %d: expected %v Mb",
			container.Limits.Memory, limits.MemLimit))
	}
	if len(errs) > 0 {
		return fmt.Errorf("%v", str.FromErrs(errs...).
			Map(str.Prefix(" + ")).
			Join("\n"))
	}
	return nil
}
<commit_msg>fix error list allocation<commit_after>package container

import (
	"fmt"
	"path"
	"strings"

	"github.com/containerum/chkit/pkg/model/limits"
	"github.com/containerum/chkit/pkg/util/validation"
	"github.com/ninedraft/boxofstuff/str"
)

func (container Container) Validate() error {
	var errs []error
	if err := container.validateName(); err != nil {
		errs = append(errs, err)
	}
	if err := container.validateLimits(); err != nil {
		errs = append(errs, err)
	}
	if err := container.validateEnvs(); err != nil {
		errs = append(errs, err)
	}
	if err := container.validateVolumeMounts(); err != nil {
		errs = append(errs, err)
	}
	if err := container.validateConfigmaps(); err != nil {
		errs = append(errs, err)
	}
	if len(errs) > 0 {
		return fmt.Errorf("invalid container %q:\n%v\n",
			container.Name,
			str.FromErrs(errs...).Join("\n"))
	}
	return nil
}

func (container Container) validateEnvs() error {
	var errs = make([]error, 0, len(container.Env))
	for _, env := range container.Env {
		if env.Name == "" || strings.Contains(env.Name, " ") {
			errs = append(errs, fmt.Errorf("invalid env name %q", env.Name))
		}
		if env.Value == "" {
			errs = append(errs, fmt.Errorf("invalud env %q value: empty values are not allowed", env.Name, env.Value))
		}
	}
	if len(errs) > 0 {
		return fmt.Errorf(" + invalid envs:\n",
			str.FromErrs(errs...).
				Map(str.Prefix(" ++ ")).
				Join("\n"))
	}
	return nil
}

func (container Container) validateName() error {
	return validation.ValidateContainerName(container.Name)
}

func (container Container) validateVolumeMounts() error {
	var errs []error
	for _, vol := range container.VolumeMounts {
		if err := validation.ValidateLabel(vol.Name); err != nil {
			errs = append(errs, fmt.Errorf("invalid name %q: %v", vol.Name, err))
		}
		if !path.IsAbs(vol.MountPath) {
			errs = append(errs, fmt.Errorf("invalid mount path %q: expected absolute path", vol.MountPath))
		}
	}
	if len(errs) > 0 {
		return fmt.Errorf(" + invalid volume mounts:\n%v", str.FromErrs(errs...).
			Map(str.Prefix(" ++ ")).Join("\n"))
	}
	return nil
}

func (container Container) validateConfigmaps() error {
	var errs []error
	for _, configmap := range container.ConfigMaps {
		if err := validation.ValidateLabel(configmap.Name); err != nil {
			errs = append(errs, fmt.Errorf("invalid name %q: %v", configmap.Name, err))
		}
		if !path.IsAbs(configmap.MountPath) {
			errs = append(errs, fmt.Errorf("invalid mount path %q: expected absolute path", configmap.MountPath))
		}
	}
	if len(errs) > 0 {
		return fmt.Errorf(" + invalid configmaps:\n%v", str.FromErrs(errs...).
			Map(str.Prefix(" ++ ")).
			Join("\n"))
	}
	return nil
}

func (container Container) validateLimits() error {
	var errs []error
	if !limits.CPULimit.Containing(int(container.Limits.CPU)) {
		errs = append(errs, fmt.Errorf("invalid CPU limit %d: expected %v mCPU",
			container.Limits.CPU, limits.CPULimit))
	}
	if !limits.MemLimit.Containing(int(container.Limits.Memory)) {
		errs = append(errs, fmt.Errorf("invalid memory limit %d: expected %v Mb",
			container.Limits.Memory, limits.MemLimit))
	}
	if len(errs) > 0 {
		return fmt.Errorf("%v", str.FromErrs(errs...).
			Map(str.Prefix(" + ")).
			Join("\n"))
	}
	return nil
}
<|endoftext|>


<commit_before>package netutils

import (
	"testing"
)

func TestAllocateIP(t *testing.T) {
	ipa, err := NewIPAllocator("10.1.2.0/24")
	if err != nil {
		t.Fatal("Failed to initialize IP allocator: %v", err)
	}
	t.Log(ipa.GetIP())
}
<commit_msg>Add tests for IP Allocator.<commit_after>package netutils

import (
	"testing"
)

func TestAllocateIP(t *testing.T) {
	ipa, err := NewIPAllocator("10.1.2.0/24", nil)
	if err != nil {
		t.Fatal("Failed to initialize IP allocator: %v", err)
	}

	ip, err := ipa.GetIP()
	if err != nil {
		t.Fatal("Failed to get IP: ", err)
	}
	if ip.String() != "10.1.2.1/24" {
		t.Fatal("Did not get expected IP")
	}
	ip, err = ipa.GetIP()
	if err != nil {
		t.Fatal("Failed to get IP: ", err)
	}
	if ip.String() != "10.1.2.2/24" {
		t.Fatal("Did not get expected IP")
	}
	ip, err = ipa.GetIP()
	if err != nil {
		t.Fatal("Failed to get IP: ", err)
	}
	if ip.String() != "10.1.2.3/24" {
		t.Fatal("Did not get expected IP")
	}
}

func TestAllocateIPInUse(t *testing.T) {
	inUse := []string{"10.1.2.1/24", "10.1.2.2/24", "10.2.2.3/24", "Invalid"}
	ipa, err := NewIPAllocator("10.1.2.0/24", inUse)
	if err != nil {
		t.Fatal("Failed to initialize IP allocator: %v", err)
	}

	ip, err := ipa.GetIP()
	if err != nil {
		t.Fatal("Failed to get IP: ", err)
	}
	if ip.String() != "10.1.2.3/24" {
		t.Fatal("Did not get expected IP", ip)
	}
	ip, err = ipa.GetIP()
	if err != nil {
		t.Fatal("Failed to get IP: ", err)
	}
	if ip.String() != "10.1.2.4/24" {
		t.Fatal("Did not get expected IP", ip)
	}
}
<|endoftext|>


<commit_before>package alerting

import (
	"errors"
	"fmt"

	"golang.org/x/sync/errgroup"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/imguploader"
	"github.com/grafana/grafana/pkg/components/renderer"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/metrics"

	m "github.com/grafana/grafana/pkg/models"
)

type NotifierPlugin struct {
	Type            string          `json:"type"`
	Name            string          `json:"name"`
	Description     string          `json:"description"`
	OptionsTemplate string          `json:"optionsTemplate"`
	Factory         NotifierFactory `json:"-"`
}

type NotificationService interface {
	SendIfNeeded(context *EvalContext) error
}

func NewNotificationService() NotificationService {
	return newNotificationService()
}

type notificationService struct {
	log log.Logger
}

func newNotificationService() *notificationService {
	return &notificationService{
		log: log.New("alerting.notifier"),
	}
}

func (n *notificationService) SendIfNeeded(context *EvalContext) error {
	notifiers, err := n.getNeededNotifiers(context.Rule.OrgId, context.Rule.Notifications, context)
	if err != nil {
		return err
	}

	n.log.Info("Sending notifications for", "ruleId", context.Rule.Id, "sent count", len(notifiers))

	if len(notifiers) == 0 {
		return nil
	}

	if notifiers.ShouldUploadImage() {
		if err = n.uploadImage(context); err != nil {
			n.log.Error("Failed to upload alert panel image.", "error", err)
		}
	}

	return n.sendNotifications(context, notifiers)
}

func (n *notificationService) sendNotifications(context *EvalContext, notifiers []Notifier) error {
	g, _ := errgroup.WithContext(context.Ctx)

	for _, notifier := range notifiers {
		not := notifier //avoid updating scope variable in go routine
		n.log.Info("Sending notification", "type", not.GetType(), "id", not.GetNotifierId(), "isDefault", not.GetIsDefault())
		metrics.M_Alerting_Notification_Sent.WithLabelValues(not.GetType()).Inc()
		g.Go(func() error { return not.Notify(context) })
	}

	return g.Wait()
}

func (n *notificationService) uploadImage(context *EvalContext) (err error) {
	uploader, err := imguploader.NewImageUploader()
	if err != nil {
		return err
	}

	renderOpts := &renderer.RenderOpts{
		Width:   "800",
		Height:  "400",
		Timeout: "30",
		OrgId:   context.Rule.OrgId,
	}

	if slug, err := context.GetDashboardSlug(); err != nil {
		return err
	} else {
		renderOpts.Path = fmt.Sprintf("dashboard-solo/db/%s?&panelId=%d", slug, context.Rule.PanelId)
	}

	if imagePath, err := renderer.RenderToPng(renderOpts); err != nil {
		return err
	} else {
		context.ImageOnDiskPath = imagePath
	}

	context.ImagePublicUrl, err = uploader.Upload(context.Ctx, context.ImageOnDiskPath)
	if err != nil {
		return err
	}

	n.log.Info("uploaded", "url", context.ImagePublicUrl)
	return nil
}

func (n *notificationService) getNeededNotifiers(orgId int64, notificationIds []int64, context *EvalContext) (NotifierSlice, error) {
	query := &m.GetAlertNotificationsToSendQuery{OrgId: orgId, Ids: notificationIds}

	if err := bus.Dispatch(query); err != nil {
		return nil, err
	}

	var result []Notifier
	for _, notification := range query.Result {
		if not, err := n.createNotifierFor(notification); err != nil {
			return nil, err
		} else {
			if not.ShouldNotify(context) {
				result = append(result, not)
			}
		}
	}

	return result, nil
}

func (n *notificationService) createNotifierFor(model *m.AlertNotification) (Notifier, error) {
	notifierPlugin, found := notifierFactories[model.Type]
	if !found {
		return nil, errors.New("Unsupported notification type")
	}

	return notifierPlugin.Factory(model)
}

type NotifierFactory func(notification *m.AlertNotification) (Notifier, error)

var notifierFactories map[string]*NotifierPlugin = make(map[string]*NotifierPlugin)

func RegisterNotifier(plugin *NotifierPlugin) {
	notifierFactories[plugin.Type] = plugin
}

func GetNotifiers() []*NotifierPlugin {
	list := make([]*NotifierPlugin, 0)

	for _, value := range notifierFactories {
		list = append(list, value)
	}

	return list
}
<commit_msg>removes verbose logging<commit_after>package alerting

import (
	"errors"
	"fmt"

	"golang.org/x/sync/errgroup"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/imguploader"
	"github.com/grafana/grafana/pkg/components/renderer"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/metrics"

	m "github.com/grafana/grafana/pkg/models"
)

type NotifierPlugin struct {
	Type            string          `json:"type"`
	Name            string          `json:"name"`
	Description     string          `json:"description"`
	OptionsTemplate string          `json:"optionsTemplate"`
	Factory         NotifierFactory `json:"-"`
}

type NotificationService interface {
	SendIfNeeded(context *EvalContext) error
}

func NewNotificationService() NotificationService {
	return newNotificationService()
}

type notificationService struct {
	log log.Logger
}

func newNotificationService() *notificationService {
	return &notificationService{
		log: log.New("alerting.notifier"),
	}
}

func (n *notificationService) SendIfNeeded(context *EvalContext) error {
	notifiers, err := n.getNeededNotifiers(context.Rule.OrgId, context.Rule.Notifications, context)
	if err != nil {
		return err
	}

	if len(notifiers) == 0 {
		return nil
	}

	if notifiers.ShouldUploadImage() {
		if err = n.uploadImage(context); err != nil {
			n.log.Error("Failed to upload alert panel image.", "error", err)
		}
	}

	return n.sendNotifications(context, notifiers)
}

func (n *notificationService) sendNotifications(context *EvalContext, notifiers []Notifier) error {
	g, _ := errgroup.WithContext(context.Ctx)

	for _, notifier := range notifiers {
		not := notifier //avoid updating scope variable in go routine
		n.log.Info("Sending notification", "type", not.GetType(), "id", not.GetNotifierId(), "isDefault", not.GetIsDefault())
		metrics.M_Alerting_Notification_Sent.WithLabelValues(not.GetType()).Inc()
		g.Go(func() error { return not.Notify(context) })
	}

	return g.Wait()
}

func (n *notificationService) uploadImage(context *EvalContext) (err error) {
	uploader, err := imguploader.NewImageUploader()
	if err != nil {
		return err
	}

	renderOpts := &renderer.RenderOpts{
		Width:   "800",
		Height:  "400",
		Timeout: "30",
		OrgId:   context.Rule.OrgId,
	}

	if slug, err := context.GetDashboardSlug(); err != nil {
		return err
	} else {
		renderOpts.Path = fmt.Sprintf("dashboard-solo/db/%s?&panelId=%d", slug, context.Rule.PanelId)
	}

	if imagePath, err := renderer.RenderToPng(renderOpts); err != nil {
		return err
	} else {
		context.ImageOnDiskPath = imagePath
	}

	context.ImagePublicUrl, err = uploader.Upload(context.Ctx, context.ImageOnDiskPath)
	if err != nil {
		return err
	}

	n.log.Info("uploaded", "url", context.ImagePublicUrl)
	return nil
}

func (n *notificationService) getNeededNotifiers(orgId int64, notificationIds []int64, context *EvalContext) (NotifierSlice, error) {
	query := &m.GetAlertNotificationsToSendQuery{OrgId: orgId, Ids: notificationIds}

	if err := bus.Dispatch(query); err != nil {
		return nil, err
	}

	var result []Notifier
	for _, notification := range query.Result {
		if not, err := n.createNotifierFor(notification); err != nil {
			return nil, err
		} else {
			if not.ShouldNotify(context) {
				result = append(result, not)
			}
		}
	}

	return result, nil
}

func (n *notificationService) createNotifierFor(model *m.AlertNotification) (Notifier, error) {
	notifierPlugin, found := notifierFactories[model.Type]
	if !found {
		return nil, errors.New("Unsupported notification type")
	}

	return notifierPlugin.Factory(model)
}

type NotifierFactory func(notification *m.AlertNotification) (Notifier, error)

var notifierFactories map[string]*NotifierPlugin = make(map[string]*NotifierPlugin)

func RegisterNotifier(plugin *NotifierPlugin) {
	notifierFactories[plugin.Type] = plugin
}

func GetNotifiers() []*NotifierPlugin {
	list := make([]*NotifierPlugin, 0)

	for _, value := range notifierFactories {
		list = append(list, value)
	}

	return list
}
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.76"
<commit_msg>functions: 0.3.77 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.77"
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.356"
<commit_msg>fnserver: 0.3.357 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.357"
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.495"
<commit_msg>fnserver: 0.3.496 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.496"
<|endoftext|>


<commit_before>package bradescoNetEmpresa

const registerBradescoNetEmpresa = `
## Content-Type:application/json
{
    {{if (eq .Recipient.Document.Type "CNPJ")}}
        "nuCPFCNPJ": "{{splitValues (extractNumbers .Recipient.Document.Number) 0 8}}",
    {{else}}
         "nuCPFCNPJ": "{{splitValues (extractNumbers .Recipient.Document.Number) 0 9}}",
	{{end}}

	{{if (eq .Recipient.Document.Type "CNPJ")}}
        "filialCPFCNPJ": "{{splitValues (extractNumbers .Recipient.Document.Number) 8 12}}",
    {{else}}
            "filialCPFCNPJ": "0",
	{{end}}

    {{if (eq .Recipient.Document.Type "CNPJ")}}
        "ctrlCPFCNPJ": "{{splitValues (extractNumbers .Recipient.Document.Number) 12 14}}",
    {{else}}
        "ctrlCPFCNPJ": "{{splitValues (extractNumbers .Recipient.Document.Number) 9 11}}",
    {{end}}
    "cdTipoAcesso": "2",
    "clubBanco": "2269651",
    "cdTipoContrato": "48",
    "idProduto": "{{padLeft (toString16 .Agreement.Wallet) "0" 2}}",
    "nuNegociacao": "{{.Agreement.Agency}}0000000{{.Agreement.Account}}",
    "cdBanco": "237",
    "tpRegistro": "1",
    "nuTitulo": "{{.Title.OurNumber}}",
    "nuCliente": "{{.Title.DocumentNumber}}",
	"dtEmissaoTitulo": "{{brDateDelimiterTime .Title.CreateDate "."}}",
    "dtVencimentoTitulo": "{{brDateDelimiter .Title.ExpireDate "."}}",
    "tpVencimento": "0",
    "vlNominalTitulo": "{{.Title.AmountInCents}}",
    "cdEspecieTitulo": "{{ .Title.BoletoTypeCode}}",
    "nomePagador": "{{truncate .Buyer.Name 70}}",
    "logradouroPagador": "{{truncate .Buyer.Address.Street 40}}",
    "nuLogradouroPagador": "{{truncate .Buyer.Address.Number 10}}",
    "complementoLogradouroPagador": "{{truncate .Buyer.Address.Complement 15}}",
	{{ $length := len .Buyer.Address.ZipCode }} {{ if ge $length 5}}
		"cepPagador": "{{splitValues (extractNumbers .Buyer.Address.ZipCode) 0 5}}",
	{{ end }}

	{{ $length := len .Buyer.Address.ZipCode }} {{ if ge $length 8}}
		"complementoCepPagador": "{{splitValues (extractNumbers .Buyer.Address.ZipCode) 5 8}}",
	{{ end }}

    "bairroPagador": "{{truncate .Buyer.Address.District 40}}",
    "municipioPagador": "{{truncate .Buyer.Address.City 30}}",
    "ufPagador": "{{truncate .Buyer.Address.StateCode 2}}",
    {{if (eq .Buyer.Document.Type "CPF")}}
    	"cdIndCpfcnpjPagador": "1",
    {{else}}
        "cdIndCpfcnpjPagador": "2",
    {{end}}
    "nuCpfcnpjPagador": "{{extractNumbers .Buyer.Document.Number}}",
    "endEletronicoPagador": "{{truncate .Buyer.Email 70}}",
}
`

const reponseBradescoNetEmpresaXml = `<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <ns2:registrarTituloResponse xmlns:ns2="http://ws.registrotitulo.ibpj.web.bradesco.com.br/">
			<return>{{contentJson}}</return>
        </ns2:registrarTituloResponse>
    </soapenv:Body>
</soapenv:Envelope>
`

const responseBradescoNetEmpresaJson = `{{.contentJson}}`

const reponseBradescoNetEmpresa = `
{
    "cdErro": "{{returnCode}}",
    "msgErro": "{{returnMessage}}",
    "linhaDigitavel": "{{digitableLine}}"
}
`

func getRequestBradescoNetEmpresa() string {
	return registerBradescoNetEmpresa
}

func getResponseBradescoNetEmpresaXml() string {
	return reponseBradescoNetEmpresaXml
}

func getResponseBradescoNetEmpresaJson() string {
	return responseBradescoNetEmpresaJson
}

func getResponseBradescoNetEmpresa() string {
	return reponseBradescoNetEmpresa
}
<commit_msg>declare if one time<commit_after>package bradescoNetEmpresa

const registerBradescoNetEmpresa = `
## Content-Type:application/json
{
    {{if (eq .Recipient.Document.Type "CNPJ")}}
        "nuCPFCNPJ": "{{splitValues (extractNumbers .Recipient.Document.Number) 0 8}}",
    {{else}}
         "nuCPFCNPJ": "{{splitValues (extractNumbers .Recipient.Document.Number) 0 9}}",
	{{end}}

	{{if (eq .Recipient.Document.Type "CNPJ")}}
        "filialCPFCNPJ": "{{splitValues (extractNumbers .Recipient.Document.Number) 8 12}}",
    {{else}}
            "filialCPFCNPJ": "0",
	{{end}}

    {{if (eq .Recipient.Document.Type "CNPJ")}}
        "ctrlCPFCNPJ": "{{splitValues (extractNumbers .Recipient.Document.Number) 12 14}}",
    {{else}}
        "ctrlCPFCNPJ": "{{splitValues (extractNumbers .Recipient.Document.Number) 9 11}}",
    {{end}}
    "cdTipoAcesso": "2",
    "clubBanco": "2269651",
    "cdTipoContrato": "48",
    "idProduto": "{{padLeft (toString16 .Agreement.Wallet) "0" 2}}",
    "nuNegociacao": "{{.Agreement.Agency}}0000000{{.Agreement.Account}}",
    "cdBanco": "237",
    "tpRegistro": "1",
    "nuTitulo": "{{.Title.OurNumber}}",
    "nuCliente": "{{.Title.DocumentNumber}}",
	"dtEmissaoTitulo": "{{brDateDelimiterTime .Title.CreateDate "."}}",
    "dtVencimentoTitulo": "{{brDateDelimiter .Title.ExpireDate "."}}",
    "tpVencimento": "0",
    "vlNominalTitulo": "{{.Title.AmountInCents}}",
    "cdEspecieTitulo": "{{ .Title.BoletoTypeCode}}",
    "nomePagador": "{{truncate .Buyer.Name 70}}",
    "logradouroPagador": "{{truncate .Buyer.Address.Street 40}}",
    "nuLogradouroPagador": "{{truncate .Buyer.Address.Number 10}}",
    "complementoLogradouroPagador": "{{truncate .Buyer.Address.Complement 15}}",
	{{ $length := len .Buyer.Address.ZipCode }}
	{{ if ge $length 5}}
		"cepPagador": "{{splitValues (extractNumbers .Buyer.Address.ZipCode) 0 5}}",
	{{ end }}

	{{ if ge $length 8}}
		"complementoCepPagador": "{{splitValues (extractNumbers .Buyer.Address.ZipCode) 5 8}}",
	{{ end }}

    "bairroPagador": "{{truncate .Buyer.Address.District 40}}",
    "municipioPagador": "{{truncate .Buyer.Address.City 30}}",
    "ufPagador": "{{truncate .Buyer.Address.StateCode 2}}",
    {{if (eq .Buyer.Document.Type "CPF")}}
    	"cdIndCpfcnpjPagador": "1",
    {{else}}
        "cdIndCpfcnpjPagador": "2",
    {{end}}
    "nuCpfcnpjPagador": "{{extractNumbers .Buyer.Document.Number}}",
    "endEletronicoPagador": "{{truncate .Buyer.Email 70}}",
}
`

const reponseBradescoNetEmpresaXml = `<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <ns2:registrarTituloResponse xmlns:ns2="http://ws.registrotitulo.ibpj.web.bradesco.com.br/">
			<return>{{contentJson}}</return>
        </ns2:registrarTituloResponse>
    </soapenv:Body>
</soapenv:Envelope>
`

const responseBradescoNetEmpresaJson = `{{.contentJson}}`

const reponseBradescoNetEmpresa = `
{
    "cdErro": "{{returnCode}}",
    "msgErro": "{{returnMessage}}",
    "linhaDigitavel": "{{digitableLine}}"
}
`

func getRequestBradescoNetEmpresa() string {
	return registerBradescoNetEmpresa
}

func getResponseBradescoNetEmpresaXml() string {
	return reponseBradescoNetEmpresaXml
}

func getResponseBradescoNetEmpresaJson() string {
	return responseBradescoNetEmpresaJson
}

func getResponseBradescoNetEmpresa() string {
	return reponseBradescoNetEmpresa
}
<|endoftext|>