<commit_before>package runconfig

import (
	"fmt"
	"github.com/dotcloud/docker/nat"
	"github.com/dotcloud/docker/opts"
	"github.com/dotcloud/docker/pkg/label"
	flag "github.com/dotcloud/docker/pkg/mflag"
	"github.com/dotcloud/docker/pkg/sysinfo"
	"github.com/dotcloud/docker/runtime/execdriver"
	"github.com/dotcloud/docker/utils"
	"io/ioutil"
	"path"
	"strings"
)

var (
	ErrInvalidWorikingDirectory = fmt.Errorf("The working directory is invalid. It needs to be an absolute path.")
	ErrConflictAttachDetach     = fmt.Errorf("Conflicting options: -a and -d")
	ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
)

//FIXME Only used in tests
func Parse(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
	cmd := flag.NewFlagSet("run", flag.ContinueOnError)
	cmd.SetOutput(ioutil.Discard)
	cmd.Usage = nil
	return parseRun(cmd, args, sysInfo)
}

// FIXME: this maps the legacy commands.go code. It should be merged with Parse to only expose a single parse function.
func ParseSubcommand(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
	return parseRun(cmd, args, sysInfo)
}

func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
	var (
		processLabel string
		mountLabel   string
	)
	var (
		// FIXME: use utils.ListOpts for attach and volumes?
		flAttach  = opts.NewListOpts(opts.ValidateAttach)
		flVolumes = opts.NewListOpts(opts.ValidatePath)
		flLinks   = opts.NewListOpts(opts.ValidateLink)
		flEnv     = opts.NewListOpts(opts.ValidateEnv)

		flPublish     opts.ListOpts
		flExpose      opts.ListOpts
		flDns         opts.ListOpts
		flDnsSearch   = opts.NewListOpts(opts.ValidateDomain)
		flVolumesFrom opts.ListOpts
		flLxcOpts     opts.ListOpts
		flDriverOpts  opts.ListOpts

		flAutoRemove      = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
		flDetach          = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
		flNetwork         = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container")
		flPrivileged      = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
		flPublishAll      = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
		flStdin           = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
		flTty             = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-tty")
		flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file")
		flEntrypoint      = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default entrypoint of the image")
		flHostname        = cmd.String([]string{"h", "-hostname"}, "", "Container host name")
		flMemoryString    = cmd.String([]string{"m", "-memory"}, "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
		flUser            = cmd.String([]string{"u", "-user"}, "", "Username or UID")
		flWorkingDir      = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
		flLabelOptions    = cmd.String([]string{"Z", "-label"}, "", "Options to pass to underlying labeling system")
		flEnvFile         = cmd.String([]string{"#env-file", "-env-file"}, "", "Read in a line delimited file of ENV variables")

		// For documentation purpose
		_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
		_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
	)

	cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to stdin, stdout or stderr.")
	cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
	cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
	cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")

	cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat))
	cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
	cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
	cmd.Var(&flDnsSearch, []string{"-dns-search"}, "Set custom dns search domains")
	cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
	cmd.Var(&flLxcOpts, []string{"#lxc-conf", "#-lxc-conf"}, "(lxc exec-driver only) Add custom lxc options --lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
	cmd.Var(&flDriverOpts, []string{"o", "-opt"}, "Add custom driver options")

	if err := cmd.Parse(args); err != nil {
		return nil, nil, cmd, err
	}

	// Check if the kernel supports memory limit cgroup.
	if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit {
		*flMemoryString = ""
	}

	// Validate input params
	if *flDetach && flAttach.Len() > 0 {
		return nil, nil, cmd, ErrConflictAttachDetach
	}
	if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
		return nil, nil, cmd, ErrInvalidWorikingDirectory
	}
	if *flDetach && *flAutoRemove {
		return nil, nil, cmd, ErrConflictDetachAutoRemove
	}

	// If neither -d or -a are set, attach to everything by default
	if flAttach.Len() == 0 && !*flDetach {
		if !*flDetach {
			flAttach.Set("stdout")
			flAttach.Set("stderr")
			if *flStdin {
				flAttach.Set("stdin")
			}
		}
	}

	var flMemory int64
	if *flMemoryString != "" {
		parsedMemory, err := utils.RAMInBytes(*flMemoryString)
		if err != nil {
			return nil, nil, cmd, err
		}
		flMemory = parsedMemory
	}

	var binds []string
	// add any bind targets to the list of container volumes
	for bind := range flVolumes.GetMap() {
		if arr := strings.Split(bind, ":"); len(arr) > 1 {
			if arr[0] == "/" {
				return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
			}
			dstDir := arr[1]
			flVolumes.Set(dstDir)
			binds = append(binds, bind)
			flVolumes.Delete(bind)
		} else if bind == "/" {
			return nil, nil, cmd, fmt.Errorf("Invalid volume: path can't be '/'")
		}
	}

	var (
		parsedArgs = cmd.Args()
		runCmd     []string
		entrypoint []string
		image      string
	)
	if len(parsedArgs) >= 1 {
		image = cmd.Arg(0)
	}
	if len(parsedArgs) > 1 {
		runCmd = parsedArgs[1:]
	}
	if *flEntrypoint != "" {
		entrypoint = []string{*flEntrypoint}
	}

	if !*flPrivileged {
		pLabel, mLabel, e := label.GenLabels(*flLabelOptions)
		if e != nil {
			return nil, nil, cmd, fmt.Errorf("Invalid security labels : %s", e)
		}
		processLabel = pLabel
		mountLabel = mLabel
	}

	lxcConf, err := parseKeyValueOpts(flLxcOpts)
	if err != nil {
		return nil, nil, cmd, err
	}

	var (
		domainname string
		hostname   = *flHostname
		parts      = strings.SplitN(hostname, ".", 2)
	)
	if len(parts) > 1 {
		hostname = parts[0]
		domainname = parts[1]
	}

	ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
	if err != nil {
		return nil, nil, cmd, err
	}

	// Merge in exposed ports to the map of published ports
	for _, e := range flExpose.GetAll() {
		if strings.Contains(e, ":") {
			return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
		}
		p := nat.NewPort(nat.SplitProtoPort(e))
		if _, exists := ports[p]; !exists {
			ports[p] = struct{}{}
		}
	}

	// collect all the environment variables for the container
	envVariables := []string{}
	envVariables = append(envVariables, flEnv.GetAll()...)
	parsedVars, err := opts.ParseEnvFile(*flEnvFile)
	if err != nil {
		return nil, nil, cmd, err
	}
	envVariables = append(envVariables, parsedVars...)
	// boo, there's no debug output for docker run
	//utils.Debugf("Environment variables for the container: %#v", envVariables)

	config := &Config{
		Hostname:        hostname,
		Domainname:      domainname,
		PortSpecs:       nil, // Deprecated
		ExposedPorts:    ports,
		User:            *flUser,
		Tty:             *flTty,
		NetworkDisabled: !*flNetwork,
		OpenStdin:       *flStdin,
		Memory:          flMemory,
		CpuShares:       *flCpuShares,
		AttachStdin:     flAttach.Get("stdin"),
		AttachStdout:    flAttach.Get("stdout"),
		AttachStderr:    flAttach.Get("stderr"),
		Env:             envVariables,
		Cmd:             runCmd,
		Dns:             flDns.GetAll(),
		DnsSearch:       flDnsSearch.GetAll(),
		Image:           image,
		Volumes:         flVolumes.GetMap(),
		VolumesFrom:     strings.Join(flVolumesFrom.GetAll(), ","),
		Entrypoint:      entrypoint,
		WorkingDir:      *flWorkingDir,
		Context: execdriver.Context{
			"mount_label":   mountLabel,
			"process_label": processLabel,
		},
	}

	driverOptions, err := parseDriverOpts(flDriverOpts)
	if err != nil {
		return nil, nil, cmd, err
	}

	hostConfig := &HostConfig{
		Binds:           binds,
		ContainerIDFile: *flContainerIDFile,
		LxcConf:         lxcConf,
		Privileged:      *flPrivileged,
		PortBindings:    portBindings,
		Links:           flLinks.GetAll(),
		PublishAllPorts: *flPublishAll,
		DriverOptions:   driverOptions,
	}

	if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
		config.MemorySwap = -1
	}

	// When allocating stdin in attached mode, close stdin at client disconnect
	if config.OpenStdin && config.AttachStdin {
		config.StdinOnce = true
	}
	return config, hostConfig, cmd, nil
}

// options will come in the format of name.key=value or name.option
func parseDriverOpts(opts opts.ListOpts) (map[string][]string, error) {
	out := make(map[string][]string, len(opts.GetAll()))
	for _, o := range opts.GetAll() {
		parts := strings.SplitN(o, ".", 2)
		if len(parts) < 2 {
			return nil, fmt.Errorf("invalid opt format %s", o)
		}
		values, exists := out[parts[0]]
		if !exists {
			values = []string{}
		}
		out[parts[0]] = append(values, parts[1])
	}
	return out, nil
}

func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) {
	out := make([]utils.KeyValuePair, opts.Len())
	for i, o := range opts.GetAll() {
		k, v, err := utils.ParseKeyValueOpt(o)
		if err != nil {
			return nil, err
		}
		out[i] = utils.KeyValuePair{Key: k, Value: v}
	}
	return out, nil
}
<commit_msg>make the --env-file accept multiple flags<commit_after>package runconfig

import (
	"fmt"
	"github.com/dotcloud/docker/nat"
	"github.com/dotcloud/docker/opts"
	"github.com/dotcloud/docker/pkg/label"
	flag "github.com/dotcloud/docker/pkg/mflag"
	"github.com/dotcloud/docker/pkg/sysinfo"
	"github.com/dotcloud/docker/runtime/execdriver"
	"github.com/dotcloud/docker/utils"
	"io/ioutil"
	"path"
	"strings"
)

var (
	ErrInvalidWorikingDirectory = fmt.Errorf("The working directory is invalid. It needs to be an absolute path.")
	ErrConflictAttachDetach     = fmt.Errorf("Conflicting options: -a and -d")
	ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
)

//FIXME Only used in tests
func Parse(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
	cmd := flag.NewFlagSet("run", flag.ContinueOnError)
	cmd.SetOutput(ioutil.Discard)
	cmd.Usage = nil
	return parseRun(cmd, args, sysInfo)
}

// FIXME: this maps the legacy commands.go code. It should be merged with Parse to only expose a single parse function.
func ParseSubcommand(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
	return parseRun(cmd, args, sysInfo)
}

