<commit_before>package client

import (
	"fmt"
	"net"
	"strings"
	"text/tabwriter"

	Cli "github.com/docker/docker/cli"
	"github.com/docker/docker/opts"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/pkg/stringid"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/filters"
	"github.com/docker/engine-api/types/network"
)

// CmdNetwork is the parent subcommand for all network commands
//
// Usage: docker network <COMMAND> [OPTIONS]
func (cli *DockerCli) CmdNetwork(args ...string) error {
	cmd := Cli.Subcmd("network", []string{"COMMAND [OPTIONS]"}, networkUsage(), false)
	cmd.Require(flag.Min, 1)
	err := cmd.ParseFlags(args, true)
	cmd.Usage()
	return err
}

// CmdNetworkCreate creates a new network with a given name
//
// Usage: docker network create [OPTIONS] <NETWORK-NAME>
func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
	cmd := Cli.Subcmd("network create", []string{"NETWORK-NAME"}, "Creates a new network with a name specified by the user", false)
	flDriver := cmd.String([]string{"d", "-driver"}, "bridge", "Driver to manage the Network")
	flOpts := opts.NewMapOpts(nil, nil)

	flIpamDriver := cmd.String([]string{"-ipam-driver"}, "default", "IP Address Management Driver")
	flIpamSubnet := opts.NewListOpts(nil)
	flIpamIPRange := opts.NewListOpts(nil)
	flIpamGateway := opts.NewListOpts(nil)
	flIpamAux := opts.NewMapOpts(nil, nil)

	cmd.Var(&flIpamSubnet, []string{"-subnet"}, "subnet in CIDR format that represents a network segment")
	cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range")
	cmd.Var(&flIpamGateway, []string{"-gateway"}, "ipv4 or ipv6 Gateway for the master subnet")
	cmd.Var(flIpamAux, []string{"-aux-address"}, "auxiliary ipv4 or ipv6 addresses used by Network driver")
	cmd.Var(flOpts, []string{"o", "-opt"}, "set driver specific options")

	cmd.Require(flag.Exact, 1)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	// Set the default driver to "" if the user didn't set the value.
	// That way we can know whether it was user input or not.
	driver := *flDriver
	if !cmd.IsSet("-driver") && !cmd.IsSet("d") {
		driver = ""
	}

	ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll())
	if err != nil {
		return err
	}

	// Construct network create request body
	nc := types.NetworkCreate{
		Name:           cmd.Arg(0),
		Driver:         driver,
		IPAM:           network.IPAM{Driver: *flIpamDriver, Config: ipamCfg},
		Options:        flOpts.GetAll(),
		CheckDuplicate: true,
	}

	resp, err := cli.client.NetworkCreate(nc)
	if err != nil {
		return err
	}
	fmt.Fprintf(cli.out, "%s\n", resp.ID)
	return nil
}

// CmdNetworkRm deletes one or more networks
//
// Usage: docker network rm NETWORK-NAME|NETWORK-ID [NETWORK-NAME|NETWORK-ID...]
func (cli *DockerCli) CmdNetworkRm(args ...string) error {
	cmd := Cli.Subcmd("network rm", []string{"NETWORK [NETWORK...]"}, "Deletes one or more networks", false)
	cmd.Require(flag.Min, 1)
	if err := cmd.ParseFlags(args, true); err != nil {
		return err
	}

	status := 0
	for _, net := range cmd.Args() {
		if err := cli.client.NetworkRemove(net); err != nil {
			fmt.Fprintf(cli.err, "%s\n", err)
			status = 1
			continue
		}
	}
	if status != 0 {
		return Cli.StatusError{StatusCode: status}
	}
	return nil
}

// CmdNetworkConnect connects a container to a network
//
// Usage: docker network connect [OPTIONS] <NETWORK> <CONTAINER>
func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
	cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
	flIPAddress := cmd.String([]string{"-ip"}, "", "IP Address")
	flIPv6Address := cmd.String([]string{"-ip6"}, "", "IPv6 Address")
	cmd.Require(flag.Min, 2)
	if err := cmd.ParseFlags(args, true); err != nil {
		return err
	}
	epConfig := &network.EndpointSettings{
		IPAMConfig: &network.EndpointIPAMConfig{
			IPv4Address: *flIPAddress,
			IPv6Address: *flIPv6Address,
		},
	}
	return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), epConfig)
}

// CmdNetworkDisconnect disconnects a container from a network
//
// Usage: docker network disconnect <NETWORK> <CONTAINER>
func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
	cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false)
	cmd.Require(flag.Exact, 2)
	if err := cmd.ParseFlags(args, true); err != nil {
		return err
	}

	return cli.client.NetworkDisconnect(cmd.Arg(0), cmd.Arg(1))
}

// CmdNetworkLs lists all the networks managed by docker daemon
//
// Usage: docker network ls [OPTIONS]
func (cli *DockerCli) CmdNetworkLs(args ...string) error {
	cmd := Cli.Subcmd("network ls", nil, "Lists networks", true)
	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
	noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")

	flFilter := opts.NewListOpts(nil)
	cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")

	cmd.Require(flag.Exact, 0)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	// Consolidate all filter flags, and sanity check them early.
	// They'll get process after get response from server.
	netFilterArgs := filters.NewArgs()
	for _, f := range flFilter.GetAll() {
		if netFilterArgs, err = filters.ParseFlag(f, netFilterArgs); err != nil {
			return err
		}
	}

	options := types.NetworkListOptions{
		Filters: netFilterArgs,
	}

	networkResources, err := cli.client.NetworkList(options)
	if err != nil {
		return err
	}

	wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)

	// unless quiet (-q) is specified, print field titles
	if !*quiet {
		fmt.Fprintln(wr, "NETWORK ID\tNAME\tDRIVER")
	}

	for _, networkResource := range networkResources {
		ID := networkResource.ID
		netName := networkResource.Name
		if !*noTrunc {
			ID = stringid.TruncateID(ID)
		}
		if *quiet {
			fmt.Fprintln(wr, ID)
			continue
		}
		driver := networkResource.Driver
		fmt.Fprintf(wr, "%s\t%s\t%s\t",
			ID,
			netName,
			driver)
		fmt.Fprint(wr, "\n")
	}
	wr.Flush()
	return nil
}

// CmdNetworkInspect inspects the network object for more details
//
// Usage: docker network inspect [OPTIONS] <NETWORK> [NETWORK...]
func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
	cmd := Cli.Subcmd("network inspect", []string{"NETWORK [NETWORK...]"}, "Displays detailed information on one or more networks", false)
	tmplStr := cmd.String([]string{"f", "-format"}, "", "Format the output using the given go template")
	cmd.Require(flag.Min, 1)

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

	inspectSearcher := func(name string) (interface{}, []byte, error) {
		i, err := cli.client.NetworkInspect(name)
		return i, nil, err
	}

	return cli.inspectElements(*tmplStr, cmd.Args(), inspectSearcher)
}

// Consolidates the ipam configuration as a group from different related configurations
// user can configure network with multiple non-overlapping subnets and hence it is
// possible to correlate the various related parameters and consolidate them.
// consoidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into
// structured ipam data.
func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
	if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
		return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
	}
	iData := map[string]*network.IPAMConfig{}

	// Populate non-overlapping subnets into consolidation map
	for _, s := range subnets {
		for k := range iData {
			ok1, err := subnetMatches(s, k)
			if err != nil {
				return nil, err
			}
			ok2, err := subnetMatches(k, s)
			if err != nil {
				return nil, err
			}
			if ok1 || ok2 {
				return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
			}
		}
		iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
	}

	// Validate and add valid ip ranges
	for _, r := range ranges {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, r)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			if iData[s].IPRange != "" {
				return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
			}
			d := iData[s]
			d.IPRange = r
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for range %s", r)
		}
	}

	// Validate and add valid gateways
	for _, g := range gateways {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, g)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			if iData[s].Gateway != "" {
				return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
			}
			d := iData[s]
			d.Gateway = g
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for gateway %s", g)
		}
	}

	// Validate and add aux-addresses
	for key, aa := range auxaddrs {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, aa)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			iData[s].AuxAddress[key] = aa
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
		}
	}

	idl := []network.IPAMConfig{}
	for _, v := range iData {
		idl = append(idl, *v)
	}
	return idl, nil
}

func subnetMatches(subnet, data string) (bool, error) {
	var (
		ip net.IP
	)

	_, s, err := net.ParseCIDR(subnet)
	if err != nil {
		return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
	}

	if strings.Contains(data, "/") {
		ip, _, err = net.ParseCIDR(data)
		if err != nil {
			return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
		}
	} else {
		ip = net.ParseIP(data)
	}

	return s.Contains(ip), nil
}

func networkUsage() string {
	networkCommands := map[string]string{
		"create":     "Create a network",
		"connect":    "Connect container to a network",
		"disconnect": "Disconnect container from a network",
		"inspect":    "Display detailed network information",
		"ls":         "List all networks",
		"rm":         "Remove a network",
	}

	help := "Commands:\n"

	for cmd, description := range networkCommands {
		help += fmt.Sprintf("  %-25.25s%s\n", cmd, description)
	}

	help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.")
	return help
}
<commit_msg>Do not force network disconnection.<commit_after>package client

import (
	"fmt"
	"net"
	"strings"
	"text/tabwriter"

	Cli "github.com/docker/docker/cli"
	"github.com/docker/docker/opts"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/pkg/stringid"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/filters"
	"github.com/docker/engine-api/types/network"
)

// CmdNetwork is the parent subcommand for all network commands
//
// Usage: docker network <COMMAND> [OPTIONS]
func (cli *DockerCli) CmdNetwork(args ...string) error {
	cmd := Cli.Subcmd("network", []string{"COMMAND [OPTIONS]"}, networkUsage(), false)
	cmd.Require(flag.Min, 1)
	err := cmd.ParseFlags(args, true)
	cmd.Usage()
	return err
}

// CmdNetworkCreate creates a new network with a given name
//
// Usage: docker network create [OPTIONS] <NETWORK-NAME>
func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
	cmd := Cli.Subcmd("network create", []string{"NETWORK-NAME"}, "Creates a new network with a name specified by the user", false)
	flDriver := cmd.String([]string{"d", "-driver"}, "bridge", "Driver to manage the Network")
	flOpts := opts.NewMapOpts(nil, nil)

	flIpamDriver := cmd.String([]string{"-ipam-driver"}, "default", "IP Address Management Driver")
	flIpamSubnet := opts.NewListOpts(nil)
	flIpamIPRange := opts.NewListOpts(nil)
	flIpamGateway := opts.NewListOpts(nil)
	flIpamAux := opts.NewMapOpts(nil, nil)

	cmd.Var(&flIpamSubnet, []string{"-subnet"}, "subnet in CIDR format that represents a network segment")
	cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range")
	cmd.Var(&flIpamGateway, []string{"-gateway"}, "ipv4 or ipv6 Gateway for the master subnet")
	cmd.Var(flIpamAux, []string{"-aux-address"}, "auxiliary ipv4 or ipv6 addresses used by Network driver")
	cmd.Var(flOpts, []string{"o", "-opt"}, "set driver specific options")

	cmd.Require(flag.Exact, 1)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	// Set the default driver to "" if the user didn't set the value.
	// That way we can know whether it was user input or not.
	driver := *flDriver
	if !cmd.IsSet("-driver") && !cmd.IsSet("d") {
		driver = ""
	}

	ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll())
	if err != nil {
		return err
	}

	// Construct network create request body
	nc := types.NetworkCreate{
		Name:           cmd.Arg(0),
		Driver:         driver,
		IPAM:           network.IPAM{Driver: *flIpamDriver, Config: ipamCfg},
		Options:        flOpts.GetAll(),
		CheckDuplicate: true,
	}

	resp, err := cli.client.NetworkCreate(nc)
	if err != nil {
		return err
	}
	fmt.Fprintf(cli.out, "%s\n", resp.ID)
	return nil
}

// CmdNetworkRm deletes one or more networks
//
// Usage: docker network rm NETWORK-NAME|NETWORK-ID [NETWORK-NAME|NETWORK-ID...]
func (cli *DockerCli) CmdNetworkRm(args ...string) error {
	cmd := Cli.Subcmd("network rm", []string{"NETWORK [NETWORK...]"}, "Deletes one or more networks", false)
	cmd.Require(flag.Min, 1)
	if err := cmd.ParseFlags(args, true); err != nil {
		return err
	}

	status := 0
	for _, net := range cmd.Args() {
		if err := cli.client.NetworkRemove(net); err != nil {
			fmt.Fprintf(cli.err, "%s\n", err)
			status = 1
			continue
		}
	}
	if status != 0 {
		return Cli.StatusError{StatusCode: status}
	}
	return nil
}

