<commit_before>package runconfig

import (
	"fmt"
	"io/ioutil"
	"path"
	"strings"

	"github.com/dotcloud/docker/nat"
	"github.com/dotcloud/docker/opts"
	flag "github.com/dotcloud/docker/pkg/mflag"
	"github.com/dotcloud/docker/pkg/sysinfo"
	"github.com/dotcloud/docker/utils"
)

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 (
		// 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
		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)")
		flNetMode         = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container\n'bridge': creates a new network stack for the container on the docker bridge\n'none': no networking for this container\n'container:<name|id>': reuses another container network stack\n'host': use the host network stack inside the contaner")
		// 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"}, "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\nformat: %s\n(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\"")

	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}
	}

	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{}
	for _, ef := range flEnvFile.GetAll() {
		parsedVars, err := opts.ParseEnvFile(ef)
		if err != nil {
			return nil, nil, cmd, err
		}
		envVariables = append(envVariables, parsedVars...)
	}
	// parse the '-e' and '--env' after, to allow override
	envVariables = append(envVariables, flEnv.GetAll()...)
	// boo, there's no debug output for docker run
	//utils.Debugf("Environment variables for the container: %#v", envVariables)

	netMode, err := parseNetMode(*flNetMode)
	if err != nil {
		return nil, nil, cmd, fmt.Errorf("--net: invalid net mode: %v", err)
	}

	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,
		Image:           image,
		Volumes:         flVolumes.GetMap(),
		Entrypoint:      entrypoint,
		WorkingDir:      *flWorkingDir,
	}

	hostConfig := &HostConfig{
		Binds:           binds,
		ContainerIDFile: *flContainerIDFile,
		LxcConf:         lxcConf,
		Privileged:      *flPrivileged,
		PortBindings:    portBindings,
		Links:           flLinks.GetAll(),
		PublishAllPorts: *flPublishAll,
		Dns:             flDns.GetAll(),
		DnsSearch:       flDnsSearch.GetAll(),
		VolumesFrom:     flVolumesFrom.GetAll(),
		NetworkMode:     netMode,
	}

	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)
		} else if strings.TrimSpace(parts[0]) == "" {
			return nil, fmt.Errorf("key cannot be empty %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
}

func parseNetMode(netMode string) (NetworkMode, error) {
	parts := strings.Split(netMode, ":")
	switch mode := parts[0]; mode {
	case "bridge", "none", "host":
	case "container":
		if len(parts) < 2 || parts[1] == "" {
			return "", fmt.Errorf("invalid container format container:<name|id>")
		}
	default:
		return "", fmt.Errorf("invalid --net: %s", netMode)
	}
	return NetworkMode(netMode), nil
}
<commit_msg>Fix error name typo (ErrInvalidWorikingDirectory)<commit_after>package runconfig

import (
	"fmt"
	"io/ioutil"
	"path"
	"strings"

	"github.com/dotcloud/docker/nat"
	"github.com/dotcloud/docker/opts"
	flag "github.com/dotcloud/docker/pkg/mflag"
	"github.com/dotcloud/docker/pkg/sysinfo"
	"github.com/dotcloud/docker/utils"
)

var (
	ErrInvalidWorkingDirectory  = 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 (
		// 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
		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)")
		flNetMode         = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container\n'bridge': creates a new network stack for the container on the docker bridge\n'none': no networking for this container\n'container:<name|id>': reuses another container network stack\n'host': use the host network stack inside the contaner")
		// 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"}, "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\nformat: %s\n(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\"")

	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, ErrInvalidWorkingDirectory
	}
	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}
	}

	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{}
	for _, ef := range flEnvFile.GetAll() {
		parsedVars, err := opts.ParseEnvFile(ef)
		if err != nil {
			return nil, nil, cmd, err
		}
		envVariables = append(envVariables, parsedVars...)
	}
	// parse the '-e' and '--env' after, to allow override
	envVariables = append(envVariables, flEnv.GetAll()...)
	// boo, there's no debug output for docker run
	//utils.Debugf("Environment variables for the container: %#v", envVariables)

	netMode, err := parseNetMode(*flNetMode)
	if err != nil {
		return nil, nil, cmd, fmt.Errorf("--net: invalid net mode: %v", err)
	}

	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,
		Image:           image,
		Volumes:         flVolumes.GetMap(),
		Entrypoint:      entrypoint,
		WorkingDir:      *flWorkingDir,
	}

	hostConfig := &HostConfig{
		Binds:           binds,
		ContainerIDFile: *flContainerIDFile,
		LxcConf:         lxcConf,
		Privileged:      *flPrivileged,
		PortBindings:    portBindings,
		Links:           flLinks.GetAll(),
		PublishAllPorts: *flPublishAll,
		Dns:             flDns.GetAll(),
		DnsSearch:       flDnsSearch.GetAll(),
		VolumesFrom:     flVolumesFrom.GetAll(),
		NetworkMode:     netMode,
	}

	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)
		} else if strings.TrimSpace(parts[0]) == "" {
			return nil, fmt.Errorf("key cannot be empty %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
}

func parseNetMode(netMode string) (NetworkMode, error) {
	parts := strings.Split(netMode, ":")
	switch mode := parts[0]; mode {
	case "bridge", "none", "host":
	case "container":
		if len(parts) < 2 || parts[1] == "" {
			return "", fmt.Errorf("invalid container format container:<name|id>")
		}
	default:
		return "", fmt.Errorf("invalid --net: %s", netMode)
	}
	return NetworkMode(netMode), nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zanzibar

import (
	"context"
	"io"
	"net/http"
	"net/http/pprof"
	"os"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	"github.com/pkg/errors"
	"github.com/uber-go/tally"
	"github.com/uber-go/tally/m3"
	"github.com/uber-go/zap"
)

const defaultM3MaxQueueSize = 10000
const defaultM3MaxPacketSize = 1440 // 1440kb in UDP M3MaxPacketSize
const defaultM3FlushInterval = 500 * time.Millisecond

// Clients interface is a placeholder for the generated clients
type Clients interface {
}

// Options configures the gateway
type Options struct {
	Clients        Clients
	MetricsBackend tally.CachedStatsReporter
}

// Gateway type
type Gateway struct {
	IP          string
	Port        int32
	RealPort    int32
	RealAddr    string
	WaitGroup   *sync.WaitGroup
	Clients     Clients
	Logger      zap.Logger
	MetricScope tally.Scope
	ServiceName string

	router            *Router
	loggerFile        *os.File
	metricScopeCloser io.Closer
	metricsBackend    tally.CachedStatsReporter
	server            *HTTPServer
	tchannelServer    *TChannelServer
	// clients?
	//	- panic ???
	//	- process reporter ?
}

// CreateGateway func
func CreateGateway(
	config *StaticConfig, opts *Options,
) (*Gateway, error) {
	if opts.Clients == nil {
		panic("opts.Clients required")
	}
	if opts.MetricsBackend != nil {
		panic("wtf m8")
	}

	gateway := &Gateway{
		IP:          config.MustGetString("ip"),
		Port:        int32(config.MustGetInt("port")),
		ServiceName: config.MustGetString("serviceName"),
		WaitGroup:   &sync.WaitGroup{},
		Clients:     opts.Clients,

		metricsBackend: opts.MetricsBackend,
	}

	gateway.router = NewRouter(gateway)

	if err := gateway.setupLogger(config); err != nil {
		return nil, err
	}

	if err := gateway.setupMetrics(config); err != nil {
		return nil, err
	}

	if err := gateway.setupHTTPServer(); err != nil {
		return nil, err
	}

	if err := gateway.setupTChannel(config); err != nil {
		return nil, err
	}

	return gateway, nil
}

// RegisterFn type used to avoid cyclic dependencies
type RegisterFn func(gateway *Gateway, router *Router)

// Bootstrap func
func (gateway *Gateway) Bootstrap(register RegisterFn) error {
	gateway.register(register)

	_, err := gateway.server.JustListen()
	if err != nil {
		gateway.Logger.Error("Error listening on port",
			zap.String("error", err.Error()),
		)
		return errors.Wrap(err, "error listening on port")
	}

	gateway.RealPort = gateway.server.RealPort
	gateway.RealAddr = gateway.server.RealAddr

	gateway.WaitGroup.Add(1)
	go gateway.server.JustServe(gateway.WaitGroup)

	return nil
}

func (gateway *Gateway) register(register RegisterFn) {
	gateway.router.RegisterRaw("GET", "/debug/pprof", pprof.Index)
	gateway.router.RegisterRaw("GET", "/debug/pprof/cmdline", pprof.Cmdline)
	gateway.router.RegisterRaw("GET", "/debug/pprof/profile", pprof.Profile)
	gateway.router.RegisterRaw("GET", "/debug/pprof/symbol", pprof.Symbol)
	gateway.router.RegisterRaw("POST", "/debug/pprof/symbol", pprof.Symbol)
	gateway.router.RegisterRaw(
		"GET", "/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP,
	)
	gateway.router.RegisterRaw(
		"GET", "/debug/pprof/heap", pprof.Handler("heap").ServeHTTP,
	)
	gateway.router.RegisterRaw(
		"GET", "/debug/pprof/threadcreate",
		pprof.Handler("threadcreate").ServeHTTP,
	)
	gateway.router.RegisterRaw(
		"GET", "/debug/pprof/block", pprof.Handler("block").ServeHTTP,
	)

	gateway.router.Register("GET", "/health", NewEndpoint(
		gateway, "health", "health", handleHealthRequest,
	))

	register(gateway, gateway.router)
}

func handleHealthRequest(
	ctx context.Context, inc *IncomingMessage, g *Gateway,
) {
	message := "Healthy, from " + g.ServiceName
	bytes := []byte(
		"{\"ok\":true,\"message\":\"" + message + "\"}\n",
	)

	inc.WriteJSONBytes(200, bytes)
}

// Close the http server
func (gateway *Gateway) Close() {
	if gateway.loggerFile != nil {
		_ = gateway.loggerFile.Sync()
		_ = gateway.loggerFile.Close()
	}

	gateway.metricsBackend.Flush()
	_ = gateway.metricScopeCloser.Close()
	gateway.server.Close()
}

// Wait for gateway to close the server
func (gateway *Gateway) Wait() {
	gateway.WaitGroup.Wait()
}

func (gateway *Gateway) setupMetrics(config *StaticConfig) error {
	metricsType := config.MustGetString("metrics.type")

	var metricsBackend tally.CachedStatsReporter
	if metricsType == "m3" {
		if gateway.metricsBackend != nil {
			panic("expected no metrics backend in gateway.")
		}

		m3FlushIntervalConfig := config.MustGetInt("metrics.m3.flushInterval")
		env := config.MustGetString("env")

		commonTags := map[string]string{"env": env}
		m3Backend, err := metrics.NewM3Backend(
			config.MustGetString("metrics.m3.hostPort"),
			config.MustGetString("metrics.tally.service"),
			commonTags, // default tags
			false,      // include host
			defaultM3MaxQueueSize,
			defaultM3MaxPacketSize,
			time.Duration(m3FlushIntervalConfig)*time.Millisecond,
		)
		if err != nil {
			return err
		}

		metricsBackend = m3Backend
		gateway.metricsBackend = m3Backend
	} else {
		if gateway.metricsBackend == nil {
			panic("expected gateway to have MetricsBackend in opts")
		}
		metricsBackend = gateway.metricsBackend
	}

	// TODO: decide what default tags we want...
	defaultTags := &map[string]string{}

	prefix := config.MustGetString("metrics.tally.service") +
		".production.all-workers"
	flushIntervalConfig := config.MustGetInt("metrics.tally.flushInterval")

	scope, closer := tally.NewCachedRootScope(
		prefix,
		*defaultTags,
		metricsBackend,
		time.Duration(flushIntervalConfig)*time.Millisecond,
		tally.DefaultSeparator,
	)
	gateway.MetricScope = scope
	gateway.metricScopeCloser = closer

	return nil
}

func (gateway *Gateway) setupLogger(config *StaticConfig) error {
	var output zap.Option
	tempLogger := zap.New(
		zap.NewJSONEncoder(),
		zap.Output(os.Stderr),
	)

	loggerFileName := config.MustGetString("logger.fileName")
	loggerOutput := config.MustGetString("logger.output")

	if loggerFileName == "" || loggerOutput == "stdout" {
		output = zap.Output(os.Stdout)
	} else {
		err := os.MkdirAll(filepath.Dir(loggerFileName), 0777)
		if err != nil {
			tempLogger.Error("Error creating log directory",
				zap.String("error", err.Error()),
			)
			return errors.Wrap(err, "Error creating log directory")
		}

		loggerFile, err := os.OpenFile(
			loggerFileName,
			os.O_APPEND|os.O_WRONLY|os.O_CREATE,
			0666,
		)
		if err != nil {
			tempLogger.Error("Error opening log file",
				zap.String("error", err.Error()),
			)
			return errors.Wrap(err, "Error opening log file")
		}
		gateway.loggerFile = loggerFile
		output = zap.Output(loggerFile)
	}

	// Default to a STDOUT logger
	gateway.Logger = zap.New(
		zap.NewJSONEncoder(
			zap.RFC3339Formatter("ts"),
		),
		output,
	)
	return nil
}

func (gateway *Gateway) setupHTTPServer() error {
	gateway.server = &HTTPServer{
		Server: &http.Server{
			Addr:    gateway.IP + ":" + strconv.FormatInt(int64(gateway.Port), 10),
			Handler: gateway.router,
		},
		Logger: gateway.Logger,
	}

	return nil
}

func (gateway *Gateway) setupTChannel(config *StaticConfig) error {
	tchannelServer, err := NewTChannelServer(
		&TChannelServerOptions{
			ServiceName: config.MustGetString("tchannel.serviceName"),
			ProcessName: config.MustGetString("tchannel.processName"),
		})

	if err != nil {
		return err
	}

	gateway.tchannelServer = tchannelServer
	return nil
}
<commit_msg>remove debug statements<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zanzibar

import (
	"context"
	"io"
	"net/http"
	"net/http/pprof"
	"os"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	"github.com/pkg/errors"
	"github.com/uber-go/tally"
	"github.com/uber-go/tally/m3"
	"github.com/uber-go/zap"
)

const defaultM3MaxQueueSize = 10000
const defaultM3MaxPacketSize = 1440 // 1440kb in UDP M3MaxPacketSize
const defaultM3FlushInterval = 500 * time.Millisecond

// Clients interface is a placeholder for the generated clients
type Clients interface {
}

// Options configures the gateway
type Options struct {
	Clients        Clients
	MetricsBackend tally.CachedStatsReporter
}

// Gateway type
type Gateway struct {
	IP          string
	Port        int32
	RealPort    int32
	RealAddr    string
	WaitGroup   *sync.WaitGroup
	Clients     Clients
	Logger      zap.Logger
	MetricScope tally.Scope
	ServiceName string

	router            *Router
	loggerFile        *os.File
	metricScopeCloser io.Closer
	metricsBackend    tally.CachedStatsReporter
	server            *HTTPServer
	tchannelServer    *TChannelServer
	// clients?
	//	- panic ???
	//	- process reporter ?
}

// CreateGateway func
func CreateGateway(
	config *StaticConfig, opts *Options,
) (*Gateway, error) {
	if opts.Clients == nil {
		panic("opts.Clients required")
	}

	gateway := &Gateway{
		IP:          config.MustGetString("ip"),
		Port:        int32(config.MustGetInt("port")),
		ServiceName: config.MustGetString("serviceName"),
		WaitGroup:   &sync.WaitGroup{},
		Clients:     opts.Clients,

		metricsBackend: opts.MetricsBackend,
	}

	gateway.router = NewRouter(gateway)

	if err := gateway.setupLogger(config); err != nil {
		return nil, err
	}

	if err := gateway.setupMetrics(config); err != nil {
		return nil, err
	}

	if err := gateway.setupHTTPServer(); err != nil {
		return nil, err
	}

	if err := gateway.setupTChannel(config); err != nil {
		return nil, err
	}

	return gateway, nil
}

// RegisterFn type used to avoid cyclic dependencies
type RegisterFn func(gateway *Gateway, router *Router)

// Bootstrap func
func (gateway *Gateway) Bootstrap(register RegisterFn) error {
	gateway.register(register)

	_, err := gateway.server.JustListen()
	if err != nil {
		gateway.Logger.Error("Error listening on port",
			zap.String("error", err.Error()),
		)
		return errors.Wrap(err, "error listening on port")
	}

	gateway.RealPort = gateway.server.RealPort
	gateway.RealAddr = gateway.server.RealAddr

	gateway.WaitGroup.Add(1)
	go gateway.server.JustServe(gateway.WaitGroup)

	return nil
}

func (gateway *Gateway) register(register RegisterFn) {
	gateway.router.RegisterRaw("GET", "/debug/pprof", pprof.Index)
	gateway.router.RegisterRaw("GET", "/debug/pprof/cmdline", pprof.Cmdline)
	gateway.router.RegisterRaw("GET", "/debug/pprof/profile", pprof.Profile)
	gateway.router.RegisterRaw("GET", "/debug/pprof/symbol", pprof.Symbol)
	gateway.router.RegisterRaw("POST", "/debug/pprof/symbol", pprof.Symbol)
	gateway.router.RegisterRaw(
		"GET", "/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP,
	)
	gateway.router.RegisterRaw(
		"GET", "/debug/pprof/heap", pprof.Handler("heap").ServeHTTP,
	)
	gateway.router.RegisterRaw(
		"GET", "/debug/pprof/threadcreate",
		pprof.Handler("threadcreate").ServeHTTP,
	)
	gateway.router.RegisterRaw(
		"GET", "/debug/pprof/block", pprof.Handler("block").ServeHTTP,
	)

	gateway.router.Register("GET", "/health", NewEndpoint(
		gateway, "health", "health", handleHealthRequest,
	))

	register(gateway, gateway.router)
}

func handleHealthRequest(
	ctx context.Context, inc *IncomingMessage, g *Gateway,
) {
	message := "Healthy, from " + g.ServiceName
	bytes := []byte(
		"{\"ok\":true,\"message\":\"" + message + "\"}\n",
	)

	inc.WriteJSONBytes(200, bytes)
}

// Close the http server
func (gateway *Gateway) Close() {
	if gateway.loggerFile != nil {
		_ = gateway.loggerFile.Sync()
		_ = gateway.loggerFile.Close()
	}

	gateway.metricsBackend.Flush()
	_ = gateway.metricScopeCloser.Close()
	gateway.server.Close()
}

// Wait for gateway to close the server
func (gateway *Gateway) Wait() {
	gateway.WaitGroup.Wait()
}

func (gateway *Gateway) setupMetrics(config *StaticConfig) error {
	metricsType := config.MustGetString("metrics.type")

	var metricsBackend tally.CachedStatsReporter
	if metricsType == "m3" {
		if gateway.metricsBackend != nil {
			panic("expected no metrics backend in gateway.")
		}

		m3FlushIntervalConfig := config.MustGetInt("metrics.m3.flushInterval")
		env := config.MustGetString("env")

		commonTags := map[string]string{"env": env}
		m3Backend, err := metrics.NewM3Backend(
			config.MustGetString("metrics.m3.hostPort"),
			config.MustGetString("metrics.tally.service"),
			commonTags, // default tags
			false,      // include host
			defaultM3MaxQueueSize,
			defaultM3MaxPacketSize,
			time.Duration(m3FlushIntervalConfig)*time.Millisecond,
		)
		if err != nil {
			return err
		}

		metricsBackend = m3Backend
		gateway.metricsBackend = m3Backend
	} else {
		if gateway.metricsBackend == nil {
			panic("expected gateway to have MetricsBackend in opts")
		}
		metricsBackend = gateway.metricsBackend
	}

	// TODO: decide what default tags we want...
	defaultTags := &map[string]string{}

	prefix := config.MustGetString("metrics.tally.service") +
		".production.all-workers"
	flushIntervalConfig := config.MustGetInt("metrics.tally.flushInterval")

	scope, closer := tally.NewCachedRootScope(
		prefix,
		*defaultTags,
		metricsBackend,
		time.Duration(flushIntervalConfig)*time.Millisecond,
		tally.DefaultSeparator,
	)
	gateway.MetricScope = scope
	gateway.metricScopeCloser = closer

	return nil
}

func (gateway *Gateway) setupLogger(config *StaticConfig) error {
	var output zap.Option
	tempLogger := zap.New(
		zap.NewJSONEncoder(),
		zap.Output(os.Stderr),
	)

	loggerFileName := config.MustGetString("logger.fileName")
	loggerOutput := config.MustGetString("logger.output")

	if loggerFileName == "" || loggerOutput == "stdout" {
		output = zap.Output(os.Stdout)
	} else {
		err := os.MkdirAll(filepath.Dir(loggerFileName), 0777)
		if err != nil {
			tempLogger.Error("Error creating log directory",
				zap.String("error", err.Error()),
			)
			return errors.Wrap(err, "Error creating log directory")
		}

		loggerFile, err := os.OpenFile(
			loggerFileName,
			os.O_APPEND|os.O_WRONLY|os.O_CREATE,
			0666,
		)
		if err != nil {
			tempLogger.Error("Error opening log file",
				zap.String("error", err.Error()),
			)
			return errors.Wrap(err, "Error opening log file")
		}
		gateway.loggerFile = loggerFile
		output = zap.Output(loggerFile)
	}

	// Default to a STDOUT logger
	gateway.Logger = zap.New(
		zap.NewJSONEncoder(
			zap.RFC3339Formatter("ts"),
		),
		output,
	)
	return nil
}

func (gateway *Gateway) setupHTTPServer() error {
	gateway.server = &HTTPServer{
		Server: &http.Server{
			Addr:    gateway.IP + ":" + strconv.FormatInt(int64(gateway.Port), 10),
			Handler: gateway.router,
		},
		Logger: gateway.Logger,
	}

	return nil
}

func (gateway *Gateway) setupTChannel(config *StaticConfig) error {
	tchannelServer, err := NewTChannelServer(
		&TChannelServerOptions{
			ServiceName: config.MustGetString("tchannel.serviceName"),
			ProcessName: config.MustGetString("tchannel.processName"),
		})

	if err != nil {
		return err
	}

	gateway.tchannelServer = tchannelServer
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp 2016 All Rights Reserved.

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

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

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

package main

import (
	"errors"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init resets all the things
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting 1")
	}

	err := stub.PutState("hello_world", []byte(args[0]))
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// Invoke isur entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	// Handle different functions
	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "write" {
		return t.write(stub, args)
	}
	fmt.Println("invoke did not find func: " + function)

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query is our entry point for queries
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	// Handle different functions
	if function == "read" { //read a variable
		return t.read(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Received unknown function query: " + function)
}

// write - invoke function to write key/value pair
func (t *SimpleChaincode) write(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, value string
	var err error
	fmt.Println("running write()")

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2. name of the key and value to set")
	}

	key = args[0] //rename for funsies
	value = args[1]
	err = stub.PutState(key, []byte(value)) //write the variable into the chaincode state
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the key to query")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}

	return valAsbytes, nil
}<commit_msg>adding dummy value<commit_after>/*
Copyright IBM Corp 2016 All Rights Reserved.

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

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

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

package main

import (
	"errors"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init resets all the things
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting 1")
	}

	err := stub.PutState("hello_world", []byte(args[0]))
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// Invoke isur entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	// Handle different functions
	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "write" {
		return t.write(stub, args)
	}
	fmt.Println("invoke did not find func: " + function)

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query is our entry point for queries
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	// Handle different functions
	if function == "read" { //read a variable
		return t.read(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Received unknown function query: " + function)
}

// write - invoke function to write key/value pair
func (t *SimpleChaincode) write(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, value string
	var err error
	fmt.Println("running write()")

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2. name of the key and value to set")
	}

	key = args[0] //rename for funsies
	value = args[1]
	err = stub.PutState(key, []byte(value)) //write the variable into the chaincode state
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	/*var key, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the key to query")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}

	return valAsbytes, nil*/
	return []byte("dummy value"), nil
}<|endoftext|>


<commit_before>package lru1

import "testing"

func TestCache(t *testing.T) {
	size := 4
	cache := NewCache(size)
	items := []*Item{
		&Item{"A", "a"},
		&Item{"B", "b"},
		&Item{"C", "c"},
		&Item{"D", "d"},
		&Item{"E", "e"},
		&Item{"F", "f"},
	}
	for _, i := range items {
		cache.Put(i.key, i.value)
	}

	if cache.lst.Len() != size {
		t.Errorf("got %d list size but expected %d", cache.lst.Len(), size)
	}
	if len(cache.data) != size {
		t.Errorf("got %d map size but expected %d", len(cache.data), size)
	}

	for _, i := range items[2:] {
		c := cache.Get(i.key)
		if *c != *i {
			t.Errorf("got %v item but expected %v", *c, *i)
		}
	}
	for _, i := range items[:2] {
		if c := cache.Get(i.key); c != nil {
			t.Errorf("got %v item but expected %v", *c, nil)
		}
	}
	// TODO: add check order of items in cache
}
<commit_msg>Simplified new slice creation.<commit_after>package lru1

import "testing"

func TestCache(t *testing.T) {
	size := 4
	cache := NewCache(size)
	items := []*Item{
		{"A", "a"},
		{"B", "b"},
		{"C", "c"},
		{"D", "d"},
		{"E", "e"},
		{"F", "f"},
	}
	for _, i := range items {
		cache.Put(i.key, i.value)
	}

	if cache.lst.Len() != size {
		t.Errorf("got %d list size but expected %d", cache.lst.Len(), size)
	}
	if len(cache.data) != size {
		t.Errorf("got %d map size but expected %d", len(cache.data), size)
	}

	for _, i := range items[2:] {
		c := cache.Get(i.key)
		if *c != *i {
			t.Errorf("got %v item but expected %v", *c, *i)
		}
	}
	for _, i := range items[:2] {
		if c := cache.Get(i.key); c != nil {
			t.Errorf("got %v item but expected %v", *c, nil)
		}
	}
	// TODO: add check order of items in cache
}
<|endoftext|>


<commit_before>package mackerel

import (
	"encoding/json"
	"fmt"
	"net/http"
)

/*
{
  "alerts": [
    {
      "id": "2wpLU5fBXbG",
      "status": "CRITICAL",
      "monitorId": "2cYjfibBkaj",
      "type": "connectivity",
      "openedAt": 1445399342,
      "hostId": "2vJ965ygiXf"
    },
    {
      "id": "2ust8jNxFH3",
      "status": "CRITICAL",
      "monitorId": "2cYjfibBkaj",
      "type": "connectivity",
      "openedAt": 1441939801,
      "hostId": "2tFrtykgMib"
    }
  ]
}
*/

// Alert information
type Alert struct {
	ID        string `json:"id,omitempty"`
	Status    string `json:"status,omitempty"`
	MonitorID string `json:"monitorId,omitempty"`
	Type      string `json:"type,omitempty"`
	HostID    string `json:"hostId,omitempty"`
	Value     uint64 `json:"value,omitempty"`
	Message   string `json:"message,omitempty"`
	Reason    string `json:"reason,omitempty"`
	OpenedAt  uint64 `json:"openedAt,omitempty"`
	ClosedAt  uint64 `json:"closedAt,omitempty"`
}

// FindAlerts find monitors
func (c *Client) FindAlerts() ([]*Alert, error) {
	req, err := http.NewRequest("GET", c.urlFor("/api/v0/alerts").String(), nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data struct {
		Alerts []*(Alert) `json:"alerts"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return data.Alerts, err
}

// CloseAlert update monitor
func (c *Client) CloseAlert(alertID string, reason string) (*Alert, error) {
	var reqBody struct {
		Reason string `json:"reason"`
	}
	reqBody.Reason = reason
	resp, err := c.PostJSON(fmt.Sprintf("/api/v0/alerts/%s/close", alertID), &reqBody)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data Alert
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return &data, nil
}
<commit_msg>fix types of json<commit_after>package mackerel

import (
	"encoding/json"
	"fmt"
	"net/http"
)

/*
{
  "alerts": [
    {
      "id": "2wpLU5fBXbG",
      "status": "CRITICAL",
      "monitorId": "2cYjfibBkaj",
      "type": "connectivity",
      "openedAt": 1445399342,
      "hostId": "2vJ965ygiXf"
    },
    {
      "id": "2ust8jNxFH3",
      "status": "CRITICAL",
      "monitorId": "2cYjfibBkaj",
      "type": "connectivity",
      "openedAt": 1441939801,
      "hostId": "2tFrtykgMib"
    }
  ]
}
*/

// Alert information
type Alert struct {
	ID        string  `json:"id,omitempty"`
	Status    string  `json:"status,omitempty"`
	MonitorID string  `json:"monitorId,omitempty"`
	Type      string  `json:"type,omitempty"`
	HostID    string  `json:"hostId,omitempty"`
	Value     float64 `json:"value,omitempty"`
	Message   string  `json:"message,omitempty"`
	Reason    string  `json:"reason,omitempty"`
	OpenedAt  int64   `json:"openedAt,omitempty"`
	ClosedAt  int64   `json:"closedAt,omitempty"`
}

// FindAlerts find monitors
func (c *Client) FindAlerts() ([]*Alert, error) {
	req, err := http.NewRequest("GET", c.urlFor("/api/v0/alerts").String(), nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data struct {
		Alerts []*(Alert) `json:"alerts"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return data.Alerts, err
}

// CloseAlert update monitor
func (c *Client) CloseAlert(alertID string, reason string) (*Alert, error) {
	var reqBody struct {
		Reason string `json:"reason"`
	}
	reqBody.Reason = reason
	resp, err := c.PostJSON(fmt.Sprintf("/api/v0/alerts/%s/close", alertID), &reqBody)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data Alert
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return &data, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package checkmgr

import apiclient "github.com/circonus-labs/go-apiclient"

// IsMetricActive checks whether a given metric name is currently active(enabled)
func (cm *CheckManager) IsMetricActive(name string) bool {
	if !cm.enabled { // short circuit for metric filters
		return true
	}

	cm.availableMetricsmu.Lock()
	defer cm.availableMetricsmu.Unlock()

	return cm.availableMetrics[name]
}

// ActivateMetric determines if a given metric should be activated
func (cm *CheckManager) ActivateMetric(name string) bool {
	if !cm.enabled { // short circuit for metric filters
		return false
	}

	cm.availableMetricsmu.Lock()
	defer cm.availableMetricsmu.Unlock()

	active, exists := cm.availableMetrics[name]

	if !exists {
		return true
	}

	if !active && cm.forceMetricActivation {
		return true
	}

	return false
}

// AddMetricTags updates check bundle metrics with tags
func (cm *CheckManager) AddMetricTags(metricName string, tags []string, appendTags bool) bool {
	tagsUpdated := false

	if appendTags && len(tags) == 0 {
		return tagsUpdated
	}

	currentTags, exists := cm.metricTags[metricName]
	if !exists {
		foundMetric := false

		if cm.checkBundle != nil {
			for _, metric := range cm.checkBundle.Metrics {
				if metric.Name == metricName {
					foundMetric = true
					currentTags = metric.Tags
					break
				}
			}
		}

		if !foundMetric {
			currentTags = []string{}
		}
	}

	action := ""
	if appendTags {
		numNewTags := countNewTags(currentTags, tags)
		if numNewTags > 0 {
			action = "Added"
			currentTags = append(currentTags, tags...)
			tagsUpdated = true
		}
	} else {
		if len(tags) != len(currentTags) {
			action = "Set"
			currentTags = tags
			tagsUpdated = true
		} else {
			numNewTags := countNewTags(currentTags, tags)
			if numNewTags > 0 {
				action = "Set"
				currentTags = tags
				tagsUpdated = true
			}
		}
	}

	if tagsUpdated {
		cm.metricTags[metricName] = currentTags
	}

	if cm.Debug && action != "" {
		cm.Log.Printf("[DEBUG] %s metric tag(s) %s %v\n", action, metricName, tags)
	}

	return tagsUpdated
}

// addNewMetrics updates a check bundle with new metrics
func (cm *CheckManager) addNewMetrics(newMetrics map[string]*apiclient.CheckBundleMetric) bool {
	updatedCheckBundle := false

	if len(newMetrics) == 0 {
		return updatedCheckBundle
	}

	if cm.checkBundle == nil {
		return updatedCheckBundle
	}

	cm.cbmu.Lock()
	defer cm.cbmu.Unlock()

	numCurrMetrics := len(cm.checkBundle.Metrics)
	numNewMetrics := len(newMetrics)

	if numCurrMetrics+numNewMetrics >= cap(cm.checkBundle.Metrics) {
		nm := make([]apiclient.CheckBundleMetric, numCurrMetrics+numNewMetrics)
		copy(nm, cm.checkBundle.Metrics)
		cm.checkBundle.Metrics = nm
	}

	cm.checkBundle.Metrics = cm.checkBundle.Metrics[0 : numCurrMetrics+numNewMetrics]

	i := 0
	for _, metric := range newMetrics {
		cm.checkBundle.Metrics[numCurrMetrics+i] = *metric
		i++
		updatedCheckBundle = true
	}

	if updatedCheckBundle {
		cm.forceCheckUpdate = true
	}

	return updatedCheckBundle
}

// inventoryMetrics creates list of active metrics in check bundle
func (cm *CheckManager) inventoryMetrics() {
	availableMetrics := make(map[string]bool)
	for _, metric := range cm.checkBundle.Metrics {
		availableMetrics[metric.Name] = metric.Status == "active"
	}
	cm.availableMetricsmu.Lock()
	cm.availableMetrics = availableMetrics
	cm.availableMetricsmu.Unlock()
}

// countNewTags returns a count of new tags which do not exist in the current list of tags
func countNewTags(currTags []string, newTags []string) int {
	if len(newTags) == 0 {
		return 0
	}

	if len(currTags) == 0 {
		return len(newTags)
	}

	newTagCount := 0

	for _, newTag := range newTags {
		found := false
		for _, currTag := range currTags {
			if newTag == currTag {
				found = true
				break
			}
		}
		if !found {
			newTagCount++
		}
	}

	return newTagCount
}
<commit_msg>upd: short circuit AddMetricTags if check management is not enabled<commit_after>// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package checkmgr

import apiclient "github.com/circonus-labs/go-apiclient"

// IsMetricActive checks whether a given metric name is currently active(enabled)
func (cm *CheckManager) IsMetricActive(name string) bool {
	if !cm.enabled { // short circuit for metric filters
		return true
	}

	cm.availableMetricsmu.Lock()
	defer cm.availableMetricsmu.Unlock()

	return cm.availableMetrics[name]
}

// ActivateMetric determines if a given metric should be activated
func (cm *CheckManager) ActivateMetric(name string) bool {
	if !cm.enabled { // short circuit for metric filters
		return false
	}

	cm.availableMetricsmu.Lock()
	defer cm.availableMetricsmu.Unlock()

	active, exists := cm.availableMetrics[name]

	if !exists {
		return true
	}

	if !active && cm.forceMetricActivation {
		return true
	}

	return false
}

// AddMetricTags updates check bundle metrics with tags
func (cm *CheckManager) AddMetricTags(metricName string, tags []string, appendTags bool) bool {
	tagsUpdated := false

	if !cm.enabled {
		return tagsUpdated
	}

	if appendTags && len(tags) == 0 {
		return tagsUpdated
	}

	currentTags, exists := cm.metricTags[metricName]
	if !exists {
		foundMetric := false

		if cm.checkBundle != nil {
			for _, metric := range cm.checkBundle.Metrics {
				if metric.Name == metricName {
					foundMetric = true
					currentTags = metric.Tags
					break
				}
			}
		}

		if !foundMetric {
			currentTags = []string{}
		}
	}

	action := ""
	if appendTags {
		numNewTags := countNewTags(currentTags, tags)
		if numNewTags > 0 {
			action = "Added"
			currentTags = append(currentTags, tags...)
			tagsUpdated = true
		}
	} else {
		if len(tags) != len(currentTags) {
			action = "Set"
			currentTags = tags
			tagsUpdated = true
		} else {
			numNewTags := countNewTags(currentTags, tags)
			if numNewTags > 0 {
				action = "Set"
				currentTags = tags
				tagsUpdated = true
			}
		}
	}

	if tagsUpdated {
		cm.metricTags[metricName] = currentTags
	}

	if cm.Debug && action != "" {
		cm.Log.Printf("[DEBUG] %s metric tag(s) %s %v\n", action, metricName, tags)
	}

	return tagsUpdated
}

// addNewMetrics updates a check bundle with new metrics
func (cm *CheckManager) addNewMetrics(newMetrics map[string]*apiclient.CheckBundleMetric) bool {
	updatedCheckBundle := false

	if len(newMetrics) == 0 {
		return updatedCheckBundle
	}

	if cm.checkBundle == nil {
		return updatedCheckBundle
	}

	cm.cbmu.Lock()
	defer cm.cbmu.Unlock()

	numCurrMetrics := len(cm.checkBundle.Metrics)
	numNewMetrics := len(newMetrics)

	if numCurrMetrics+numNewMetrics >= cap(cm.checkBundle.Metrics) {
		nm := make([]apiclient.CheckBundleMetric, numCurrMetrics+numNewMetrics)
		copy(nm, cm.checkBundle.Metrics)
		cm.checkBundle.Metrics = nm
	}

	cm.checkBundle.Metrics = cm.checkBundle.Metrics[0 : numCurrMetrics+numNewMetrics]

	i := 0
	for _, metric := range newMetrics {
		cm.checkBundle.Metrics[numCurrMetrics+i] = *metric
		i++
		updatedCheckBundle = true
	}

	if updatedCheckBundle {
		cm.forceCheckUpdate = true
	}

	return updatedCheckBundle
}

// inventoryMetrics creates list of active metrics in check bundle
func (cm *CheckManager) inventoryMetrics() {
	availableMetrics := make(map[string]bool)
	for _, metric := range cm.checkBundle.Metrics {
		availableMetrics[metric.Name] = metric.Status == "active"
	}
	cm.availableMetricsmu.Lock()
	cm.availableMetrics = availableMetrics
	cm.availableMetricsmu.Unlock()
}

// countNewTags returns a count of new tags which do not exist in the current list of tags
func countNewTags(currTags []string, newTags []string) int {
	if len(newTags) == 0 {
		return 0
	}

	if len(currTags) == 0 {
		return len(newTags)
	}

	newTagCount := 0

	for _, newTag := range newTags {
		found := false
		for _, currTag := range currTags {
			if newTag == currTag {
				found = true
				break
			}
		}
		if !found {
			newTagCount++
		}
	}

	return newTagCount
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"sync"

	log "github.com/sirupsen/logrus"

	"github.com/kristjank/ark-go/arkcoin"
	"github.com/kristjank/ark-go/core"

	"github.com/fatih/color"
	"github.com/spf13/viper"

	"github.com/asdine/storm"
)

var arkclient = core.NewArkClient(nil)
var reader = bufio.NewReader(os.Stdin)
var arkpooldb *storm.DB
var wg sync.WaitGroup

var ArkGoPoolVersion string

func initLogger() {
	// Log as JSON instead of the default ASCII formatter.
	//log.SetFormatter(&log.JSONFormatter{})

	// You could set this to any `io.Writer` such as a file
	file, err := os.OpenFile("log/arkgo-pool.log", os.O_CREATE|os.O_WRONLY, 0666)
	if err == nil {
		log.SetOutput(io.MultiWriter(file))
	} else {
		log.Error("Failed to log to file, using default stderr")
	}

}

func initializeBoltClient() {
	var err error
	arkpooldb, err = storm.Open(viper.GetString("client.dbfilename"))

	if err != nil {
		log.Panic(err.Error())
		broadCastServiceMode(false)
	}

	log.Println("DB Opened at:", arkpooldb.Path)
	//defer arkpooldb.Close()
}

func broadCastServiceMode(status bool) {
	var url string
	if status {
		url = "http://127.0.0.1:" + strconv.Itoa(viper.GetInt("server.port")) + "/service/start"
	} else {
		url = "http://127.0.0.1:" + strconv.Itoa(viper.GetInt("server.port")) + "/service/stop"
		arkpooldb.Close()
	}

	res, err := http.Get(url)
	if err != nil {
		log.Error("Error setting service mode on arkgopool server", err.Error(), url)
	} else {
		log.Info("Status mode set to", status, res.StatusCode)
	}
}

func readAccountData() (string, string) {
	fmt.Println("\nEnter account passphrase")
	fmt.Print("-->")
	pass1, _ := reader.ReadString('\n')
	re := regexp.MustCompile("\r?\n")
	pass1 = re.ReplaceAllString(pass1, "")

	pass2 := ""
	key := arkcoin.NewPrivateKeyFromPassword(pass1, arkcoin.ActiveCoinConfig)

	accountResp, _, _ := arkclient.GetAccount(core.AccountQueryParams{Address: key.PublicKey.Address()})
	deleResp, _, _ := arkclient.GetDelegate(core.DelegateQueryParams{PublicKey: string(key.PublicKey.Serialize())})
	if !accountResp.Success {
		log.Info("Error getting account data for delegate: " + deleResp.SingleDelegate.Username + "[" + key.PublicKey.Address() + "]")
		return "error", ""
	}

	if accountResp.Account.SecondSignature == 1 {
		fmt.Println("\nEnter second account passphrase for delegate: " + deleResp.SingleDelegate.Username + "[" + key.PublicKey.Address() + "]")
		fmt.Print("-->")
		pass2, _ = reader.ReadString('\n')
		re := regexp.MustCompile("\r?\n")
		pass2 = re.ReplaceAllString(pass2, "")
	}

	return pass1, pass2
}

func loadConfig() {
	viper.SetConfigName("config")   // name of config file (without extension)
	viper.AddConfigPath("settings") // path to look for the config file in
	viper.AddConfigPath(".")        // optionally look for config in the working directory
	err := viper.ReadInConfig()     // Find and read the config file

	if err != nil {
		log.Info("No productive config found - loading sample")
		// try to load sample config
		viper.SetConfigName("sample.config")
		viper.AddConfigPath("settings")
		err := viper.ReadInConfig()

		if err != nil { // Handle errors reading the config file
			log.Info("No configuration file loaded - using defaults")
		}
	}

	viper.SetDefault("delegate.address", "")
	viper.SetDefault("delegate.pubkey", "")
	viper.SetDefault("delegate.Daddress", "")
	viper.SetDefault("delegate.Dpubkey", "")

	viper.SetDefault("voters.shareRatio", 0.0)
	viper.SetDefault("voters.txdescription", "share tx by ark-go")
	viper.SetDefault("voters.fidelity", true)
	viper.SetDefault("voters.fidelityLimit", 24)
	viper.SetDefault("voters.minamount", 0.0)
	viper.SetDefault("voters.deductTxFees", true)

	viper.SetDefault("costs.address", "")
	viper.SetDefault("costs.shareRatio", 0.0)
	viper.SetDefault("costs.txdescription", "cost tx by ark-go")
	viper.SetDefault("costs.Daddress", "")

	viper.SetDefault("reserve.address", "")
	viper.SetDefault("reserve.shareRatio", 0.0)
	viper.SetDefault("reserve.txdescription", "reserve tx by ark-go")
	viper.SetDefault("reserve.Daddress", "")

	viper.SetDefault("personal.address", "")
	viper.SetDefault("personal.shareRatio", 0.0)
	viper.SetDefault("personal.txdescription", "personal tx by ark-go")
	viper.SetDefault("personal.Daddress", "")

	viper.SetDefault("client.network", "DEVNET")
	viper.SetDefault("client.dbFilename", "payment.db")
	viper.SetDefault("client.multibroadcast", 10)
}

//////////////////////////////////////////////////////////////////////////////
//GUI RELATED STUFF
func pause() {
	color.Set(color.FgHiYellow)
	fmt.Println("")
	fmt.Print("Press 'ENTER' key to continue... ")
	//bufio.NewReader(os.Stdin).ReadBytes('\n')
	reader.ReadString('\n')
}

func clearScreen() {
	cmd := exec.Command("clear")
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cmd", "/c", "cls")
	}

	cmd.Stdout = os.Stdout
	cmd.Run()

}

func printNetworkInfo() {
	color.Set(color.FgHiCyan)
	if core.EnvironmentParams.Network.Type == core.MAINNET {
		fmt.Println("Connected to MAINNET on peer:", core.BaseURL, "| ARKGoPool version", ArkGoPoolVersion)
	}

	if core.EnvironmentParams.Network.Type == core.DEVNET {
		fmt.Println("Connected to DEVNET on peer:", core.BaseURL, "| ARKGoPool version", ArkGoPoolVersion)
	}
}

func printBanner() {
	color.Set(color.FgHiGreen)
	dat, _ := ioutil.ReadFile("settings/banner.txt")
	fmt.Print(string(dat))
}

func printMenu() {
	log.Info("--------- MAIN MENU ----------------")
	clearScreen()
	printBanner()
	printNetworkInfo()
	color.Set(color.FgHiYellow)
	fmt.Println("")
	fmt.Println("\t1-Display contributors")
	fmt.Println("\t2-Send reward payments")
	fmt.Println("\t3-Switch network")
	fmt.Println("\t4-Link account")
	fmt.Println("\t5-Send bonus payments N/A currently")
	fmt.Println("\t6-List history payments")
	fmt.Println("\t0-Exit")
	fmt.Println("")
	fmt.Print("\tSelect option [1-9]:")
	color.Unset()
}

func main() {
	//sending ARKGO Server that we are working with payments
	//setting the version
	ArkGoPoolVersion = "v0.7.7"

	// Load configration and defaults
	// Order is important
	loadConfig()
	broadCastServiceMode(true)
	initLogger()

	log.Info("Ark-golang client starting")
	log.Info("ArkApiClient connected, active peer: ", arkclient.GetActivePeer())

	initializeBoltClient()

	//switch to preset network
	if viper.GetString("client.network") == "DEVNET" {
		arkclient = arkclient.SetActiveConfiguration(core.DEVNET)
	}

	//SILENT MODE CHECKING AND AUTOMATION RUNNING
	modeSilentPtr := flag.Bool("silent", false, "Is silent mode")
	//autoPayment := flag.Bool("autopay", true, "Process auto payment")
	flag.Parse()
	log.Info(flag.Args())
	if *modeSilentPtr {
		log.Info("Silent Mode active")
		log.Info("Starting to send payments")
		SendPayments(true)
		log.Info("Waiting for threads to complete")
		color.Unset()
		wg.Wait()
		log.Info("Exiting silent mode and arkgopool")
		os.Exit(1985)
		//sending ARKGO Server that we are working with payments
		broadCastServiceMode(false)
	}

	var choice = 1
	for choice != 0 {
		//pause()
		printMenu()

		//fmt.Scan(&choice)
		fmt.Fscan(reader, &choice)
		reader.ReadString('\n')

		switch choice {
		case 1:
			clearScreen()
			color.Set(color.FgMagenta)
			DisplayCalculatedVoteRatio()
			color.Unset()
		case 2:
			clearScreen()
			color.Set(color.FgHiGreen)
			SendPayments(false)
			wg.Wait()
			color.Unset()
		case 3:
			if core.EnvironmentParams.Network.Type == core.MAINNET {
				arkclient = arkclient.SetActiveConfiguration(core.DEVNET)
			} else {
				arkclient = arkclient.SetActiveConfiguration(core.MAINNET)
			}
		case 4:
			clearScreen()
			save(readAccountData())
			color.Set(color.FgHiGreen)
			log.Info("Account succesfully linked")
			fmt.Println("Account succesfully linked")
			pause()
			color.Unset()
		case 5:
			clearScreen()
			color.Set(color.FgHiGreen)
			//SendBonus()
			color.Unset()
		case 6:
			clearScreen()
			color.Set(color.FgHiGreen)
			listPaymentsDB()
			pause()
			//listPaymentsDetailsFromDB()
			//pause()
			color.Unset()
		}
	}
	color.Unset()
	broadCastServiceMode(false)
}
<commit_msg>closing db before signaling server<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"sync"

	log "github.com/sirupsen/logrus"

	"github.com/kristjank/ark-go/arkcoin"
	"github.com/kristjank/ark-go/core"

	"github.com/fatih/color"
	"github.com/spf13/viper"

	"github.com/asdine/storm"
)

var arkclient = core.NewArkClient(nil)
var reader = bufio.NewReader(os.Stdin)
var arkpooldb *storm.DB
var wg sync.WaitGroup

var ArkGoPoolVersion string

func initLogger() {
	// Log as JSON instead of the default ASCII formatter.
	//log.SetFormatter(&log.JSONFormatter{})

	// You could set this to any `io.Writer` such as a file
	file, err := os.OpenFile("log/arkgo-pool.log", os.O_CREATE|os.O_WRONLY, 0666)
	if err == nil {
		log.SetOutput(io.MultiWriter(file))
	} else {
		log.Error("Failed to log to file, using default stderr")
	}

}

func initializeBoltClient() {
	var err error
	arkpooldb, err = storm.Open(viper.GetString("client.dbfilename"))

	if err != nil {
		log.Panic(err.Error())
		broadCastServiceMode(false)
	}

	log.Println("DB Opened at:", arkpooldb.Path)
	//defer arkpooldb.Close()
}

func broadCastServiceMode(status bool) {
	var url string
	if status {
		url = "http://127.0.0.1:" + strconv.Itoa(viper.GetInt("server.port")) + "/service/start"
	} else {
		arkpooldb.Close()
		url = "http://127.0.0.1:" + strconv.Itoa(viper.GetInt("server.port")) + "/service/stop"
	}

	res, err := http.Get(url)
	if err != nil {
		log.Error("Error setting service mode on arkgopool server", err.Error(), url)
	} else {
		log.Info("Status mode set to", status, res.StatusCode)
	}
}

func readAccountData() (string, string) {
	fmt.Println("\nEnter account passphrase")
	fmt.Print("-->")
	pass1, _ := reader.ReadString('\n')
	re := regexp.MustCompile("\r?\n")
	pass1 = re.ReplaceAllString(pass1, "")

	pass2 := ""
	key := arkcoin.NewPrivateKeyFromPassword(pass1, arkcoin.ActiveCoinConfig)

	accountResp, _, _ := arkclient.GetAccount(core.AccountQueryParams{Address: key.PublicKey.Address()})
	deleResp, _, _ := arkclient.GetDelegate(core.DelegateQueryParams{PublicKey: string(key.PublicKey.Serialize())})
	if !accountResp.Success {
		log.Info("Error getting account data for delegate: " + deleResp.SingleDelegate.Username + "[" + key.PublicKey.Address() + "]")
		return "error", ""
	}

	if accountResp.Account.SecondSignature == 1 {
		fmt.Println("\nEnter second account passphrase for delegate: " + deleResp.SingleDelegate.Username + "[" + key.PublicKey.Address() + "]")
		fmt.Print("-->")
		pass2, _ = reader.ReadString('\n')
		re := regexp.MustCompile("\r?\n")
		pass2 = re.ReplaceAllString(pass2, "")
	}

	return pass1, pass2
}

func loadConfig() {
	viper.SetConfigName("config")   // name of config file (without extension)
	viper.AddConfigPath("settings") // path to look for the config file in
	viper.AddConfigPath(".")        // optionally look for config in the working directory
	err := viper.ReadInConfig()     // Find and read the config file

	if err != nil {
		log.Info("No productive config found - loading sample")
		// try to load sample config
		viper.SetConfigName("sample.config")
		viper.AddConfigPath("settings")
		err := viper.ReadInConfig()

		if err != nil { // Handle errors reading the config file
			log.Info("No configuration file loaded - using defaults")
		}
	}

	viper.SetDefault("delegate.address", "")
	viper.SetDefault("delegate.pubkey", "")
	viper.SetDefault("delegate.Daddress", "")
	viper.SetDefault("delegate.Dpubkey", "")

	viper.SetDefault("voters.shareRatio", 0.0)
	viper.SetDefault("voters.txdescription", "share tx by ark-go")
	viper.SetDefault("voters.fidelity", true)
	viper.SetDefault("voters.fidelityLimit", 24)
	viper.SetDefault("voters.minamount", 0.0)
	viper.SetDefault("voters.deductTxFees", true)

	viper.SetDefault("costs.address", "")
	viper.SetDefault("costs.shareRatio", 0.0)
	viper.SetDefault("costs.txdescription", "cost tx by ark-go")
	viper.SetDefault("costs.Daddress", "")

	viper.SetDefault("reserve.address", "")
	viper.SetDefault("reserve.shareRatio", 0.0)
	viper.SetDefault("reserve.txdescription", "reserve tx by ark-go")
	viper.SetDefault("reserve.Daddress", "")

	viper.SetDefault("personal.address", "")
	viper.SetDefault("personal.shareRatio", 0.0)
	viper.SetDefault("personal.txdescription", "personal tx by ark-go")
	viper.SetDefault("personal.Daddress", "")

	viper.SetDefault("client.network", "DEVNET")
	viper.SetDefault("client.dbFilename", "payment.db")
	viper.SetDefault("client.multibroadcast", 10)
}

//////////////////////////////////////////////////////////////////////////////
//GUI RELATED STUFF
func pause() {
	color.Set(color.FgHiYellow)
	fmt.Println("")
	fmt.Print("Press 'ENTER' key to continue... ")
	//bufio.NewReader(os.Stdin).ReadBytes('\n')
	reader.ReadString('\n')
}

func clearScreen() {
	cmd := exec.Command("clear")
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cmd", "/c", "cls")
	}

	cmd.Stdout = os.Stdout
	cmd.Run()

}

func printNetworkInfo() {
	color.Set(color.FgHiCyan)
	if core.EnvironmentParams.Network.Type == core.MAINNET {
		fmt.Println("Connected to MAINNET on peer:", core.BaseURL, "| ARKGoPool version", ArkGoPoolVersion)
	}

	if core.EnvironmentParams.Network.Type == core.DEVNET {
		fmt.Println("Connected to DEVNET on peer:", core.BaseURL, "| ARKGoPool version", ArkGoPoolVersion)
	}
}

func printBanner() {
	color.Set(color.FgHiGreen)
	dat, _ := ioutil.ReadFile("settings/banner.txt")
	fmt.Print(string(dat))
}

func printMenu() {
	log.Info("--------- MAIN MENU ----------------")
	clearScreen()
	printBanner()
	printNetworkInfo()
	color.Set(color.FgHiYellow)
	fmt.Println("")
	fmt.Println("\t1-Display contributors")
	fmt.Println("\t2-Send reward payments")
	fmt.Println("\t3-Switch network")
	fmt.Println("\t4-Link account")
	fmt.Println("\t5-Send bonus payments N/A currently")
	fmt.Println("\t6-List history payments")
	fmt.Println("\t0-Exit")
	fmt.Println("")
	fmt.Print("\tSelect option [1-9]:")
	color.Unset()
}

func main() {
	//sending ARKGO Server that we are working with payments
	//setting the version
	ArkGoPoolVersion = "v0.7.7"

	// Load configration and defaults
	// Order is important
	loadConfig()
	broadCastServiceMode(true)
	initLogger()

	log.Info("Ark-golang client starting")
	log.Info("ArkApiClient connected, active peer: ", arkclient.GetActivePeer())

	initializeBoltClient()

	//switch to preset network
	if viper.GetString("client.network") == "DEVNET" {
		arkclient = arkclient.SetActiveConfiguration(core.DEVNET)
	}

	//SILENT MODE CHECKING AND AUTOMATION RUNNING
	modeSilentPtr := flag.Bool("silent", false, "Is silent mode")
	//autoPayment := flag.Bool("autopay", true, "Process auto payment")
	flag.Parse()
	log.Info(flag.Args())
	if *modeSilentPtr {
		log.Info("Silent Mode active")
		log.Info("Starting to send payments")
		SendPayments(true)
		log.Info("Waiting for threads to complete")
		color.Unset()
		wg.Wait()
		log.Info("Exiting silent mode and arkgopool")
		os.Exit(1985)
		//sending ARKGO Server that we are working with payments
		broadCastServiceMode(false)
	}

	var choice = 1
	for choice != 0 {
		//pause()
		printMenu()

		//fmt.Scan(&choice)
		fmt.Fscan(reader, &choice)
		reader.ReadString('\n')

		switch choice {
		case 1:
			clearScreen()
			color.Set(color.FgMagenta)
			DisplayCalculatedVoteRatio()
			color.Unset()
		case 2:
			clearScreen()
			color.Set(color.FgHiGreen)
			SendPayments(false)
			wg.Wait()
			color.Unset()
		case 3:
			if core.EnvironmentParams.Network.Type == core.MAINNET {
				arkclient = arkclient.SetActiveConfiguration(core.DEVNET)
			} else {
				arkclient = arkclient.SetActiveConfiguration(core.MAINNET)
			}
		case 4:
			clearScreen()
			save(readAccountData())
			color.Set(color.FgHiGreen)
			log.Info("Account succesfully linked")
			fmt.Println("Account succesfully linked")
			pause()
			color.Unset()
		case 5:
			clearScreen()
			color.Set(color.FgHiGreen)
			//SendBonus()
			color.Unset()
		case 6:
			clearScreen()
			color.Set(color.FgHiGreen)
			listPaymentsDB()
			pause()
			//listPaymentsDetailsFromDB()
			//pause()
			color.Unset()
		}
	}
	color.Unset()
	broadCastServiceMode(false)
}
<|endoftext|>


<commit_before>package main

import (
	"expvar"
	"log"
	"os"
	"os/signal"
	"strings"
	"sync/atomic"
	"syscall"
	"time"

	"github.com/VojtechVitek/go-trello"
	"github.com/pkg/errors"
	"github.com/thecodearchive/gitarchive/index"
)

type Backpanel struct {
	c     *trello.Client
	i     *index.Index
	exp   *expvar.Map
	pause time.Duration

	blacklistID string

	closing uint32
}

type Label struct {
	Color string `json:"color"`
	Name  string `json:"name"`
}

var (
	labelGH     = Label{"green", "github.com"}
	labelTooBig = Label{"blue", "TOO BIG"}
	labelCrash  = Label{"black", "BLACK"}
)

func (b *Backpanel) Run() error {
	for atomic.LoadUint32(&b.closing) == 0 {
		board, err := b.c.Board(b.blacklistID)
		if err != nil {
			return errors.Wrapf(err, "getting board %s", b.blacklistID)
		}

		var whitelist, blacklist *trello.List
		lists, err := board.Lists()
		if err != nil {
			return errors.Wrapf(err, "getting board %s lists", b.blacklistID)
		}
		for _, list := range lists {
			if list.Name == "Whitelist" {
				whitelist = &list
			}
			if list.Name == "Blacklist" {
				blacklist = &list
			}
		}
		if whitelist == nil || blacklist == nil {
			return errors.New("Lists not found.")
		}

		tr := make(map[string]*trello.Card)
		cards, err := board.Cards()
		if err != nil {
			return errors.Wrapf(err, "getting board %s cards", b.blacklistID)
		}
		for _, card := range cards {
			tr[card.Name] = &card
		}

		ble, err := b.i.ListBlacklist()
		if err != nil {
			return err
		}

		for _, e := range ble {
			n := strings.TrimPrefix(e.Name, "github.com/")
			if card, ok := tr[n]; !ok {
				// New line in the database, add card.
				card := trello.Card{Name: n}
				card.Desc = "https://github.com/" + n
				card.Labels = append(card.Labels, labelGH)
				if e.Reason == "Too big." {
					card.Labels = append(card.Labels, labelTooBig)
				} else {
					card.Labels = append(card.Labels, labelCrash)
					card.Desc += "\n\n" + e.Reason
				}
				list := blacklist
				if e.State == index.Whitelisted {
					list = whitelist
				}
				_, err := list.AddCard(card)
				if err != nil {
					return errors.Wrapf(err, "Adding card %#v", card)
				}
				b.exp.Add("newcard", 1)

			} else {
				// Card present, check if it matches.
				// So far the only supported action is changing lists.
				if card.IdList == whitelist.Id && e.State != index.Whitelisted {
					log.Println("Whitelisting", e.Name)
					err := b.i.SetBlacklistState(e.Name, index.Whitelisted)
					if err != nil {
						return err
					}
					_, err = card.AddComment("Applied whitelist!")
					if err != nil {
						return errors.Wrapf(err, "Adding comment to card %s", card.Name)
					}
					b.exp.Add("moved", 1)
				}
				if card.IdList == blacklist.Id && e.State != index.Blacklisted {
					log.Println("Blacklisting", e.Name)
					err := b.i.SetBlacklistState(e.Name, index.Blacklisted)
					if err != nil {
						return err
					}
					_, err = card.AddComment("Applied blacklist!")
					if err != nil {
						return errors.Wrapf(err, "Adding comment to card %s", card.Name)
					}
					b.exp.Add("moved", 1)
				}
				delete(tr, n)
			}
		}

		// Only new entries are left now in tr.
		for _, card := range tr {
			if card.IdList == blacklist.Id {
				log.Println("Blacklisting (new)", card.Name)
			} else if card.IdList == whitelist.Id {
				log.Println("Whitelisting (new)", card.Name)
			} else {
				continue
			}
			err := b.i.AddBlacklist("github.com/"+card.Name, card.Desc)
			if err != nil {
				return err
			}
			msg := "blacklist"
			if card.IdList == whitelist.Id {
				err := b.i.SetBlacklistState("github.com/"+card.Name, index.Whitelisted)
				if err != nil {
					return err
				}
				msg = "whitelist"
			}
			_, err = card.AddComment("Added to " + msg + "!")
			if err != nil {
				return errors.Wrapf(err, "Adding comment to card %s", card.Name)
			}
			b.exp.Add("newline", 1)
		}

		// TODO: also keep the labels and descriptions up to date.

		if !interruptableSleep(b.pause) {
			return nil
		}
	}
	return nil
}

func (b *Backpanel) Stop() {
	atomic.StoreUint32(&b.closing, 1)
}

func interruptableSleep(d time.Duration) bool {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	defer signal.Stop(c)
	t := time.NewTimer(d)
	select {
	case <-c:
		return false
	case <-t.C:
		return true
	}
}
<commit_msg>backpanel: fix memory management in loop<commit_after>package main

import (
	"expvar"
	"log"
	"os"
	"os/signal"
	"strings"
	"sync/atomic"
	"syscall"
	"time"

	"github.com/VojtechVitek/go-trello"
	"github.com/pkg/errors"
	"github.com/thecodearchive/gitarchive/index"
)

type Backpanel struct {
	c     *trello.Client
	i     *index.Index
	exp   *expvar.Map
	pause time.Duration

	blacklistID string

	closing uint32
}

type Label struct {
	Color string `json:"color"`
	Name  string `json:"name"`
}

var (
	labelGH     = Label{"green", "github.com"}
	labelTooBig = Label{"blue", "TOO BIG"}
	labelCrash  = Label{"black", "BLACK"}
)

func (b *Backpanel) Run() error {
	for atomic.LoadUint32(&b.closing) == 0 {
		board, err := b.c.Board(b.blacklistID)
		if err != nil {
			return errors.Wrapf(err, "getting board %s", b.blacklistID)
		}

		var whitelist, blacklist *trello.List
		lists, err := board.Lists()
		if err != nil {
			return errors.Wrapf(err, "getting board %s lists", b.blacklistID)
		}
		for _, list := range lists {
			if list.Name == "Whitelist" {
				l := list
				whitelist = &l
			}
			if list.Name == "Blacklist" {
				l := list
				blacklist = &l
			}
		}
		if whitelist == nil || blacklist == nil {
			return errors.New("Lists not found.")
		}

		tr := make(map[string]trello.Card)
		cards, err := board.Cards()
		if err != nil {
			return errors.Wrapf(err, "getting board %s cards", b.blacklistID)
		}
		for _, card := range cards {
			tr[card.Name] = card
		}

		ble, err := b.i.ListBlacklist()
		if err != nil {
			return err
		}

		for _, e := range ble {
			n := strings.TrimPrefix(e.Name, "github.com/")
			if card, ok := tr[n]; !ok {
				// New line in the database, add card.
				card := trello.Card{Name: n}
				card.Desc = "https://github.com/" + n
				card.Labels = append(card.Labels, labelGH)
				if e.Reason == "Too big." {
					card.Labels = append(card.Labels, labelTooBig)
				} else {
					card.Labels = append(card.Labels, labelCrash)
					card.Desc += "\n\n" + e.Reason
				}
				list := blacklist
				if e.State == index.Whitelisted {
					list = whitelist
				}
				_, err := list.AddCard(card)
				if err != nil {
					return errors.Wrapf(err, "Adding card %#v", card)
				}
				b.exp.Add("newcard", 1)

			} else {
				// Card present, check if it matches.
				// So far the only supported action is changing lists.
				if card.IdList == whitelist.Id && e.State != index.Whitelisted {
					log.Println("Whitelisting", e.Name)
					err := b.i.SetBlacklistState(e.Name, index.Whitelisted)
					if err != nil {
						return err
					}
					_, err = card.AddComment("Applied whitelist!")
					if err != nil {
						return errors.Wrapf(err, "Adding comment to card %s", card.Name)
					}
					b.exp.Add("moved", 1)
				}
				if card.IdList == blacklist.Id && e.State != index.Blacklisted {
					log.Println("Blacklisting", e.Name)
					err := b.i.SetBlacklistState(e.Name, index.Blacklisted)
					if err != nil {
						return err
					}
					_, err = card.AddComment("Applied blacklist!")
					if err != nil {
						return errors.Wrapf(err, "Adding comment to card %s", card.Name)
					}
					b.exp.Add("moved", 1)
				}
				delete(tr, n)
			}
		}

		// Only new entries are left now in tr.
		for _, card := range tr {
			if card.IdList == blacklist.Id {
				log.Println("Blacklisting (new)", card.Name)
			} else if card.IdList == whitelist.Id {
				log.Println("Whitelisting (new)", card.Name)
			} else {
				continue
			}
			err := b.i.AddBlacklist("github.com/"+card.Name, card.Desc)
			if err != nil {
				return err
			}
			msg := "blacklist"
			if card.IdList == whitelist.Id {
				err := b.i.SetBlacklistState("github.com/"+card.Name, index.Whitelisted)
				if err != nil {
					return err
				}
				msg = "whitelist"
			}
			_, err = card.AddComment("Added to " + msg + "!")
			if err != nil {
				return errors.Wrapf(err, "Adding comment to card %s", card.Name)
			}
			b.exp.Add("newline", 1)
		}

		// TODO: also keep the labels and descriptions up to date.

		if !interruptableSleep(b.pause) {
			return nil
		}
	}
	return nil
}

func (b *Backpanel) Stop() {
	atomic.StoreUint32(&b.closing, 1)
}

func interruptableSleep(d time.Duration) bool {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	defer signal.Stop(c)
	t := time.NewTimer(d)
	select {
	case <-c:
		return false
	case <-t.C:
		return true
	}
}
<|endoftext|>


<commit_before>package main_test

import (
	"fmt"
	"syscall"
	"time"

	"github.com/cloudfoundry/storeadapter"
	"github.com/cloudfoundry/storeadapter/storerunner/etcdstorerunner"
	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/config"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-golang/clock"
	"github.com/pivotal-golang/lager"
	"github.com/pivotal-golang/lager/lagertest"

	"github.com/cloudfoundry-incubator/converger/converger_runner"
	Bbs "github.com/cloudfoundry-incubator/runtime-schema/bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/shared"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
)

var _ = Describe("Converger", func() {
	const (
		exitDuration = 4 * time.Second
	)

	var (
		etcdRunner *etcdstorerunner.ETCDClusterRunner
		bbs        *Bbs.BBS
		runner     *converger_runner.ConvergerRunner

		convergeRepeatInterval      time.Duration
		taskKickInterval            time.Duration
		expireCompletedTaskDuration time.Duration

		etcdClient storeadapter.StoreAdapter

		logger lager.Logger
	)

	SynchronizedBeforeSuite(func() []byte {
		convergerBinPath, err := gexec.Build("github.com/cloudfoundry-incubator/converger/cmd/converger", "-race")
		Ω(err).ShouldNot(HaveOccurred())
		return []byte(convergerBinPath)
	}, func(convergerBinPath []byte) {
		etcdPort := 5001 + config.GinkgoConfig.ParallelNode
		etcdCluster := fmt.Sprintf("http://127.0.0.1:%d", etcdPort)
		etcdRunner = etcdstorerunner.NewETCDClusterRunner(etcdPort, 1)

		etcdClient = etcdRunner.Adapter()
		logger = lagertest.NewTestLogger("test")
		bbs = Bbs.NewBBS(etcdClient, clock.NewClock(), logger)

		runner = converger_runner.New(string(convergerBinPath), etcdCluster, "info")
	})

	SynchronizedAfterSuite(func() {
		etcdRunner.Stop()
	}, func() {
		gexec.CleanupBuildArtifacts()
	})

	BeforeEach(func() {
		etcdRunner.Start()
		capacity := models.NewCellCapacity(512, 1024, 124)
		cellPresence := models.NewCellPresence("the-cell-id", "the-stack", "1.2.3.4", "the-zone", capacity)

		value, err := models.ToJSON(cellPresence)
		Ω(err).ShouldNot(HaveOccurred())
		etcdClient.Create(storeadapter.StoreNode{
			Key:   shared.CellSchemaPath(cellPresence.CellID),
			Value: value,
		})

		convergeRepeatInterval = 200 * time.Millisecond
		taskKickInterval = convergeRepeatInterval
		expireCompletedTaskDuration = 3 * convergeRepeatInterval
	})

	AfterEach(func() {
		runner.KillWithFire()
		etcdRunner.Stop()
	})

	startConverger := func() {
		runner.Start(convergeRepeatInterval, taskKickInterval, 30*time.Minute, expireCompletedTaskDuration)
		time.Sleep(convergeRepeatInterval)
	}

	createRunningTaskWithDeadCell := func() {
		task := models.Task{
			Domain: "tests",

			TaskGuid: "task-guid",
			Stack:    "stack",
			Action: &models.RunAction{
				Path: "cat",
				Args: []string{"/tmp/file"},
			},
		}

		err := bbs.DesireTask(logger, task)
		Ω(err).ShouldNot(HaveOccurred())

		_, err = bbs.StartTask(logger, task.TaskGuid, "dead-cell")
		Ω(err).ShouldNot(HaveOccurred())
	}

	itIsInactive := func() {
		Describe("when a task is desired but its cell is dead", func() {
			JustBeforeEach(createRunningTaskWithDeadCell)

			It("does not converge the task", func() {
				Consistently(func() ([]models.Task, error) {
					return bbs.CompletedTasks(logger)
				}, 3*convergeRepeatInterval).Should(BeEmpty())
			})
		})
	}

	Context("when the converger has the lock", func() {
		Describe("when a task is desired but its cell is dead", func() {
			JustBeforeEach(createRunningTaskWithDeadCell)

			It("marks the task as completed and failed", func() {
				Consistently(func() ([]models.Task, error) {
					return bbs.CompletedTasks(logger)
				}, 0.5).Should(BeEmpty())

				startConverger()

				Eventually(func() ([]models.Task, error) {
					return bbs.CompletedTasks(logger)
				}, 3*convergeRepeatInterval).Should(HaveLen(1))
			})
		})
	})

	Context("when the converger loses the lock", func() {
		BeforeEach(func() {
			startConverger()
			Eventually(runner.Session, 5*time.Second).Should(gbytes.Say("acquired-lock"))
			err := etcdClient.Update(storeadapter.StoreNode{
				Key:   shared.LockSchemaPath("converge_lock"),
				Value: []byte("something-else"),
			})

			Ω(err).ShouldNot(HaveOccurred())
		})

		It("exits with an error", func() {
			Eventually(runner.Session, exitDuration).Should(gexec.Exit(1))
		})
	})

	Context("when the converger initially does not have the lock", func() {
		BeforeEach(func() {
			err := etcdClient.Create(storeadapter.StoreNode{
				Key:   shared.LockSchemaPath("converge_lock"),
				Value: []byte("something-else"),
			})
			Ω(err).ShouldNot(HaveOccurred())

			startConverger()
		})

		itIsInactive()

		Describe("when the lock becomes available", func() {
			BeforeEach(func() {
				err := etcdClient.Delete(shared.LockSchemaPath("converge_lock"))
				Ω(err).ShouldNot(HaveOccurred())

				time.Sleep(convergeRepeatInterval + 10*time.Millisecond)
			})

			Describe("when a running task with a dead cell is present", func() {
				JustBeforeEach(createRunningTaskWithDeadCell)

				It("eventually marks the task as failed", func() {
					Eventually(func() ([]models.Task, error) {
						return bbs.CompletedTasks(logger)
					}, 3*convergeRepeatInterval).Should(HaveLen(1))
				})
			})
		})
	})

	Describe("signal handling", func() {
		BeforeEach(func() {
			startConverger()
		})

		Describe("when it receives SIGINT", func() {
			It("exits successfully", func() {
				runner.Session.Command.Process.Signal(syscall.SIGINT)
				Eventually(runner.Session, exitDuration).Should(gexec.Exit(0))
			})
		})

		Describe("when it receives SIGTERM", func() {
			It("exits successfully", func() {
				runner.Session.Command.Process.Signal(syscall.SIGTERM)
				Eventually(runner.Session, exitDuration).Should(gexec.Exit(0))
			})
		})
	})
})
<commit_msg>Tweak timing parameters in component test<commit_after>package main_test

import (
	"fmt"
	"syscall"
	"time"

	"github.com/cloudfoundry/storeadapter"
	"github.com/cloudfoundry/storeadapter/storerunner/etcdstorerunner"
	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/config"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-golang/clock"
	"github.com/pivotal-golang/lager"
	"github.com/pivotal-golang/lager/lagertest"

	"github.com/cloudfoundry-incubator/converger/converger_runner"
	Bbs "github.com/cloudfoundry-incubator/runtime-schema/bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/shared"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
)

var _ = Describe("Converger", func() {
	const (
		exitDuration = 4 * time.Second
	)

	var (
		etcdRunner *etcdstorerunner.ETCDClusterRunner
		bbs        *Bbs.BBS
		runner     *converger_runner.ConvergerRunner

		convergeRepeatInterval      time.Duration
		taskKickInterval            time.Duration
		expireCompletedTaskDuration time.Duration

		etcdClient storeadapter.StoreAdapter

		logger lager.Logger
	)

	SynchronizedBeforeSuite(func() []byte {
		convergerBinPath, err := gexec.Build("github.com/cloudfoundry-incubator/converger/cmd/converger", "-race")
		Ω(err).ShouldNot(HaveOccurred())
		return []byte(convergerBinPath)
	}, func(convergerBinPath []byte) {
		etcdPort := 5001 + config.GinkgoConfig.ParallelNode
		etcdCluster := fmt.Sprintf("http://127.0.0.1:%d", etcdPort)
		etcdRunner = etcdstorerunner.NewETCDClusterRunner(etcdPort, 1)

		etcdClient = etcdRunner.Adapter()
		logger = lagertest.NewTestLogger("test")
		bbs = Bbs.NewBBS(etcdClient, clock.NewClock(), logger)

		runner = converger_runner.New(string(convergerBinPath), etcdCluster, "info")
	})

	SynchronizedAfterSuite(func() {
		etcdRunner.Stop()
	}, func() {
		gexec.CleanupBuildArtifacts()
	})

	BeforeEach(func() {
		etcdRunner.Start()
		capacity := models.NewCellCapacity(512, 1024, 124)
		cellPresence := models.NewCellPresence("the-cell-id", "the-stack", "1.2.3.4", "the-zone", capacity)

		value, err := models.ToJSON(cellPresence)
		Ω(err).ShouldNot(HaveOccurred())
		etcdClient.Create(storeadapter.StoreNode{
			Key:   shared.CellSchemaPath(cellPresence.CellID),
			Value: value,
		})

		convergeRepeatInterval = 500 * time.Millisecond
		taskKickInterval = convergeRepeatInterval
		expireCompletedTaskDuration = 3 * convergeRepeatInterval
	})

	AfterEach(func() {
		runner.KillWithFire()
		etcdRunner.Stop()
	})

	startConverger := func() {
		runner.Start(convergeRepeatInterval, taskKickInterval, 30*time.Minute, expireCompletedTaskDuration)
		time.Sleep(convergeRepeatInterval)
	}

	createRunningTaskWithDeadCell := func() {
		task := models.Task{
			Domain: "tests",

			TaskGuid: "task-guid",
			Stack:    "stack",
			Action: &models.RunAction{
				Path: "cat",
				Args: []string{"/tmp/file"},
			},
		}

		err := bbs.DesireTask(logger, task)
		Ω(err).ShouldNot(HaveOccurred())

		_, err = bbs.StartTask(logger, task.TaskGuid, "dead-cell")
		Ω(err).ShouldNot(HaveOccurred())
	}

	itIsInactive := func() {
		Describe("when a task is desired but its cell is dead", func() {
			JustBeforeEach(createRunningTaskWithDeadCell)

			It("does not converge the task", func() {
				Consistently(func() ([]models.Task, error) {
					return bbs.CompletedTasks(logger)
				}, 5*convergeRepeatInterval).Should(BeEmpty())
			})
		})
	}

	Context("when the converger has the lock", func() {
		Describe("when a task is desired but its cell is dead", func() {
			JustBeforeEach(createRunningTaskWithDeadCell)

			It("marks the task as completed and failed", func() {
				Consistently(func() ([]models.Task, error) {
					return bbs.CompletedTasks(logger)
				}, 0.5).Should(BeEmpty())

				startConverger()

				Eventually(func() ([]models.Task, error) {
					return bbs.CompletedTasks(logger)
				}, 5*convergeRepeatInterval).Should(HaveLen(1))
			})
		})
	})

	Context("when the converger loses the lock", func() {
		BeforeEach(func() {
			startConverger()
			Eventually(runner.Session, 5*time.Second).Should(gbytes.Say("acquired-lock"))
			err := etcdClient.Update(storeadapter.StoreNode{
				Key:   shared.LockSchemaPath("converge_lock"),
				Value: []byte("something-else"),
			})

			Ω(err).ShouldNot(HaveOccurred())
		})

		It("exits with an error", func() {
			Eventually(runner.Session, exitDuration).Should(gexec.Exit(1))
		})
	})

	Context("when the converger initially does not have the lock", func() {
		BeforeEach(func() {
			err := etcdClient.Create(storeadapter.StoreNode{
				Key:   shared.LockSchemaPath("converge_lock"),
				Value: []byte("something-else"),
			})
			Ω(err).ShouldNot(HaveOccurred())

			startConverger()
		})

		itIsInactive()

		Describe("when the lock becomes available", func() {
			BeforeEach(func() {
				err := etcdClient.Delete(shared.LockSchemaPath("converge_lock"))
				Ω(err).ShouldNot(HaveOccurred())

				time.Sleep(convergeRepeatInterval + 10*time.Millisecond)
			})

			Describe("when a running task with a dead cell is present", func() {
				JustBeforeEach(createRunningTaskWithDeadCell)

				It("eventually marks the task as failed", func() {
					Eventually(func() ([]models.Task, error) {
						return bbs.CompletedTasks(logger)
					}, 5*convergeRepeatInterval).Should(HaveLen(1))
				})
			})
		})
	})

	Describe("signal handling", func() {
		BeforeEach(func() {
			startConverger()
		})

		Describe("when it receives SIGINT", func() {
			It("exits successfully", func() {
				runner.Session.Command.Process.Signal(syscall.SIGINT)
				Eventually(runner.Session, exitDuration).Should(gexec.Exit(0))
			})
		})

		Describe("when it receives SIGTERM", func() {
			It("exits successfully", func() {
				runner.Session.Command.Process.Signal(syscall.SIGTERM)
				Eventually(runner.Session, exitDuration).Should(gexec.Exit(0))
			})
		})
	})
})
<|endoftext|>