func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
	var (
		processLabel string
		mountLabel   string
	)
	var (
		// FIXME: use utils.ListOpts for attach and volumes?
		flAttach  = opts.NewListOpts(opts.ValidateAttach)
		flVolumes = opts.NewListOpts(opts.ValidatePath)
		flLinks   = opts.NewListOpts(opts.ValidateLink)
		flEnv     = opts.NewListOpts(opts.ValidateEnv)

		flPublish     opts.ListOpts
		flExpose      opts.ListOpts
		flDns         opts.ListOpts
		flDnsSearch   = opts.NewListOpts(opts.ValidateDomain)
		flVolumesFrom opts.ListOpts
		flLxcOpts     opts.ListOpts
		flDriverOpts  opts.ListOpts
		flEnvFile     opts.ListOpts

		flAutoRemove      = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
		flDetach          = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
		flNetwork         = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container")
		flPrivileged      = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
		flPublishAll      = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
		flStdin           = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
		flTty             = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-tty")
		flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file")
		flEntrypoint      = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default entrypoint of the image")
		flHostname        = cmd.String([]string{"h", "-hostname"}, "", "Container host name")
		flMemoryString    = cmd.String([]string{"m", "-memory"}, "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
		flUser            = cmd.String([]string{"u", "-user"}, "", "Username or UID")
		flWorkingDir      = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
		flLabelOptions    = cmd.String([]string{"Z", "-label"}, "", "Options to pass to underlying labeling system")

		// For documentation purpose
		_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
		_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
	)

	cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to stdin, stdout or stderr.")
	cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
	cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
	cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
	cmd.Var(&flEnvFile, []string{"#env-file", "-env-file"}, "Read in a line delimited file of ENV variables")

	cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat))
	cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
	cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
	cmd.Var(&flDnsSearch, []string{"-dns-search"}, "Set custom dns search domains")
	cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
	cmd.Var(&flLxcOpts, []string{"#lxc-conf", "#-lxc-conf"}, "(lxc exec-driver only) Add custom lxc options --lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
	cmd.Var(&flDriverOpts, []string{"o", "-opt"}, "Add custom driver options")

	if err := cmd.Parse(args); err != nil {
		return nil, nil, cmd, err
	}

	// Check if the kernel supports memory limit cgroup.
	if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit {
		*flMemoryString = ""
	}

	// Validate input params
	if *flDetach && flAttach.Len() > 0 {
		return nil, nil, cmd, ErrConflictAttachDetach
	}
	if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
		return nil, nil, cmd, ErrInvalidWorikingDirectory
	}
	if *flDetach && *flAutoRemove {
		return nil, nil, cmd, ErrConflictDetachAutoRemove
	}

	// If neither -d or -a are set, attach to everything by default
	if flAttach.Len() == 0 && !*flDetach {
		if !*flDetach {
			flAttach.Set("stdout")
			flAttach.Set("stderr")
			if *flStdin {
				flAttach.Set("stdin")
			}
		}
	}

	var flMemory int64
	if *flMemoryString != "" {
		parsedMemory, err := utils.RAMInBytes(*flMemoryString)
		if err != nil {
			return nil, nil, cmd, err
		}
		flMemory = parsedMemory
	}

	var binds []string
	// add any bind targets to the list of container volumes
	for bind := range flVolumes.GetMap() {
		if arr := strings.Split(bind, ":"); len(arr) > 1 {
			if arr[0] == "/" {
				return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
			}
			dstDir := arr[1]
			flVolumes.Set(dstDir)
			binds = append(binds, bind)
			flVolumes.Delete(bind)
		} else if bind == "/" {
			return nil, nil, cmd, fmt.Errorf("Invalid volume: path can't be '/'")
		}
	}

	var (
		parsedArgs = cmd.Args()
		runCmd     []string
		entrypoint []string
		image      string
	)
	if len(parsedArgs) >= 1 {
		image = cmd.Arg(0)
	}
	if len(parsedArgs) > 1 {
		runCmd = parsedArgs[1:]
	}
	if *flEntrypoint != "" {
		entrypoint = []string{*flEntrypoint}
	}

	if !*flPrivileged {
		pLabel, mLabel, e := label.GenLabels(*flLabelOptions)
		if e != nil {
			return nil, nil, cmd, fmt.Errorf("Invalid security labels : %s", e)
		}
		processLabel = pLabel
		mountLabel = mLabel
	}

	lxcConf, err := parseKeyValueOpts(flLxcOpts)
	if err != nil {
		return nil, nil, cmd, err
	}

	var (
		domainname string
		hostname   = *flHostname
		parts      = strings.SplitN(hostname, ".", 2)
	)
	if len(parts) > 1 {
		hostname = parts[0]
		domainname = parts[1]
	}

	ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
	if err != nil {
		return nil, nil, cmd, err
	}

	// Merge in exposed ports to the map of published ports
	for _, e := range flExpose.GetAll() {
		if strings.Contains(e, ":") {
			return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
		}
		p := nat.NewPort(nat.SplitProtoPort(e))
		if _, exists := ports[p]; !exists {
			ports[p] = struct{}{}
		}
	}

	// collect all the environment variables for the container
	envVariables := []string{}
	envVariables = append(envVariables, flEnv.GetAll()...)
	for _, ef := range flEnvFile.GetAll() {
		parsedVars, err := opts.ParseEnvFile(ef)
		if err != nil {
			return nil, nil, cmd, err
		}
		envVariables = append(envVariables, parsedVars...)
	}
	// boo, there's no debug output for docker run
	//utils.Debugf("Environment variables for the container: %#v", envVariables)

	config := &Config{
		Hostname:        hostname,
		Domainname:      domainname,
		PortSpecs:       nil, // Deprecated
		ExposedPorts:    ports,
		User:            *flUser,
		Tty:             *flTty,
		NetworkDisabled: !*flNetwork,
		OpenStdin:       *flStdin,
		Memory:          flMemory,
		CpuShares:       *flCpuShares,
		AttachStdin:     flAttach.Get("stdin"),
		AttachStdout:    flAttach.Get("stdout"),
		AttachStderr:    flAttach.Get("stderr"),
		Env:             envVariables,
		Cmd:             runCmd,
		Dns:             flDns.GetAll(),
		DnsSearch:       flDnsSearch.GetAll(),
		Image:           image,
		Volumes:         flVolumes.GetMap(),
		VolumesFrom:     strings.Join(flVolumesFrom.GetAll(), ","),
		Entrypoint:      entrypoint,
		WorkingDir:      *flWorkingDir,
		Context: execdriver.Context{
			"mount_label":   mountLabel,
			"process_label": processLabel,
		},
	}

	driverOptions, err := parseDriverOpts(flDriverOpts)
	if err != nil {
		return nil, nil, cmd, err
	}

	hostConfig := &HostConfig{
		Binds:           binds,
		ContainerIDFile: *flContainerIDFile,
		LxcConf:         lxcConf,
		Privileged:      *flPrivileged,
		PortBindings:    portBindings,
		Links:           flLinks.GetAll(),
		PublishAllPorts: *flPublishAll,
		DriverOptions:   driverOptions,
	}

	if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
		config.MemorySwap = -1
	}

	// When allocating stdin in attached mode, close stdin at client disconnect
	if config.OpenStdin && config.AttachStdin {
		config.StdinOnce = true
	}
	return config, hostConfig, cmd, nil
}

// options will come in the format of name.key=value or name.option
func parseDriverOpts(opts opts.ListOpts) (map[string][]string, error) {
	out := make(map[string][]string, len(opts.GetAll()))
	for _, o := range opts.GetAll() {
		parts := strings.SplitN(o, ".", 2)
		if len(parts) < 2 {
			return nil, fmt.Errorf("invalid opt format %s", o)
		}
		values, exists := out[parts[0]]
		if !exists {
			values = []string{}
		}
		out[parts[0]] = append(values, parts[1])
	}
	return out, nil
}

func parseKeyValueOpts(opts opts.ListOpts) ([]utils.KeyValuePair, error) {
	out := make([]utils.KeyValuePair, opts.Len())
	for i, o := range opts.GetAll() {
		k, v, err := utils.ParseKeyValueOpt(o)
		if err != nil {
			return nil, err
		}
		out[i] = utils.KeyValuePair{Key: k, Value: v}
	}
	return out, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/vivowares/octopus/Godeps/_workspace/src/github.com/gorilla/websocket"
	"math/rand"
	"net/http"
	"net/url"
	"runtime"
	"sync"
	"time"
)

func main() {
	s := flag.String("s", "localhost:8081", "the target server host:port")
	n := flag.Int("n", 1000, "number of concurrent clients")
	ch := flag.String("ch", "MQ==", "channel id for testing")
	tk := flag.String("t", "1234567", "access token used for testing")
	np := flag.Int("p", 100, "number of ping messages to send")
	nm := flag.Int("m", 50, "number of payload messages to send")
	rw := flag.Duration("r", 15*time.Second, "wait time for reading messages")
	ww := flag.Duration("w", 2*time.Second, "wait time for writing messages")
	itv := flag.Int("i", 3, "wait interval between each sends in client, randomized")
	citv := flag.Int("I", 5, "wait interval between each connection, randomized")

	flag.Parse()

	runtime.GOMAXPROCS(runtime.NumCPU())

	clients := make([]*WsClient, *n)
	var wg sync.WaitGroup
	wg.Add(*n)

	for i := 0; i < *n; i++ {
		time.Sleep(time.Duration(rand.Intn(*citv)) * time.Second)
		go func(idx int) {
			defer wg.Done()
			c := &WsClient{
				Server:      *s,
				ChannelId:   *ch,
				DeviceId:    fmt.Sprintf("device-%d", idx),
				AccessToken: *tk,
				NPing:       *np,
				NMessage:    *nm,
				RWait:       *rw,
				WWait:       *ww,
				Itv:         *itv,
				ch:          make(chan struct{}),
			}

			clients[idx] = c
			c.StartTest()
		}(i)
	}

	wg.Wait()

	report := make(map[string]interface{})
	report["total_clients"] = *n

	var connErrs int
	var pingErrs int
	var pings int
	var msgs int
	var pongs int
	var closeErrs int
	var msgErrs int
	var msgSent int
	var pingSent int

	for _, c := range clients {
		pings += c.NPing
		msgs += c.NMessage
		pongs += c.Pongs
		msgErrs += c.MessageErr
		pingErrs += c.PingErr
		msgSent += c.MessageSent
		pingSent += c.PingSent

		if c.ConnErr != nil {
			connErrs += 1
		}

		if c.MessageCloseErr != nil {
			closeErrs += 1
		}
	}

	report["total_conn_errs"] = connErrs
	report["total_ping_errs"] = pingErrs
	report["total_close_errs"] = closeErrs
	report["total_pings"] = pings
	report["total_pongs"] = pongs
	report["total_msgs"] = msgs
	report["total_msg_errs"] = msgErrs
	report["total_msg_sent"] = msgSent
	report["total_ping_sent"] = pingSent

	js, _ := json.MarshalIndent(report, "", "  ")
	fmt.Println(string(js))
}

type WsClient struct {
	Server      string
	ChannelId   string
	DeviceId    string
	AccessToken string
	NPing       int
	NMessage    int
	RWait       time.Duration
	WWait       time.Duration
	Itv         int
	wg          sync.WaitGroup
	ch          chan struct{}

	Cli             *websocket.Conn
	ConnErr         error
	ConnResp        *http.Response
	PingErr         int
	Pongs           int
	MessageErr      int
	MessageCloseErr error
	MessageSent     int
	PingSent        int
}