// CmdNetworkConnect connects a container to a network
//
// Usage: docker network connect [OPTIONS] <NETWORK> <CONTAINER>
func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
	cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
	flIPAddress := cmd.String([]string{"-ip"}, "", "IP Address")
	flIPv6Address := cmd.String([]string{"-ip6"}, "", "IPv6 Address")
	cmd.Require(flag.Min, 2)
	if err := cmd.ParseFlags(args, true); err != nil {
		return err
	}
	epConfig := &network.EndpointSettings{
		IPAMConfig: &network.EndpointIPAMConfig{
			IPv4Address: *flIPAddress,
			IPv6Address: *flIPv6Address,
		},
	}
	return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), epConfig)
}

// CmdNetworkDisconnect disconnects a container from a network
//
// Usage: docker network disconnect <NETWORK> <CONTAINER>
func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
	cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false)
	cmd.Require(flag.Exact, 2)
	if err := cmd.ParseFlags(args, true); err != nil {
		return err
	}

	return cli.client.NetworkDisconnect(cmd.Arg(0), cmd.Arg(1), false)
}

// CmdNetworkLs lists all the networks managed by docker daemon
//
// Usage: docker network ls [OPTIONS]
func (cli *DockerCli) CmdNetworkLs(args ...string) error {
	cmd := Cli.Subcmd("network ls", nil, "Lists networks", true)
	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
	noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")

	flFilter := opts.NewListOpts(nil)
	cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")

	cmd.Require(flag.Exact, 0)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	// Consolidate all filter flags, and sanity check them early.
	// They'll get process after get response from server.
	netFilterArgs := filters.NewArgs()
	for _, f := range flFilter.GetAll() {
		if netFilterArgs, err = filters.ParseFlag(f, netFilterArgs); err != nil {
			return err
		}
	}

	options := types.NetworkListOptions{
		Filters: netFilterArgs,
	}

	networkResources, err := cli.client.NetworkList(options)
	if err != nil {
		return err
	}

	wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)

	// unless quiet (-q) is specified, print field titles
	if !*quiet {
		fmt.Fprintln(wr, "NETWORK ID\tNAME\tDRIVER")
	}

	for _, networkResource := range networkResources {
		ID := networkResource.ID
		netName := networkResource.Name
		if !*noTrunc {
			ID = stringid.TruncateID(ID)
		}
		if *quiet {
			fmt.Fprintln(wr, ID)
			continue
		}
		driver := networkResource.Driver
		fmt.Fprintf(wr, "%s\t%s\t%s\t",
			ID,
			netName,
			driver)
		fmt.Fprint(wr, "\n")
	}
	wr.Flush()
	return nil
}

// CmdNetworkInspect inspects the network object for more details
//
// Usage: docker network inspect [OPTIONS] <NETWORK> [NETWORK...]
func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
	cmd := Cli.Subcmd("network inspect", []string{"NETWORK [NETWORK...]"}, "Displays detailed information on one or more networks", false)
	tmplStr := cmd.String([]string{"f", "-format"}, "", "Format the output using the given go template")
	cmd.Require(flag.Min, 1)

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

	inspectSearcher := func(name string) (interface{}, []byte, error) {
		i, err := cli.client.NetworkInspect(name)
		return i, nil, err
	}

	return cli.inspectElements(*tmplStr, cmd.Args(), inspectSearcher)
}

// Consolidates the ipam configuration as a group from different related configurations
// user can configure network with multiple non-overlapping subnets and hence it is
// possible to correlate the various related parameters and consolidate them.
// consoidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into
// structured ipam data.
func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
	if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
		return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
	}
	iData := map[string]*network.IPAMConfig{}

	// Populate non-overlapping subnets into consolidation map
	for _, s := range subnets {
		for k := range iData {
			ok1, err := subnetMatches(s, k)
			if err != nil {
				return nil, err
			}
			ok2, err := subnetMatches(k, s)
			if err != nil {
				return nil, err
			}
			if ok1 || ok2 {
				return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
			}
		}
		iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
	}

	// Validate and add valid ip ranges
	for _, r := range ranges {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, r)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			if iData[s].IPRange != "" {
				return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
			}
			d := iData[s]
			d.IPRange = r
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for range %s", r)
		}
	}

	// Validate and add valid gateways
	for _, g := range gateways {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, g)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			if iData[s].Gateway != "" {
				return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
			}
			d := iData[s]
			d.Gateway = g
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for gateway %s", g)
		}
	}

	// Validate and add aux-addresses
	for key, aa := range auxaddrs {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, aa)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			iData[s].AuxAddress[key] = aa
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
		}
	}

	idl := []network.IPAMConfig{}
	for _, v := range iData {
		idl = append(idl, *v)
	}
	return idl, nil
}

func subnetMatches(subnet, data string) (bool, error) {
	var (
		ip net.IP
	)

	_, s, err := net.ParseCIDR(subnet)
	if err != nil {
		return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
	}

	if strings.Contains(data, "/") {
		ip, _, err = net.ParseCIDR(data)
		if err != nil {
			return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
		}
	} else {
		ip = net.ParseIP(data)
	}

	return s.Contains(ip), nil
}

func networkUsage() string {
	networkCommands := map[string]string{
		"create":     "Create a network",
		"connect":    "Connect container to a network",
		"disconnect": "Disconnect container from a network",
		"inspect":    "Display detailed network information",
		"ls":         "List all networks",
		"rm":         "Remove a network",
	}

	help := "Commands:\n"

	for cmd, description := range networkCommands {
		help += fmt.Sprintf("  %-25.25s%s\n", cmd, description)
	}

	help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.")
	return help
}
<|endoftext|>


<commit_before>package runtime

import (
	"fmt"
	"github.com/dotcloud/docker/utils"
	"sync"
	"time"
)

type State struct {
	sync.RWMutex
	Running    bool
	Pid        int
	ExitCode   int
	StartedAt  time.Time
	FinishedAt time.Time
	Ghost      bool
}

// String returns a human-readable description of the state
func (s *State) String() string {
	s.RLock()
	defer s.RUnlock()

	if s.Running {
		if s.Ghost {
			return fmt.Sprintf("Ghost")
		}
		return fmt.Sprintf("Up %s", utils.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
	}
	return fmt.Sprintf("Exit %d", s.ExitCode)
}

func (s *State) IsRunning() bool {
	s.RLock()
	defer s.RUnlock()

	return s.Running
}

func (s *State) IsGhost() bool {
	s.RLock()
	defer s.RUnlock()

	return s.Ghost
}

func (s *State) GetExitCode() int {
	s.RLock()
	defer s.RUnlock()

	return s.ExitCode
}

func (s *State) SetGhost(val bool) {
	s.Lock()
	defer s.Unlock()

	s.Ghost = val
}

func (s *State) SetRunning(pid int) {
	s.Lock()
	defer s.Unlock()

	s.Running = true
	s.Ghost = false
	s.ExitCode = 0
	s.Pid = pid
	s.StartedAt = time.Now().UTC()
}

func (s *State) SetStopped(exitCode int) {
	s.Lock()
	defer s.Unlock()

	s.Running = false
	s.Pid = 0
	s.FinishedAt = time.Now().UTC()
	s.ExitCode = exitCode
}
<commit_msg>add time since exit in docker ps<commit_after>package runtime

import (
	"fmt"
	"github.com/dotcloud/docker/utils"
	"sync"
	"time"
)

type State struct {
	sync.RWMutex
	Running    bool
	Pid        int
	ExitCode   int
	StartedAt  time.Time
	FinishedAt time.Time
	Ghost      bool
}

// String returns a human-readable description of the state
func (s *State) String() string {
	s.RLock()
	defer s.RUnlock()

	if s.Running {
		if s.Ghost {
			return fmt.Sprintf("Ghost")
		}
		return fmt.Sprintf("Up %s", utils.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
	}
	return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, utils.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
}

func (s *State) IsRunning() bool {
	s.RLock()
	defer s.RUnlock()

	return s.Running
}

func (s *State) IsGhost() bool {
	s.RLock()
	defer s.RUnlock()

	return s.Ghost
}

func (s *State) GetExitCode() int {
	s.RLock()
	defer s.RUnlock()

	return s.ExitCode
}

func (s *State) SetGhost(val bool) {
	s.Lock()
	defer s.Unlock()

	s.Ghost = val
}

func (s *State) SetRunning(pid int) {
	s.Lock()
	defer s.Unlock()

	s.Running = true
	s.Ghost = false
	s.ExitCode = 0
	s.Pid = pid
	s.StartedAt = time.Now().UTC()
}

func (s *State) SetStopped(exitCode int) {
	s.Lock()
	defer s.Unlock()

	s.Running = false
	s.Pid = 0
	s.FinishedAt = time.Now().UTC()
	s.ExitCode = exitCode
}
<|endoftext|>


<commit_before>package nethttp

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"path/filepath"
	"regexp"
	"sort"
	"strings"

	"github.com/lestrrat/go-hsup/internal/genutil"
	"github.com/lestrrat/go-jshschema"
	"github.com/lestrrat/go-jsval"
	"github.com/lestrrat/go-jsval/builder"
	"github.com/lestrrat/go-pdebug"
)

type Builder struct {
	AppPkg       string
	ValidatorPkg string
}

type genctx struct {
	apppkg            string
	schema            *hschema.HyperSchema
	clientpkg         string
	validatorpkg      string
	methods           map[string]string
	methodPayloadType map[string]string
	methodNames       []string
	methodValidators  map[string]*jsval.JSVal
	pathToMethods     map[string]string
}

func New() *Builder {
	return &Builder{
		AppPkg:       "app",
		ValidatorPkg: "validator",
	}
}

func (b *Builder) ProcessFile(f string) error {
	s, err := hschema.ReadFile(f)
	if err != nil {
		return err
	}
	return b.Process(s)
}

func (b *Builder) Process(s *hschema.HyperSchema) error {
	ctx := genctx{
		schema:            s,
		methodNames:       make([]string, len(s.Links)),
		apppkg:            b.AppPkg,
		clientpkg:         "client",
		validatorpkg:      b.ValidatorPkg,
		methods:           make(map[string]string),
		methodPayloadType: make(map[string]string),
		methodValidators:  make(map[string]*jsval.JSVal),
		pathToMethods:     make(map[string]string),
	}

	if err := parse(&ctx, s); err != nil {
		return err
	}

	if err := generateFiles(&ctx); err != nil {
		return err
	}

	return nil
}

var wsrx = regexp.MustCompile(`\s+`)

func title2method(s string) string {
	// inefficient as hell
	buf := bytes.Buffer{}
	buf.WriteString("http")
	for _, p := range wsrx.Split(s, -1) {
		buf.WriteString(strings.ToUpper(p[:1]))
		buf.WriteString(p[1:])
	}
	return buf.String()
}

func parse(ctx *genctx, s *hschema.HyperSchema) error {
	for i, link := range s.Links {
		methodName := title2method(link.Title)

		// Got to do this first, because validators are used in makeMethod()
		if link.Schema != nil {
			v, err := makeValidator(ctx, link)
			if err != nil {
				return err
			}
			v.Name = strings.Replace(fmt.Sprintf("%sRequest", methodName), "http", "HTTP", 1)
			ctx.methodValidators[methodName] = v
		}
		ctx.methodPayloadType[methodName] = "interface{}"
		if ls := link.Schema; ls != nil {
			if !ls.IsResolved() {
				rs, err := ls.Resolve(ctx.schema)
				if err != nil {
					return err
				}
				ls = rs
			}
			if pdebug.Enabled {
				pdebug.Printf("checking extras for %s: %#v", link.Path(), ls.Extras)
			}
			if gt, ok := ls.Extras["gotype"]; ok {
				ctx.methodPayloadType[methodName] = gt.(string)
			}
		}
		ctx.methodNames[i] = methodName
		ctx.pathToMethods[link.Path()] = methodName
		ctx.methods[methodName] = makeMethod(ctx, methodName, link)
	}

	sort.Strings(ctx.methodNames)
	return nil
}

func makeMethod(ctx *genctx, name string, l *hschema.Link) string {
	buf := bytes.Buffer{}

	fmt.Fprintf(&buf, `func %s(w http.ResponseWriter, r *http.Response) {`+"\n", name)
	if m := l.Method; m != "" {
		buf.WriteString("\tif strings.ToLower(r.Method) != `")
		fmt.Fprintf(&buf, "%s", strings.ToLower(m))
		buf.WriteString("` {\n\t\thttp.Error(w, `Not Found`, http.StatusNotFound)\n\t}\n")
	}

	if v := ctx.methodValidators[name]; v != nil {
		payloadType := ctx.methodPayloadType[name]
		fmt.Fprintf(&buf, "\n\tvar payload %s", payloadType)
		buf.WriteString("\n\tif err := json.NewDecoder(r.Body).Decode(&payload); err != nil {")
		buf.WriteString("\n\t\thttp.Error(w, `Invalid input`, http.StatusInternalServerError)")
		buf.WriteString("\n\t\treturn")
		buf.WriteString("\n\t}")
		fmt.Fprintf(&buf, "\n\n\tif err := %s.%s.Validate(payload); err != nil {", ctx.validatorpkg, v.Name)
		buf.WriteString("\n\t\thttp.Error(w, `Invalid input`, http.StatusInternalServerError)")
		buf.WriteString("\n\t\treturn")
		buf.WriteString("\n\t}")
	}

	buf.WriteString("\n\thttp.Error(w, `Unimplemented`, http.StatusInternalServerError)")

	fmt.Fprint(&buf, "\n}\n")
	return buf.String()
}

func makeValidator(ctx *genctx, l *hschema.Link) (*jsval.JSVal, error) {
	b := builder.New()
	v, err := b.BuildWithCtx(l.Schema, ctx.schema)
	if err != nil {
		return nil, err
	}

	return v, nil
}

func generateFile(ctx *genctx, fn string, cb func(io.Writer, *genctx) error) error {
	f, err := genutil.CreateFile(fn)
	if err != nil {
		return err
	}
	defer f.Close()
	return cb(f, ctx)
}

func generateFiles(ctxif interface{}) error {
	ctx, ok := ctxif.(*genctx)
	if !ok {
		return errors.New("expected genctx type")
	}

	{
		fn := filepath.Join(ctx.apppkg, fmt.Sprintf("%s.go", ctx.apppkg))
		if err := generateFile(ctx, fn, generateServerCode); err != nil {
			return err
		}
	}

	{
		fn := filepath.Join(ctx.apppkg, ctx.validatorpkg, fmt.Sprintf("%s.go", ctx.validatorpkg))
		if err := generateFile(ctx, fn, generateValidatorCode); err != nil {
			return err
		}
	}

	return nil
}

func generateServerCode(out io.Writer, ctx *genctx) error {
	buf := bytes.Buffer{}

	buf.WriteString("package apiserver\n\n")

	genutil.WriteImports(
		&buf,
		[]string{
			"net/http",
			"strings",
		},
		[]string{
			"github.com/gorilla/mux",
		},
	)

	buf.WriteString(`
type Server struct {
	*mux.Router
}

func New() *Server {
	s := &Server{
		Router: mux.NewRouter(),
	}
	s.SetupRoutes()
	return s
}

`)

	for _, methodName := range ctx.methodNames {
		buf.WriteString(ctx.methods[methodName])
		buf.WriteString("\n")
	}

	buf.WriteString("func (s *Server) SetupRoutes() {")
	buf.WriteString("\n\tr := mux.NewRouter()")
	paths := make([]string, 0, len(ctx.pathToMethods))
	for path := range ctx.pathToMethods {
		paths = append(paths, path)
	}
	sort.Strings(paths)
	for _, path := range paths {
		method := ctx.pathToMethods[path]
		fmt.Fprintf(&buf, "\tr.HandleFunc(`%s`, %s)\n", path, method)
	}
	buf.WriteString("}\n")

	if _, err := buf.WriteTo(out); err != nil {
		return err
	}
	return nil
}

func generateValidatorCode(out io.Writer, ctx *genctx) error {
	g := jsval.NewGenerator()
	validators := make([]*jsval.JSVal, 0, len(ctx.methodValidators))
	for _, v := range ctx.methodValidators {
		validators = append(validators, v)
	}

	buf := bytes.Buffer{}
	buf.WriteString("package " + ctx.validatorpkg + "\n\n")

	genutil.WriteImports(
		&buf,
		nil,
		[]string{
			"github.com/lestrrat/go-jsval",
		},
	)
	if err := g.Process(&buf, validators...); err != nil {
		return err
	}
	buf.WriteString("\n\n")

	if _, err := buf.WriteTo(out); err != nil {
		return err
	}

	return nil
}
<commit_msg>various tweaks<commit_after>package nethttp

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"log"
	"path/filepath"
	"regexp"
	"sort"
	"strings"

	"github.com/lestrrat/go-hsup/internal/genutil"
	"github.com/lestrrat/go-jshschema"
	"github.com/lestrrat/go-jsval"
	"github.com/lestrrat/go-jsval/builder"
	"github.com/lestrrat/go-pdebug"
)

type Builder struct {
	AppPkg       string
	ValidatorPkg string
}

type genctx struct {
	apppkg            string
	schema            *hschema.HyperSchema
	clientpkg         string
	validatorpkg      string
	methods           map[string]string
	methodPayloadType map[string]string
	methodNames       []string
	methodValidators  map[string]*jsval.JSVal
	pathToMethods     map[string]string
}

func New() *Builder {
	return &Builder{
		AppPkg:       "app",
		ValidatorPkg: "validator",
	}
}

func (b *Builder) ProcessFile(f string) error {
	log.Printf(" ===> Using schema file '%s'", f)
	s, err := hschema.ReadFile(f)
	if err != nil {
		return err
	}
	return b.Process(s)
}

func (b *Builder) Process(s *hschema.HyperSchema) error {
	ctx := genctx{
		schema:            s,
		methodNames:       make([]string, len(s.Links)),
		apppkg:            b.AppPkg,
		clientpkg:         "client",
		validatorpkg:      b.ValidatorPkg,
		methods:           make(map[string]string),
		methodPayloadType: make(map[string]string),
		methodValidators:  make(map[string]*jsval.JSVal),
		pathToMethods:     make(map[string]string),
	}

	if err := parse(&ctx, s); err != nil {
		return err
	}

	if err := generateFiles(&ctx); err != nil {
		return err
	}

	log.Printf(" <=== All files generated")
	return nil
}

var wsrx = regexp.MustCompile(`\s+`)

func title2name(s string) string {
	buf := bytes.Buffer{}
	for _, p := range wsrx.Split(s, -1) {
		buf.WriteString(strings.ToUpper(p[:1]))
		buf.WriteString(p[1:])
	}
	return buf.String()
}

func parse(ctx *genctx, s *hschema.HyperSchema) error {
	for i, link := range s.Links {
		methodName := title2name(link.Title)

		// Got to do this first, because validators are used in makeMethod()
		if link.Schema != nil {
			v, err := makeValidator(ctx, link)
			if err != nil {
				return err
			}
			v.Name = fmt.Sprintf("HTTP%sRequest", methodName)
			ctx.methodValidators[methodName] = v
		}
		ctx.methodPayloadType[methodName] = "interface{}"
		if ls := link.Schema; ls != nil {
			if !ls.IsResolved() {
				rs, err := ls.Resolve(ctx.schema)
				if err != nil {
					return err
				}
				ls = rs
			}
			if pdebug.Enabled {
				pdebug.Printf("checking extras for %s: %#v", link.Path(), ls.Extras)
			}
			if gt, ok := ls.Extras["gotype"]; ok {
				ctx.methodPayloadType[methodName] = gt.(string)
			}
		}
		ctx.methodNames[i] = methodName
		ctx.pathToMethods[link.Path()] = methodName
		ctx.methods[methodName] = makeMethod(ctx, methodName, link)
	}

	sort.Strings(ctx.methodNames)
	return nil
}

func makeMethod(ctx *genctx, name string, l *hschema.Link) string {
	buf := bytes.Buffer{}

	fmt.Fprintf(&buf, `func http%s(w http.ResponseWriter, r *http.Response) {`+"\n", name)
	if m := l.Method; m != "" {
		buf.WriteString("\tif strings.ToLower(r.Method) != `")
		fmt.Fprintf(&buf, "%s", strings.ToLower(m))
		buf.WriteString("` {\n\t\thttp.Error(w, `Not Found`, http.StatusNotFound)\n\t}\n")
	}

	if v := ctx.methodValidators[name]; v != nil {
		payloadType := ctx.methodPayloadType[name]
		fmt.Fprintf(&buf, "\n\tvar payload %s", payloadType)
		buf.WriteString("\n\tif err := json.NewDecoder(r.Body).Decode(&payload); err != nil {")
		buf.WriteString("\n\t\thttp.Error(w, `Invalid input`, http.StatusInternalServerError)")
		buf.WriteString("\n\t\treturn")
		buf.WriteString("\n\t}")
		fmt.Fprintf(&buf, "\n\n\tif err := %s.%s.Validate(payload); err != nil {", ctx.validatorpkg, v.Name)
		buf.WriteString("\n\t\thttp.Error(w, `Invalid input`, http.StatusInternalServerError)")
		buf.WriteString("\n\t\treturn")
		buf.WriteString("\n\t}")
	}

	fmt.Fprintf(&buf, "\n\tdo%s(context.Background(), w, r, payload)", name)
	buf.WriteString("\n}\n")
	return buf.String()
}

func makeValidator(ctx *genctx, l *hschema.Link) (*jsval.JSVal, error) {
	b := builder.New()
	v, err := b.BuildWithCtx(l.Schema, ctx.schema)
	if err != nil {
		return nil, err
	}

	return v, nil
}

func generateFile(ctx *genctx, fn string, cb func(io.Writer, *genctx) error) error {
	log.Printf(" + Generating file '%s'", fn)
	f, err := genutil.CreateFile(fn)
	if err != nil {
		return err
	}
	defer f.Close()
	return cb(f, ctx)
}

func generateFiles(ctxif interface{}) error {
	ctx, ok := ctxif.(*genctx)
	if !ok {
		return errors.New("expected genctx type")
	}

	{
		fn := filepath.Join(ctx.apppkg, fmt.Sprintf("%s.go", ctx.apppkg))
		if err := generateFile(ctx, fn, generateServerCode); err != nil {
			return err
		}
	}

	{
		fn := filepath.Join(ctx.apppkg, ctx.validatorpkg, fmt.Sprintf("%s.go", ctx.validatorpkg))
		if err := generateFile(ctx, fn, generateValidatorCode); err != nil {
			return err
		}
	}

	return nil
}

func generateServerCode(out io.Writer, ctx *genctx) error {
	buf := bytes.Buffer{}

	fmt.Fprintf(&buf, "package %s\n\n", ctx.apppkg)

	genutil.WriteImports(
		&buf,
		[]string{
			"net/http",
			"strings",
		},
		[]string{
			"github.com/gorilla/mux",
			"golang.org/x/context",
		},
	)

	buf.WriteString(`
type Server struct {
	*mux.Router
}

func New() *Server {
	s := &Server{
		Router: mux.NewRouter(),
	}
	s.SetupRoutes()
	return s
}

`)

	for _, methodName := range ctx.methodNames {
		buf.WriteString(ctx.methods[methodName])
		buf.WriteString("\n")
	}

	buf.WriteString("func (s *Server) SetupRoutes() {")
	buf.WriteString("\n\tr := s.Router")
	paths := make([]string, 0, len(ctx.pathToMethods))
	for path := range ctx.pathToMethods {
		paths = append(paths, path)
	}
	sort.Strings(paths)
	for _, path := range paths {
		method := ctx.pathToMethods[path]
		fmt.Fprintf(&buf, "\n\tr.HandleFunc(`%s`, %s)", path, method)
	}
	buf.WriteString("\n}\n")

	if _, err := buf.WriteTo(out); err != nil {
		return err
	}
	return nil
}