func (c *WsClient) StartTest() {
	p := fmt.Sprintf("/ws/channels/%s/devices/%s", c.ChannelId, c.DeviceId)
	u := url.URL{Scheme: "ws", Host: c.Server, Path: p}
	h := map[string][]string{"AccessToken": []string{c.AccessToken}}

	cli, resp, err := websocket.DefaultDialer.Dial(u.String(), h)
	c.ConnErr = err
	c.ConnResp = resp
	c.Cli = cli

	if err != nil {
		return
	}

	cli.SetPongHandler(func(string) error {
		c.Pongs += 1
		return nil
	})
	c.wg.Add(2)

	go func() {
		defer c.wg.Done()

		for {
			select {
			case <-c.ch:
				return
			default:
				cli.SetReadDeadline(time.Now().Add(c.RWait))
				cli.ReadMessage()
			}
		}

	}()

	go func() {
		defer c.wg.Done()

		n := 0
		m := 0

		for n < c.NPing || m < c.NMessage {
			cli.SetWriteDeadline(time.Now().Add(c.WWait))
			if n >= c.NPing {
				err := cli.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("1|%d|temperature=%.2f", rand.Int31(), rand.Float32()*100)))
				m += 1
				if err != nil {
					c.MessageErr += 1
				}
			} else if m >= c.NMessage {
				err := cli.WriteMessage(websocket.PingMessage, []byte{})
				n += 1
				if err != nil {
					c.PingErr += 1
				}
			} else {
				r := rand.Intn(2)
				if r == 0 {
					err := cli.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("1|%d|temperature=%.2f", rand.Int31(), rand.Float32()*100)))
					m += 1
					if err != nil {
						c.MessageErr += 1
					}
				} else {
					err := cli.WriteMessage(websocket.PingMessage, []byte{})
					n += 1
					if err != nil {
						c.PingErr += 1
					}
				}
			}
			time.Sleep(time.Duration(c.Itv) * time.Second)
		}

		c.MessageSent = m
		c.PingSent = n

		time.Sleep(3 * time.Second)
		close(c.ch)

	}()

	c.wg.Wait()

	cli.SetWriteDeadline(time.Now().Add(c.WWait))
	err = cli.WriteMessage(websocket.CloseMessage, []byte{})
	if err != nil {
		c.MessageCloseErr = err
	}

}
<commit_msg>use ms to better control the benchmark<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/vivowares/octopus/Godeps/_workspace/src/github.com/gorilla/websocket"
	"math/rand"
	"net/http"
	"net/url"
	"runtime"
	"sync"
	"time"
)

func main() {
	s := flag.String("s", "localhost:8081", "the target server host:port")
	n := flag.Int("n", 1000, "number of concurrent clients")
	ch := flag.String("ch", "MQ==", "channel id for testing")
	tk := flag.String("t", "1234567", "access token used for testing")
	np := flag.Int("p", 100, "number of ping messages to send")
	nm := flag.Int("m", 50, "number of payload messages to send")
	rw := flag.Duration("r", 15*time.Second, "wait time for reading messages")
	ww := flag.Duration("w", 2*time.Second, "wait time for writing messages")
	itv := flag.Int("i", 5000, "wait milliseconds interval between each sends in client, randomized")
	citv := flag.Int("I", 1000, "wait milliseconds interval between each connection, randomized")

	flag.Parse()

	runtime.GOMAXPROCS(runtime.NumCPU())

	clients := make([]*WsClient, *n)
	var wg sync.WaitGroup
	wg.Add(*n)

	for i := 0; i < *n; i++ {
		time.Sleep(time.Duration(rand.Intn(*citv)) * time.Millisecond)
		go func(idx int) {
			defer wg.Done()
			c := &WsClient{
				Server:      *s,
				ChannelId:   *ch,
				DeviceId:    fmt.Sprintf("device-%d", idx),
				AccessToken: *tk,
				NPing:       *np,
				NMessage:    *nm,
				RWait:       *rw,
				WWait:       *ww,
				Itv:         *itv,
				ch:          make(chan struct{}),
			}

			clients[idx] = c
			c.StartTest()
		}(i)
	}

	wg.Wait()

	report := make(map[string]interface{})
	report["total_clients"] = *n

	var connErrs int
	var pingErrs int
	var pings int
	var msgs int
	var pongs int
	var closeErrs int
	var msgErrs int
	var msgSent int
	var pingSent int

	for _, c := range clients {
		pings += c.NPing
		msgs += c.NMessage
		pongs += c.Pongs
		msgErrs += c.MessageErr
		pingErrs += c.PingErr
		msgSent += c.MessageSent
		pingSent += c.PingSent

		if c.ConnErr != nil {
			connErrs += 1
		}

		if c.MessageCloseErr != nil {
			closeErrs += 1
		}
	}

	report["total_conn_errs"] = connErrs
	report["total_ping_errs"] = pingErrs
	report["total_close_errs"] = closeErrs
	report["total_pings"] = pings
	report["total_pongs"] = pongs
	report["total_msgs"] = msgs
	report["total_msg_errs"] = msgErrs
	report["total_msg_sent"] = msgSent
	report["total_ping_sent"] = pingSent

	js, _ := json.MarshalIndent(report, "", "  ")
	fmt.Println(string(js))
}

type WsClient struct {
	Server      string
	ChannelId   string
	DeviceId    string
	AccessToken string
	NPing       int
	NMessage    int
	RWait       time.Duration
	WWait       time.Duration
	Itv         int
	wg          sync.WaitGroup
	ch          chan struct{}

	Cli             *websocket.Conn
	ConnErr         error
	ConnResp        *http.Response
	PingErr         int
	Pongs           int
	MessageErr      int
	MessageCloseErr error
	MessageSent     int
	PingSent        int
}

func (c *WsClient) StartTest() {
	p := fmt.Sprintf("/ws/channels/%s/devices/%s", c.ChannelId, c.DeviceId)
	u := url.URL{Scheme: "ws", Host: c.Server, Path: p}
	h := map[string][]string{"AccessToken": []string{c.AccessToken}}

	cli, resp, err := websocket.DefaultDialer.Dial(u.String(), h)
	c.ConnErr = err
	c.ConnResp = resp
	c.Cli = cli

	if err != nil {
		return
	}

	cli.SetPongHandler(func(string) error {
		c.Pongs += 1
		return nil
	})
	c.wg.Add(2)

	go func() {
		defer c.wg.Done()

		for {
			select {
			case <-c.ch:
				return
			default:
				cli.SetReadDeadline(time.Now().Add(c.RWait))
				cli.ReadMessage()
			}
		}

	}()

	go func() {
		defer c.wg.Done()

		n := 0
		m := 0

		for n < c.NPing || m < c.NMessage {
			cli.SetWriteDeadline(time.Now().Add(c.WWait))
			if n >= c.NPing {
				err := cli.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("1|%d|temperature=%.2f", rand.Int31(), rand.Float32()*100)))
				m += 1
				if err != nil {
					c.MessageErr += 1
				}
			} else if m >= c.NMessage {
				err := cli.WriteMessage(websocket.PingMessage, []byte{})
				n += 1
				if err != nil {
					c.PingErr += 1
				}
			} else {
				r := rand.Intn(2)
				if r == 0 {
					err := cli.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("1|%d|temperature=%.2f", rand.Int31(), rand.Float32()*100)))
					m += 1
					if err != nil {
						c.MessageErr += 1
					}
				} else {
					err := cli.WriteMessage(websocket.PingMessage, []byte{})
					n += 1
					if err != nil {
						c.PingErr += 1
					}
				}
			}
			time.Sleep(time.Duration(c.Itv) * time.Millisecond)
		}

		c.MessageSent = m
		c.PingSent = n

		time.Sleep(3 * time.Second)
		close(c.ch)

	}()

	c.wg.Wait()

	cli.SetWriteDeadline(time.Now().Add(c.WWait))
	err = cli.WriteMessage(websocket.CloseMessage, []byte{})
	if err != nil {
		c.MessageCloseErr = err
	}

}
<|endoftext|>


<commit_before>package main

// Originally adapted from https://github.com/awslabs/service-discovery-ecs-dns
// This service is intended to run on AWS ECS instances. It will look for special
// SERVICE environment variables and add those services to a Redis ZSET with the
// timestamp as the key. The accompanying service, red53_agent will check this
// ZSET and write changes to AWS Route53.

import (
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"gopkg.in/redis.v5"
	"os"
	"time"
)

const reportInterval = 30 * time.Second // how often to report

type config struct {
	LocalIp    string
	RedisHost  string
}

var configuration config