func generateValidatorCode(out io.Writer, ctx *genctx) error {
	g := jsval.NewGenerator()
	validators := make([]*jsval.JSVal, 0, len(ctx.methodValidators))
	for _, v := range ctx.methodValidators {
		validators = append(validators, v)
	}

	buf := bytes.Buffer{}
	buf.WriteString("package " + ctx.validatorpkg + "\n\n")

	genutil.WriteImports(
		&buf,
		nil,
		[]string{
			"github.com/lestrrat/go-jsval",
		},
	)
	if err := g.Process(&buf, validators...); err != nil {
		return err
	}
	buf.WriteString("\n\n")

	if _, err := buf.WriteTo(out); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package network

import (
	"sort"
	"time"

	"github.com/golang/glog"

	"github.com/BotBotMe/botbot-bot/common"
	"github.com/BotBotMe/botbot-bot/line"
	"github.com/BotBotMe/botbot-bot/network/irc"
)

type NetworkManager struct {
	chatbots   map[int]common.ChatBot
	fromServer chan *line.Line
	storage    common.Storage
	isRunning  bool
}

func NewNetworkManager(storage common.Storage, fromServer chan *line.Line) *NetworkManager {

	netMan := &NetworkManager{
		chatbots:   make(map[int]common.ChatBot),
		fromServer: fromServer,
		storage:    storage,
		isRunning:  true,
	}

	return netMan
}

// Get the User for a ChatbotId
func (self *NetworkManager) GetUserByChatbotId(id int) string {
	return self.getChatbotById(id).GetUser()
}

// Connect to networks / start chatbots. Loads chatbot configuration from DB.
func (self *NetworkManager) RefreshChatbots() {
	if glog.V(2) {
		glog.Infoln("Entering in NetworkManager.RefreshChatbots")
	}

	botConfigs := self.storage.BotConfig()

	var current common.ChatBot
	var id int
	active := make(sort.IntSlice, 0)

	// Create new ones
	for _, config := range botConfigs {
		id = config.Id
		active = append(active, id)

		current = self.chatbots[id]
		if current == nil {
			// Create
			if glog.V(2) {
				glog.Infoln("Connect the bot with the following config:", config)
			}
			self.chatbots[id] = self.Connect(config)
		} else {
			// Update
			if glog.V(2) {
				glog.Infoln("Update the bot with the following config:", config)
			}
			self.chatbots[id].Update(config)
		}

	}

	// Stop old ones

	active.Sort()
	numActive := len(active)

	for currId, _ := range self.chatbots {

		if active.Search(currId) == numActive { // if currId not in active:
			glog.Infoln("Stopping chatbot: ", currId)

			self.chatbots[currId].Close()
			delete(self.chatbots, currId)
		}
	}
	if glog.V(2) {
		glog.Infoln("Exiting NetworkManager.RefreshChatbots")
	}

}

func (self *NetworkManager) Connect(config *common.BotConfig) common.ChatBot {

	glog.Infoln("Creating chatbot: %+v\n", config)
	return irc.NewBot(config, self.fromServer)
}

func (self *NetworkManager) Send(chatbotId int, channel, msg string) {
	self.chatbots[chatbotId].Send(channel, msg)
}

// Check out chatbots are alive, recreating them if not. Run this in go-routine.
func (self *NetworkManager) MonitorChatbots() {

	for self.isRunning {
		for id, bot := range self.chatbots {
			if !bot.IsRunning() {
				self.restart(id)
			}
		}
		time.Sleep(1 * time.Second)
	}
}

// get a chatbot by id
func (self *NetworkManager) getChatbotById(id int) common.ChatBot {
	return self.chatbots[id]
}

// Restart a chatbot
func (self *NetworkManager) restart(botId int) {

	glog.Infoln("Restarting bot ", botId)

	var config *common.BotConfig

	// Find configuration for this bot

	botConfigs := self.storage.BotConfig()
	for _, botConf := range botConfigs {
		if botConf.Id == botId {
			config = botConf
			break
		}
	}

	if config == nil {
		glog.Infoln("Could not find configuration for bot ", botId, ". Bot will not run.")
		delete(self.chatbots, botId)
		return
	}

	self.chatbots[botId] = self.Connect(config)
}

// Stop all bots
func (self *NetworkManager) Shutdown() {
	self.isRunning = false
	for _, bot := range self.chatbots {
		bot.Close()
	}
}
<commit_msg>golint<commit_after>package network

import (
	"sort"
	"time"

	"github.com/golang/glog"

	"github.com/BotBotMe/botbot-bot/common"
	"github.com/BotBotMe/botbot-bot/line"
	"github.com/BotBotMe/botbot-bot/network/irc"
)

type NetworkManager struct {
	chatbots   map[int]common.ChatBot
	fromServer chan *line.Line
	storage    common.Storage
	isRunning  bool
}

func NewNetworkManager(storage common.Storage, fromServer chan *line.Line) *NetworkManager {

	netMan := &NetworkManager{
		chatbots:   make(map[int]common.ChatBot),
		fromServer: fromServer,
		storage:    storage,
		isRunning:  true,
	}

	return netMan
}

// Get the User for a ChatbotId
func (self *NetworkManager) GetUserByChatbotId(id int) string {
	return self.getChatbotById(id).GetUser()
}

// Connect to networks / start chatbots. Loads chatbot configuration from DB.
func (self *NetworkManager) RefreshChatbots() {
	if glog.V(2) {
		glog.Infoln("Entering in NetworkManager.RefreshChatbots")
	}
	botConfigs := self.storage.BotConfig()

	var current common.ChatBot
	var id int
	active := make(sort.IntSlice, 0)

	// Create new ones
	for _, config := range botConfigs {
		id = config.Id
		active = append(active, id)

		current = self.chatbots[id]
		if current == nil {
			// Create
			if glog.V(2) {
				glog.Infoln("Connect the bot with the following config:", config)
			}
			self.chatbots[id] = self.Connect(config)
		} else {
			// Update
			if glog.V(2) {
				glog.Infoln("Update the bot with the following config:", config)
			}
			self.chatbots[id].Update(config)
		}

	}

	// Stop old ones

	active.Sort()
	numActive := len(active)

	for currId := range self.chatbots {

		if active.Search(currId) == numActive { // if currId not in active:
			glog.Infoln("Stopping chatbot: ", currId)

			self.chatbots[currId].Close()
			delete(self.chatbots, currId)
		}
	}
	if glog.V(2) {
		glog.Infoln("Exiting NetworkManager.RefreshChatbots")
	}

}

func (self *NetworkManager) Connect(config *common.BotConfig) common.ChatBot {

	glog.Infoln("Creating chatbot: %+v\n", config)
	return irc.NewBot(config, self.fromServer)
}

func (self *NetworkManager) Send(chatbotId int, channel, msg string) {
	self.chatbots[chatbotId].Send(channel, msg)
}

// Check out chatbots are alive, recreating them if not. Run this in go-routine.
func (self *NetworkManager) MonitorChatbots() {

	for self.isRunning {
		for id, bot := range self.chatbots {
			if !bot.IsRunning() {
				self.restart(id)
			}
		}
		time.Sleep(1 * time.Second)
	}
}

// get a chatbot by id
func (self *NetworkManager) getChatbotById(id int) common.ChatBot {
	return self.chatbots[id]
}

// Restart a chatbot
func (self *NetworkManager) restart(botId int) {

	glog.Infoln("Restarting bot ", botId)

	var config *common.BotConfig

	// Find configuration for this bot

	botConfigs := self.storage.BotConfig()
	for _, botConf := range botConfigs {
		if botConf.Id == botId {
			config = botConf
			break
		}
	}

	if config == nil {
		glog.Infoln("Could not find configuration for bot ", botId, ". Bot will not run.")
		delete(self.chatbots, botId)
		return
	}

	self.chatbots[botId] = self.Connect(config)
}

// Stop all bots
func (self *NetworkManager) Shutdown() {
	self.isRunning = false
	for _, bot := range self.chatbots {
		bot.Close()
	}
}
<|endoftext|>


<commit_before>// NOTE: Subject to change, do not rely on this package from outside git-lfs source
package api

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/git-lfs/git-lfs/auth"
	"github.com/git-lfs/git-lfs/config"
	"github.com/git-lfs/git-lfs/endpoint"
	"github.com/git-lfs/git-lfs/httputil"
)

var (
	// ErrNoOperationGiven is an error which is returned when no operation
	// is provided in a RequestSchema object.
	ErrNoOperationGiven = errors.New("lfs/api: no operation provided in schema")
)

// HttpLifecycle serves as the default implementation of the Lifecycle interface
// for HTTP requests. Internally, it leverages the *http.Client type to execute
// HTTP requests against a root *url.URL, as given in `NewHttpLifecycle`.
type HttpLifecycle struct {
	cfg *config.Configuration
}

var _ Lifecycle = new(HttpLifecycle)

// NewHttpLifecycle initializes a new instance of the *HttpLifecycle type with a
// new *http.Client, and the given root (see above).
// Passing a nil Configuration will use the global config
func NewHttpLifecycle(cfg *config.Configuration) *HttpLifecycle {
	if cfg == nil {
		cfg = config.Config
	}
	return &HttpLifecycle{
		cfg: cfg,
	}
}

// Build implements the Lifecycle.Build function.
//
// HttpLifecycle in particular, builds an absolute path by parsing and then
// relativizing the `schema.Path` with respsect to the `HttpLifecycle.root`. If
// there was an error in determining this URL, then that error will be returned,
//
// After this is complete, a body is attached to the request if the
// schema contained one. If a body was present, and there an error occurred while
// serializing it into JSON, then that error will be returned and the
// *http.Request will not be generated.
//
// In all cases, credentials are attached to the HTTP request as described in
// the `auth` package (see github.com/git-lfs/git-lfs/auth#GetCreds).
//
// Finally, all of these components are combined together and the resulting
// request is returned.
func (l *HttpLifecycle) Build(schema *RequestSchema) (*http.Request, error) {
	path, err := l.absolutePath(schema.Operation, schema.Path)
	if err != nil {
		return nil, err
	}

	body, err := l.body(schema)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(schema.Method, path.String(), body)
	if err != nil {
		return nil, err
	}

	if _, err = auth.GetCreds(l.cfg, req); err != nil {
		return nil, err
	}

	req.URL.RawQuery = l.queryParameters(schema).Encode()

	return req, nil
}

// Execute implements the Lifecycle.Execute function.
//
// Internally, the *http.Client is used to execute the underlying *http.Request.
// If the client returned an error corresponding to a failure to make the
// request, then that error will be returned immediately, and the response is
// guaranteed not to be serialized.
//
// Once the response has been gathered from the server, it is unmarshled into
// the given `into interface{}` which is identical to the one provided in the
// original RequestSchema. If an error occured while decoding, then that error
// is returned.
//
// Otherwise, the api.Response is returned, along with no error, signaling that
// the request completed successfully.
func (l *HttpLifecycle) Execute(req *http.Request, into interface{}) (Response, error) {
	resp, err := httputil.DoHttpRequestWithRedirects(l.cfg, req, []*http.Request{}, true)
	if err != nil {
		return nil, err
	}

	// TODO(taylor): check status >=500, handle content type, return error,
	// halt immediately.

	if into != nil {
		decoder := json.NewDecoder(resp.Body)
		if err = decoder.Decode(into); err != nil {
			return nil, err
		}
	}

	return WrapHttpResponse(resp), nil
}

// Cleanup implements the Lifecycle.Cleanup function by closing the Body
// attached to the response.
func (l *HttpLifecycle) Cleanup(resp Response) error {
	return resp.Body().Close()
}

// absolutePath returns the absolute path made by combining a given relative
// path with the root URL of the endpoint corresponding to the given operation.
//
// If there was an error in parsing the relative path, then that error will be
// returned.
func (l *HttpLifecycle) absolutePath(operation Operation, path string) (*url.URL, error) {
	if len(operation) == 0 {
		return nil, ErrNoOperationGiven
	}

	root, err := url.Parse(l.cfg.Endpoint(string(operation)).Url)
	if err != nil {
		return nil, err
	}

	rel, err := url.Parse(path)
	if err != nil {
		return nil, err

	}

	return root.ResolveReference(rel), nil
}

// body returns an io.Reader which reads out a JSON-encoded copy of the payload
// attached to a given *RequestSchema, if it is present. If no body is present
// in the request, then nil is returned instead.
//
// If an error was encountered while attempting to marshal the body, then that
// will be returned instead, along with a nil io.Reader.
func (l *HttpLifecycle) body(schema *RequestSchema) (io.ReadCloser, error) {
	if schema.Body == nil {
		return nil, nil
	}

	body, err := json.Marshal(schema.Body)
	if err != nil {
		return nil, err
	}

	return ioutil.NopCloser(bytes.NewReader(body)), nil
}

// queryParameters returns a url.Values containing all of the provided query
// parameters as given in the *RequestSchema. If no query parameters were given,
// then an empty url.Values is returned instead.
func (l *HttpLifecycle) queryParameters(schema *RequestSchema) url.Values {
	vals := url.Values{}
	if schema.Query != nil {
		for k, v := range schema.Query {
			vals.Add(k, v)
		}
	}

	return vals
}
<commit_msg>api: remove unused import<commit_after>// NOTE: Subject to change, do not rely on this package from outside git-lfs source
package api

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/git-lfs/git-lfs/auth"
	"github.com/git-lfs/git-lfs/config"
	"github.com/git-lfs/git-lfs/httputil"
)

var (
	// ErrNoOperationGiven is an error which is returned when no operation
	// is provided in a RequestSchema object.
	ErrNoOperationGiven = errors.New("lfs/api: no operation provided in schema")
)

// HttpLifecycle serves as the default implementation of the Lifecycle interface
// for HTTP requests. Internally, it leverages the *http.Client type to execute
// HTTP requests against a root *url.URL, as given in `NewHttpLifecycle`.
type HttpLifecycle struct {
	cfg *config.Configuration
}