func logErrorAndFail(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func logErrorNoFatal(err error) {
	if err != nil {
		log.Error(err)
	}
}

var redisClient *redis.Client

// This reports an instance as active. It adds it to the redecs:service_pings set
// and publishes a redecs:service_channel event.
func reportActiveService(serviceName string) error {
	log.Debugf("Reporting %s as active with IP %s", serviceName, configuration.LocalIp)
	value := serviceName + "_" + configuration.LocalIp
	// write {timestamp => SERVICENAME_IP} to redecs:service_pings
	err := redisClient.ZAdd("redecs:service_pings", redis.Z{float64(time.Now().Unix()), value}).Err()
	if err != nil {
		return err
	}
	// notify the channel that this service has been reported active
	err = redisClient.Publish("redecs:service_channel", "+"+value).Err()
	return err
}

// Gets the service name from the SERVICE_NAME environment variable.
func getServiceName() string {
	return os.Getenv("SERVICE_NAME")
}

func main() {
	var err error
	var sum int
	if len(os.Args) < 2 || len(os.Args) > 3 {
		err = errors.New(fmt.Sprintf("Usage: %s [Redis host] {IP for testing purposes}\n", os.Args[0]))
		logErrorAndFail(err)
	}
	if len(os.Args) == 3 {
		configuration.LocalIp = os.Args[2]
	} else {
		metadataClient := ec2metadata.New(session.New())
		localIp, err := metadataClient.GetMetadata("/local-ipv4")
		logErrorAndFail(err)
		configuration.LocalIp = localIp
	}
	configuration.RedisHost = os.Args[1]

	serviceName := getServiceName()
	if serviceName == "" {
		err = errors.New("'SERVICE_NAME' environment variable is not defined! Exiting.")
		logErrorAndFail(err)
	}

	sum = 1
	for {
		// We try to get the Redis connection using exponential backoff
		redisClient = redis.NewClient(&redis.Options{
			Addr:       configuration.RedisHost + ":6379",
			Password:   "", // none
			DB:         0,  // default DB
			MaxRetries: 3,
		})
		// Check the connection.
		err := redisClient.Ping().Err()

		if err == nil {
			break
		}
		if sum > 8 {
			logErrorAndFail(err)
		}
		time.Sleep(time.Duration(sum) * time.Second)
		sum += 2
	}

	log.Debug("redecs_agent started")

	// continue processing once per minute
	ticker := time.NewTicker(reportInterval)

	for {
		reportActiveService(serviceName)
		<-ticker.C
	}
}
<commit_msg>Remove redecs_agent.go publish events (causing too many route53 requests with red53)<commit_after>package main

// Originally adapted from https://github.com/awslabs/service-discovery-ecs-dns
// This service is intended to run on AWS ECS instances. It will look for special
// SERVICE environment variables and add those services to a Redis ZSET with the
// timestamp as the key. The accompanying service, red53_agent will check this
// ZSET and write changes to AWS Route53.

import (
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"gopkg.in/redis.v5"
	"os"
	"time"
)

const reportInterval = 30 * time.Second // how often to report

type config struct {
	LocalIp    string
	RedisHost  string
}

var configuration config

func logErrorAndFail(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func logErrorNoFatal(err error) {
	if err != nil {
		log.Error(err)
	}
}

var redisClient *redis.Client

// This reports an instance as active. It adds it to the redecs:service_pings set
// and publishes a redecs:service_channel event.
func reportActiveService(serviceName string) error {
	log.Debugf("Reporting %s as active with IP %s", serviceName, configuration.LocalIp)
	value := serviceName + "_" + configuration.LocalIp
	// write {timestamp => SERVICENAME_IP} to redecs:service_pings
	err := redisClient.ZAdd("redecs:service_pings", redis.Z{float64(time.Now().Unix()), value}).Err()
	return err
}

// Gets the service name from the SERVICE_NAME environment variable.
func getServiceName() string {
	return os.Getenv("SERVICE_NAME")
}

func main() {
	var err error
	var sum int
	if len(os.Args) < 2 || len(os.Args) > 3 {
		err = errors.New(fmt.Sprintf("Usage: %s [Redis host] {IP for testing purposes}\n", os.Args[0]))
		logErrorAndFail(err)
	}
	if len(os.Args) == 3 {
		configuration.LocalIp = os.Args[2]
	} else {
		metadataClient := ec2metadata.New(session.New())
		localIp, err := metadataClient.GetMetadata("/local-ipv4")
		logErrorAndFail(err)
		configuration.LocalIp = localIp
	}
	configuration.RedisHost = os.Args[1]

	serviceName := getServiceName()
	if serviceName == "" {
		err = errors.New("'SERVICE_NAME' environment variable is not defined! Exiting.")
		logErrorAndFail(err)
	}

	sum = 1
	for {
		// We try to get the Redis connection using exponential backoff
		redisClient = redis.NewClient(&redis.Options{
			Addr:       configuration.RedisHost + ":6379",
			Password:   "", // none
			DB:         0,  // default DB
			MaxRetries: 3,
		})
		// Check the connection.
		err := redisClient.Ping().Err()

		if err == nil {
			break
		}
		if sum > 8 {
			logErrorAndFail(err)
		}
		time.Sleep(time.Duration(sum) * time.Second)
		sum += 2
	}

	log.Debug("redecs_agent started")

	// continue processing once per minute
	ticker := time.NewTicker(reportInterval)

	for {
		reportActiveService(serviceName)
		<-ticker.C
	}
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd 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 docker

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/log"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"golang.org/x/net/context/ctxhttp"
)

type dockerAuthorizer struct {
	credentials func(string) (string, string, error)

	client *http.Client
	header http.Header
	mu     sync.Mutex

	// indexed by host name
	handlers map[string]*authHandler
}

// NewAuthorizer creates a Docker authorizer using the provided function to
// get credentials for the token server or basic auth.
// Deprecated: Use NewDockerAuthorizer
func NewAuthorizer(client *http.Client, f func(string) (string, string, error)) Authorizer {
	return NewDockerAuthorizer(WithAuthClient(client), WithAuthCreds(f))
}

type authorizerConfig struct {
	credentials func(string) (string, string, error)
	client      *http.Client
	header      http.Header
}

// AuthorizerOpt configures an authorizer
type AuthorizerOpt func(*authorizerConfig)

// WithAuthClient provides the HTTP client for the authorizer
func WithAuthClient(client *http.Client) AuthorizerOpt {
	return func(opt *authorizerConfig) {
		opt.client = client
	}
}

// WithAuthCreds provides a credential function to the authorizer
func WithAuthCreds(creds func(string) (string, string, error)) AuthorizerOpt {
	return func(opt *authorizerConfig) {
		opt.credentials = creds
	}
}

// WithAuthHeader provides HTTP headers for authorization
func WithAuthHeader(hdr http.Header) AuthorizerOpt {
	return func(opt *authorizerConfig) {
		opt.header = hdr
	}
}

// NewDockerAuthorizer creates an authorizer using Docker's registry
// authentication spec.
// See https://docs.docker.com/registry/spec/auth/
func NewDockerAuthorizer(opts ...AuthorizerOpt) Authorizer {
	var ao authorizerConfig
	for _, opt := range opts {
		opt(&ao)
	}

	if ao.client == nil {
		ao.client = http.DefaultClient
	}

	return &dockerAuthorizer{
		credentials: ao.credentials,
		client:      ao.client,
		header:      ao.header,
		handlers:    make(map[string]*authHandler),
	}
}

// Authorize handles auth request.
func (a *dockerAuthorizer) Authorize(ctx context.Context, req *http.Request) error {
	// skip if there is no auth handler
	ah := a.getAuthHandler(req.URL.Host)
	if ah == nil {
		return nil
	}

	auth, err := ah.authorize(ctx)
	if err != nil {
		return err
	}

	req.Header.Set("Authorization", auth)
	return nil
}

func (a *dockerAuthorizer) getAuthHandler(host string) *authHandler {
	a.mu.Lock()
	defer a.mu.Unlock()

	return a.handlers[host]
}

func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.Response) error {
	last := responses[len(responses)-1]
	host := last.Request.URL.Host

	a.mu.Lock()
	defer a.mu.Unlock()
	for _, c := range parseAuthHeader(last.Header) {
		if c.scheme == bearerAuth {
			if err := invalidAuthorization(c, responses); err != nil {
				delete(a.handlers, host)
				return err
			}

			// reuse existing handler
			//
			// assume that one registry will return the common
			// challenge information, including realm and service.
			// and the resource scope is only different part
			// which can be provided by each request.
			if _, ok := a.handlers[host]; ok {
				return nil
			}

			common, err := a.generateTokenOptions(ctx, host, c)
			if err != nil {
				return err
			}

			a.handlers[host] = newAuthHandler(a.client, a.header, c.scheme, common)
			return nil
		} else if c.scheme == basicAuth && a.credentials != nil {
			username, secret, err := a.credentials(host)
			if err != nil {
				return err
			}

			if username != "" && secret != "" {
				common := tokenOptions{
					username: username,
					secret:   secret,
				}

				a.handlers[host] = newAuthHandler(a.client, a.header, c.scheme, common)
				return nil
			}
		}
	}
	return errors.Wrap(errdefs.ErrNotImplemented, "failed to find supported auth scheme")
}

func (a *dockerAuthorizer) generateTokenOptions(ctx context.Context, host string, c challenge) (tokenOptions, error) {
	realm, ok := c.parameters["realm"]
	if !ok {
		return tokenOptions{}, errors.New("no realm specified for token auth challenge")
	}

	realmURL, err := url.Parse(realm)
	if err != nil {
		return tokenOptions{}, errors.Wrap(err, "invalid token auth challenge realm")
	}

	to := tokenOptions{
		realm:   realmURL.String(),
		service: c.parameters["service"],
	}

	scope, ok := c.parameters["scope"]
	if !ok {
		return tokenOptions{}, errors.Errorf("no scope specified for token auth challenge")
	}
	to.scopes = append(to.scopes, scope)

	if a.credentials != nil {
		to.username, to.secret, err = a.credentials(host)
		if err != nil {
			return tokenOptions{}, err
		}
	}
	return to, nil
}

// authResult is used to control limit rate.
type authResult struct {
	sync.WaitGroup
	token string
	err   error
}

// authHandler is used to handle auth request per registry server.
type authHandler struct {
	sync.Mutex

	header http.Header

	client *http.Client

	// only support basic and bearer schemes
	scheme authenticationScheme

	// common contains common challenge answer
	common tokenOptions

	// scopedTokens caches token indexed by scopes, which used in
	// bearer auth case
	scopedTokens map[string]*authResult
}

func newAuthHandler(client *http.Client, hdr http.Header, scheme authenticationScheme, opts tokenOptions) *authHandler {
	return &authHandler{
		header:       hdr,
		client:       client,
		scheme:       scheme,
		common:       opts,
		scopedTokens: map[string]*authResult{},
	}
}

func (ah *authHandler) authorize(ctx context.Context) (string, error) {
	switch ah.scheme {
	case basicAuth:
		return ah.doBasicAuth(ctx)
	case bearerAuth:
		return ah.doBearerAuth(ctx)
	default:
		return "", errors.Wrap(errdefs.ErrNotImplemented, "failed to find supported auth scheme")
	}
}

func (ah *authHandler) doBasicAuth(ctx context.Context) (string, error) {
	username, secret := ah.common.username, ah.common.secret

	if username == "" || secret == "" {
		return "", fmt.Errorf("failed to handle basic auth because missing username or secret")
	}

	auth := base64.StdEncoding.EncodeToString([]byte(username + ":" + secret))
	return fmt.Sprintf("%s %s", "Basic", auth), nil
}

func (ah *authHandler) doBearerAuth(ctx context.Context) (string, error) {
	// copy common tokenOptions
	to := ah.common

	to.scopes = getTokenScopes(ctx, to.scopes)
	if len(to.scopes) == 0 {
		return "", errors.Errorf("no scope specified for token auth challenge")
	}

	// Docs: https://docs.docker.com/registry/spec/auth/scope
	scoped := strings.Join(to.scopes, " ")

	ah.Lock()
	if r, exist := ah.scopedTokens[scoped]; exist {
		ah.Unlock()
		r.Wait()
		return r.token, r.err
	}

	// only one fetch token job
	r := new(authResult)
	r.Add(1)
	ah.scopedTokens[scoped] = r
	ah.Unlock()

	// fetch token for the resource scope
	var (
		token string
		err   error
	)
	if to.secret != "" {
		// credential information is provided, use oauth POST endpoint
		token, err = ah.fetchTokenWithOAuth(ctx, to)
		err = errors.Wrap(err, "failed to fetch oauth token")
	} else {
		// do request anonymously
		token, err = ah.fetchToken(ctx, to)
		err = errors.Wrap(err, "failed to fetch anonymous token")
	}
	token = fmt.Sprintf("%s %s", "Bearer", token)

	r.token, r.err = token, err
	r.Done()
	return r.token, r.err
}

type tokenOptions struct {
	realm    string
	service  string
	scopes   []string
	username string
	secret   string
}

type postTokenResponse struct {
	AccessToken  string    `json:"access_token"`
	RefreshToken string    `json:"refresh_token"`
	ExpiresIn    int       `json:"expires_in"`
	IssuedAt     time.Time `json:"issued_at"`
	Scope        string    `json:"scope"`
}

func (ah *authHandler) fetchTokenWithOAuth(ctx context.Context, to tokenOptions) (string, error) {
	form := url.Values{}
	form.Set("scope", strings.Join(to.scopes, " "))
	form.Set("service", to.service)
	// TODO: Allow setting client_id
	form.Set("client_id", "containerd-client")

	if to.username == "" {
		form.Set("grant_type", "refresh_token")
		form.Set("refresh_token", to.secret)
	} else {
		form.Set("grant_type", "password")
		form.Set("username", to.username)
		form.Set("password", to.secret)
	}

	req, err := http.NewRequest("POST", to.realm, strings.NewReader(form.Encode()))
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
	if ah.header != nil {
		for k, v := range ah.header {
			req.Header[k] = append(req.Header[k], v...)
		}
	}

	resp, err := ctxhttp.Do(ctx, ah.client, req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	// Registries without support for POST may return 404 for POST /v2/token.
	// As of September 2017, GCR is known to return 404.
	// As of February 2018, JFrog Artifactory is known to return 401.
	if (resp.StatusCode == 405 && to.username != "") || resp.StatusCode == 404 || resp.StatusCode == 401 {
		return ah.fetchToken(ctx, to)
	} else if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB
		log.G(ctx).WithFields(logrus.Fields{
			"status": resp.Status,
			"body":   string(b),
		}).Debugf("token request failed")
		// TODO: handle error body and write debug output
		return "", errors.Errorf("unexpected status: %s", resp.Status)
	}

	decoder := json.NewDecoder(resp.Body)

	var tr postTokenResponse
	if err = decoder.Decode(&tr); err != nil {
		return "", fmt.Errorf("unable to decode token response: %s", err)
	}

	return tr.AccessToken, nil
}

type getTokenResponse struct {
	Token        string    `json:"token"`
	AccessToken  string    `json:"access_token"`
	ExpiresIn    int       `json:"expires_in"`
	IssuedAt     time.Time `json:"issued_at"`
	RefreshToken string    `json:"refresh_token"`
}

// fetchToken fetches a token using a GET request
func (ah *authHandler) fetchToken(ctx context.Context, to tokenOptions) (string, error) {
	req, err := http.NewRequest("GET", to.realm, nil)
	if err != nil {
		return "", err
	}

	if ah.header != nil {
		for k, v := range ah.header {
			req.Header[k] = append(req.Header[k], v...)
		}
	}

	reqParams := req.URL.Query()

	if to.service != "" {
		reqParams.Add("service", to.service)
	}

	for _, scope := range to.scopes {
		reqParams.Add("scope", scope)
	}

	if to.secret != "" {
		req.SetBasicAuth(to.username, to.secret)
	}

	req.URL.RawQuery = reqParams.Encode()

	resp, err := ctxhttp.Do(ctx, ah.client, req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		// TODO: handle error body and write debug output
		return "", errors.Errorf("unexpected status: %s", resp.Status)
	}

	decoder := json.NewDecoder(resp.Body)

	var tr getTokenResponse
	if err = decoder.Decode(&tr); err != nil {
		return "", fmt.Errorf("unable to decode token response: %s", err)
	}

	// `access_token` is equivalent to `token` and if both are specified
	// the choice is undefined.  Canonicalize `access_token` by sticking
	// things in `token`.
	if tr.AccessToken != "" {
		tr.Token = tr.AccessToken
	}

	if tr.Token == "" {
		return "", ErrNoToken
	}

	return tr.Token, nil
}

func invalidAuthorization(c challenge, responses []*http.Response) error {
	errStr := c.parameters["error"]
	if errStr == "" {
		return nil
	}

	n := len(responses)
	if n == 1 || (n > 1 && !sameRequest(responses[n-2].Request, responses[n-1].Request)) {
		return nil
	}

	return errors.Wrapf(ErrInvalidAuthorization, "server message: %s", errStr)
}

func sameRequest(r1, r2 *http.Request) bool {
	if r1.Method != r2.Method {
		return false
	}
	if *r1.URL != *r2.URL {
		return false
	}
	return true
}
<commit_msg>remotes: remove unnecessary string formatting<commit_after>/*
   Copyright The containerd 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 docker

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/log"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"golang.org/x/net/context/ctxhttp"
)

type dockerAuthorizer struct {
	credentials func(string) (string, string, error)

	client *http.Client
	header http.Header
	mu     sync.Mutex

	// indexed by host name
	handlers map[string]*authHandler
}

// NewAuthorizer creates a Docker authorizer using the provided function to
// get credentials for the token server or basic auth.
// Deprecated: Use NewDockerAuthorizer
func NewAuthorizer(client *http.Client, f func(string) (string, string, error)) Authorizer {
	return NewDockerAuthorizer(WithAuthClient(client), WithAuthCreds(f))
}

type authorizerConfig struct {
	credentials func(string) (string, string, error)
	client      *http.Client
	header      http.Header
}

// AuthorizerOpt configures an authorizer
type AuthorizerOpt func(*authorizerConfig)

// WithAuthClient provides the HTTP client for the authorizer
func WithAuthClient(client *http.Client) AuthorizerOpt {
	return func(opt *authorizerConfig) {
		opt.client = client
	}
}

// WithAuthCreds provides a credential function to the authorizer
func WithAuthCreds(creds func(string) (string, string, error)) AuthorizerOpt {
	return func(opt *authorizerConfig) {
		opt.credentials = creds
	}
}

// WithAuthHeader provides HTTP headers for authorization
func WithAuthHeader(hdr http.Header) AuthorizerOpt {
	return func(opt *authorizerConfig) {
		opt.header = hdr
	}
}

// NewDockerAuthorizer creates an authorizer using Docker's registry
// authentication spec.
// See https://docs.docker.com/registry/spec/auth/
func NewDockerAuthorizer(opts ...AuthorizerOpt) Authorizer {
	var ao authorizerConfig
	for _, opt := range opts {
		opt(&ao)
	}

	if ao.client == nil {
		ao.client = http.DefaultClient
	}

	return &dockerAuthorizer{
		credentials: ao.credentials,
		client:      ao.client,
		header:      ao.header,
		handlers:    make(map[string]*authHandler),
	}
}

// Authorize handles auth request.
func (a *dockerAuthorizer) Authorize(ctx context.Context, req *http.Request) error {
	// skip if there is no auth handler
	ah := a.getAuthHandler(req.URL.Host)
	if ah == nil {
		return nil
	}

	auth, err := ah.authorize(ctx)
	if err != nil {
		return err
	}

	req.Header.Set("Authorization", auth)
	return nil
}

func (a *dockerAuthorizer) getAuthHandler(host string) *authHandler {
	a.mu.Lock()
	defer a.mu.Unlock()

	return a.handlers[host]
}

func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.Response) error {
	last := responses[len(responses)-1]
	host := last.Request.URL.Host

	a.mu.Lock()
	defer a.mu.Unlock()
	for _, c := range parseAuthHeader(last.Header) {
		if c.scheme == bearerAuth {
			if err := invalidAuthorization(c, responses); err != nil {
				delete(a.handlers, host)
				return err
			}

			// reuse existing handler
			//
			// assume that one registry will return the common
			// challenge information, including realm and service.
			// and the resource scope is only different part
			// which can be provided by each request.
			if _, ok := a.handlers[host]; ok {
				return nil
			}

			common, err := a.generateTokenOptions(ctx, host, c)
			if err != nil {
				return err
			}

			a.handlers[host] = newAuthHandler(a.client, a.header, c.scheme, common)
			return nil
		} else if c.scheme == basicAuth && a.credentials != nil {
			username, secret, err := a.credentials(host)
			if err != nil {
				return err
			}

			if username != "" && secret != "" {
				common := tokenOptions{
					username: username,
					secret:   secret,
				}

				a.handlers[host] = newAuthHandler(a.client, a.header, c.scheme, common)
				return nil
			}
		}
	}
	return errors.Wrap(errdefs.ErrNotImplemented, "failed to find supported auth scheme")
}

func (a *dockerAuthorizer) generateTokenOptions(ctx context.Context, host string, c challenge) (tokenOptions, error) {
	realm, ok := c.parameters["realm"]
	if !ok {
		return tokenOptions{}, errors.New("no realm specified for token auth challenge")
	}

	realmURL, err := url.Parse(realm)
	if err != nil {
		return tokenOptions{}, errors.Wrap(err, "invalid token auth challenge realm")
	}

	to := tokenOptions{
		realm:   realmURL.String(),
		service: c.parameters["service"],
	}

	scope, ok := c.parameters["scope"]
	if !ok {
		return tokenOptions{}, errors.Errorf("no scope specified for token auth challenge")
	}
	to.scopes = append(to.scopes, scope)

	if a.credentials != nil {
		to.username, to.secret, err = a.credentials(host)
		if err != nil {
			return tokenOptions{}, err
		}
	}
	return to, nil
}

// authResult is used to control limit rate.
type authResult struct {
	sync.WaitGroup
	token string
	err   error
}

// authHandler is used to handle auth request per registry server.
type authHandler struct {
	sync.Mutex

	header http.Header

	client *http.Client

	// only support basic and bearer schemes
	scheme authenticationScheme

	// common contains common challenge answer
	common tokenOptions

	// scopedTokens caches token indexed by scopes, which used in
	// bearer auth case
	scopedTokens map[string]*authResult
}

func newAuthHandler(client *http.Client, hdr http.Header, scheme authenticationScheme, opts tokenOptions) *authHandler {
	return &authHandler{
		header:       hdr,
		client:       client,
		scheme:       scheme,
		common:       opts,
		scopedTokens: map[string]*authResult{},
	}
}

func (ah *authHandler) authorize(ctx context.Context) (string, error) {
	switch ah.scheme {
	case basicAuth:
		return ah.doBasicAuth(ctx)
	case bearerAuth:
		return ah.doBearerAuth(ctx)
	default:
		return "", errors.Wrap(errdefs.ErrNotImplemented, "failed to find supported auth scheme")
	}
}

func (ah *authHandler) doBasicAuth(ctx context.Context) (string, error) {
	username, secret := ah.common.username, ah.common.secret

	if username == "" || secret == "" {
		return "", fmt.Errorf("failed to handle basic auth because missing username or secret")
	}

	auth := base64.StdEncoding.EncodeToString([]byte(username + ":" + secret))
	return fmt.Sprintf("Basic %s", auth), nil
}

func (ah *authHandler) doBearerAuth(ctx context.Context) (string, error) {
	// copy common tokenOptions
	to := ah.common

	to.scopes = getTokenScopes(ctx, to.scopes)
	if len(to.scopes) == 0 {
		return "", errors.Errorf("no scope specified for token auth challenge")
	}

	// Docs: https://docs.docker.com/registry/spec/auth/scope
	scoped := strings.Join(to.scopes, " ")

	ah.Lock()
	if r, exist := ah.scopedTokens[scoped]; exist {
		ah.Unlock()
		r.Wait()
		return r.token, r.err
	}

	// only one fetch token job
	r := new(authResult)
	r.Add(1)
	ah.scopedTokens[scoped] = r
	ah.Unlock()

	// fetch token for the resource scope
	var (
		token string
		err   error
	)
	if to.secret != "" {
		// credential information is provided, use oauth POST endpoint
		token, err = ah.fetchTokenWithOAuth(ctx, to)
		err = errors.Wrap(err, "failed to fetch oauth token")
	} else {
		// do request anonymously
		token, err = ah.fetchToken(ctx, to)
		err = errors.Wrap(err, "failed to fetch anonymous token")
	}
	token = fmt.Sprintf("Bearer %s", token)

	r.token, r.err = token, err
	r.Done()
	return r.token, r.err
}

type tokenOptions struct {
	realm    string
	service  string
	scopes   []string
	username string
	secret   string
}

type postTokenResponse struct {
	AccessToken  string    `json:"access_token"`
	RefreshToken string    `json:"refresh_token"`
	ExpiresIn    int       `json:"expires_in"`
	IssuedAt     time.Time `json:"issued_at"`
	Scope        string    `json:"scope"`
}

func (ah *authHandler) fetchTokenWithOAuth(ctx context.Context, to tokenOptions) (string, error) {
	form := url.Values{}
	form.Set("scope", strings.Join(to.scopes, " "))
	form.Set("service", to.service)
	// TODO: Allow setting client_id
	form.Set("client_id", "containerd-client")

	if to.username == "" {
		form.Set("grant_type", "refresh_token")
		form.Set("refresh_token", to.secret)
	} else {
		form.Set("grant_type", "password")
		form.Set("username", to.username)
		form.Set("password", to.secret)
	}

	req, err := http.NewRequest("POST", to.realm, strings.NewReader(form.Encode()))
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
	if ah.header != nil {
		for k, v := range ah.header {
			req.Header[k] = append(req.Header[k], v...)
		}
	}

	resp, err := ctxhttp.Do(ctx, ah.client, req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	// Registries without support for POST may return 404 for POST /v2/token.
	// As of September 2017, GCR is known to return 404.
	// As of February 2018, JFrog Artifactory is known to return 401.
	if (resp.StatusCode == 405 && to.username != "") || resp.StatusCode == 404 || resp.StatusCode == 401 {
		return ah.fetchToken(ctx, to)
	} else if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB
		log.G(ctx).WithFields(logrus.Fields{
			"status": resp.Status,
			"body":   string(b),
		}).Debugf("token request failed")
		// TODO: handle error body and write debug output
		return "", errors.Errorf("unexpected status: %s", resp.Status)
	}

	decoder := json.NewDecoder(resp.Body)

	var tr postTokenResponse
	if err = decoder.Decode(&tr); err != nil {
		return "", fmt.Errorf("unable to decode token response: %s", err)
	}

	return tr.AccessToken, nil
}

type getTokenResponse struct {
	Token        string    `json:"token"`
	AccessToken  string    `json:"access_token"`
	ExpiresIn    int       `json:"expires_in"`
	IssuedAt     time.Time `json:"issued_at"`
	RefreshToken string    `json:"refresh_token"`
}

// fetchToken fetches a token using a GET request
func (ah *authHandler) fetchToken(ctx context.Context, to tokenOptions) (string, error) {
	req, err := http.NewRequest("GET", to.realm, nil)
	if err != nil {
		return "", err
	}

	if ah.header != nil {
		for k, v := range ah.header {
			req.Header[k] = append(req.Header[k], v...)
		}
	}

	reqParams := req.URL.Query()

	if to.service != "" {
		reqParams.Add("service", to.service)
	}

	for _, scope := range to.scopes {
		reqParams.Add("scope", scope)
	}

	if to.secret != "" {
		req.SetBasicAuth(to.username, to.secret)
	}

	req.URL.RawQuery = reqParams.Encode()

	resp, err := ctxhttp.Do(ctx, ah.client, req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		// TODO: handle error body and write debug output
		return "", errors.Errorf("unexpected status: %s", resp.Status)
	}

	decoder := json.NewDecoder(resp.Body)

	var tr getTokenResponse
	if err = decoder.Decode(&tr); err != nil {
		return "", fmt.Errorf("unable to decode token response: %s", err)
	}

	// `access_token` is equivalent to `token` and if both are specified
	// the choice is undefined.  Canonicalize `access_token` by sticking
	// things in `token`.
	if tr.AccessToken != "" {
		tr.Token = tr.AccessToken
	}

	if tr.Token == "" {
		return "", ErrNoToken
	}

	return tr.Token, nil
}

func invalidAuthorization(c challenge, responses []*http.Response) error {
	errStr := c.parameters["error"]
	if errStr == "" {
		return nil
	}

	n := len(responses)
	if n == 1 || (n > 1 && !sameRequest(responses[n-2].Request, responses[n-1].Request)) {
		return nil
	}

	return errors.Wrapf(ErrInvalidAuthorization, "server message: %s", errStr)
}

func sameRequest(r1, r2 *http.Request) bool {
	if r1.Method != r2.Method {
		return false
	}
	if *r1.URL != *r2.URL {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package standard

import (
	"io"
	"net"
	"net/http"

	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/logger"
)

type Response struct {
	http.ResponseWriter
	config         *engine.Config
	request        *http.Request
	header         engine.Header
	status         int
	size           int64
	committed      bool
	writer         io.Writer
	logger         logger.Logger
	body           []byte
	keepBody       bool
	responseWriter *responseWriter
}

func NewResponse(w http.ResponseWriter, r *http.Request, l logger.Logger) *Response {
	return &Response{
		ResponseWriter: w,
		request:        r,
		header:         &Header{Header: w.Header()},
		writer:         w,
		logger:         l,
	}
}

func (r *Response) Header() engine.Header {
	return r.header
}

func (r *Response) WriteHeader(code int) {
	if r.committed {
		r.logger.Warn("response already committed")
		return
	}
	r.status = code
	r.ResponseWriter.WriteHeader(code)
	r.committed = true
}

func (r *Response) KeepBody(on bool) {
	r.keepBody = on
}

func (r *Response) Write(b []byte) (n int, err error) {
	if !r.committed {
		if r.status == 0 {
			r.status = http.StatusOK
		}
		r.WriteHeader(r.status)
	}
	if r.keepBody {
		r.body = append(r.body, b...)
	}
	n, err = r.writer.Write(b)
	r.size += int64(n)
	return
}

func (r *Response) Status() int {
	return r.status
}

func (r *Response) Size() int64 {
	return r.size
}

func (r *Response) Committed() bool {
	return r.committed
}

func (r *Response) SetWriter(w io.Writer) {
	r.writer = w
}

func (r *Response) Writer() io.Writer {
	return r.writer
}

func (r *Response) Object() interface{} {
	return r.ResponseWriter
}

func (r *Response) Error(errMsg string, args ...int) {
	if len(args) > 0 {
		r.status = args[0]
	} else {
		r.status = http.StatusInternalServerError
	}
	r.Write(engine.Str2bytes(errMsg))
	r.WriteHeader(r.status)
}

func (r *Response) reset(w http.ResponseWriter, req *http.Request, h engine.Header) {
	r.ResponseWriter = w
	r.request = req
	r.header = h
	r.status = http.StatusOK
	r.size = 0
	r.committed = false
	r.writer = w
	r.body = nil
	r.keepBody = false
	r.responseWriter = &responseWriter{r}
}

func (r *Response) Hijacker(fn func(net.Conn)) error {
	conn, bufrw, err := r.ResponseWriter.(http.Hijacker).Hijack()
	if err != nil {
		return err
	}
	_ = bufrw
	fn(conn)
	conn.Close()
	r.committed = true
	return nil
}

func (r *Response) Body() []byte {
	return r.body
}

func (r *Response) Redirect(url string, code int) {
	http.Redirect(r.ResponseWriter, r.request, url, code)
	r.committed = true
}

func (r *Response) NotFound() {
	http.Error(r.ResponseWriter, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	r.committed = true
}

func (r *Response) SetCookie(cookie *http.Cookie) {
	r.header.Add(engine.HeaderSetCookie, cookie.String())
}

func (r *Response) ServeFile(file string) {
	r.keepBody = false
	http.ServeFile(r.ResponseWriter, r.request, file)
	r.committed = true
}

func (r *Response) Stream(step func(io.Writer) bool) {
	w := r.ResponseWriter
	clientGone := w.(http.CloseNotifier).CloseNotify()
	for {
		select {
		case <-clientGone:
			return
		default:
			keepOpen := step(w)
			w.(http.Flusher).Flush()
			if !keepOpen {
				return
			}
		}
	}
}

func (r *Response) StdResponseWriter() http.ResponseWriter {
	return r.responseWriter
}

type responseWriter struct {
	*Response
}

func (r *responseWriter) StatusCode() int {
	if r.Response.Status() == 0 {
		return http.StatusOK
	}
	return r.Response.Status()
}

func (r *responseWriter) Header() http.Header {
	return r.Response.header.(*Header).Header
}

func (r *responseWriter) Write(b []byte) (n int, err error) {
	return r.Response.Write(b)
}

func (r *responseWriter) WriteHeader(code int) {
	r.Response.WriteHeader(code)
}
<commit_msg>update<commit_after>package standard

import (
	"bufio"
	"io"
	"net"
	"net/http"

	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/logger"
)

type Response struct {
	http.ResponseWriter
	config         *engine.Config
	request        *http.Request
	header         engine.Header
	status         int
	size           int64
	committed      bool
	writer         io.Writer
	logger         logger.Logger
	body           []byte
	keepBody       bool
	responseWriter *responseWriter
}

func NewResponse(w http.ResponseWriter, r *http.Request, l logger.Logger) *Response {
	return &Response{
		ResponseWriter: w,
		request:        r,
		header:         &Header{Header: w.Header()},
		writer:         w,
		logger:         l,
	}
}

func (r *Response) Header() engine.Header {
	return r.header
}

func (r *Response) WriteHeader(code int) {
	if r.committed {
		r.logger.Warn("response already committed")
		return
	}
	r.status = code
	r.ResponseWriter.WriteHeader(code)
	r.committed = true
}

func (r *Response) KeepBody(on bool) {
	r.keepBody = on
}

func (r *Response) Write(b []byte) (n int, err error) {
	if !r.committed {
		if r.status == 0 {
			r.status = http.StatusOK
		}
		r.WriteHeader(r.status)
	}
	if r.keepBody {
		r.body = append(r.body, b...)
	}
	n, err = r.writer.Write(b)
	r.size += int64(n)
	return
}

func (r *Response) Status() int {
	return r.status
}

func (r *Response) Size() int64 {
	return r.size
}

func (r *Response) Committed() bool {
	return r.committed
}

func (r *Response) SetWriter(w io.Writer) {
	r.writer = w
}

func (r *Response) Writer() io.Writer {
	return r.writer
}

func (r *Response) Object() interface{} {
	return r.ResponseWriter
}

func (r *Response) Error(errMsg string, args ...int) {
	if len(args) > 0 {
		r.status = args[0]
	} else {
		r.status = http.StatusInternalServerError
	}
	r.Write(engine.Str2bytes(errMsg))
	r.WriteHeader(r.status)
}

func (r *Response) reset(w http.ResponseWriter, req *http.Request, h engine.Header) {
	r.ResponseWriter = w
	r.request = req
	r.header = h
	r.status = http.StatusOK
	r.size = 0
	r.committed = false
	r.writer = w
	r.body = nil
	r.keepBody = false
	r.responseWriter = &responseWriter{r}
}

func (r *Response) Hijacker(fn func(net.Conn)) error {
	conn, bufrw, err := r.ResponseWriter.(http.Hijacker).Hijack()
	if err != nil {
		return err
	}
	_ = bufrw
	fn(conn)
	conn.Close()
	r.committed = true
	return nil
}

func (r *Response) Body() []byte {
	return r.body
}

func (r *Response) Redirect(url string, code int) {
	http.Redirect(r.ResponseWriter, r.request, url, code)
	r.committed = true
}

func (r *Response) NotFound() {
	http.Error(r.ResponseWriter, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	r.committed = true
}

func (r *Response) SetCookie(cookie *http.Cookie) {
	r.header.Add(engine.HeaderSetCookie, cookie.String())
}

func (r *Response) ServeFile(file string) {
	r.keepBody = false
	http.ServeFile(r.ResponseWriter, r.request, file)
	r.committed = true
}

func (r *Response) Stream(step func(io.Writer) bool) {
	w := r.ResponseWriter
	clientGone := w.(http.CloseNotifier).CloseNotify()
	for {
		select {
		case <-clientGone:
			return
		default:
			keepOpen := step(w)
			w.(http.Flusher).Flush()
			if !keepOpen {
				return
			}
		}
	}
}

func (r *Response) Flush() {
	if flusher, ok := r.ResponseWriter.(http.Flusher); ok {
		flusher.Flush()
	}
}

func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	return r.ResponseWriter.(http.Hijacker).Hijack()
}

func (r *Response) CloseNotify() <-chan bool {
	return r.ResponseWriter.(http.CloseNotifier).CloseNotify()
}

func (r *Response) StdResponseWriter() http.ResponseWriter {
	return r.responseWriter
}

type responseWriter struct {
	*Response
}

func (r *responseWriter) StatusCode() int {
	if r.Response.Status() == 0 {
		return http.StatusOK
	}
	return r.Response.Status()
}

func (r *responseWriter) Header() http.Header {
	return r.Response.header.(*Header).Header
}

func (r *responseWriter) Write(b []byte) (n int, err error) {
	return r.Response.Write(b)
}

func (r *responseWriter) WriteHeader(code int) {
	r.Response.WriteHeader(code)
}
<|endoftext|>


<commit_before>package cluster

import (
	"io/ioutil"

	"github.com/cosminrentea/gobbler/server/store/filestore"

	"github.com/cosminrentea/gobbler/protocol"
	"github.com/cosminrentea/gobbler/server/store"

	"github.com/hashicorp/go-multierror"
	"github.com/stretchr/testify/assert"

	"errors"
	"net"
	"testing"
	"time"
)

const basePort = 11000

var (
	index = 1
)

func testConfig() (config Config) {
	remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: basePort + index}
	var remotes []*net.TCPAddr
	remotes = append(remotes, &remoteAddr)
	config = Config{ID: uint8(index), Host: "127.0.0.1", Port: basePort + index, Remotes: remotes}
	index++
	return
}

func testConfigAnother() (config Config) {
	remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: basePort + index - 1}
	var remotes []*net.TCPAddr
	remotes = append(remotes, &remoteAddr)
	config = Config{ID: uint8(index), Host: "127.0.0.1", Port: basePort + index, Remotes: remotes}
	index++
	return
}

func TestCluster_StartCheckStop(t *testing.T) {
	a := assert.New(t)

	conf := testConfig()
	node, err := New(&conf)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node.Router = newDummyRouter(t)

	err = node.Start()
	a.NoError(err, "No error should be raised when Starting the Cluster")

	err = node.Check()
	a.NoError(err, "Health-check score of a Cluster with a single node should be OK")

	err = node.Stop()
	a.NoError(err, "No error should be raised when Stopping the Cluster")
}

func TestCluster_BroadcastStringAndMessageAndCheck(t *testing.T) {
	a := assert.New(t)

	config1 := testConfig()
	node1, err := New(&config1)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node1.Router = newDummyRouter(t)

	//start the cluster node 1
	defer node1.Stop()
	err = node1.Start()
	a.NoError(err, "No error should be raised when starting node 1 of the Cluster")

	config2 := testConfigAnother()
	node2, err := New(&config2)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node2.Router = newDummyRouter(t)

	//start the cluster node 2
	defer node2.Stop()
	err = node2.Start()
	a.NoError(err, "No error should be raised when starting node 2 of the Cluster")

	// Send a String Message
	str := "TEST"
	err = node1.BroadcastString(&str)
	a.NoError(err, "No error should be raised when sending a string to Cluster")

	// and a protocol message
	pmsg := protocol.Message{
		ID:            1,
		Path:          "/stuff",
		UserID:        "id",
		ApplicationID: "appId",
		Time:          time.Now().Unix(),
		HeaderJSON:    "{}",
		Body:          []byte("test"),
		NodeID:        1}
	err = node1.BroadcastMessage(&pmsg)
	a.NoError(err, "No error should be raised when sending a protocol message to Cluster")

	err = node1.Check()
	a.NoError(err, "Health-check score of a Cluster with 2 nodes should be OK for node 1")

	err = node2.Check()
	a.NoError(err, "Health-check score of a Cluster with 2 nodes should be OK for node 2")
}

func TestCluster_NewShouldReturnErrorWhenPortIsInvalid(t *testing.T) {
	a := assert.New(t)

	remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: basePort + index - 1}
	var remotes []*net.TCPAddr
	remotes = append(remotes, &remoteAddr)
	index++

	config := Config{ID: 1, Host: "localhost", Port: -1, Remotes: remotes}
	_, err := New(&config)
	a.Error(err, "An error was expected when Creating the Cluster")
}

func TestCluster_StartShouldReturnErrorWhenNoRemotes(t *testing.T) {
	a := assert.New(t)

	var remotes []*net.TCPAddr
	index++

	config := Config{ID: 1, Host: "localhost", Port: basePort + index - 1, Remotes: remotes}
	node, err := New(&config)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node.Router = newDummyRouter(t)

	defer node.Stop()
	err = node.Start()
	if a.Error(err, "An error is expected when Starting the Cluster") {
		a.Equal(err, errors.New("No remote hosts were successfully contacted when this node wanted to join the cluster"),
			"Error should be precisely defined")
	}
}

func TestCluster_StartShouldReturnErrorWhenInvalidRemotes(t *testing.T) {
	a := assert.New(t)

	remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 0}
	var remotes []*net.TCPAddr
	remotes = append(remotes, &remoteAddr)
	index++

	config := Config{ID: 1, Host: "localhost", Port: basePort + index - 1, Remotes: remotes}
	node, err := New(&config)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node.Router = newDummyRouter(t)

	defer node.Stop()
	err = node.Start()
	if a.Error(err, "An error is expected when Starting the Cluster") {
		expected := multierror.Append(errors.New("Failed to join 127.0.0.1: dial tcp 127.0.0.1:0: getsockopt: connection refused"))
		a.Equal(err, expected, "Error should be precisely defined")
	}
}

func TestCluster_StartShouldReturnErrorWhenNoMessageHandler(t *testing.T) {
	a := assert.New(t)

	config := testConfig()
	node, err := New(&config)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	defer node.Stop()
	err = node.Start()
	if a.Error(err, "An error is expected when Starting the Cluster") {
		expected := errors.New("There should be a valid Router already set-up")
		a.Equal(expected, err, "Error should be precisely defined")
	}
}

func TestCluster_NotifyMsgShouldSimplyReturnWhenDecodingInvalidMessage(t *testing.T) {
	a := assert.New(t)

	config := testConfig()
	node, err := New(&config)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node.Router = newDummyRouter(t)

	defer node.Stop()
	err = node.Start()
	a.NoError(err, "No error should be raised when Starting the Cluster")

	node.NotifyMsg([]byte{})

	//TODO Cosmin check that HandleMessage is not invoked (i.e. invalid message is not dispatched)
}

func TestCluster_broadcastClusterMessage(t *testing.T) {
	a := assert.New(t)

	config := testConfig()
	node, err := New(&config)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node.Router = newDummyRouter(t)

	defer node.Stop()
	err = node.Start()
	a.NoError(err, "No error should be raised when Starting the Cluster")

	err = node.broadcastClusterMessage(nil)
	if a.Error(err, "An error is expected from broadcastClusterMessage") {
		expected := errors.New("Could not broadcast a nil cluster-message")
		a.Equal(err, expected, "Error should be precisely defined")
	}
}

type dummyRouter struct {
	store store.MessageStore
}

func newDummyRouter(t *testing.T) *dummyRouter {
	dir, err := ioutil.TempDir("", "guble_cluster_test")
	assert.NoError(t, err)
	return &dummyRouter{store: filestore.New(dir)}
}

func (_ *dummyRouter) HandleMessage(pmsg *protocol.Message) error {
	return nil
}

func (d *dummyRouter) MessageStore() (store.MessageStore, error) {
	return d.store, nil
}
<commit_msg>fix in cluster test - port incrementing<commit_after>package cluster

import (
	"io/ioutil"

	"github.com/cosminrentea/gobbler/server/store/filestore"

	"github.com/cosminrentea/gobbler/protocol"
	"github.com/cosminrentea/gobbler/server/store"

	"github.com/hashicorp/go-multierror"
	"github.com/stretchr/testify/assert"

	"errors"
	"net"
	"testing"
	"time"
)

const basePort = 11000

var (
	index = 1
)

func testConfig() (config Config) {
	remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: basePort + index}
	var remotes []*net.TCPAddr
	remotes = append(remotes, &remoteAddr)
	config = Config{ID: uint8(index), Host: "127.0.0.1", Port: basePort + index, Remotes: remotes}
	index++
	return
}

func testConfigAnother() (config Config) {
	remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: basePort + index - 1}
	var remotes []*net.TCPAddr
	remotes = append(remotes, &remoteAddr)
	config = Config{ID: uint8(index), Host: "127.0.0.1", Port: basePort + index, Remotes: remotes}
	index+=2
	return
}

func TestCluster_StartCheckStop(t *testing.T) {
	a := assert.New(t)

	conf := testConfig()
	node, err := New(&conf)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node.Router = newDummyRouter(t)

	err = node.Start()
	a.NoError(err, "No error should be raised when Starting the Cluster")

	err = node.Check()
	a.NoError(err, "Health-check score of a Cluster with a single node should be OK")

	err = node.Stop()
	a.NoError(err, "No error should be raised when Stopping the Cluster")
}

func TestCluster_BroadcastStringAndMessageAndCheck(t *testing.T) {
	a := assert.New(t)

	config1 := testConfig()
	node1, err := New(&config1)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node1.Router = newDummyRouter(t)

	//start the cluster node 1
	defer node1.Stop()
	err = node1.Start()
	a.NoError(err, "No error should be raised when starting node 1 of the Cluster")

	config2 := testConfigAnother()
	node2, err := New(&config2)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node2.Router = newDummyRouter(t)

	//start the cluster node 2
	defer node2.Stop()
	err = node2.Start()
	a.NoError(err, "No error should be raised when starting node 2 of the Cluster")

	// Send a String Message
	str := "TEST"
	err = node1.BroadcastString(&str)
	a.NoError(err, "No error should be raised when sending a string to Cluster")

	// and a protocol message
	pmsg := protocol.Message{
		ID:            1,
		Path:          "/stuff",
		UserID:        "id",
		ApplicationID: "appId",
		Time:          time.Now().Unix(),
		HeaderJSON:    "{}",
		Body:          []byte("test"),
		NodeID:        1}
	err = node1.BroadcastMessage(&pmsg)
	a.NoError(err, "No error should be raised when sending a protocol message to Cluster")

	err = node1.Check()
	a.NoError(err, "Health-check score of a Cluster with 2 nodes should be OK for node 1")

	err = node2.Check()
	a.NoError(err, "Health-check score of a Cluster with 2 nodes should be OK for node 2")
}

func TestCluster_NewShouldReturnErrorWhenPortIsInvalid(t *testing.T) {
	a := assert.New(t)

	remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: basePort + index - 1}
	var remotes []*net.TCPAddr
	remotes = append(remotes, &remoteAddr)
	index++

	config := Config{ID: 1, Host: "localhost", Port: -1, Remotes: remotes}
	_, err := New(&config)
	a.Error(err, "An error was expected when Creating the Cluster")
}

func TestCluster_StartShouldReturnErrorWhenNoRemotes(t *testing.T) {
	a := assert.New(t)

	var remotes []*net.TCPAddr
	index++

	config := Config{ID: 1, Host: "localhost", Port: basePort + index - 1, Remotes: remotes}
	node, err := New(&config)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node.Router = newDummyRouter(t)

	defer node.Stop()
	err = node.Start()
	if a.Error(err, "An error is expected when Starting the Cluster") {
		a.Equal(err, errors.New("No remote hosts were successfully contacted when this node wanted to join the cluster"),
			"Error should be precisely defined")
	}
}

func TestCluster_StartShouldReturnErrorWhenInvalidRemotes(t *testing.T) {
	a := assert.New(t)

	remoteAddr := net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 0}
	var remotes []*net.TCPAddr
	remotes = append(remotes, &remoteAddr)
	index++

	config := Config{ID: 1, Host: "localhost", Port: basePort + index - 1, Remotes: remotes}
	node, err := New(&config)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node.Router = newDummyRouter(t)

	defer node.Stop()
	err = node.Start()
	if a.Error(err, "An error is expected when Starting the Cluster") {
		expected := multierror.Append(errors.New("Failed to join 127.0.0.1: dial tcp 127.0.0.1:0: getsockopt: connection refused"))
		a.Equal(err, expected, "Error should be precisely defined")
	}
}

func TestCluster_StartShouldReturnErrorWhenNoMessageHandler(t *testing.T) {
	a := assert.New(t)

	config := testConfig()
	node, err := New(&config)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	defer node.Stop()
	err = node.Start()
	if a.Error(err, "An error is expected when Starting the Cluster") {
		expected := errors.New("There should be a valid Router already set-up")
		a.Equal(expected, err, "Error should be precisely defined")
	}
}

func TestCluster_NotifyMsgShouldSimplyReturnWhenDecodingInvalidMessage(t *testing.T) {
	a := assert.New(t)

	config := testConfig()
	node, err := New(&config)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node.Router = newDummyRouter(t)

	defer node.Stop()
	err = node.Start()
	a.NoError(err, "No error should be raised when Starting the Cluster")

	node.NotifyMsg([]byte{})

	//TODO Cosmin check that HandleMessage is not invoked (i.e. invalid message is not dispatched)
}

func TestCluster_broadcastClusterMessage(t *testing.T) {
	a := assert.New(t)

	config := testConfig()
	node, err := New(&config)
	a.NoError(err, "No error should be raised when Creating the Cluster")

	node.Router = newDummyRouter(t)

	defer node.Stop()
	err = node.Start()
	a.NoError(err, "No error should be raised when Starting the Cluster")

	err = node.broadcastClusterMessage(nil)
	if a.Error(err, "An error is expected from broadcastClusterMessage") {
		expected := errors.New("Could not broadcast a nil cluster-message")
		a.Equal(err, expected, "Error should be precisely defined")
	}
}

type dummyRouter struct {
	store store.MessageStore
}

func newDummyRouter(t *testing.T) *dummyRouter {
	dir, err := ioutil.TempDir("", "guble_cluster_test")
	assert.NoError(t, err)
	return &dummyRouter{store: filestore.New(dir)}
}

func (_ *dummyRouter) HandleMessage(pmsg *protocol.Message) error {
	return nil
}

func (d *dummyRouter) MessageStore() (store.MessageStore, error) {
	return d.store, nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSCloudwatchLogSubscriptionFilter_basic(t *testing.T) {
	var conf lambda.GetFunctionOutput

	rstring := acctest.RandString(5)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudwatchLogSubscriptionFilterDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSCloudwatchLogSubscriptionFilterConfig(rstring),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsCloudwatchLogSubscriptionFilterExists("aws_cloudwatch_log_subscription_filter.test_lambdafunction_logfilter", &conf, rstring),
					testAccCheckAWSCloudwatchLogSubscriptionFilterAttributes(&conf, rstring),
				),
			},
		},
	})
}

func testAccCheckCloudwatchLogSubscriptionFilterDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).lambdaconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_cloudwatch_log_subscription_filter" {
			continue
		}

		_, err := conn.GetFunction(&lambda.GetFunctionInput{
			FunctionName: aws.String(rs.Primary.ID),
		})

		if err == nil {
			return fmt.Errorf("Lambda Function still exists")
		}

	}

	return nil

}

func testAccCheckAwsCloudwatchLogSubscriptionFilterExists(n string, function *lambda.GetFunctionOutput, rstring string) resource.TestCheckFunc {
	// Wait for IAM role
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Lambda function not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("Lambda function ID not set")
		}

		conn := testAccProvider.Meta().(*AWSClient).lambdaconn

		params := &lambda.GetFunctionInput{
			FunctionName: aws.String("example_lambda_name_" + rstring),
		}

		getFunction, err := conn.GetFunction(params)
		if err != nil {
			return err
		}

		*function = *getFunction

		return nil
	}
}

func testAccCheckAWSCloudwatchLogSubscriptionFilterAttributes(function *lambda.GetFunctionOutput, rstring string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		c := function.Configuration
		expectedName := fmt.Sprintf("example_lambda_name_%s", rstring)
		if *c.FunctionName != expectedName {
			return fmt.Errorf("Expected function name %s, got %s", expectedName, *c.FunctionName)
		}

		if *c.FunctionArn == "" {
			return fmt.Errorf("Could not read Lambda Function's ARN")
		}

		return nil
	}
}

func testAccAWSCloudwatchLogSubscriptionFilterConfig(rstring string) string {
	return fmt.Sprintf(`
resource "aws_cloudwatch_log_subscription_filter" "test_lambdafunction_logfilter" {
  name            = "test_lambdafunction_logfilter_%s"
  log_group_name  = "example_lambda_name"
  filter_pattern  = "logtype test"
  destination_arn = "${aws_lambda_function.test_lambdafunction.arn}"
}

resource "aws_lambda_function" "test_lambdafunction" {
  filename      = "test-fixtures/lambdatest.zip"
  function_name = "example_lambda_name_%s"
  role          = "${aws_iam_role.iam_for_lambda.arn}"
  runtime       = "nodejs4.3"
  handler       = "exports.handler"
}

resource "aws_cloudwatch_log_group" "logs" {
  name              = "example_lambda_name_%s"
  retention_in_days = 1
}

resource "aws_lambda_permission" "allow_cloudwatch_logs" {
  statement_id  = "AllowExecutionFromCloudWatchLogs"
  action        = "lambda:*"
  function_name = "${aws_lambda_function.test_lambdafunction.arn}"
  principal     = "logs.us-west-2.amazonaws.com"
}

resource "aws_iam_role" "iam_for_lambda" {
  name = "test_lambdafuntion_iam_role_%s"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "test_lambdafunction_iam_policy" {
  name = "test_lambdafunction_iam_policy_%s"
  role = "${aws_iam_role.iam_for_lambda.id}"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1441111030000",
      "Effect": "Allow",
      "Action": [
        "lambda:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
EOF
}
`, rstring, rstring, rstring, rstring, rstring)
}
<commit_msg>provider/aws: fix TestAccAWSCloudwatchLogSubscriptionFilter_basic by linking the name<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSCloudwatchLogSubscriptionFilter_basic(t *testing.T) {
	var conf lambda.GetFunctionOutput

	rstring := acctest.RandString(5)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckCloudwatchLogSubscriptionFilterDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSCloudwatchLogSubscriptionFilterConfig(rstring),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsCloudwatchLogSubscriptionFilterExists("aws_cloudwatch_log_subscription_filter.test_lambdafunction_logfilter", &conf, rstring),
					testAccCheckAWSCloudwatchLogSubscriptionFilterAttributes(&conf, rstring),
				),
			},
		},
	})
}

func testAccCheckCloudwatchLogSubscriptionFilterDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).lambdaconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_cloudwatch_log_subscription_filter" {
			continue
		}

		_, err := conn.GetFunction(&lambda.GetFunctionInput{
			FunctionName: aws.String(rs.Primary.ID),
		})

		if err == nil {
			return fmt.Errorf("Lambda Function still exists")
		}

	}

	return nil

}

func testAccCheckAwsCloudwatchLogSubscriptionFilterExists(n string, function *lambda.GetFunctionOutput, rstring string) resource.TestCheckFunc {
	// Wait for IAM role
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Lambda function not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("Lambda function ID not set")
		}

		conn := testAccProvider.Meta().(*AWSClient).lambdaconn

		params := &lambda.GetFunctionInput{
			FunctionName: aws.String("example_lambda_name_" + rstring),
		}

		getFunction, err := conn.GetFunction(params)
		if err != nil {
			return err
		}

		*function = *getFunction

		return nil
	}
}

func testAccCheckAWSCloudwatchLogSubscriptionFilterAttributes(function *lambda.GetFunctionOutput, rstring string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		c := function.Configuration
		expectedName := fmt.Sprintf("example_lambda_name_%s", rstring)
		if *c.FunctionName != expectedName {
			return fmt.Errorf("Expected function name %s, got %s", expectedName, *c.FunctionName)
		}

		if *c.FunctionArn == "" {
			return fmt.Errorf("Could not read Lambda Function's ARN")
		}

		return nil
	}
}

func testAccAWSCloudwatchLogSubscriptionFilterConfig(rstring string) string {
	return fmt.Sprintf(`
resource "aws_cloudwatch_log_subscription_filter" "test_lambdafunction_logfilter" {
  name            = "test_lambdafunction_logfilter_%s"
  log_group_name  = "${aws_cloudwatch_log_group.logs.name}"
  filter_pattern  = "logtype test"
  destination_arn = "${aws_lambda_function.test_lambdafunction.arn}"
}

resource "aws_lambda_function" "test_lambdafunction" {
  filename      = "test-fixtures/lambdatest.zip"
  function_name = "example_lambda_name_%s"
  role          = "${aws_iam_role.iam_for_lambda.arn}"
  runtime       = "nodejs4.3"
  handler       = "exports.handler"
}

resource "aws_cloudwatch_log_group" "logs" {
  name              = "example_lambda_name_%s"
  retention_in_days = 1
}

resource "aws_lambda_permission" "allow_cloudwatch_logs" {
  statement_id  = "AllowExecutionFromCloudWatchLogs"
  action        = "lambda:*"
  function_name = "${aws_lambda_function.test_lambdafunction.arn}"
  principal     = "logs.us-west-2.amazonaws.com"
}

resource "aws_iam_role" "iam_for_lambda" {
  name = "test_lambdafuntion_iam_role_%s"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "test_lambdafunction_iam_policy" {
  name = "test_lambdafunction_iam_policy_%s"
  role = "${aws_iam_role.iam_for_lambda.id}"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1441111030000",
      "Effect": "Allow",
      "Action": [
        "lambda:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
EOF
}
`, rstring, rstring, rstring, rstring, rstring)
}
<|endoftext|>


<commit_before>package lnwallet

import (
	"encoding/hex"

	"github.com/roasbeef/btcd/btcjson"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
	"github.com/roasbeef/btcutil/coinset"
)

// lnCoin represents a single unspet output. Its purpose is to convert a regular
// output to a struct adhering to the coinset.Coin interface
type lnCoin struct {
	hash     *wire.ShaHash
	index    uint32
	value    btcutil.Amount
	pkScript []byte
	numConfs int64
	valueAge int64
}

func (l *lnCoin) Hash() *wire.ShaHash   { return l.hash }
func (l *lnCoin) Index() uint32         { return l.index }
func (l *lnCoin) Value() btcutil.Amount { return l.value }
func (l *lnCoin) PkScript() []byte      { return l.pkScript }
func (l *lnCoin) NumConfs() int64       { return l.numConfs }
func (l *lnCoin) ValueAge() int64       { return l.valueAge }

// Ensure lnCoin adheres to the coinset.Coin interface.
var _ coinset.Coin = (*lnCoin)(nil)

// newLnCoin creates a new "coin" from the passed output. Coins are required
// in order to perform coin selection upon.
func newLnCoin(output *btcjson.ListUnspentResult) (coinset.Coin, error) {
	txid, err := wire.NewShaHashFromStr(output.TxID)
	if err != nil {
		return nil, err
	}

	pkScript, err := hex.DecodeString(output.ScriptPubKey)
	if err != nil {
		return nil, err
	}

	return &lnCoin{
		hash: txid,
		// btcjson.ListUnspentResult shows the amount in BTC,
		// translate into Satoshi so coin selection can work properly.
		value:    btcutil.Amount(output.Amount * 1e8),
		index:    output.Vout,
		pkScript: pkScript,
		numConfs: output.Confirmations,
		// TODO(roasbeef): output.Amount should be a int64, damn json-RPC :/
		valueAge: output.Confirmations * int64(output.Amount),
	}, nil
}

// outputsToCoins converts a slice of transaction outputs to a coin-selectable
// slice of "Coins"s.
func outputsToCoins(outputs []*btcjson.ListUnspentResult) ([]coinset.Coin, error) {
	coins := make([]coinset.Coin, len(outputs))
	for i, output := range outputs {
		coin, err := newLnCoin(output)
		if err != nil {
			return nil, err
		}

		coins[i] = coin
	}

	return coins, nil
}
<commit_msg>lnwallet: remove interaction with the btcutil.coinset package<commit_after><|endoftext|>