var _ Lifecycle = new(HttpLifecycle)

// NewHttpLifecycle initializes a new instance of the *HttpLifecycle type with a
// new *http.Client, and the given root (see above).
// Passing a nil Configuration will use the global config
func NewHttpLifecycle(cfg *config.Configuration) *HttpLifecycle {
	if cfg == nil {
		cfg = config.Config
	}
	return &HttpLifecycle{
		cfg: cfg,
	}
}

// Build implements the Lifecycle.Build function.
//
// HttpLifecycle in particular, builds an absolute path by parsing and then
// relativizing the `schema.Path` with respsect to the `HttpLifecycle.root`. If
// there was an error in determining this URL, then that error will be returned,
//
// After this is complete, a body is attached to the request if the
// schema contained one. If a body was present, and there an error occurred while
// serializing it into JSON, then that error will be returned and the
// *http.Request will not be generated.
//
// In all cases, credentials are attached to the HTTP request as described in
// the `auth` package (see github.com/git-lfs/git-lfs/auth#GetCreds).
//
// Finally, all of these components are combined together and the resulting
// request is returned.
func (l *HttpLifecycle) Build(schema *RequestSchema) (*http.Request, error) {
	path, err := l.absolutePath(schema.Operation, schema.Path)
	if err != nil {
		return nil, err
	}

	body, err := l.body(schema)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(schema.Method, path.String(), body)
	if err != nil {
		return nil, err
	}

	if _, err = auth.GetCreds(l.cfg, req); err != nil {
		return nil, err
	}

	req.URL.RawQuery = l.queryParameters(schema).Encode()

	return req, nil
}

// Execute implements the Lifecycle.Execute function.
//
// Internally, the *http.Client is used to execute the underlying *http.Request.
// If the client returned an error corresponding to a failure to make the
// request, then that error will be returned immediately, and the response is
// guaranteed not to be serialized.
//
// Once the response has been gathered from the server, it is unmarshled into
// the given `into interface{}` which is identical to the one provided in the
// original RequestSchema. If an error occured while decoding, then that error
// is returned.
//
// Otherwise, the api.Response is returned, along with no error, signaling that
// the request completed successfully.
func (l *HttpLifecycle) Execute(req *http.Request, into interface{}) (Response, error) {
	resp, err := httputil.DoHttpRequestWithRedirects(l.cfg, req, []*http.Request{}, true)
	if err != nil {
		return nil, err
	}

	// TODO(taylor): check status >=500, handle content type, return error,
	// halt immediately.

	if into != nil {
		decoder := json.NewDecoder(resp.Body)
		if err = decoder.Decode(into); err != nil {
			return nil, err
		}
	}

	return WrapHttpResponse(resp), nil
}

// Cleanup implements the Lifecycle.Cleanup function by closing the Body
// attached to the response.
func (l *HttpLifecycle) Cleanup(resp Response) error {
	return resp.Body().Close()
}

// absolutePath returns the absolute path made by combining a given relative
// path with the root URL of the endpoint corresponding to the given operation.
//
// If there was an error in parsing the relative path, then that error will be
// returned.
func (l *HttpLifecycle) absolutePath(operation Operation, path string) (*url.URL, error) {
	if len(operation) == 0 {
		return nil, ErrNoOperationGiven
	}

	root, err := url.Parse(l.cfg.Endpoint(string(operation)).Url)
	if err != nil {
		return nil, err
	}

	rel, err := url.Parse(path)
	if err != nil {
		return nil, err

	}

	return root.ResolveReference(rel), nil
}

// body returns an io.Reader which reads out a JSON-encoded copy of the payload
// attached to a given *RequestSchema, if it is present. If no body is present
// in the request, then nil is returned instead.
//
// If an error was encountered while attempting to marshal the body, then that
// will be returned instead, along with a nil io.Reader.
func (l *HttpLifecycle) body(schema *RequestSchema) (io.ReadCloser, error) {
	if schema.Body == nil {
		return nil, nil
	}

	body, err := json.Marshal(schema.Body)
	if err != nil {
		return nil, err
	}

	return ioutil.NopCloser(bytes.NewReader(body)), nil
}

// queryParameters returns a url.Values containing all of the provided query
// parameters as given in the *RequestSchema. If no query parameters were given,
// then an empty url.Values is returned instead.
func (l *HttpLifecycle) queryParameters(schema *RequestSchema) url.Values {
	vals := url.Values{}
	if schema.Query != nil {
		for k, v := range schema.Query {
			vals.Add(k, v)
		}
	}

	return vals
}
<|endoftext|>


<commit_before>// Copyright 2017 Authors of Cilium
//
// 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 helpers

import (
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"

	"github.com/sirupsen/logrus"
	"golang.org/x/crypto/ssh"
)

var (
	//SSHMetaLogs is a buffer where all commands sent over ssh are saved.
	SSHMetaLogs = ginkgoext.NewWriter(new(Buffer))
)

// SSHMeta contains metadata to SSH into a remote location to run tests,
// implements Executor interface
type SSHMeta struct {
	sshClient *SSHClient
	env       []string
	rawConfig []byte
	nodeName  string
	logger    *logrus.Entry
	basePath  string
}

// CreateSSHMeta returns an SSHMeta with the specified host, port, and user, as
// well as an according SSHClient.
func CreateSSHMeta(host string, port int, user string) *SSHMeta {
	return &SSHMeta{
		sshClient: GetSSHClient(host, port, user),
	}
}

// IsLocal returns true if commands are executed on the Ginkgo host
func (s *SSHMeta) IsLocal() bool {
	return false
}

// Logger returns logger for SSHMeta
func (s *SSHMeta) Logger() *logrus.Entry {
	return s.logger
}

func (s *SSHMeta) String() string {
	return fmt.Sprintf("environment: %s, SSHClient: %s", s.env, s.sshClient.String())

}

// CloseSSHClient closes all of the connections made by the SSH Client for this
// SSHMeta.
func (s *SSHMeta) CloseSSHClient() {
	if s.sshClient == nil || s.sshClient.client == nil {
		log.Error("SSH client is nil; cannot close")
	}
	if err := s.sshClient.client.Close(); err != nil {
		log.WithError(err).Error("error closing SSH client")
	}
}

// GetVagrantSSHMeta returns a SSHMeta initialized based on the provided
// SSH-config target.
func GetVagrantSSHMeta(vmName string) *SSHMeta {
	config, err := GetVagrantSSHMetadata(vmName)
	if err != nil {
		return nil
	}

	log.Debugf("generated SSHConfig for node %s", vmName)
	nodes, err := ImportSSHconfig(config)
	if err != nil {
		log.WithError(err).Error("Error importing ssh config")
		return nil
	}
	var node *SSHConfig
	log.Debugf("done importing ssh config")
	for name := range nodes {
		if strings.HasPrefix(name, vmName) {
			node = nodes[name]
			break
		}
	}
	if node == nil {
		log.Errorf("Node %s not found in ssh config", vmName)
		return nil
	}
	sshMeta := &SSHMeta{
		sshClient: node.GetSSHClient(),
		rawConfig: config,
		nodeName:  vmName,
	}

	sshMeta.setBasePath()
	return sshMeta
}

// setBasePath if the SSHConfig is defined we set the BasePath to the GOPATH,
// from golang 1.8 GOPATH is by default $HOME/go so we also check that.
func (s *SSHMeta) setBasePath() {
	gopath := s.Exec("echo $GOPATH").SingleOut()
	if gopath != "" {
		s.basePath = filepath.Join(gopath, CiliumPath)
		return
	}

	home := s.Exec("echo $HOME").SingleOut()
	if home == "" {
		return
	}

	s.basePath = filepath.Join(home, "go", CiliumPath)
}

// ExecuteContext executes the given `cmd` and writes the cmd's stdout and
// stderr into the given io.Writers.
// Returns an error if context Deadline() is reached or if there was an error
// executing the command.
func (s *SSHMeta) ExecuteContext(ctx context.Context, cmd string, stdout io.Writer, stderr io.Writer) error {
	if stdout == nil {
		stdout = os.Stdout
	}

	if stderr == nil {
		stderr = os.Stderr
	}
	fmt.Fprintln(SSHMetaLogs, cmd)
	command := &SSHCommand{
		Path:   cmd,
		Stdin:  os.Stdin,
		Stdout: stdout,
		Stderr: stderr,
	}
	return s.sshClient.RunCommandContext(ctx, command)
}

// ExecWithSudo returns the result of executing the provided cmd via SSH using
// sudo.
func (s *SSHMeta) ExecWithSudo(cmd string, options ...ExecOptions) *CmdRes {
	command := fmt.Sprintf("sudo %s", cmd)
	return s.Exec(command, options...)
}

// ExecOptions options to execute Exec and ExecWithContext
type ExecOptions struct {
	SkipLog bool
}

// Exec returns the results of executing the provided cmd via SSH.
func (s *SSHMeta) Exec(cmd string, options ...ExecOptions) *CmdRes {
	// Bound all command executions to be at most the timeout used by the CI
	// so that commands do not block forever.
	ctx, cancel := context.WithTimeout(context.Background(), HelperTimeout)
	defer cancel()
	return s.ExecContext(ctx, cmd, options...)
}

// ExecShort runs command with the provided options. It will take up to
// ShortCommandTimeout seconds to run the command before it times out.
func (s *SSHMeta) ExecShort(cmd string, options ...ExecOptions) *CmdRes {
	ctx, cancel := context.WithTimeout(context.Background(), ShortCommandTimeout)
	defer cancel()
	return s.ExecContext(ctx, cmd, options...)
}

// ExecMiddle runs command with the provided options. It will take up to
// MidCommandTimeout seconds to run the command before it times out.
func (s *SSHMeta) ExecMiddle(cmd string, options ...ExecOptions) *CmdRes {
	ctx, cancel := context.WithTimeout(context.Background(), MidCommandTimeout)
	defer cancel()
	return s.ExecContext(ctx, cmd, options...)
}

// ExecContextShort is a wrapper around ExecContext which creates a child
// context with a timeout of ShortCommandTimeout.
func (s *SSHMeta) ExecContextShort(ctx context.Context, cmd string, options ...ExecOptions) *CmdRes {
	shortCtx, cancel := context.WithTimeout(ctx, ShortCommandTimeout)
	defer cancel()
	return s.ExecContext(shortCtx, cmd, options...)
}

// ExecContext returns the results of executing the provided cmd via SSH.
func (s *SSHMeta) ExecContext(ctx context.Context, cmd string, options ...ExecOptions) *CmdRes {
	var ops ExecOptions
	if len(options) > 0 {
		ops = options[0]
	}

	log.Debugf("running command: %s", cmd)
	stdout := new(Buffer)
	stderr := new(Buffer)
	start := time.Now()
	err := s.ExecuteContext(ctx, cmd, stdout, stderr)

	res := CmdRes{
		cmd:      cmd,
		stdout:   stdout,
		stderr:   stderr,
		success:  true, // this may be toggled when err != nil below
		duration: time.Since(start),
	}

	if err != nil {
		res.success = false
		// Set error code to 1 in case that it's another error to see that the
		// command failed. If the default value (0) indicates that command
		// works but it was not executed at all.
		res.exitcode = 1
		exiterr, isExitError := err.(*ssh.ExitError)
		if isExitError {
			// Set res's exitcode if the error is an ExitError
			res.exitcode = exiterr.Waitmsg.ExitStatus()
		} else {
			// Log other error types. They are likely from SSH or the network
			log.WithError(err).Errorf("Error executing command '%s'", cmd)
		}
		res.err = err
	}

	res.SendToLog(ops.SkipLog)
	return &res
}

// GetCopy returns a copy of SSHMeta, useful for parallel requests
func (s *SSHMeta) GetCopy() *SSHMeta {
	nodes, err := ImportSSHconfig(s.rawConfig)
	if err != nil {
		log.WithError(err).Error("while importing ssh config for meta copy")
		return nil
	}

	config := nodes[s.nodeName]
	if config == nil {
		log.Errorf("no node %s in imported config", s.nodeName)
		return nil
	}

	copy := &SSHMeta{
		sshClient: config.GetSSHClient(),
		rawConfig: s.rawConfig,
		nodeName:  s.nodeName,
	}

	return copy
}

// ExecInBackground returns the results of running cmd via SSH in the specified
// context. The command will be executed in the background until context.Context
// is canceled or the command has finish its execution.
func (s *SSHMeta) ExecInBackground(ctx context.Context, cmd string, options ...ExecOptions) *CmdRes {
	if ctx == nil {
		panic("no context provided")
	}

	var ops ExecOptions
	if len(options) > 0 {
		ops = options[0]
	}

	fmt.Fprintln(SSHMetaLogs, cmd)
	stdout := new(Buffer)
	stderr := new(Buffer)

	command := &SSHCommand{
		Path:   cmd,
		Stdin:  os.Stdin,
		Stdout: stdout,
		Stderr: stderr,
	}
	var wg sync.WaitGroup
	res := &CmdRes{
		cmd:     cmd,
		stdout:  stdout,
		stderr:  stderr,
		success: false,
		wg:      &wg,
	}

	res.wg.Add(1)
	go func(res *CmdRes) {
		defer res.wg.Done()
		start := time.Now()
		err := s.sshClient.RunCommandInBackground(ctx, command)
		if err != nil {
			exiterr, isExitError := err.(*ssh.ExitError)
			if isExitError {
				res.exitcode = exiterr.Waitmsg.ExitStatus()
				// Set success as true if SIGINT signal was sent to command
				if res.exitcode == 130 {
					res.success = true
				}
			}
			if !res.success {
				res.err = err
			}
		} else {
			res.success = true
			res.exitcode = 0
		}
		res.duration = time.Since(start)
		res.SendToLog(ops.SkipLog)
	}(res)

	return res
}

// RenderTemplateToFile renders a text/template string into a target filename
// with specific persmisions. Returns an error if the template cannot be
// validated or the file cannot be created.
func (s *SSHMeta) RenderTemplateToFile(filename string, tmplt string, perm os.FileMode) error {
	content, err := RenderTemplate(tmplt)
	if err != nil {
		return err
	}

	cmd := fmt.Sprintf("install -m %o <(echo '%s') %s\n", perm, content, filepath.Join(s.basePath, filename))
	res := s.Exec(cmd)
	if !res.WasSuccessful() {
		return fmt.Errorf("%s", res.CombineOutput())
	}
	return nil
}
<commit_msg>test: Avoid use of install with NFS<commit_after>// Copyright 2017 Authors of Cilium
//
// 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 helpers

import (
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"

	"github.com/sirupsen/logrus"
	"golang.org/x/crypto/ssh"
)

var (
	//SSHMetaLogs is a buffer where all commands sent over ssh are saved.
	SSHMetaLogs = ginkgoext.NewWriter(new(Buffer))
)

// SSHMeta contains metadata to SSH into a remote location to run tests,
// implements Executor interface
type SSHMeta struct {
	sshClient *SSHClient
	env       []string
	rawConfig []byte
	nodeName  string
	logger    *logrus.Entry
	basePath  string
}

// CreateSSHMeta returns an SSHMeta with the specified host, port, and user, as
// well as an according SSHClient.
func CreateSSHMeta(host string, port int, user string) *SSHMeta {
	return &SSHMeta{
		sshClient: GetSSHClient(host, port, user),
	}
}

// IsLocal returns true if commands are executed on the Ginkgo host
func (s *SSHMeta) IsLocal() bool {
	return false
}

// Logger returns logger for SSHMeta
func (s *SSHMeta) Logger() *logrus.Entry {
	return s.logger
}

func (s *SSHMeta) String() string {
	return fmt.Sprintf("environment: %s, SSHClient: %s", s.env, s.sshClient.String())

}

// CloseSSHClient closes all of the connections made by the SSH Client for this
// SSHMeta.
func (s *SSHMeta) CloseSSHClient() {
	if s.sshClient == nil || s.sshClient.client == nil {
		log.Error("SSH client is nil; cannot close")
	}
	if err := s.sshClient.client.Close(); err != nil {
		log.WithError(err).Error("error closing SSH client")
	}
}

// GetVagrantSSHMeta returns a SSHMeta initialized based on the provided
// SSH-config target.
func GetVagrantSSHMeta(vmName string) *SSHMeta {
	config, err := GetVagrantSSHMetadata(vmName)
	if err != nil {
		return nil
	}

	log.Debugf("generated SSHConfig for node %s", vmName)
	nodes, err := ImportSSHconfig(config)
	if err != nil {
		log.WithError(err).Error("Error importing ssh config")
		return nil
	}
	var node *SSHConfig
	log.Debugf("done importing ssh config")
	for name := range nodes {
		if strings.HasPrefix(name, vmName) {
			node = nodes[name]
			break
		}
	}
	if node == nil {
		log.Errorf("Node %s not found in ssh config", vmName)
		return nil
	}
	sshMeta := &SSHMeta{
		sshClient: node.GetSSHClient(),
		rawConfig: config,
		nodeName:  vmName,
	}

	sshMeta.setBasePath()
	return sshMeta
}

// setBasePath if the SSHConfig is defined we set the BasePath to the GOPATH,
// from golang 1.8 GOPATH is by default $HOME/go so we also check that.
func (s *SSHMeta) setBasePath() {
	gopath := s.Exec("echo $GOPATH").SingleOut()
	if gopath != "" {
		s.basePath = filepath.Join(gopath, CiliumPath)
		return
	}

	home := s.Exec("echo $HOME").SingleOut()
	if home == "" {
		return
	}

	s.basePath = filepath.Join(home, "go", CiliumPath)
}

// ExecuteContext executes the given `cmd` and writes the cmd's stdout and
// stderr into the given io.Writers.
// Returns an error if context Deadline() is reached or if there was an error
// executing the command.
func (s *SSHMeta) ExecuteContext(ctx context.Context, cmd string, stdout io.Writer, stderr io.Writer) error {
	if stdout == nil {
		stdout = os.Stdout
	}

	if stderr == nil {
		stderr = os.Stderr
	}
	fmt.Fprintln(SSHMetaLogs, cmd)
	command := &SSHCommand{
		Path:   cmd,
		Stdin:  os.Stdin,
		Stdout: stdout,
		Stderr: stderr,
	}
	return s.sshClient.RunCommandContext(ctx, command)
}

// ExecWithSudo returns the result of executing the provided cmd via SSH using
// sudo.
func (s *SSHMeta) ExecWithSudo(cmd string, options ...ExecOptions) *CmdRes {
	command := fmt.Sprintf("sudo %s", cmd)
	return s.Exec(command, options...)
}

// ExecOptions options to execute Exec and ExecWithContext
type ExecOptions struct {
	SkipLog bool
}

// Exec returns the results of executing the provided cmd via SSH.
func (s *SSHMeta) Exec(cmd string, options ...ExecOptions) *CmdRes {
	// Bound all command executions to be at most the timeout used by the CI
	// so that commands do not block forever.
	ctx, cancel := context.WithTimeout(context.Background(), HelperTimeout)
	defer cancel()
	return s.ExecContext(ctx, cmd, options...)
}

// ExecShort runs command with the provided options. It will take up to
// ShortCommandTimeout seconds to run the command before it times out.
func (s *SSHMeta) ExecShort(cmd string, options ...ExecOptions) *CmdRes {
	ctx, cancel := context.WithTimeout(context.Background(), ShortCommandTimeout)
	defer cancel()
	return s.ExecContext(ctx, cmd, options...)
}

// ExecMiddle runs command with the provided options. It will take up to
// MidCommandTimeout seconds to run the command before it times out.
func (s *SSHMeta) ExecMiddle(cmd string, options ...ExecOptions) *CmdRes {
	ctx, cancel := context.WithTimeout(context.Background(), MidCommandTimeout)
	defer cancel()
	return s.ExecContext(ctx, cmd, options...)
}

// ExecContextShort is a wrapper around ExecContext which creates a child
// context with a timeout of ShortCommandTimeout.
func (s *SSHMeta) ExecContextShort(ctx context.Context, cmd string, options ...ExecOptions) *CmdRes {
	shortCtx, cancel := context.WithTimeout(ctx, ShortCommandTimeout)
	defer cancel()
	return s.ExecContext(shortCtx, cmd, options...)
}

// ExecContext returns the results of executing the provided cmd via SSH.
func (s *SSHMeta) ExecContext(ctx context.Context, cmd string, options ...ExecOptions) *CmdRes {
	var ops ExecOptions
	if len(options) > 0 {
		ops = options[0]
	}

	log.Debugf("running command: %s", cmd)
	stdout := new(Buffer)
	stderr := new(Buffer)
	start := time.Now()
	err := s.ExecuteContext(ctx, cmd, stdout, stderr)

	res := CmdRes{
		cmd:      cmd,
		stdout:   stdout,
		stderr:   stderr,
		success:  true, // this may be toggled when err != nil below
		duration: time.Since(start),
	}

	if err != nil {
		res.success = false
		// Set error code to 1 in case that it's another error to see that the
		// command failed. If the default value (0) indicates that command
		// works but it was not executed at all.
		res.exitcode = 1
		exiterr, isExitError := err.(*ssh.ExitError)
		if isExitError {
			// Set res's exitcode if the error is an ExitError
			res.exitcode = exiterr.Waitmsg.ExitStatus()
		} else {
			// Log other error types. They are likely from SSH or the network
			log.WithError(err).Errorf("Error executing command '%s'", cmd)
		}
		res.err = err
	}

	res.SendToLog(ops.SkipLog)
	return &res
}

// GetCopy returns a copy of SSHMeta, useful for parallel requests
func (s *SSHMeta) GetCopy() *SSHMeta {
	nodes, err := ImportSSHconfig(s.rawConfig)
	if err != nil {
		log.WithError(err).Error("while importing ssh config for meta copy")
		return nil
	}

	config := nodes[s.nodeName]
	if config == nil {
		log.Errorf("no node %s in imported config", s.nodeName)
		return nil
	}

	copy := &SSHMeta{
		sshClient: config.GetSSHClient(),
		rawConfig: s.rawConfig,
		nodeName:  s.nodeName,
	}

	return copy
}

// ExecInBackground returns the results of running cmd via SSH in the specified
// context. The command will be executed in the background until context.Context
// is canceled or the command has finish its execution.
func (s *SSHMeta) ExecInBackground(ctx context.Context, cmd string, options ...ExecOptions) *CmdRes {
	if ctx == nil {
		panic("no context provided")
	}

	var ops ExecOptions
	if len(options) > 0 {
		ops = options[0]
	}

	fmt.Fprintln(SSHMetaLogs, cmd)
	stdout := new(Buffer)
	stderr := new(Buffer)

	command := &SSHCommand{
		Path:   cmd,
		Stdin:  os.Stdin,
		Stdout: stdout,
		Stderr: stderr,
	}
	var wg sync.WaitGroup
	res := &CmdRes{
		cmd:     cmd,
		stdout:  stdout,
		stderr:  stderr,
		success: false,
		wg:      &wg,
	}

	res.wg.Add(1)
	go func(res *CmdRes) {
		defer res.wg.Done()
		start := time.Now()
		err := s.sshClient.RunCommandInBackground(ctx, command)
		if err != nil {
			exiterr, isExitError := err.(*ssh.ExitError)
			if isExitError {
				res.exitcode = exiterr.Waitmsg.ExitStatus()
				// Set success as true if SIGINT signal was sent to command
				if res.exitcode == 130 {
					res.success = true
				}
			}
			if !res.success {
				res.err = err
			}
		} else {
			res.success = true
			res.exitcode = 0
		}
		res.duration = time.Since(start)
		res.SendToLog(ops.SkipLog)
	}(res)

	return res
}

// RenderTemplateToFile renders a text/template string into a target filename
// with specific persmisions. Returns an error if the template cannot be
// validated or the file cannot be created.
func (s *SSHMeta) RenderTemplateToFile(filename string, tmplt string, perm os.FileMode) error {
	content, err := RenderTemplate(tmplt)
	if err != nil {
		return err
	}

	dst := filepath.Join(s.basePath, filename)
	cmd := fmt.Sprintf("echo '%s' > %s", content, dst)
	res := s.Exec(cmd)
	if !res.WasSuccessful() {
		return fmt.Errorf("%s", res.CombineOutput())
	}
	cmd = fmt.Sprintf("chmod %o %s", perm, dst)
	res = s.Exec(cmd)
	if !res.WasSuccessful() {
		return fmt.Errorf("%s", res.CombineOutput())
	}
	return nil
}
<|endoftext|>


<commit_before>// Package io contains WDTE functions for dealing with files and other
// types of data streams.
package io

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/DeedleFake/wdte"
)

type reader interface {
	wdte.Func
	io.Reader
}

// Reader wraps an io.Reader, allowing it to be used as a WDTE function.
type Reader struct {
	io.Reader
}

func (r Reader) Call(frame wdte.Frame, args ...wdte.Func) wdte.Func { // nolint
	return r
}

type writer interface {
	wdte.Func
	io.Writer
}

// Writer wraps an io.Writer, allowing it to be used as a WDTE function.
type Writer struct {
	io.Writer
}

func (w Writer) Call(frame wdte.Frame, args ...wdte.Func) wdte.Func { // nolint
	return w
}

// Close closes a closer. This includes files opened with other
// functions in this module.
func Close(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("close")

	if len(args) == 0 {
		return wdte.GoFunc(Close)
	}

	c := args[0].Call(frame).(io.Closer)
	err := c.Close()
	if err != nil {
		return wdte.Error{Err: err, Frame: frame}
	}
	return c.(wdte.Func)
}

// Combine combines multiple readers or multiple writers. If the
// arguments passed are readers, it uses Go's io.MultiReader to
// concatenate them. If the arguments passed are writers, it uses Go's
// io.MultiWriter to combine them. If only one argument is given, it
// returns a function which combines its arguments with the argument
// originally given.
func Combine(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("combine")

	switch len(args) {
	case 0:
		return wdte.GoFunc(Combine)
	case 1:
		return wdte.GoFunc(func(frame wdte.Frame, more ...wdte.Func) wdte.Func {
			return Combine(frame, append(args, more...)...)
		})
	}

	switch a0 := args[0].Call(frame).(type) {
	case reader:
		r := make([]io.Reader, 1, len(args))
		r[0] = a0
		for _, a := range args[1:] {
			r = append(r, a.Call(frame).(reader))
		}
		return Reader{Reader: io.MultiReader(r...)}

	case writer:
		w := make([]io.Writer, 1, len(args))
		w[0] = a0
		for _, a := range args[1:] {
			w = append(w, a.Call(frame).(writer))
		}
		return Writer{Writer: io.MultiWriter(w...)}

	default:
		panic(fmt.Errorf("Unexpected argument type: %T", a0))
	}
}

// Copy copies from a reader to a writer until it hits EOF using Go's
// io.Copy. It takes its arguments in either order, and, if given one
// only argument, returns a function which performs the copy using
// that argument and a single argument that it is given. In other
// words:
//
//     io.stdout -> io.copy io.stdin;
//
// and
//
//     io.stdin -> io.copy io.stdout;
//
// are mostly equivalent. The only difference is in the return value.
// Copy returns the second argument it was given to allow for easier
// chaining. For example, in the first example above it returns
// io.stdout, while in the second it returns io.stdin.
func Copy(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("copy")

	switch len(args) {
	case 0:
		return wdte.GoFunc(Copy)
	case 1:
		return wdte.GoFunc(func(frame wdte.Frame, more ...wdte.Func) wdte.Func {
			return Copy(frame, append(args, more...)...)
		})
	}

	var w writer
	var r reader
	switch a0 := args[0].Call(frame).(type) {
	case writer:
		w = a0
		r = args[1].Call(frame).(reader)
	case reader:
		w = args[1].Call(frame).(writer)
		r = a0
	}

	_, err := io.Copy(w, r)
	if err != nil {
		return wdte.Error{Err: err, Frame: frame}
	}
	return w
}

// ReadString returns a reader that reads from a string.
func ReadString(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("readString")

	if len(args) == 0 {
		return wdte.GoFunc(ReadString)
	}

	s := args[0].Call(frame).(wdte.String)
	return Reader{Reader: strings.NewReader(string(s))}
}

// String reads the entirety of a reader into a string and returns it.
func String(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("string")

	if len(args) == 0 {
		return wdte.GoFunc(String)
	}

	r := args[0].Call(frame).(reader)

	var buf bytes.Buffer
	_, err := io.Copy(&buf, r)
	if err != nil {
		return wdte.Error{Err: err, Frame: frame}
	}
	return wdte.String(buf.String())
}

// scanner is a simple wrapper that allows a bufio.Scanner to be used
// as a stream.Stream.
type scanner struct {
	s *bufio.Scanner
}

func (s scanner) Call(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	return s
}

func (s scanner) Next(frame wdte.Frame) (wdte.Func, bool) {
	ok := s.s.Scan()
	if !ok {
		err := s.s.Err()
		if err != nil {
			return wdte.Error{Err: err, Frame: frame}, false
		}
		return nil, false
	}

	return wdte.String(s.s.Text()), true
}

// Lines returns a stream that yields, as strings, successive lines
// read from a reader.
func Lines(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("lines")

	if len(args) == 0 {
		return wdte.GoFunc(Lines)
	}

	r := args[0].Call(frame).(reader)
	return scanner{s: bufio.NewScanner(r)}
}

// Words returns a stream that yields, as strings, successive words
// read from a reader.
func Words(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("words")

	if len(args) == 0 {
		return wdte.GoFunc(Words)
	}

	r := args[0].Call(frame).(reader)
	s := bufio.NewScanner(r)
	s.Split(bufio.ScanWords)
	return scanner{s: s}
}

func write(f func(io.Writer, interface{}) error) wdte.Func {
	var gf wdte.GoFunc
	gf = func(frame wdte.Frame, args ...wdte.Func) wdte.Func {
		switch len(args) {
		case 0:
			return gf
		case 1:
			return wdte.GoFunc(func(frame wdte.Frame, more ...wdte.Func) wdte.Func {
				return gf(frame, append(more, args...)...)
			})
		}

		var w writer
		var d wdte.Func
		switch a0 := args[0].Call(frame).(type) {
		case writer:
			w = a0
			d = args[1].Call(frame)
		case wdte.Func:
			d = a0
			w = args[1].Call(frame).(writer)
		}

		err := f(w, d)
		if err != nil {
			return wdte.Error{Err: err, Frame: frame}
		}
		return w
	}
	return gf
}

// Write writes to a writer. It takes two arguments, one of which is
// the writer and one of which is the data. It is essentially
// equivalent to fmt.Fprint. It accepts the arguments in either order
// and, if given only one argument, returns a function that takes the
// other. In other words,
//
//     'Example' -> io.write io.stdout;
//
// and
//
//     io.stdout -> io.write 'Example';
//
// are equivalent.
//
// It returns the writer that it was passed.
func Write(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("write")
	return write(func(w io.Writer, v interface{}) error {
		_, err := fmt.Fprint(w, v)
		return err
	}).Call(frame, args...)
}

// Writeln is exactly like Write, but also writes a newline
// afterwards. It is essentially equivalent to fmt.Fprintln.
func Writeln(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("writeln")
	return write(func(w io.Writer, v interface{}) error {
		_, err := fmt.Fprintln(w, v)
		return err
	}).Call(frame, args...)
}

// Module returns a module for easy importing into an actual script.
// The imported functions have the same names as the functions in this
// package, except that the first letter is lowercase.
func Module() *wdte.Module {
	return &wdte.Module{
		Funcs: map[wdte.ID]wdte.Func{
			"stdin":  Reader{Reader: os.Stdin},
			"stdout": Writer{Writer: os.Stdout},
			"stderr": Writer{Writer: os.Stderr},

			"close": wdte.GoFunc(Close),

			"combine": wdte.GoFunc(Combine),
			"copy":    wdte.GoFunc(Copy),

			"readString": wdte.GoFunc(ReadString),
			"string":     wdte.GoFunc(String),
			"lines":      wdte.GoFunc(Lines),
			"words":      wdte.GoFunc(Words),

			"write":   wdte.GoFunc(Write),
			"writeln": wdte.GoFunc(Writeln),
		},
	}
}
<commit_msg>std/io: Actually return the second argument from `copy`, as documented.<commit_after>// Package io contains WDTE functions for dealing with files and other
// types of data streams.
package io

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/DeedleFake/wdte"
)

type reader interface {
	wdte.Func
	io.Reader
}

// Reader wraps an io.Reader, allowing it to be used as a WDTE function.
type Reader struct {
	io.Reader
}

func (r Reader) Call(frame wdte.Frame, args ...wdte.Func) wdte.Func { // nolint
	return r
}

type writer interface {
	wdte.Func
	io.Writer
}

// Writer wraps an io.Writer, allowing it to be used as a WDTE function.
type Writer struct {
	io.Writer
}

func (w Writer) Call(frame wdte.Frame, args ...wdte.Func) wdte.Func { // nolint
	return w
}

// Close closes a closer. This includes files opened with other
// functions in this module.
func Close(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("close")

	if len(args) == 0 {
		return wdte.GoFunc(Close)
	}

	c := args[0].Call(frame).(io.Closer)
	err := c.Close()
	if err != nil {
		return wdte.Error{Err: err, Frame: frame}
	}
	return c.(wdte.Func)
}

// Combine combines multiple readers or multiple writers. If the
// arguments passed are readers, it uses Go's io.MultiReader to
// concatenate them. If the arguments passed are writers, it uses Go's
// io.MultiWriter to combine them. If only one argument is given, it
// returns a function which combines its arguments with the argument
// originally given.
func Combine(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("combine")

	switch len(args) {
	case 0:
		return wdte.GoFunc(Combine)
	case 1:
		return wdte.GoFunc(func(frame wdte.Frame, more ...wdte.Func) wdte.Func {
			return Combine(frame, append(args, more...)...)
		})
	}

	switch a0 := args[0].Call(frame).(type) {
	case reader:
		r := make([]io.Reader, 1, len(args))
		r[0] = a0
		for _, a := range args[1:] {
			r = append(r, a.Call(frame).(reader))
		}
		return Reader{Reader: io.MultiReader(r...)}

	case writer:
		w := make([]io.Writer, 1, len(args))
		w[0] = a0
		for _, a := range args[1:] {
			w = append(w, a.Call(frame).(writer))
		}
		return Writer{Writer: io.MultiWriter(w...)}

	default:
		panic(fmt.Errorf("Unexpected argument type: %T", a0))
	}
}

// Copy copies from a reader to a writer until it hits EOF using Go's
// io.Copy. It takes its arguments in either order, and, if given one
// only argument, returns a function which performs the copy using
// that argument and a single argument that it is given. In other
// words:
//
//     io.stdout -> io.copy io.stdin;
//
// and
//
//     io.stdin -> io.copy io.stdout;
//
// are mostly equivalent. The only difference is in the return value.
// Copy returns the second argument it was given to allow for easier
// chaining. For example, in the first example above it returns
// io.stdout, while in the second it returns io.stdin.
func Copy(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("copy")

	switch len(args) {
	case 0:
		return wdte.GoFunc(Copy)
	case 1:
		return wdte.GoFunc(func(frame wdte.Frame, more ...wdte.Func) wdte.Func {
			return Copy(frame, append(args, more...)...)
		})
	}

	var w writer
	var r reader
	var a1 wdte.Func
	switch a0 := args[0].Call(frame).(type) {
	case writer:
		w = a0
		r = args[1].Call(frame).(reader)
		a1 = r

	case reader:
		w = args[1].Call(frame).(writer)
		r = a0
		a1 = w
	}

	_, err := io.Copy(w, r)
	if err != nil {
		return wdte.Error{Err: err, Frame: frame}
	}
	return a1
}

// ReadString returns a reader that reads from a string.
func ReadString(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("readString")

	if len(args) == 0 {
		return wdte.GoFunc(ReadString)
	}

	s := args[0].Call(frame).(wdte.String)
	return Reader{Reader: strings.NewReader(string(s))}
}

// String reads the entirety of a reader into a string and returns it.
func String(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("string")

	if len(args) == 0 {
		return wdte.GoFunc(String)
	}

	r := args[0].Call(frame).(reader)

	var buf bytes.Buffer
	_, err := io.Copy(&buf, r)
	if err != nil {
		return wdte.Error{Err: err, Frame: frame}
	}
	return wdte.String(buf.String())
}

// scanner is a simple wrapper that allows a bufio.Scanner to be used
// as a stream.Stream.
type scanner struct {
	s *bufio.Scanner
}

func (s scanner) Call(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	return s
}

func (s scanner) Next(frame wdte.Frame) (wdte.Func, bool) {
	ok := s.s.Scan()
	if !ok {
		err := s.s.Err()
		if err != nil {
			return wdte.Error{Err: err, Frame: frame}, false
		}
		return nil, false
	}

	return wdte.String(s.s.Text()), true
}

// Lines returns a stream that yields, as strings, successive lines
// read from a reader.
func Lines(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("lines")

	if len(args) == 0 {
		return wdte.GoFunc(Lines)
	}

	r := args[0].Call(frame).(reader)
	return scanner{s: bufio.NewScanner(r)}
}

// Words returns a stream that yields, as strings, successive words
// read from a reader.
func Words(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("words")

	if len(args) == 0 {
		return wdte.GoFunc(Words)
	}

	r := args[0].Call(frame).(reader)
	s := bufio.NewScanner(r)
	s.Split(bufio.ScanWords)
	return scanner{s: s}
}

func write(f func(io.Writer, interface{}) error) wdte.Func {
	var gf wdte.GoFunc
	gf = func(frame wdte.Frame, args ...wdte.Func) wdte.Func {
		switch len(args) {
		case 0:
			return gf
		case 1:
			return wdte.GoFunc(func(frame wdte.Frame, more ...wdte.Func) wdte.Func {
				return gf(frame, append(more, args...)...)
			})
		}

		var w writer
		var d wdte.Func
		switch a0 := args[0].Call(frame).(type) {
		case writer:
			w = a0
			d = args[1].Call(frame)
		case wdte.Func:
			d = a0
			w = args[1].Call(frame).(writer)
		}

		err := f(w, d)
		if err != nil {
			return wdte.Error{Err: err, Frame: frame}
		}
		return w
	}
	return gf
}

// Write writes to a writer. It takes two arguments, one of which is
// the writer and one of which is the data. It is essentially
// equivalent to fmt.Fprint. It accepts the arguments in either order
// and, if given only one argument, returns a function that takes the
// other. In other words,
//
//     'Example' -> io.write io.stdout;
//
// and
//
//     io.stdout -> io.write 'Example';
//
// are equivalent.
//
// It returns the writer that it was passed.
func Write(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("write")
	return write(func(w io.Writer, v interface{}) error {
		_, err := fmt.Fprint(w, v)
		return err
	}).Call(frame, args...)
}

// Writeln is exactly like Write, but also writes a newline
// afterwards. It is essentially equivalent to fmt.Fprintln.
func Writeln(frame wdte.Frame, args ...wdte.Func) wdte.Func {
	frame = frame.WithID("writeln")
	return write(func(w io.Writer, v interface{}) error {
		_, err := fmt.Fprintln(w, v)
		return err
	}).Call(frame, args...)
}

// Module returns a module for easy importing into an actual script.
// The imported functions have the same names as the functions in this
// package, except that the first letter is lowercase.
func Module() *wdte.Module {
	return &wdte.Module{
		Funcs: map[wdte.ID]wdte.Func{
			"stdin":  Reader{Reader: os.Stdin},
			"stdout": Writer{Writer: os.Stdout},
			"stderr": Writer{Writer: os.Stderr},

			"close": wdte.GoFunc(Close),

			"combine": wdte.GoFunc(Combine),
			"copy":    wdte.GoFunc(Copy),

			"readString": wdte.GoFunc(ReadString),
			"string":     wdte.GoFunc(String),
			"lines":      wdte.GoFunc(Lines),
			"words":      wdte.GoFunc(Words),

			"write":   wdte.GoFunc(Write),
			"writeln": wdte.GoFunc(Writeln),
		},
	}
}
<|endoftext|>


<commit_before>package repo

import (
	"errors"
	"fmt"
	"gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
	"io"
	"os"
	"path"

	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/namesys"
	"github.com/ipfs/go-ipfs/repo/fsrepo"
	"github.com/op/go-logging"
	"github.com/tyler-smith/go-bip39"
)

var log = logging.MustGetLogger("repo")

var ErrRepoExists = errors.New(`ipfs configuration file already exists!
Reinitializing would overwrite your keys.
(use -f to force overwrite)
`)

func DoInit(out io.Writer, repoRoot string, nBitsForKeypair int, testnet bool, password string, dbInit func(string, []byte, string) error) error {
	if err := maybeCreateOBDirectories(repoRoot); err != nil {
		return err
	}

	if fsrepo.IsInitialized(repoRoot) {
		return ErrRepoExists
	}

	if err := checkWriteable(repoRoot); err != nil {
		return err
	}

	conf, err := initConfig(out, repoRoot)
	if err != nil {
		return err
	}

	mnemonic, err := createMnemonic()
	if err != nil {
		return err
	}

	seed := bip39.NewSeed(mnemonic, "Secret Passphrase")
	fmt.Printf("generating %d-bit RSA keypair...", nBitsForKeypair)
	identityKey, err := ipfs.IdentityKeyFromSeed(seed, nBitsForKeypair)
	if err != nil {
		return err
	}
	fmt.Printf("done\n")

	identity, err := ipfs.IdentityFromKey(identityKey)
	if err != nil {
		return err
	}

	log.Infof("initializing openbazaar node at %s\n", repoRoot)
	if err := fsrepo.Init(repoRoot, conf); err != nil {
		return err
	}
	conf.Identity = identity

	if err := addConfigExtensions(repoRoot, testnet); err != nil {
		return err
	}

	if err := dbInit(mnemonic, identityKey, password); err != nil {
		return err
	}

	return initializeIpnsKeyspace(repoRoot, identityKey)
}

func maybeCreateOBDirectories(repoRoot string) error {
	if err := os.MkdirAll(path.Join(repoRoot, "root"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "listings"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "feed"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "channel"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "outbox"), os.ModePerm); err != nil {
		return err
	}
	return nil
}

func checkWriteable(dir string) error {
	_, err := os.Stat(dir)
	if err == nil {
		// dir exists, make sure we can write to it
		testfile := path.Join(dir, "test")
		fi, err := os.Create(testfile)
		if err != nil {
			if os.IsPermission(err) {
				return fmt.Errorf("%s is not writeable by the current user", dir)
			}
			return fmt.Errorf("unexpected error while checking writeablility of repo root: %s", err)
		}
		fi.Close()
		return os.Remove(testfile)
	}

	if os.IsNotExist(err) {
		// dir does not exist, check that we can create it
		return os.Mkdir(dir, 0775)
	}

	if os.IsPermission(err) {
		return fmt.Errorf("cannot write to %s, incorrect permissions", err)
	}

	return err
}

func initializeIpnsKeyspace(repoRoot string, privKeyBytes []byte) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	cfg, err := r.Config()
	if err != nil {
		log.Error(err)
		return err
	}
	identity, err := ipfs.IdentityFromKey(privKeyBytes)
	if err != nil {
		return err
	}

	cfg.Identity = identity
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
	if err != nil {
		return err
	}
	defer nd.Close()

	err = nd.SetupOfflineRouting()
	if err != nil {
		return err
	}

	return namesys.InitializeKeyspace(ctx, nd.DAG, nd.Namesys, nd.Pinning, nd.PrivateKey)
}

func addConfigExtensions(repoRoot string, testnet bool) error {
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	type Wallet struct {
		MaxFee           int
		FeeAPI           string
		HighFeeDefault   int
		MediumFeeDefault int
		LowFeeDefault    int
	}
	var w Wallet = Wallet{
		MaxFee:           2000,
		FeeAPI:           "https://bitcoinfees.21.co/api/v1/fees/recommended",
		HighFeeDefault:   60,
		MediumFeeDefault: 40,
		LowFeeDefault:    20,
	}
	type APIConfig struct {
		CORS        bool
		Enabled     bool
		HTTPHeaders map[string][]string
	}
	var a APIConfig = APIConfig{
		Enabled:     true,
		CORS:        false,
		HTTPHeaders: nil,
	}
	if err := extendConfigFile(r, "Wallet", w); err != nil {
		return err
	}
	if err := extendConfigFile(r, "Resolver", "https://resolver.onename.com/"); err != nil {
		return err
	}
	if err := extendConfigFile(r, "Dropbox-api-token", ""); err != nil {
		return err
	}
	if err := extendConfigFile(r, "OB-API", a); err != nil {
		return err
	}
	if err := r.Close(); err != nil {
		return err
	}
	return nil
}

func createMnemonic() (string, error) {
	entropy, err := bip39.NewEntropy(128)
	if err != nil {
		return "", err
	}
	mnemonic, err := bip39.NewMnemonic(entropy)
	if err != nil {
		return "", err
	}
	return mnemonic, nil
}
<commit_msg>Add files subdirectory to root dir<commit_after>package repo

import (
	"errors"
	"fmt"
	"gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
	"io"
	"os"
	"path"

	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/namesys"
	"github.com/ipfs/go-ipfs/repo/fsrepo"
	"github.com/op/go-logging"
	"github.com/tyler-smith/go-bip39"
)

var log = logging.MustGetLogger("repo")

var ErrRepoExists = errors.New(`ipfs configuration file already exists!
Reinitializing would overwrite your keys.
(use -f to force overwrite)
`)

func DoInit(out io.Writer, repoRoot string, nBitsForKeypair int, testnet bool, password string, dbInit func(string, []byte, string) error) error {
	if err := maybeCreateOBDirectories(repoRoot); err != nil {
		return err
	}

	if fsrepo.IsInitialized(repoRoot) {
		return ErrRepoExists
	}

	if err := checkWriteable(repoRoot); err != nil {
		return err
	}

	conf, err := initConfig(out, repoRoot)
	if err != nil {
		return err
	}

	mnemonic, err := createMnemonic()
	if err != nil {
		return err
	}

	seed := bip39.NewSeed(mnemonic, "Secret Passphrase")
	fmt.Printf("generating %d-bit RSA keypair...", nBitsForKeypair)
	identityKey, err := ipfs.IdentityKeyFromSeed(seed, nBitsForKeypair)
	if err != nil {
		return err
	}
	fmt.Printf("done\n")

	identity, err := ipfs.IdentityFromKey(identityKey)
	if err != nil {
		return err
	}

	log.Infof("initializing openbazaar node at %s\n", repoRoot)
	if err := fsrepo.Init(repoRoot, conf); err != nil {
		return err
	}
	conf.Identity = identity

	if err := addConfigExtensions(repoRoot, testnet); err != nil {
		return err
	}

	if err := dbInit(mnemonic, identityKey, password); err != nil {
		return err
	}

	return initializeIpnsKeyspace(repoRoot, identityKey)
}

func maybeCreateOBDirectories(repoRoot string) error {
	if err := os.MkdirAll(path.Join(repoRoot, "root"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "listings"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "feed"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "channel"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "files"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "outbox"), os.ModePerm); err != nil {
		return err
	}
	return nil
}

func checkWriteable(dir string) error {
	_, err := os.Stat(dir)
	if err == nil {
		// dir exists, make sure we can write to it
		testfile := path.Join(dir, "test")
		fi, err := os.Create(testfile)
		if err != nil {
			if os.IsPermission(err) {
				return fmt.Errorf("%s is not writeable by the current user", dir)
			}
			return fmt.Errorf("unexpected error while checking writeablility of repo root: %s", err)
		}
		fi.Close()
		return os.Remove(testfile)
	}

	if os.IsNotExist(err) {
		// dir does not exist, check that we can create it
		return os.Mkdir(dir, 0775)
	}

	if os.IsPermission(err) {
		return fmt.Errorf("cannot write to %s, incorrect permissions", err)
	}

	return err
}

func initializeIpnsKeyspace(repoRoot string, privKeyBytes []byte) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	cfg, err := r.Config()
	if err != nil {
		log.Error(err)
		return err
	}
	identity, err := ipfs.IdentityFromKey(privKeyBytes)
	if err != nil {
		return err
	}

	cfg.Identity = identity
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
	if err != nil {
		return err
	}
	defer nd.Close()

	err = nd.SetupOfflineRouting()
	if err != nil {
		return err
	}

	return namesys.InitializeKeyspace(ctx, nd.DAG, nd.Namesys, nd.Pinning, nd.PrivateKey)
}

func addConfigExtensions(repoRoot string, testnet bool) error {
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	type Wallet struct {
		MaxFee           int
		FeeAPI           string
		HighFeeDefault   int
		MediumFeeDefault int
		LowFeeDefault    int
	}
	var w Wallet = Wallet{
		MaxFee:           2000,
		FeeAPI:           "https://bitcoinfees.21.co/api/v1/fees/recommended",
		HighFeeDefault:   60,
		MediumFeeDefault: 40,
		LowFeeDefault:    20,
	}
	type APIConfig struct {
		CORS        bool
		Enabled     bool
		HTTPHeaders map[string][]string
	}
	var a APIConfig = APIConfig{
		Enabled:     true,
		CORS:        false,
		HTTPHeaders: nil,
	}
	if err := extendConfigFile(r, "Wallet", w); err != nil {
		return err
	}
	if err := extendConfigFile(r, "Resolver", "https://resolver.onename.com/"); err != nil {
		return err
	}
	if err := extendConfigFile(r, "Dropbox-api-token", ""); err != nil {
		return err
	}
	if err := extendConfigFile(r, "OB-API", a); err != nil {
		return err
	}
	if err := r.Close(); err != nil {
		return err
	}
	return nil
}

func createMnemonic() (string, error) {
	entropy, err := bip39.NewEntropy(128)
	if err != nil {
		return "", err
	}
	mnemonic, err := bip39.NewMnemonic(entropy)
	if err != nil {
		return "", err
	}
	return mnemonic, nil
}
<|endoftext|>