<commit_before>// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 ecscni

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/aws/amazon-ecs-agent/agent/logger"
	"github.com/cihub/seelog"
	"github.com/containernetworking/cni/libcni"
	cnitypes "github.com/containernetworking/cni/pkg/types"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/pkg/errors"
)

const currentCNISpec = "0.3.1"

// CNIClient defines the method of setting/cleaning up container namespace
type CNIClient interface {
	// Version returns the version of the plugin
	Version(string) (string, error)
	// Capabilities returns the capabilities supported by a plugin
	Capabilities(string) ([]string, error)
	// SetupNS sets up the namespace of container
	SetupNS(*Config) (*current.Result, error)
	// CleanupNS cleans up the container namespace
	CleanupNS(*Config) error
	// ReleaseIPResource marks the ip available in the ipam db
	ReleaseIPResource(*Config) error
}

// cniClient is the client to call plugin and setup the network
type cniClient struct {
	pluginsPath string
	cniVersion  string
	subnet      string
	libcni      libcni.CNI
}

// NewClient creates a client of ecscni which is used to invoke the plugin
func NewClient(cfg *Config) CNIClient {
	libcniConfig := &libcni.CNIConfig{
		Path: []string{cfg.PluginsPath},
	}

	return &cniClient{
		pluginsPath: cfg.PluginsPath,
		cniVersion:  cfg.MinSupportedCNIVersion,
		subnet:      ecsSubnet,
		libcni:      libcniConfig,
	}
}

// SetupNS will set up the namespace of container, including create the bridge
// and the veth pair, move the eni to container namespace, setup the routes
func (client *cniClient) SetupNS(cfg *Config) (*current.Result, error) {
	runtimeConfig := libcni.RuntimeConf{
		ContainerID: cfg.ContainerID,
		NetNS:       fmt.Sprintf(netnsFormat, cfg.ContainerPID),
	}

	seelog.Debugf("Starting setup the ENI (%s) in container namespace: %s", cfg.ENIID, cfg.ContainerID)
	os.Setenv("ECS_CNI_LOGLEVEL", logger.GetLevel())
	defer os.Unsetenv("ECS_CNI_LOGLEVEL")

	result, err := client.eniAdd(runtimeConfig, cfg)
	if err != nil {
		return nil, errors.Wrap(err, "cni setup: invoke eni plugin failed")
	}
	seelog.Debugf("Ecscni: Set up eni done: %s", result.String())

	result, err = client.bridgeAdd(runtimeConfig, cfg)
	if err != nil {
		return nil, errors.Wrap(err, "cni setup: invoke bridge plugin failed")
	}
	seelog.Debugf("Ecscni: Set up container namespace done: %s", result.String())
	if _, err = result.GetAsVersion(currentCNISpec); err != nil {
		seelog.Warnf("Ecscni: Unable to convert result to spec version %s; error: %v; result is of version: %s",
			currentCNISpec, err, result.Version())
		return nil, err
	}
	var curResult *current.Result
	curResult, ok := result.(*current.Result)
	if !ok {
		return nil, errors.Errorf(
			"cni setup: unable to convert result to expected version '%s'",
			result.String())
	}

	return curResult, nil
}

// CleanupNS will clean up the container namespace, including remove the veth
// pair and stop the dhclient
func (client *cniClient) CleanupNS(cfg *Config) error {
	runtimeConfig := libcni.RuntimeConf{
		ContainerID: cfg.ContainerID,
		NetNS:       fmt.Sprintf(netnsFormat, cfg.ContainerPID),
	}

	os.Setenv("ECS_CNI_LOGLEVEL", logger.GetLevel())
	defer os.Unsetenv("ECS_CNI_LOGLEVEL")
	seelog.Debugf("Ecscni: Starting clean up the container namespace: %s", cfg.ContainerID)
	// clean up the network namespace is separate from releasing the IP from IPAM
	err := client.bridgeDel(runtimeConfig, cfg)
	if err != nil {
		return errors.Wrap(err, "cni cleanup: invoke bridge plugin failed")
	}
	seelog.Debugf("Ecscni: bridge cleanup done: %s", cfg.ContainerID)

	err = client.eniDel(runtimeConfig, cfg)
	if err != nil {
		return errors.Wrap(err, "cni cleanup: invoke eni plugin failed")
	}
	seelog.Debugf("Ecscni: container namespace cleanup done: %s", cfg.ContainerID)
	return nil
}

// ReleaseIPResource marks the ip available in the ipam db
func (client *cniClient) ReleaseIPResource(cfg *Config) error {
	runtimeConfig := &libcni.RuntimeConf{
		ContainerID: cfg.ContainerID,
		NetNS:       fmt.Sprintf(netnsFormat, cfg.ContainerPID),
		IfName:      defaultVethName,
	}

	ipamConfig, err := client.createIPAMNetworkConfig(cfg)
	if err != nil {
		return err
	}

	seelog.Debugf("Releasing the ip resource from ipam db, id: [%s], ip: [%v]", cfg.ID, cfg.IPAMV4Address)
	os.Setenv("ECS_CNI_LOGLEVEL", logger.GetLevel())
	defer os.Unsetenv("ECS_CNI_LOGLEVEL")
	return client.libcni.DelNetwork(ipamConfig, runtimeConfig)
}

// bridgeAdd invokes the bridge and ipam plugin to set up the bridge,
// also it sets the veth pair and route in container namespace
func (client *cniClient) bridgeAdd(runtimeConfig libcni.RuntimeConf, cfg *Config) (cnitypes.Result, error) {
	deviceName, networkConfig, err := client.createBridgeNetworkConfigWithIPAM(cfg)
	if err != nil {
		return nil, err
	}
	runtimeConfig.IfName = deviceName

	return client.libcni.AddNetwork(networkConfig, &runtimeConfig)
}

// eniAdd invokes the eni plugin to setup the eni in container namespace
func (client *cniClient) eniAdd(runtimeConfig libcni.RuntimeConf, cfg *Config) (cnitypes.Result, error) {
	deviceName, networkConfig, err := client.createENINetworkConfig(cfg)
	if err != nil {
		return nil, err
	}

	runtimeConfig.IfName = deviceName
	return client.libcni.AddNetwork(networkConfig, &runtimeConfig)
}

// bridgeDel invokes the bridge plugin to clean up the container namespace
func (client *cniClient) bridgeDel(runtimeConfig libcni.RuntimeConf, cfg *Config) error {
	deviceName, networkConfig, err := client.createBridgeNetworkConfigWithoutIPAM(cfg)
	if err != nil {
		return err
	}
	runtimeConfig.IfName = deviceName

	return client.libcni.DelNetwork(networkConfig, &runtimeConfig)
}

// eniDel invokes the eni plugin to clean up the eni in container namespace
func (client *cniClient) eniDel(runtimeConfig libcni.RuntimeConf, cfg *Config) error {
	deviceName, networkConfig, err := client.createENINetworkConfig(cfg)
	if err != nil {
		return err
	}

	runtimeConfig.IfName = deviceName
	return client.libcni.DelNetwork(networkConfig, &runtimeConfig)
}

// createBridgeNetworkConfigWithIPAM creates the config of bridge for ADD command, where
// bridge plugin acquires the IP and route information from IPAM
func (client *cniClient) createBridgeNetworkConfigWithIPAM(cfg *Config) (string, *libcni.NetworkConfig, error) {
	// Create the bridge config first
	bridgeConfig := client.createBridgeConfig(cfg)

	// Create the ipam config
	ipamConfig, err := client.createIPAMConfig(cfg)
	if err != nil {
		return "", nil, err
	}

	bridgeConfig.IPAM = ipamConfig

	networkConfig, err := client.constructNetworkConfig(bridgeConfig, ECSBridgePluginName)
	return defaultVethName, networkConfig, err
}

// createBridgeNetworkConfigWithoutIPAM creates the config of the bridge for removal
func (client *cniClient) createBridgeNetworkConfigWithoutIPAM(cfg *Config) (string, *libcni.NetworkConfig, error) {
	networkConfig, err := client.constructNetworkConfig(client.createBridgeConfig(cfg), ECSBridgePluginName)
	return defaultVethName, networkConfig, err
}

func (client *cniClient) createBridgeConfig(cfg *Config) BridgeConfig {
	bridgeName := defaultBridgeName
	if len(cfg.BridgeName) != 0 {
		bridgeName = cfg.BridgeName
	}

	bridgeConfig := BridgeConfig{
		Type:       ECSBridgePluginName,
		CNIVersion: client.cniVersion,
		BridgeName: bridgeName,
	}

	return bridgeConfig
}

// constructNetworkConfig takes in the config from agent and construct the configuration
// that's accepted by the libcni
func (client *cniClient) constructNetworkConfig(cfg interface{}, plugin string) (*libcni.NetworkConfig, error) {
	configBytes, err := json.Marshal(cfg)
	if err != nil {
		seelog.Errorf("Marshal configuration for plugin %s failed, error: %v", plugin, err)
		return nil, err
	}
	networkConfig := &libcni.NetworkConfig{
		Network: &cnitypes.NetConf{
			Type: plugin,
		},
		Bytes: configBytes,
	}

	return networkConfig, nil
}

func (client *cniClient) createENINetworkConfig(cfg *Config) (string, *libcni.NetworkConfig, error) {
	eniConf := ENIConfig{
		Type:                 ECSENIPluginName,
		CNIVersion:           client.cniVersion,
		ENIID:                cfg.ENIID,
		IPV4Address:          cfg.ENIIPV4Address,
		MACAddress:           cfg.ENIMACAddress,
		IPV6Address:          cfg.ENIIPV6Address,
		BlockInstanceMetdata: cfg.BlockInstanceMetdata,
	}
	networkConfig, err := client.constructNetworkConfig(eniConf, ECSENIPluginName)
	return defaultENIName, networkConfig, err
}

// createIPAMNetworkConfig constructs the ipam configuration accepted by libcni
func (client *cniClient) createIPAMNetworkConfig(cfg *Config) (*libcni.NetworkConfig, error) {
	ipamConfig, err := client.createIPAMConfig(cfg)
	if err != nil {
		return nil, err
	}

	ipamNetworkConfig := IPAMNetworkConfig{
		Name:       ECSIPAMPluginName,
		CNIVersion: client.cniVersion,
		IPAM:       ipamConfig,
	}
	return client.constructNetworkConfig(ipamNetworkConfig, ECSIPAMPluginName)
}

func (client *cniClient) createIPAMConfig(cfg *Config) (IPAMConfig, error) {
	_, dst, err := net.ParseCIDR(TaskIAMRoleEndpoint)
	if err != nil {
		return IPAMConfig{}, err
	}

	routes := []*cnitypes.Route{
		{
			Dst: *dst,
		},
	}
	for _, route := range cfg.AdditionalLocalRoutes {
		seelog.Debugf("Adding an additional route for %s", route)
		ipNetRoute := (net.IPNet)(route)
		routes = append(routes, &cnitypes.Route{Dst: ipNetRoute})
	}

	ipamConfig := IPAMConfig{
		Type:        ECSIPAMPluginName,
		CNIVersion:  client.cniVersion,
		IPV4Subnet:  client.subnet,
		IPV4Address: cfg.IPAMV4Address,
		ID:          cfg.ID,
		IPV4Routes:  routes,
	}

	return ipamConfig, nil
}

// Version returns the version of the plugin
func (client *cniClient) Version(name string) (string, error) {
	file := filepath.Join(client.pluginsPath, name)

	// Check if the plugin file exists before executing it
	_, err := os.Stat(file)
	if err != nil {
		return "", err
	}

	cmd := exec.Command(file, versionCommand)
	versionInfo, err := cmd.Output()
	if err != nil {
		return "", err
	}

	version := &cniPluginVersion{}
	// versionInfo is of the format
	// {"version":"2017.06.0","dirty":true,"gitShortHash":"226db36"}
	// Unmarshal this
	err = json.Unmarshal(versionInfo, version)
	if err != nil {
		return "", errors.Wrapf(err, "ecscni: unmarshal version from string: %s", versionInfo)
	}

	return version.str(), nil
}

// cniPluginVersion is used to convert the JSON output of the
// '--version' command into a string
type cniPluginVersion struct {
	Version string `json:"version"`
	Dirty   bool   `json:"dirty"`
	Hash    string `json:"gitShortHash"`
}

// str generates a string version of the CNI plugin version
// Example:
// {"version":"2017.06.0","dirty":true,"gitShortHash":"226db36"} => @226db36-2017.06.0
// {"version":"2017.06.0","dirty":false,"gitShortHash":"326db36"} => 326db36-2017.06.0
func (version *cniPluginVersion) str() string {
	ver := ""
	if version.Dirty {
		ver = "@"
	}
	return ver + version.Hash + "-" + version.Version
}

// Capabilities returns the capabilities supported by a plugin
func (client *cniClient) Capabilities(name string) ([]string, error) {
	file := filepath.Join(client.pluginsPath, name)

	// Check if the plugin file exists before executing it
	_, err := os.Stat(file)
	if err != nil {
		return nil, errors.Wrapf(err, "ecscni: unable to describe file info for '%s'", file)
	}

	cmd := exec.Command(file, capabilitiesCommand)
	capabilitiesInfo, err := cmd.Output()
	if err != nil {
		return nil, errors.Wrapf(err, "ecscni: failed invoking capabilities command for '%s'", name)
	}

	capabilities := &struct {
		Capabilities []string `json:"capabilities"`
	}{}
	err = json.Unmarshal(capabilitiesInfo, capabilities)
	if err != nil {
		return nil, errors.Wrapf(err, "ecscni: failed to unmarshal capabilities for '%s' from string: %s", name, capabilitiesInfo)
	}

	return capabilities.Capabilities, nil
}
<commit_msg>ecscni: unify the plugin add/del method<commit_after>// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 ecscni

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/aws/amazon-ecs-agent/agent/logger"
	"github.com/cihub/seelog"
	"github.com/containernetworking/cni/libcni"
	cnitypes "github.com/containernetworking/cni/pkg/types"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/pkg/errors"
)

const currentCNISpec = "0.3.1"

// CNIClient defines the method of setting/cleaning up container namespace
type CNIClient interface {
	// Version returns the version of the plugin
	Version(string) (string, error)
	// Capabilities returns the capabilities supported by a plugin
	Capabilities(string) ([]string, error)
	// SetupNS sets up the namespace of container
	SetupNS(*Config) (*current.Result, error)
	// CleanupNS cleans up the container namespace
	CleanupNS(*Config) error
	// ReleaseIPResource marks the ip available in the ipam db
	ReleaseIPResource(*Config) error
}

// cniClient is the client to call plugin and setup the network
type cniClient struct {
	pluginsPath string
	cniVersion  string
	subnet      string
	libcni      libcni.CNI
}

// NewClient creates a client of ecscni which is used to invoke the plugin
func NewClient(cfg *Config) CNIClient {
	libcniConfig := &libcni.CNIConfig{
		Path: []string{cfg.PluginsPath},
	}

	return &cniClient{
		pluginsPath: cfg.PluginsPath,
		cniVersion:  cfg.MinSupportedCNIVersion,
		subnet:      ecsSubnet,
		libcni:      libcniConfig,
	}
}

// SetupNS will set up the namespace of container, including create the bridge
// and the veth pair, move the eni to container namespace, setup the routes
func (client *cniClient) SetupNS(cfg *Config) (*current.Result, error) {
	runtimeConfig := libcni.RuntimeConf{
		ContainerID: cfg.ContainerID,
		NetNS:       fmt.Sprintf(netnsFormat, cfg.ContainerPID),
	}

	seelog.Debugf("Starting setup the ENI (%s) in container namespace: %s", cfg.ENIID, cfg.ContainerID)
	os.Setenv("ECS_CNI_LOGLEVEL", logger.GetLevel())
	defer os.Unsetenv("ECS_CNI_LOGLEVEL")

	// Invoke eni plugin ADD command
	result, err := client.add(runtimeConfig, cfg, client.createENINetworkConfig)
	if err != nil {
		return nil, errors.Wrap(err, "cni setup: invoke eni plugin failed")
	}
	seelog.Debugf("Ecscni: Set up eni done: %s", result.String())

	// Invoke bridge plugin ADD command
	result, err = client.add(runtimeConfig, cfg, client.createBridgeNetworkConfigWithIPAM)
	if err != nil {
		return nil, errors.Wrap(err, "cni setup: invoke bridge plugin failed")
	}
	seelog.Debugf("Ecscni: Set up container namespace done: %s", result.String())
	if _, err = result.GetAsVersion(currentCNISpec); err != nil {
		seelog.Warnf("Ecscni: Unable to convert result to spec version %s; error: %v; result is of version: %s",
			currentCNISpec, err, result.Version())
		return nil, err
	}
	var curResult *current.Result
	curResult, ok := result.(*current.Result)
	if !ok {
		return nil, errors.Errorf(
			"cni setup: unable to convert result to expected version '%s'",
			result.String())
	}

	return curResult, nil
}

// CleanupNS will clean up the container namespace, including remove the veth
// pair and stop the dhclient
func (client *cniClient) CleanupNS(cfg *Config) error {
	runtimeConfig := libcni.RuntimeConf{
		ContainerID: cfg.ContainerID,
		NetNS:       fmt.Sprintf(netnsFormat, cfg.ContainerPID),
	}

	os.Setenv("ECS_CNI_LOGLEVEL", logger.GetLevel())
	defer os.Unsetenv("ECS_CNI_LOGLEVEL")
	seelog.Debugf("Ecscni: Starting clean up the container namespace: %s", cfg.ContainerID)
	// clean up the network namespace is separate from releasing the IP from IPAM
	err := client.del(runtimeConfig, cfg, client.createBridgeNetworkConfigWithoutIPAM)
	if err != nil {
		return errors.Wrap(err, "cni cleanup: invoke bridge plugin failed")
	}
	seelog.Debugf("Ecscni: bridge cleanup done: %s", cfg.ContainerID)

	err = client.del(runtimeConfig, cfg, client.createENINetworkConfig)
	if err != nil {
		return errors.Wrap(err, "cni cleanup: invoke eni plugin failed")
	}
	seelog.Debugf("Ecscni: container namespace cleanup done: %s", cfg.ContainerID)
	return nil
}

// ReleaseIPResource marks the ip available in the ipam db
func (client *cniClient) ReleaseIPResource(cfg *Config) error {
	runtimeConfig := libcni.RuntimeConf{
		ContainerID: cfg.ContainerID,
		NetNS:       fmt.Sprintf(netnsFormat, cfg.ContainerPID),
	}

	seelog.Debugf("Releasing the ip resource from ipam db, id: [%s], ip: [%v]", cfg.ID, cfg.IPAMV4Address)
	os.Setenv("ECS_CNI_LOGLEVEL", logger.GetLevel())
	defer os.Unsetenv("ECS_CNI_LOGLEVEL")
	return client.del(runtimeConfig, cfg, client.createIPAMNetworkConfig)
}

// add invokes the ADD command of the given plugin
func (client *cniClient) add(runtimeConfig libcni.RuntimeConf,
	cfg *Config,
	pluginConfigFunc func(*Config) (string, *libcni.NetworkConfig, error)) (cnitypes.Result, error) {

	deviceName, networkConfig, err := pluginConfigFunc(cfg)
	if err != nil {
		return nil, err
	}
	runtimeConfig.IfName = deviceName

	return client.libcni.AddNetwork(networkConfig, &runtimeConfig)
}

// del invokes the DEL command of the given plugin
func (client *cniClient) del(runtimeConfig libcni.RuntimeConf,
	cfg *Config,
	pluginConfigFunc func(*Config) (string, *libcni.NetworkConfig, error)) error {

	deviceName, networkConfig, err := pluginConfigFunc(cfg)
	if err != nil {
		return err
	}
	runtimeConfig.IfName = deviceName

	return client.libcni.DelNetwork(networkConfig, &runtimeConfig)
}

// createBridgeNetworkConfigWithIPAM creates the config of bridge for ADD command, where
// bridge plugin acquires the IP and route information from IPAM
func (client *cniClient) createBridgeNetworkConfigWithIPAM(cfg *Config) (string, *libcni.NetworkConfig, error) {
	// Create the bridge config first
	bridgeConfig := client.createBridgeConfig(cfg)

	// Create the ipam config
	ipamConfig, err := client.createIPAMConfig(cfg)
	if err != nil {
		return "", nil, err
	}

	bridgeConfig.IPAM = ipamConfig

	networkConfig, err := client.constructNetworkConfig(bridgeConfig, ECSBridgePluginName)
	return defaultVethName, networkConfig, err
}

// createBridgeNetworkConfigWithoutIPAM creates the config of the bridge for removal
func (client *cniClient) createBridgeNetworkConfigWithoutIPAM(cfg *Config) (string, *libcni.NetworkConfig, error) {
	networkConfig, err := client.constructNetworkConfig(client.createBridgeConfig(cfg), ECSBridgePluginName)
	return defaultVethName, networkConfig, err
}

func (client *cniClient) createBridgeConfig(cfg *Config) BridgeConfig {
	bridgeName := defaultBridgeName
	if len(cfg.BridgeName) != 0 {
		bridgeName = cfg.BridgeName
	}

	bridgeConfig := BridgeConfig{
		Type:       ECSBridgePluginName,
		CNIVersion: client.cniVersion,
		BridgeName: bridgeName,
	}

	return bridgeConfig
}

// constructNetworkConfig takes in the config from agent and construct the configuration
// that's accepted by the libcni
func (client *cniClient) constructNetworkConfig(cfg interface{}, plugin string) (*libcni.NetworkConfig, error) {
	configBytes, err := json.Marshal(cfg)
	if err != nil {
		seelog.Errorf("Marshal configuration for plugin %s failed, error: %v", plugin, err)
		return nil, err
	}
	networkConfig := &libcni.NetworkConfig{
		Network: &cnitypes.NetConf{
			Type: plugin,
		},
		Bytes: configBytes,
	}

	return networkConfig, nil
}

func (client *cniClient) createENINetworkConfig(cfg *Config) (string, *libcni.NetworkConfig, error) {
	eniConf := ENIConfig{
		Type:                 ECSENIPluginName,
		CNIVersion:           client.cniVersion,
		ENIID:                cfg.ENIID,
		IPV4Address:          cfg.ENIIPV4Address,
		MACAddress:           cfg.ENIMACAddress,
		IPV6Address:          cfg.ENIIPV6Address,
		BlockInstanceMetdata: cfg.BlockInstanceMetdata,
	}
	networkConfig, err := client.constructNetworkConfig(eniConf, ECSENIPluginName)
	return defaultENIName, networkConfig, err
}

// createIPAMNetworkConfig constructs the ipam configuration accepted by libcni
func (client *cniClient) createIPAMNetworkConfig(cfg *Config) (string, *libcni.NetworkConfig, error) {
	ipamConfig, err := client.createIPAMConfig(cfg)
	if err != nil {
		return defaultVethName, nil, err
	}

	ipamNetworkConfig := IPAMNetworkConfig{
		Name:       ECSIPAMPluginName,
		CNIVersion: client.cniVersion,
		IPAM:       ipamConfig,
	}

	networkConfig, err := client.constructNetworkConfig(ipamNetworkConfig, ECSIPAMPluginName)
	return defaultVethName, networkConfig, err
}

func (client *cniClient) createIPAMConfig(cfg *Config) (IPAMConfig, error) {
	_, dst, err := net.ParseCIDR(TaskIAMRoleEndpoint)
	if err != nil {
		return IPAMConfig{}, err
	}

	routes := []*cnitypes.Route{
		{
			Dst: *dst,
		},
	}
	for _, route := range cfg.AdditionalLocalRoutes {
		seelog.Debugf("Adding an additional route for %s", route)
		ipNetRoute := (net.IPNet)(route)
		routes = append(routes, &cnitypes.Route{Dst: ipNetRoute})
	}

	ipamConfig := IPAMConfig{
		Type:        ECSIPAMPluginName,
		CNIVersion:  client.cniVersion,
		IPV4Subnet:  client.subnet,
		IPV4Address: cfg.IPAMV4Address,
		ID:          cfg.ID,
		IPV4Routes:  routes,
	}

	return ipamConfig, nil
}

// Version returns the version of the plugin
func (client *cniClient) Version(name string) (string, error) {
	file := filepath.Join(client.pluginsPath, name)

	// Check if the plugin file exists before executing it
	_, err := os.Stat(file)
	if err != nil {
		return "", err
	}

	cmd := exec.Command(file, versionCommand)
	versionInfo, err := cmd.Output()
	if err != nil {
		return "", err
	}

	version := &cniPluginVersion{}
	// versionInfo is of the format
	// {"version":"2017.06.0","dirty":true,"gitShortHash":"226db36"}
	// Unmarshal this
	err = json.Unmarshal(versionInfo, version)
	if err != nil {
		return "", errors.Wrapf(err, "ecscni: unmarshal version from string: %s", versionInfo)
	}

	return version.str(), nil
}

// cniPluginVersion is used to convert the JSON output of the
// '--version' command into a string
type cniPluginVersion struct {
	Version string `json:"version"`
	Dirty   bool   `json:"dirty"`
	Hash    string `json:"gitShortHash"`
}

// str generates a string version of the CNI plugin version
// Example:
// {"version":"2017.06.0","dirty":true,"gitShortHash":"226db36"} => @226db36-2017.06.0
// {"version":"2017.06.0","dirty":false,"gitShortHash":"326db36"} => 326db36-2017.06.0
func (version *cniPluginVersion) str() string {
	ver := ""
	if version.Dirty {
		ver = "@"
	}
	return ver + version.Hash + "-" + version.Version
}

// Capabilities returns the capabilities supported by a plugin
func (client *cniClient) Capabilities(name string) ([]string, error) {
	file := filepath.Join(client.pluginsPath, name)

	// Check if the plugin file exists before executing it
	_, err := os.Stat(file)
	if err != nil {
		return nil, errors.Wrapf(err, "ecscni: unable to describe file info for '%s'", file)
	}

	cmd := exec.Command(file, capabilitiesCommand)
	capabilitiesInfo, err := cmd.Output()
	if err != nil {
		return nil, errors.Wrapf(err, "ecscni: failed invoking capabilities command for '%s'", name)
	}

	capabilities := &struct {
		Capabilities []string `json:"capabilities"`
	}{}
	err = json.Unmarshal(capabilitiesInfo, capabilities)
	if err != nil {
		return nil, errors.Wrapf(err, "ecscni: failed to unmarshal capabilities for '%s' from string: %s", name, capabilitiesInfo)
	}

	return capabilities.Capabilities, nil
}
<|endoftext|>


<commit_before>//  Copyright (c) 2018 Cisco and/or its affiliates.
//
//  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 agent

import (
	"fmt"
	"os"
	"reflect"

	"github.com/ligato/cn-infra/infra"
	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/logging/logrus"
)

var agentLogger = logrus.NewLogger("agent")

func init() {
	if os.Getenv("DEBUG_INFRA") != "" {
		agentLogger.SetLevel(logging.DebugLevel)
		agentLogger.Debugf("agent debug logger enabled")
	}
}

func findPlugins(val reflect.Value, uniqueness map[infra.Plugin]struct{}, x ...int) (
	res []infra.Plugin, err error,
) {
	n := 0
	if len(x) > 0 {
		n = x[0]
	}
	var logf = func(f string, a ...interface{}) {
		for i := 0; i < n; i++ {
			f = "\t" + f
		}
		//agentLogger.Debugf(f, a...)
		if agentLogger.GetLevel() == logging.DebugLevel {
			fmt.Printf(f+"\n", a...)
		}
	}

	typ := val.Type()

	logf("=> %v (%v)", typ, typ.Kind())
	defer logf("== %v ", typ)

	if typ.Kind() == reflect.Interface {
		if val.IsNil() {
			logf(" - val is nil")
			return nil, nil
		}
		val = val.Elem()
		typ = val.Type()
		//logf(" - interface to elem: %v (%v)", typ, val.Kind())
	}

	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		//logrus.DefaultLogger().Debug(" - typ ptr kind: ", typ)
	}
	if val.Kind() == reflect.Ptr {
		val = val.Elem()
		//logrus.DefaultLogger().Debug(" - val ptr kind: ", val)
	}

	if !val.IsValid() {
		logf(" - val is invalid")
		return nil, nil
	}

	if typ.Kind() != reflect.Struct {
		logf(" - is not a struct: %v %v", typ.Kind(), val.Kind())
		return nil, nil
	}

	//logf(" -> checking %d fields", typ.NumField())

	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)

		// PkgPath is empty for exported fields
		if exported := field.PkgPath == ""; !exported {
			continue
		}

		fieldVal := val.Field(i)

		logf("-> field %d: %v - %v (%v)", i, field.Name, field.Type, fieldVal.Kind())

		var fieldPlug infra.Plugin

		plug, implementsPlugin := isFieldPlugin(field, fieldVal)
		if implementsPlugin {
			if plug == nil {
				logf(" - found nil plugin: %v", field.Name)
				continue
			}

			_, found := uniqueness[plug]
			if found {
				logf(" - found duplicate plugin: %v %v", field.Name, field.Type)
				continue
			}

			uniqueness[plug] = struct{}{}
			/*p, ok := plug.(core.PluginNamed)
			if !ok {
				p = core.NamePlugin(field.Name, plug)
			}*/
			fieldPlug = plug

			logf(" + FOUND PLUGIN: %v - %v (%v)", plug.String(), field.Name, field.Type)

			/*var pp core.Plugin = plug
			if np, ok := p.(*core.NamedPlugin); ok {
				pp = np.Plugin
			}*/
		}

		// do recursive inspection only for plugins and fields Deps
		if fieldPlug != nil || (field.Name == "Deps" && fieldVal.Kind() == reflect.Struct) {
			//var l []core.PluginNamed
			// try to inspect structure recursively
			l, err := findPlugins(fieldVal, uniqueness, n+1)
			if err != nil {
				logf(" - Bad field: %v %v", field.Name, err)
				continue
			}
			//logf(" - listed %v plugins from %v (%v)", len(l), field.Name, field.Type)
			res = append(res, l...)
		}

		if fieldPlug != nil {
			res = append(res, fieldPlug)
		}
	}

	logf("<- got %d plugins", len(res))

	return res, nil
}

var pluginType = reflect.TypeOf((*infra.Plugin)(nil)).Elem()

func isFieldPlugin(field reflect.StructField, fieldVal reflect.Value) (infra.Plugin, bool) {
	//logrus.DefaultLogger().Debugf(" - is field plugin: %v (%v) %v", field.Type, fieldVal.Kind(), fieldVal)

	switch fieldVal.Kind() {
	case reflect.Struct:
		ptrType := reflect.PtrTo(fieldVal.Type())
		if ptrType.Implements(pluginType) {
			if fieldVal.CanAddr() {
				if plug, ok := fieldVal.Addr().Interface().(infra.Plugin); ok {
					return plug, true
				}
			}
			return nil, true
		}
	case reflect.Ptr, reflect.Interface:
		if plug, ok := fieldVal.Interface().(infra.Plugin); ok {
			if fieldVal.IsNil() {
				return nil, true
			}
			return plug, true
		}
	}

	return nil, false
}
<commit_msg>Look for plugins in all embedded fields (not just those named Deps)<commit_after>//  Copyright (c) 2018 Cisco and/or its affiliates.
//
//  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 agent

import (
	"fmt"
	"os"
	"reflect"

	"github.com/ligato/cn-infra/infra"
	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/logging/logrus"
)

var agentLogger = logrus.NewLogger("infra")

func init() {
	if os.Getenv("DEBUG_INFRA") != "" {
		agentLogger.SetLevel(logging.DebugLevel)
		agentLogger.Debugf("agent debug logger enabled")
	}
}

func findPlugins(val reflect.Value, uniqueness map[infra.Plugin]struct{}, x ...int) (
	res []infra.Plugin, err error,
) {
	n := 0
	if len(x) > 0 {
		n = x[0]
	}
	var logf = func(f string, a ...interface{}) {
		for i := 0; i < n; i++ {
			f = "\t" + f
		}
		//agentLogger.Debugf(f, a...)
		if agentLogger.GetLevel() == logging.DebugLevel {
			fmt.Printf(f+"\n", a...)
		}
	}

	typ := val.Type()

	logf("=> %v (%v)", typ, typ.Kind())
	defer logf("== %v ", typ)

	if typ.Kind() == reflect.Interface {
		if val.IsNil() {
			logf(" - val is nil")
			return nil, nil
		}
		val = val.Elem()
		typ = val.Type()
		//logf(" - interface to elem: %v (%v)", typ, val.Kind())
	}

	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		//logrus.DefaultLogger().Debug(" - typ ptr kind: ", typ)
	}
	if val.Kind() == reflect.Ptr {
		val = val.Elem()
		//logrus.DefaultLogger().Debug(" - val ptr kind: ", val)
	}

	if !val.IsValid() {
		logf(" - val is invalid")
		return nil, nil
	}

	if typ.Kind() != reflect.Struct {
		logf(" - is not a struct: %v %v", typ.Kind(), val.Kind())
		return nil, nil
	}

	//logf(" -> checking %d fields", typ.NumField())

	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)

		// PkgPath is empty for exported fields
		if exported := field.PkgPath == ""; !exported {
			continue
		}

		fieldVal := val.Field(i)

		logf("-> field %d: %v - %v (%v)", i, field.Name, field.Type, fieldVal.Kind())

		var fieldPlug infra.Plugin

		plug, implementsPlugin := isFieldPlugin(field, fieldVal)
		if implementsPlugin {
			if plug == nil {
				logf(" - found nil plugin: %v", field.Name)
				continue
			}

			_, found := uniqueness[plug]
			if found {
				logf(" - found duplicate plugin: %v %v", field.Name, field.Type)
				continue
			}

			uniqueness[plug] = struct{}{}
			fieldPlug = plug

			logf(" + FOUND PLUGIN: %v - %v (%v)", plug.String(), field.Name, field.Type)
		}

		// do recursive inspection only for plugins and fields Deps
		if fieldPlug != nil || (field.Anonymous && fieldVal.Kind() == reflect.Struct) {
			// try to inspect structure recursively
			l, err := findPlugins(fieldVal, uniqueness, n+1)
			if err != nil {
				logf(" - Bad field: %v %v", field.Name, err)
				continue
			}
			//logf(" - listed %v plugins from %v (%v)", len(l), field.Name, field.Type)
			res = append(res, l...)
		}

		if fieldPlug != nil {
			res = append(res, fieldPlug)
		}
	}

	logf("<- got %d plugins", len(res))

	return res, nil
}

var pluginType = reflect.TypeOf((*infra.Plugin)(nil)).Elem()

func isFieldPlugin(field reflect.StructField, fieldVal reflect.Value) (infra.Plugin, bool) {
	//logrus.DefaultLogger().Debugf(" - is field plugin: %v (%v) %v", field.Type, fieldVal.Kind(), fieldVal)

	switch fieldVal.Kind() {
	case reflect.Struct:
		ptrType := reflect.PtrTo(fieldVal.Type())
		if ptrType.Implements(pluginType) {
			if fieldVal.CanAddr() {
				if plug, ok := fieldVal.Addr().Interface().(infra.Plugin); ok {
					return plug, true
				}
			}
			return nil, true
		}
	case reflect.Ptr, reflect.Interface:
		if plug, ok := fieldVal.Interface().(infra.Plugin); ok {
			if fieldVal.IsNil() {
				return nil, true
			}
			return plug, true
		}
	}

	return nil, false
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The websocket package implements Web Socket protocol server.
package websocket

// References:
//   The Web Socket protocol: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol

// TODO(ukai):
//   better logging.

import (
	"bufio";
	"io";
	"net";
	"os";
)

type WebSocketAddr string

func (addr WebSocketAddr) Network() string	{ return "websocket" }

func (addr WebSocketAddr) String() string	{ return string(addr) }

// Conn is an channels to communicate over Web Socket.
type Conn struct {
	// An origin URI of the Web Socket.
	Origin	string;
	// A location URI of the Web Socket.
	Location	string;
	// A subprotocol of the Web Socket.
	Protocol	string;

	buf	*bufio.ReadWriter;
	rwc	io.ReadWriteCloser;
}

// newConn creates a new Web Socket.
func newConn(origin, location, protocol string, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
	if buf == nil {
		br := bufio.NewReader(rwc);
		bw := bufio.NewWriter(rwc);
		buf = bufio.NewReadWriter(br, bw);
	}
	ws := &Conn{origin, location, protocol, buf, rwc};
	return ws;
}

func (ws *Conn) Read(msg []byte) (n int, err os.Error) {
	for {
		frameByte, err := ws.buf.ReadByte();
		if err != nil {
			return
		}
		if (frameByte & 0x80) == 0x80 {
			length := 0;
			for {
				c, err := ws.buf.ReadByte();
				if err != nil {
					return
				}
				if (c & 0x80) == 0x80 {
					length = length*128 + int(c&0x7f)
				} else {
					break
				}
			}
			for length > 0 {
				_, err := ws.buf.ReadByte();
				if err != nil {
					return
				}
				length--;
			}
		} else {
			for {
				c, err := ws.buf.ReadByte();
				if err != nil {
					return
				}
				if c == '\xff' {
					return
				}
				if frameByte == 0 {
					if n+1 <= cap(msg) {
						msg = msg[0 : n+1]
					}
					msg[n] = c;
					n++;
				}
				if n >= cap(msg) {
					err = os.E2BIG;
					return;
				}
			}
		}
	}
	return;
}

func (ws *Conn) Write(msg []byte) (n int, err os.Error) {
	ws.buf.WriteByte(0);
	ws.buf.Write(msg);
	ws.buf.WriteByte(0xff);
	err = ws.buf.Flush();
	return len(msg), err;
}

func (ws *Conn) Close() os.Error	{ return ws.rwc.Close() }

func (ws *Conn) LocalAddr() net.Addr	{ return WebSocketAddr(ws.Origin) }

func (ws *Conn) RemoteAddr() net.Addr	{ return WebSocketAddr(ws.Location) }

func (ws *Conn) SetTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetTimeout(nsec)
	}
	return os.EINVAL;
}

func (ws *Conn) SetReadTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetReadTimeout(nsec)
	}
	return os.EINVAL;
}

func (ws *Conn) SetWriteTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetWriteTimeout(nsec)
	}
	return os.EINVAL;
}

var _ net.Conn = (*Conn)(nil)	// compile-time check that *Conn implements net.Conn.
<commit_msg>Explicitly return values where it's shadowing the parameter.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The websocket package implements Web Socket protocol server.
package websocket

// References:
//   The Web Socket protocol: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol

// TODO(ukai):
//   better logging.

import (
	"bufio";
	"io";
	"net";
	"os";
)

type WebSocketAddr string

func (addr WebSocketAddr) Network() string	{ return "websocket" }

func (addr WebSocketAddr) String() string	{ return string(addr) }

// Conn is an channels to communicate over Web Socket.
type Conn struct {
	// An origin URI of the Web Socket.
	Origin	string;
	// A location URI of the Web Socket.
	Location	string;
	// A subprotocol of the Web Socket.
	Protocol	string;

	buf	*bufio.ReadWriter;
	rwc	io.ReadWriteCloser;
}

// newConn creates a new Web Socket.
func newConn(origin, location, protocol string, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
	if buf == nil {
		br := bufio.NewReader(rwc);
		bw := bufio.NewWriter(rwc);
		buf = bufio.NewReadWriter(br, bw);
	}
	ws := &Conn{origin, location, protocol, buf, rwc};
	return ws;
}

func (ws *Conn) Read(msg []byte) (n int, err os.Error) {
	for {
		frameByte, err := ws.buf.ReadByte();
		if err != nil {
			return n, err
		}
		if (frameByte & 0x80) == 0x80 {
			length := 0;
			for {
				c, err := ws.buf.ReadByte();
				if err != nil {
					return n, err
				}
				if (c & 0x80) == 0x80 {
					length = length*128 + int(c&0x7f)
				} else {
					break
				}
			}
			for length > 0 {
				_, err := ws.buf.ReadByte();
				if err != nil {
					return n, err
				}
				length--;
			}
		} else {
			for {
				c, err := ws.buf.ReadByte();
				if err != nil {
					return n, err
				}
				if c == '\xff' {
					return n, err
				}
				if frameByte == 0 {
					if n+1 <= cap(msg) {
						msg = msg[0 : n+1]
					}
					msg[n] = c;
					n++;
				}
				if n >= cap(msg) {
					return n, os.E2BIG
				}
			}
		}
	}

	panic("unreachable");
}

func (ws *Conn) Write(msg []byte) (n int, err os.Error) {
	ws.buf.WriteByte(0);
	ws.buf.Write(msg);
	ws.buf.WriteByte(0xff);
	err = ws.buf.Flush();
	return len(msg), err;
}

func (ws *Conn) Close() os.Error	{ return ws.rwc.Close() }

func (ws *Conn) LocalAddr() net.Addr	{ return WebSocketAddr(ws.Origin) }

func (ws *Conn) RemoteAddr() net.Addr	{ return WebSocketAddr(ws.Location) }

func (ws *Conn) SetTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetTimeout(nsec)
	}
	return os.EINVAL;
}

func (ws *Conn) SetReadTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetReadTimeout(nsec)
	}
	return os.EINVAL;
}

func (ws *Conn) SetWriteTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetWriteTimeout(nsec)
	}
	return os.EINVAL;
}

var _ net.Conn = (*Conn)(nil)	// compile-time check that *Conn implements net.Conn.
<|endoftext|>


<commit_before>package system

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

	"github.com/docker/docker/api"
	"github.com/docker/docker/api/server/httputils"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/events"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/registry"
	timetypes "github.com/docker/docker/api/types/time"
	"github.com/docker/docker/api/types/versions"
	"github.com/docker/docker/pkg/ioutils"
	pkgerrors "github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"golang.org/x/net/context"
)

func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	w.WriteHeader(http.StatusOK)
	return nil
}

func pingHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	_, err := w.Write([]byte{'O', 'K'})
	return err
}

func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	info, err := s.backend.SystemInfo()
	if err != nil {
		return err
	}
	if s.cluster != nil {
		info.Swarm = s.cluster.Info()
	}

	if versions.LessThan(httputils.VersionFromContext(ctx), "1.25") {
		// TODO: handle this conversion in engine-api
		type oldInfo struct {
			*types.Info
			ExecutionDriver string
		}
		old := &oldInfo{
			Info:            info,
			ExecutionDriver: "<not supported>",
		}
		nameOnlySecurityOptions := []string{}
		kvSecOpts, err := types.DecodeSecurityOptions(old.SecurityOptions)
		if err != nil {
			return err
		}
		for _, s := range kvSecOpts {
			nameOnlySecurityOptions = append(nameOnlySecurityOptions, s.Name)
		}
		old.SecurityOptions = nameOnlySecurityOptions
		return httputils.WriteJSON(w, http.StatusOK, old)
	}
	return httputils.WriteJSON(w, http.StatusOK, info)
}

func (s *systemRouter) getVersion(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	info := s.backend.SystemVersion()
	info.APIVersion = api.DefaultVersion

	return httputils.WriteJSON(w, http.StatusOK, info)
}

func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	du, err := s.backend.SystemDiskUsage(ctx)
	if err != nil {
		return err
	}
	builderSize, err := s.builder.DiskUsage()
	if err != nil {
		return pkgerrors.Wrap(err, "error getting build cache usage")
	}
	du.BuilderSize = builderSize

	return httputils.WriteJSON(w, http.StatusOK, du)
}

type invalidRequestError struct {
	Err error
}

func (e invalidRequestError) Error() string {
	return e.Err.Error()
}

func (e invalidRequestError) InvalidParameter() {}

func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	if err := httputils.ParseForm(r); err != nil {
		return err
	}

	since, err := eventTime(r.Form.Get("since"))
	if err != nil {
		return err
	}
	until, err := eventTime(r.Form.Get("until"))
	if err != nil {
		return err
	}

	var (
		timeout        <-chan time.Time
		onlyPastEvents bool
	)
	if !until.IsZero() {
		if until.Before(since) {
			return invalidRequestError{fmt.Errorf("`since` time (%s) cannot be after `until` time (%s)", r.Form.Get("since"), r.Form.Get("until"))}
		}

		now := time.Now()

		onlyPastEvents = until.Before(now)

		if !onlyPastEvents {
			dur := until.Sub(now)
			timeout = time.NewTimer(dur).C
		}
	}

	ef, err := filters.FromJSON(r.Form.Get("filters"))
	if err != nil {
		return err
	}

	w.Header().Set("Content-Type", "application/json")
	output := ioutils.NewWriteFlusher(w)
	defer output.Close()
	output.Flush()

	enc := json.NewEncoder(output)

	buffered, l := s.backend.SubscribeToEvents(since, until, ef)
	defer s.backend.UnsubscribeFromEvents(l)

	for _, ev := range buffered {
		if err := enc.Encode(ev); err != nil {
			return err
		}
	}

	if onlyPastEvents {
		return nil
	}

	for {
		select {
		case ev := <-l:
			jev, ok := ev.(events.Message)
			if !ok {
				logrus.Warnf("unexpected event message: %q", ev)
				continue
			}
			if err := enc.Encode(jev); err != nil {
				return err
			}
		case <-timeout:
			return nil
		case <-ctx.Done():
			logrus.Debug("Client context cancelled, stop sending events")
			return nil
		}
	}
}

func (s *systemRouter) postAuth(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	var config *types.AuthConfig
	err := json.NewDecoder(r.Body).Decode(&config)
	r.Body.Close()
	if err != nil {
		return err
	}
	status, token, err := s.backend.AuthenticateToRegistry(ctx, config)
	if err != nil {
		return err
	}
	return httputils.WriteJSON(w, http.StatusOK, &registry.AuthenticateOKBody{
		Status:        status,
		IdentityToken: token,
	})
}

func eventTime(formTime string) (time.Time, error) {
	t, tNano, err := timetypes.ParseTimestamps(formTime, -1)
	if err != nil {
		return time.Time{}, err
	}
	if t == -1 {
		return time.Time{}, nil
	}
	return time.Unix(t, tNano), nil
}
<commit_msg>Replace NewTimer().C with After<commit_after>package system

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

	"github.com/docker/docker/api"
	"github.com/docker/docker/api/server/httputils"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/events"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/registry"
	timetypes "github.com/docker/docker/api/types/time"
	"github.com/docker/docker/api/types/versions"
	"github.com/docker/docker/pkg/ioutils"
	pkgerrors "github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"golang.org/x/net/context"
)

func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	w.WriteHeader(http.StatusOK)
	return nil
}

func pingHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	_, err := w.Write([]byte{'O', 'K'})
	return err
}

func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	info, err := s.backend.SystemInfo()
	if err != nil {
		return err
	}
	if s.cluster != nil {
		info.Swarm = s.cluster.Info()
	}

	if versions.LessThan(httputils.VersionFromContext(ctx), "1.25") {
		// TODO: handle this conversion in engine-api
		type oldInfo struct {
			*types.Info
			ExecutionDriver string
		}
		old := &oldInfo{
			Info:            info,
			ExecutionDriver: "<not supported>",
		}
		nameOnlySecurityOptions := []string{}
		kvSecOpts, err := types.DecodeSecurityOptions(old.SecurityOptions)
		if err != nil {
			return err
		}
		for _, s := range kvSecOpts {
			nameOnlySecurityOptions = append(nameOnlySecurityOptions, s.Name)
		}
		old.SecurityOptions = nameOnlySecurityOptions
		return httputils.WriteJSON(w, http.StatusOK, old)
	}
	return httputils.WriteJSON(w, http.StatusOK, info)
}

func (s *systemRouter) getVersion(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	info := s.backend.SystemVersion()
	info.APIVersion = api.DefaultVersion

	return httputils.WriteJSON(w, http.StatusOK, info)
}

func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	du, err := s.backend.SystemDiskUsage(ctx)
	if err != nil {
		return err
	}
	builderSize, err := s.builder.DiskUsage()
	if err != nil {
		return pkgerrors.Wrap(err, "error getting build cache usage")
	}
	du.BuilderSize = builderSize

	return httputils.WriteJSON(w, http.StatusOK, du)
}

type invalidRequestError struct {
	Err error
}

func (e invalidRequestError) Error() string {
	return e.Err.Error()
}

func (e invalidRequestError) InvalidParameter() {}

func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	if err := httputils.ParseForm(r); err != nil {
		return err
	}

	since, err := eventTime(r.Form.Get("since"))
	if err != nil {
		return err
	}
	until, err := eventTime(r.Form.Get("until"))
	if err != nil {
		return err
	}

	var (
		timeout        <-chan time.Time
		onlyPastEvents bool
	)
	if !until.IsZero() {
		if until.Before(since) {
			return invalidRequestError{fmt.Errorf("`since` time (%s) cannot be after `until` time (%s)", r.Form.Get("since"), r.Form.Get("until"))}
		}

		now := time.Now()

		onlyPastEvents = until.Before(now)

		if !onlyPastEvents {
			dur := until.Sub(now)
			timeout = time.After(dur)
		}
	}

	ef, err := filters.FromJSON(r.Form.Get("filters"))
	if err != nil {
		return err
	}

	w.Header().Set("Content-Type", "application/json")
	output := ioutils.NewWriteFlusher(w)
	defer output.Close()
	output.Flush()

	enc := json.NewEncoder(output)

	buffered, l := s.backend.SubscribeToEvents(since, until, ef)
	defer s.backend.UnsubscribeFromEvents(l)

	for _, ev := range buffered {
		if err := enc.Encode(ev); err != nil {
			return err
		}
	}

	if onlyPastEvents {
		return nil
	}

	for {
		select {
		case ev := <-l:
			jev, ok := ev.(events.Message)
			if !ok {
				logrus.Warnf("unexpected event message: %q", ev)
				continue
			}
			if err := enc.Encode(jev); err != nil {
				return err
			}
		case <-timeout:
			return nil
		case <-ctx.Done():
			logrus.Debug("Client context cancelled, stop sending events")
			return nil
		}
	}
}

func (s *systemRouter) postAuth(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	var config *types.AuthConfig
	err := json.NewDecoder(r.Body).Decode(&config)
	r.Body.Close()
	if err != nil {
		return err
	}
	status, token, err := s.backend.AuthenticateToRegistry(ctx, config)
	if err != nil {
		return err
	}
	return httputils.WriteJSON(w, http.StatusOK, &registry.AuthenticateOKBody{
		Status:        status,
		IdentityToken: token,
	})
}

func eventTime(formTime string) (time.Time, error) {
	t, tNano, err := timetypes.ParseTimestamps(formTime, -1)
	if err != nil {
		return time.Time{}, err
	}
	if t == -1 {
		return time.Time{}, nil
	}
	return time.Unix(t, tNano), nil
}
<|endoftext|>


<commit_before>/*                          _       _
 *__      _____  __ ___   ___  __ _| |_ ___
 *\ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
 * \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
 *  \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
 *
 * Copyright © 2016 - 2018 Weaviate. All rights reserved.
 * LICENSE: https://github.com/creativesoftwarefdn/weaviate/blob/develop/LICENSE.md
 * AUTHOR: Bob van Luijt (bob@kub.design)
 * See www.creativesoftwarefdn.org for details
 * Contact: @CreativeSofwFdn / bob@kub.design
 */
package p2p

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/creativesoftwarefdn/weaviate/client"
	"github.com/creativesoftwarefdn/weaviate/client/graphql"
	networkGet "github.com/creativesoftwarefdn/weaviate/graphqlapi/network/get"
	"github.com/creativesoftwarefdn/weaviate/models"
)

// ProxyGetInstance proxies a single SubQuery to a single
// Target Instance. It is inteded to be called multiple
// times if you need to Network.Get from multiple instances.
func (n *network) ProxyGetInstance(params networkGet.ProxyGetInstanceParams) (*models.GraphQLResponse, error) {
	peer, err := n.GetPeerByName(params.TargetInstance)
	if err != nil {
		knownPeers, _ := n.ListPeers()
		return nil, fmt.Errorf("could not connect to %s: %s, known peers are %#v", params.TargetInstance, err, knownPeers)
	}

	peerClient, err := peer.CreateClient()
	if err != nil {
		return nil, fmt.Errorf("could not build client for peer %s: %s", peer.Name, err)
	}

	result, err := postToPeer(peerClient, params.SubQuery, params.Principal)
	if err != nil {
		return nil, fmt.Errorf("could post to peer %s: %s", peer.Name, err)
	}

	return result.Payload, nil
}

func postToPeer(client *client.WeaviateDecentralisedKnowledgeGraph, subQuery networkGet.SubQuery,
	principal *models.KeyTokenGetResponse) (*graphql.WeaviateGraphqlPostOK, error) {
	localContext := context.Background()
	localContext, _ = context.WithTimeout(localContext, 1*time.Second)
	requestParams := &graphql.WeaviateGraphqlPostParams{
		Body:       &models.GraphQLQuery{Query: subQuery.WrapInLocalQuery()},
		Context:    localContext,
		HTTPClient: clientWithTokenInjectorRoundTripper(principal),
	}
	return client.Graphql.WeaviateGraphqlPost(requestParams, nil)
}

type tokenInjectorRoundTripper struct {
	key   string
	token string
}

func (rt *tokenInjectorRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	req.Header.Set("X-API-KEY", rt.key)
	req.Header.Set("X-API-TOKEN", rt.token)
	return http.DefaultTransport.RoundTrip(req)
}

func clientWithTokenInjectorRoundTripper(principal *models.KeyTokenGetResponse) *http.Client {
	return &http.Client{
		Transport: &tokenInjectorRoundTripper{
			key:   string(principal.KeyID),
			token: string(principal.Token),
		},
	}
}
<commit_msg>gh-660: don't discard context cancel fn but call it deferred<commit_after>/*                          _       _
 *__      _____  __ ___   ___  __ _| |_ ___
 *\ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
 * \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
 *  \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
 *
 * Copyright © 2016 - 2018 Weaviate. All rights reserved.
 * LICENSE: https://github.com/creativesoftwarefdn/weaviate/blob/develop/LICENSE.md
 * AUTHOR: Bob van Luijt (bob@kub.design)
 * See www.creativesoftwarefdn.org for details
 * Contact: @CreativeSofwFdn / bob@kub.design
 */
package p2p

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/creativesoftwarefdn/weaviate/client"
	"github.com/creativesoftwarefdn/weaviate/client/graphql"
	networkGet "github.com/creativesoftwarefdn/weaviate/graphqlapi/network/get"
	"github.com/creativesoftwarefdn/weaviate/models"
)

// ProxyGetInstance proxies a single SubQuery to a single
// Target Instance. It is inteded to be called multiple
// times if you need to Network.Get from multiple instances.
func (n *network) ProxyGetInstance(params networkGet.ProxyGetInstanceParams) (*models.GraphQLResponse, error) {
	peer, err := n.GetPeerByName(params.TargetInstance)
	if err != nil {
		knownPeers, _ := n.ListPeers()
		return nil, fmt.Errorf("could not connect to %s: %s, known peers are %#v", params.TargetInstance, err, knownPeers)
	}

	peerClient, err := peer.CreateClient()
	if err != nil {
		return nil, fmt.Errorf("could not build client for peer %s: %s", peer.Name, err)
	}

	result, err := postToPeer(peerClient, params.SubQuery, params.Principal)
	if err != nil {
		return nil, fmt.Errorf("could post to peer %s: %s", peer.Name, err)
	}

	return result.Payload, nil
}

func postToPeer(client *client.WeaviateDecentralisedKnowledgeGraph, subQuery networkGet.SubQuery,
	principal *models.KeyTokenGetResponse) (*graphql.WeaviateGraphqlPostOK, error) {
	localContext := context.Background()
	localContext, cancel := context.WithTimeout(localContext, 1*time.Second)
	defer cancel()
	requestParams := &graphql.WeaviateGraphqlPostParams{
		Body:       &models.GraphQLQuery{Query: subQuery.WrapInLocalQuery()},
		Context:    localContext,
		HTTPClient: clientWithTokenInjectorRoundTripper(principal),
	}
	return client.Graphql.WeaviateGraphqlPost(requestParams, nil)
}

type tokenInjectorRoundTripper struct {
	key   string
	token string
}

func (rt *tokenInjectorRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	req.Header.Set("X-API-KEY", rt.key)
	req.Header.Set("X-API-TOKEN", rt.token)
	return http.DefaultTransport.RoundTrip(req)
}

func clientWithTokenInjectorRoundTripper(principal *models.KeyTokenGetResponse) *http.Client {
	return &http.Client{
		Transport: &tokenInjectorRoundTripper{
			key:   string(principal.KeyID),
			token: string(principal.Token),
		},
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Ben-Kuang. All rights reserved.
// Use of this source code is governed by The MIT License
// license that can be found in the LICENSE file.

/*
	Package provide method alloc task to Node server(base the Node execution speed)
*/
package scheduler

import (
	"distributed-task/gocommand"
	"distributed-task/gonet"
	"log"
	"strconv"
)

var nodeChan chan *Node

/*
	allocate data task with node performance
	//define the user collect data
*/
func AllocateData(method string, commandType string, data map[string]string, userCollect func(msg string)) {
	loadNodeChan()
	switch commandType {
	case gocommand.TypeSequence:
		//TO-DO performance
		count := 0
		avg := len(data) / len(NodeConfig)
		tempMap := make(map[string]string)
		for key, value := range data {
			tempMap[key] = value
			count++
			// log.Printf("datalength: %s \n", len(tempMap))
			if len(tempMap) >= avg {
				command := &gocommand.Command{method, commandType, tempMap}
				commandString := command.GetCommandString()
				content := gocommand.EnCode(commandString)
				sendNode(content, userCollect)
				// log.Printf("datalength1: %s \n", len(tempMapOther))
				tempMap = make(map[string]string)
				// log.Printf("datalength2: %s \n", len(tempMap))
			} else {
				if count >= len(data) {
					//at the end
					command := &gocommand.Command{method, commandType, tempMap}
					commandString := command.GetCommandString()
					content := gocommand.EnCode(commandString)
					sendNode(content, userCollect)
				}
			}
		}

	case gocommand.TypeStartEnd:
		//TO-DO performance

		start, err := strconv.ParseInt(data["start"], 10, 64)
		if err != nil {
			log.Printf("scheduler, execute start end at start type wrong: %v\n", err)
		}
		end, err1 := strconv.ParseInt(data["end"], 10, 64)
		if err1 != nil {
			log.Printf("scheduler, execute start end at end type wrong: %v\n", err)
		}

		avg := (end - start) / int64(len(NodeConfig))
		tempMap := make(map[string]string)

		for current := int64(start); current <= end; current = current + int64(avg) {
			tempMap["start"] = strconv.FormatInt(current, 10)
			tempEnd := current + int64(avg) - 1
			if tempEnd > end {
				tempEnd = end
			}
			tempMap["end"] = strconv.FormatInt(tempEnd, 10)
			command := &gocommand.Command{method, commandType, tempMap}
			commandString := command.GetCommandString()
			content := gocommand.EnCode(commandString)
			sendNode(content, userCollect)
			tempMap = make(map[string]string)
		}
	}
}

/*
	load the nodeconfig in the nodechan sequence
*/
func loadNodeChan() {
	nodeChan = make(chan *Node, len(NodeConfig))
	for _, config := range NodeConfig {
		nodeChan <- config
	}
}

func sendNode(content string, userCollect func(msg string)) {
	node := <-nodeChan
	log.Printf("send msg to node:%s with message: %s\n", node.Config["NodeAddr"], content)
	msg := gonet.Message{node.Config["NodeAddr"], content}
	gonet.Send(msg, userCollect)
	nodeChan <- node
}

/*
	user define the task data and task function name
*/
// func Scheduler(){
// 	var taskData[string]string
// 	var taskType string
// 	var methodName string
// 	//user define data

// 	//
// 	allocateData(methodName, taskType, taskData)
// }
<commit_msg>fix avg<commit_after>// Copyright 2014 Ben-Kuang. All rights reserved.
// Use of this source code is governed by The MIT License
// license that can be found in the LICENSE file.

/*
	Package provide method alloc task to Node server(base the Node execution speed)
*/
package scheduler

import (
	"distributed-task/gocommand"
	"distributed-task/gonet"
	"log"
	"strconv"
)

var nodeChan chan *Node

/*
	allocate data task with node performance
	//define the user collect data
*/
func AllocateData(method string, commandType string, data map[string]string, userCollect func(msg string)) {
	loadNodeChan()
	switch commandType {
	case gocommand.TypeSequence:
		//TO-DO performance
		count := 0
		avg := len(data) / len(NodeConfig)
		tempMap := make(map[string]string)
		for key, value := range data {
			tempMap[key] = value
			count++
			// log.Printf("datalength: %s \n", len(tempMap))
			if len(tempMap) >= avg {
				command := &gocommand.Command{method, commandType, tempMap}
				commandString := command.GetCommandString()
				content := gocommand.EnCode(commandString)
				sendNode(content, userCollect)
				// log.Printf("datalength1: %s \n", len(tempMapOther))
				tempMap = make(map[string]string)
				// log.Printf("datalength2: %s \n", len(tempMap))
			} else {
				if count >= len(data) {
					//at the end
					command := &gocommand.Command{method, commandType, tempMap}
					commandString := command.GetCommandString()
					content := gocommand.EnCode(commandString)
					sendNode(content, userCollect)
				}
			}
		}

	case gocommand.TypeStartEnd:
		//TO-DO performance

		start, err := strconv.ParseInt(data["start"], 10, 64)
		if err != nil {
			log.Printf("scheduler, execute start end at start type wrong: %v\n", err)
		}
		end, err1 := strconv.ParseInt(data["end"], 10, 64)
		if err1 != nil {
			log.Printf("scheduler, execute start end at end type wrong: %v\n", err)
		}

		avg := (end - start) / int64(len(NodeConfig))
		tempMap := make(map[string]string)

		for current := int64(start); current <= end; current = current + int64(avg) + 1 {
			tempMap["start"] = strconv.FormatInt(current, 10)
			tempEnd := current + int64(avg)
			if tempEnd > end {
				tempEnd = end
			}
			tempMap["end"] = strconv.FormatInt(tempEnd, 10)
			command := &gocommand.Command{method, commandType, tempMap}
			commandString := command.GetCommandString()
			content := gocommand.EnCode(commandString)
			sendNode(content, userCollect)
			tempMap = make(map[string]string)
		}
	}
}

/*
	load the nodeconfig in the nodechan sequence
*/
func loadNodeChan() {
	nodeChan = make(chan *Node, len(NodeConfig))
	for _, config := range NodeConfig {
		nodeChan <- config
	}
}

func sendNode(content string, userCollect func(msg string)) {
	node := <-nodeChan
	log.Printf("send msg to node:%s with message: %s\n", node.Config["NodeAddr"], content)
	msg := gonet.Message{node.Config["NodeAddr"], content}
	gonet.Send(msg, userCollect)
	nodeChan <- node
}

/*
	user define the task data and task function name
*/
// func Scheduler(){
// 	var taskData[string]string
// 	var taskType string
// 	var methodName string
// 	//user define data

// 	//
// 	allocateData(methodName, taskType, taskData)
// }
<|endoftext|>


<commit_before>package main

import "github.com/andlabs/ui"

var window ui.Window

func main() {
  go ui.Do(func() {
    name := ui.NewTextField()
    button := ui.NewButton("greet")
    greeting := ui.NewLabel("")

    stack := ui.NewVerticalStack(
      ui.NewLabel("name:"),
      name,
      button,
      greeting)

    window = ui.NewWindow("hello world", 200, 100, stack)

    button.OnClicked(func() {
      greeting.SetText("hello, " + name.Text() + "!")
    })

    window.OnClosing(func() bool {
      ui.Stop()
      return true
    })

    window.Show()
  })

  err := ui.Go()
  if err != nil {
    panic(err)
  }
}
<commit_msg>go fmt gui-test.go<commit_after>package main

import "github.com/andlabs/ui"

var window ui.Window

func main() {
	go ui.Do(func() {
		name := ui.NewTextField()
		button := ui.NewButton("greet")
		greeting := ui.NewLabel("")

		stack := ui.NewVerticalStack(
			ui.NewLabel("name:"),
			name,
			button,
			greeting)

		window = ui.NewWindow("hello world", 200, 100, stack)

		button.OnClicked(func() {
			greeting.SetText("hello, " + name.Text() + "!")
		})

		window.OnClosing(func() bool {
			ui.Stop()
			return true
		})

		window.Show()
	})

	err := ui.Go()
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package scheduler

import (
	"fmt"
	"sync"

	"github.com/docker/swarm/cluster"
	"github.com/docker/swarm/scheduler/filter"
	"github.com/docker/swarm/scheduler/strategy"
	"github.com/samalba/dockerclient"
)

type Scheduler struct {
	sync.Mutex

	cluster  *cluster.Cluster
	strategy strategy.PlacementStrategy
	filters  []filter.Filter
}

func NewScheduler(cluster *cluster.Cluster, strategy strategy.PlacementStrategy, filters []filter.Filter) *Scheduler {
	return &Scheduler{
		cluster:  cluster,
		strategy: strategy,
		filters:  filters,
	}
}

// Find a nice home for our container.
func (s *Scheduler) selectNodeForContainer(config *dockerclient.ContainerConfig) (*cluster.Node, error) {
	candidates := []*cluster.Node{}
	for _, node := range s.cluster.Nodes() {
		candidates = append(candidates, node)
	}

	accepted, err := filter.ApplyFilters(s.filters, config, candidates)
	if err != nil {
		return nil, err
	}

	return s.strategy.PlaceContainer(config, accepted)
}

// Schedule a brand new container into the cluster.
func (s *Scheduler) CreateContainer(config *dockerclient.ContainerConfig, name string) (*cluster.Container, error) {
	if config.Memory == 0 || config.CpuShares == 0 {
		return nil, fmt.Errorf("Creating containers in clustering mode requires resource constraints (-c and -m) to be set")
	}

	s.Lock()
	defer s.Unlock()

	node, err := s.selectNodeForContainer(config)
	if err != nil {
		return nil, err
	}
	return node.Create(config, name, true)
}

// Remove a container from the cluster. Containers should always be destroyed
// through the scheduler to guarantee atomicity.
func (s *Scheduler) RemoveContainer(container *cluster.Container, force bool) error {
	s.Lock()
	defer s.Unlock()

	return container.Node().Remove(container, force)
}
<commit_msg>re-enable without -m/-c<commit_after>package scheduler

import (
	"sync"

	"github.com/docker/swarm/cluster"
	"github.com/docker/swarm/scheduler/filter"
	"github.com/docker/swarm/scheduler/strategy"
	"github.com/samalba/dockerclient"
)

type Scheduler struct {
	sync.Mutex

	cluster  *cluster.Cluster
	strategy strategy.PlacementStrategy
	filters  []filter.Filter
}

func NewScheduler(cluster *cluster.Cluster, strategy strategy.PlacementStrategy, filters []filter.Filter) *Scheduler {
	return &Scheduler{
		cluster:  cluster,
		strategy: strategy,
		filters:  filters,
	}
}

// Find a nice home for our container.
func (s *Scheduler) selectNodeForContainer(config *dockerclient.ContainerConfig) (*cluster.Node, error) {
	candidates := []*cluster.Node{}
	for _, node := range s.cluster.Nodes() {
		candidates = append(candidates, node)
	}

	accepted, err := filter.ApplyFilters(s.filters, config, candidates)
	if err != nil {
		return nil, err
	}

	return s.strategy.PlaceContainer(config, accepted)
}

// Schedule a brand new container into the cluster.
func (s *Scheduler) CreateContainer(config *dockerclient.ContainerConfig, name string) (*cluster.Container, error) {
	/*Disable for now
	if config.Memory == 0 || config.CpuShares == 0 {
		return nil, fmt.Errorf("Creating containers in clustering mode requires resource constraints (-c and -m) to be set")
	}
	*/

	s.Lock()
	defer s.Unlock()

	node, err := s.selectNodeForContainer(config)
	if err != nil {
		return nil, err
	}
	return node.Create(config, name, true)
}

// Remove a container from the cluster. Containers should always be destroyed
// through the scheduler to guarantee atomicity.
func (s *Scheduler) RemoveContainer(container *cluster.Container, force bool) error {
	s.Lock()
	defer s.Unlock()

	return container.Node().Remove(container, force)
}
<|endoftext|>


<commit_before>package sensurer_test

import (
	"database/sql"
	"fmt"
	"io/ioutil"
	"log"
	"reflect"
	"testing"

	_ "code.google.com/p/gosqlite/sqlite3"
	"github.com/modcloth/schema_ensurer"
)

type columnSchema struct {
	Name     string
	DataType string
	Nullable int
	Default  sql.NullString
}

var tests = []struct {
	migrations map[string][]string
	schema     map[string]columnSchema
}{
	{
		map[string][]string{
			"20120505000000": {`
	  CREATE TABLE IF NOT EXISTS some_table(
      some_number INTEGER
	  );
	  `,
				`ALTER TABLE some_table ADD COLUMN some_text TEXT`,
			},
			"20130725000000": {
				`ALTER TABLE some_table ADD COLUMN some_real REAL`,
			},
		},
		map[string]columnSchema{
			"some_real": columnSchema{
				Name:     "some_real",
				DataType: "REAL",
				Nullable: 0,
				Default:  sql.NullString{Valid: false},
			},
			"some_number": columnSchema{
				Name:     "some_number",
				DataType: "INTEGER",
				Nullable: 0,
				Default:  sql.NullString{Valid: false},
			},
			"some_text": columnSchema{
				Name:     "some_text",
				DataType: "TEXT",
				Nullable: 0,
				Default:  sql.NullString{Valid: false},
			},
		},
	},
}

var nullLogger = log.New(ioutil.Discard, "", 0)

func tableSchema(db *sql.DB, table string) (columnSchemas map[string]columnSchema, err error) {
	ignore := []byte{}
	columnSchemas = make(map[string]columnSchema)

	rows, err := db.Query(fmt.Sprintf("PRAGMA table_info(%s)", table))
	if err != nil {
		return nil, err
	}

	for rows.Next() {
		p := columnSchema{}

		if err := rows.Scan(&ignore, &p.Name, &p.DataType, &p.Nullable, &p.Default, &ignore); err != nil {
			return nil, err
		}

		columnSchemas[p.Name] = p
	}

	return columnSchemas, nil
}

func TestEnsureSchema(t *testing.T) {
	var (
		db              *sql.DB
		generatedSchema map[string]columnSchema
		err             error
	)

	for i, tt := range tests {
		if db, err = sql.Open("sqlite3", ":memory:"); err != nil {
			t.Fatal(err)
		}

		schemaEnsurer := sensurer.New(db, tt.migrations, nullLogger)

		schemaEnsurer.EnsureSchema()

		if generatedSchema, err = tableSchema(db, "some_table"); err != nil {
			t.Fatal(err)
		}

		if !reflect.DeepEqual(generatedSchema, tt.schema) {
			t.Errorf("%d. Expected to get %+v, but got %+v\n", i, tt.schema, generatedSchema)
		}
	}
}
<commit_msg>Fixing import path in test to refer to modcloth-labs<commit_after>package sensurer_test

import (
	"database/sql"
	"fmt"
	"io/ioutil"
	"log"
	"reflect"
	"testing"

	_ "code.google.com/p/gosqlite/sqlite3"
	"github.com/modcloth-labs/schema_ensurer"
)

type columnSchema struct {
	Name     string
	DataType string
	Nullable int
	Default  sql.NullString
}

var tests = []struct {
	migrations map[string][]string
	schema     map[string]columnSchema
}{
	{
		map[string][]string{
			"20120505000000": {`
	  CREATE TABLE IF NOT EXISTS some_table(
      some_number INTEGER
	  );
	  `,
				`ALTER TABLE some_table ADD COLUMN some_text TEXT`,
			},
			"20130725000000": {
				`ALTER TABLE some_table ADD COLUMN some_real REAL`,
			},
		},
		map[string]columnSchema{
			"some_real": columnSchema{
				Name:     "some_real",
				DataType: "REAL",
				Nullable: 0,
				Default:  sql.NullString{Valid: false},
			},
			"some_number": columnSchema{
				Name:     "some_number",
				DataType: "INTEGER",
				Nullable: 0,
				Default:  sql.NullString{Valid: false},
			},
			"some_text": columnSchema{
				Name:     "some_text",
				DataType: "TEXT",
				Nullable: 0,
				Default:  sql.NullString{Valid: false},
			},
		},
	},
}

var nullLogger = log.New(ioutil.Discard, "", 0)

func tableSchema(db *sql.DB, table string) (columnSchemas map[string]columnSchema, err error) {
	ignore := []byte{}
	columnSchemas = make(map[string]columnSchema)

	rows, err := db.Query(fmt.Sprintf("PRAGMA table_info(%s)", table))
	if err != nil {
		return nil, err
	}

	for rows.Next() {
		p := columnSchema{}

		if err := rows.Scan(&ignore, &p.Name, &p.DataType, &p.Nullable, &p.Default, &ignore); err != nil {
			return nil, err
		}

		columnSchemas[p.Name] = p
	}

	return columnSchemas, nil
}

func TestEnsureSchema(t *testing.T) {
	var (
		db              *sql.DB
		generatedSchema map[string]columnSchema
		err             error
	)

	for i, tt := range tests {
		if db, err = sql.Open("sqlite3", ":memory:"); err != nil {
			t.Fatal(err)
		}

		schemaEnsurer := sensurer.New(db, tt.migrations, nullLogger)

		schemaEnsurer.EnsureSchema()

		if generatedSchema, err = tableSchema(db, "some_table"); err != nil {
			t.Fatal(err)
		}

		if !reflect.DeepEqual(generatedSchema, tt.schema) {
			t.Errorf("%d. Expected to get %+v, but got %+v\n", i, tt.schema, generatedSchema)
		}
	}
}
<|endoftext|>


<commit_before>package api

// InstanceStatePut represents the modifiable fields of a LXD instance's state.
//
// API extension: instances
type InstanceStatePut struct {
	Action   string `json:"action" yaml:"action"`
	Timeout  int    `json:"timeout" yaml:"timeout"`
	Force    bool   `json:"force" yaml:"force"`
	Stateful bool   `json:"stateful" yaml:"stateful"`
}

// InstanceState represents a LXD instance's state.
//
// API extension: instances
type InstanceState struct {
	Status     string                          `json:"status" yaml:"status"`
	StatusCode StatusCode                      `json:"status_code" yaml:"status_code"`
	Disk       map[string]InstanceStateDisk    `json:"disk" yaml:"disk"`
	Memory     InstanceStateMemory             `json:"memory" yaml:"memory"`
	Network    map[string]InstanceStateNetwork `json:"network" yaml:"network"`
	Pid        int64                           `json:"pid" yaml:"pid"`
	Processes  int64                           `json:"processes" yaml:"processes"`
	CPU        InstanceStateCPU                `json:"cpu" yaml:"cpu"`
}

// InstanceStateDisk represents the disk information section of a LXD instance's state.
//
// API extension: instances
type InstanceStateDisk struct {
	Usage int64 `json:"usage" yaml:"usage"`
}

// InstanceStateCPU represents the cpu information section of a LXD instance's state.
//
// API extension: instances
type InstanceStateCPU struct {
	Usage int64 `json:"usage" yaml:"usage"`
}

// InstanceStateMemory represents the memory information section of a LXD instance's state.
//
// API extension: instances
type InstanceStateMemory struct {
	Usage         int64 `json:"usage" yaml:"usage"`
	UsagePeak     int64 `json:"usage_peak" yaml:"usage_peak"`
	SwapUsage     int64 `json:"swap_usage" yaml:"swap_usage"`
	SwapUsagePeak int64 `json:"swap_usage_peak" yaml:"swap_usage_peak"`
}

// InstanceStateNetwork represents the network information section of a LXD instance's state.
//
// API extension: instances
type InstanceStateNetwork struct {
	Addresses []InstanceStateNetworkAddress `json:"addresses" yaml:"addresses"`
	Counters  InstanceStateNetworkCounters  `json:"counters" yaml:"counters"`
	Hwaddr    string                        `json:"hwaddr" yaml:"hwaddr"`
	HostName  string                        `json:"host_name" yaml:"host_name"`
	Mtu       int                           `json:"mtu" yaml:"mtu"`
	State     string                        `json:"state" yaml:"state"`
	Type      string                        `json:"type" yaml:"type"`
}

// InstanceStateNetworkAddress represents a network address as part of the network section of a LXD
// instance's state.
//
// API extension: instances
type InstanceStateNetworkAddress struct {
	Family  string `json:"family" yaml:"family"`
	Address string `json:"address" yaml:"address"`
	Netmask string `json:"netmask" yaml:"netmask"`
	Scope   string `json:"scope" yaml:"scope"`
}

// InstanceStateNetworkCounters represents packet counters as part of the network section of a LXD
// instance's state.
//
// API extension: instances
type InstanceStateNetworkCounters struct {
	BytesReceived   int64 `json:"bytes_received" yaml:"bytes_received"`
	BytesSent       int64 `json:"bytes_sent" yaml:"bytes_sent"`
	PacketsReceived int64 `json:"packets_received" yaml:"packets_received"`
	PacketsSent     int64 `json:"packets_sent" yaml:"packets_sent"`
}
<commit_msg>shared/api: Add swagger metadata for instance state<commit_after>package api

// InstanceStatePut represents the modifiable fields of a LXD instance's state.
//
// swagger:model
//
// API extension: instances
type InstanceStatePut struct {
	// State change action (start, stop, restart, freeze, unfreeze)
	// Example: start
	Action string `json:"action" yaml:"action"`

	// How long to wait (in s) before giving up (when force isn't set)
	// Example: 30
	Timeout int `json:"timeout" yaml:"timeout"`

	// Whether to force the action (for stop and restart)
	// Example: false
	Force bool `json:"force" yaml:"force"`

	// Whether to store the runtime state (for stop)
	// Example: false
	Stateful bool `json:"stateful" yaml:"stateful"`
}

// InstanceState represents a LXD instance's state.
//
// swagger:model
//
// API extension: instances
type InstanceState struct {
	// Current status (Running, Stopped, Frozen or Error)
	// Example: Running
	Status string `json:"status" yaml:"status"`

	// Numeric status code (101, 102, 110, 112)
	// Example: 101
	StatusCode StatusCode `json:"status_code" yaml:"status_code"`

	// Dict of disk usage
	Disk map[string]InstanceStateDisk `json:"disk" yaml:"disk"`

	// Memory usage information
	Memory InstanceStateMemory `json:"memory" yaml:"memory"`

	// Dict of network usage
	Network map[string]InstanceStateNetwork `json:"network" yaml:"network"`

	// PID of the runtime
	// Example: 7281
	Pid int64 `json:"pid" yaml:"pid"`

	// Number of processes in the instance
	// Example: 50
	Processes int64 `json:"processes" yaml:"processes"`

	// CPU usage information
	CPU InstanceStateCPU `json:"cpu" yaml:"cpu"`
}

// InstanceStateDisk represents the disk information section of a LXD instance's state.
//
// swagger:model
//
// API extension: instances
type InstanceStateDisk struct {
	// Disk usage in bytes
	// Example: 502239232
	Usage int64 `json:"usage" yaml:"usage"`
}

// InstanceStateCPU represents the cpu information section of a LXD instance's state.
//
// swagger:model
//
// API extension: instances
type InstanceStateCPU struct {
	// CPU usage in nanoseconds
	// Example: 3637691016
	Usage int64 `json:"usage" yaml:"usage"`
}

// InstanceStateMemory represents the memory information section of a LXD instance's state.
//
// swagger:model
//
// API extension: instances
type InstanceStateMemory struct {
	// Memory usage in bytes
	// Example: 73248768
	Usage int64 `json:"usage" yaml:"usage"`

	// Peak memory usage in bytes
	// Example: 73785344
	UsagePeak int64 `json:"usage_peak" yaml:"usage_peak"`

	// SWAP usage in bytes
	// Example: 12297557
	SwapUsage int64 `json:"swap_usage" yaml:"swap_usage"`

	// Peak SWAP usage in bytes
	// Example: 12297557
	SwapUsagePeak int64 `json:"swap_usage_peak" yaml:"swap_usage_peak"`
}

// InstanceStateNetwork represents the network information section of a LXD instance's state.
//
// swagger:model
//
// API extension: instances
type InstanceStateNetwork struct {
	// List of IP addresses
	Addresses []InstanceStateNetworkAddress `json:"addresses" yaml:"addresses"`

	// Traffic counters
	Counters InstanceStateNetworkCounters `json:"counters" yaml:"counters"`

	// MAC address
	// Example: 00:16:3e:0c:ee:dd
	Hwaddr string `json:"hwaddr" yaml:"hwaddr"`

	// Name of the interface on the host
	// Example: vethbbcd39c7
	HostName string `json:"host_name" yaml:"host_name"`

	// MTU (maximum transmit unit) for the interface
	// Example: 1500
	Mtu int `json:"mtu" yaml:"mtu"`

	// Administrative state of the interface (up/down)
	// Example: up
	State string `json:"state" yaml:"state"`

	// Type of interface (broadcast, loopback, point-to-point, ...)
	// Example: broadcast
	Type string `json:"type" yaml:"type"`
}

// InstanceStateNetworkAddress represents a network address as part of the network section of a LXD
// instance's state.
//
// swagger:model
//
// API extension: instances
type InstanceStateNetworkAddress struct {
	// Network family (inet or inet6)
	// Example: inet6
	Family string `json:"family" yaml:"family"`

	// IP address
	// Example: fd42:4c81:5770:1eaf:216:3eff:fe0c:eedd
	Address string `json:"address" yaml:"address"`

	// Network mask
	// Example: 64
	Netmask string `json:"netmask" yaml:"netmask"`

	// Address scope (local, link or global)
	// Example: global
	Scope string `json:"scope" yaml:"scope"`
}

// InstanceStateNetworkCounters represents packet counters as part of the network section of a LXD
// instance's state.
//
// swagger:model
//
// API extension: instances
type InstanceStateNetworkCounters struct {
	// Number of bytes received
	// Example: 192021
	BytesReceived int64 `json:"bytes_received" yaml:"bytes_received"`

	// Number of bytes sent
	// Example: 10888579
	BytesSent int64 `json:"bytes_sent" yaml:"bytes_sent"`

	// Number of packets received
	// Example: 1748
	PacketsReceived int64 `json:"packets_received" yaml:"packets_received"`

	// Number of packets sent
	// Example: 964
	PacketsSent int64 `json:"packets_sent" yaml:"packets_sent"`
}
<|endoftext|>


<commit_before>package algo

import (
	"fmt"
	"strconv"
	"testing"
	"time"

	gbt "github.com/dirkolbrich/gobacktest"
)

func TestSMAIntegration(t *testing.T) {
	// define dates
	var dates = []string{
		"2018-07-01",
		"2018-07-02",
		"2018-07-03",
		"2018-07-04",
		"2018-07-05",
		"2018-07-06",
		"2018-07-07",
		"2018-07-08",
		"2018-07-09",
		"2018-07-10",
	}
	// set up mock Data Events
	mockdata := []gbt.DataEvent{}
	for i, d := range dates {
		time, _ := time.Parse("2006-01-02", d)
		symbol := "Date" + strconv.Itoa(i)

		event := &gbt.Event{}
		event.SetSymbol(symbol)
		event.SetTime(time)

		bar := &gbt.Bar{
			Event: *event,
			Close: float64(i),
		}
		mockdata = append(mockdata, bar)
	}

	// set up data handler
	data := &gbt.Data{}
	data.SetStream(mockdata)
	event, _ := data.Next()

	// set up strategy
	strategy := &gbt.Strategy{}
	strategy.SetData(data)
	strategy.SetEvent(event)

	// create Algo
	algo := NewSMA(5)

	// first run, no data in history, sma can not be calculated
	ok, err := algo.Run(strategy)
	if ok || (err == nil) {
		t.Errorf("first run, no data in history: \nexpected %v %#v, \nactual   %v %#v",
			false, fmt.Errorf("invalid value length for indicator sma"),
			ok, err,
		)
	}

}
<commit_msg>Refactor: indicator tests use testhelper methods<commit_after>package algo

import (
	"fmt"
	"reflect"
	"testing"

	gbt "github.com/dirkolbrich/gobacktest"
)

func TestSMAIntegration(t *testing.T) {
	// set up mock Data Events
	mockdata := testHelperMockData([]string{
		"2018-07-01",
		"2018-07-02",
		"2018-07-03",
		"2018-07-04",
		"2018-07-05",
	})

	// set close price from 1 to n on mockdata
	for i, data := range mockdata {
		bar := data.(*gbt.Bar)
		bar.Close = float64(i + 1)
		mockdata[i] = bar
	}

	var testCases = []struct {
		msg       string
		mockdata  []gbt.DataEvent
		period    int
		runBefore int
		expOk     bool
		expErr    error
	}{
		{msg: "test too few data points",
			mockdata: mockdata[:1],
			period:   5,
			expOk:    false,
			expErr:   fmt.Errorf("invalid value length for indicator sma"),
		},
		{msg: "test normal run",
			mockdata:  mockdata[:3],
			period:    3,
			runBefore: 2,
			expOk:     true,
			expErr:    nil,
		},
	}

	for _, tc := range testCases {
		// set up data handler
		data := &gbt.Data{}
		data.SetStream(tc.mockdata)
		event, _ := data.Next()

		// set up strategy
		strategy := &gbt.Strategy{}
		strategy.SetData(data)
		strategy.SetEvent(event)

		// run the backtest n times to  pull data from stream and fill data.list
		for i := 0; i < tc.runBefore; i++ {
			data.Next()
		}

		// create Algo
		algo := SMA(tc.period)

		ok, err := algo.Run(strategy)
		if (ok != tc.expOk) || !reflect.DeepEqual(err, tc.expErr) {
			t.Errorf("%v: SMA(%v): \nexpected %v %#v, \nactual   %v %#v",
				tc.msg, tc.period, tc.expOk, tc.expErr, ok, err)
		}

	}

}
<|endoftext|>


<commit_before>package collectors

import (
	"encoding/json"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"bosun.org/metadata"
	"bosun.org/opentsdb"
	"bosun.org/util"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_ifstat_linux})
	collectors = append(collectors, &IntervalCollector{F: c_ipcount_linux})
	collectors = append(collectors, &IntervalCollector{F: c_if_team_linux})
	collectors = append(collectors, &IntervalCollector{F: c_if_bond_linux})
}

var netFields = []struct {
	key  string
	rate metadata.RateType
	unit metadata.Unit
}{
	{"bytes", metadata.Counter, metadata.Bytes},
	{"packets", metadata.Counter, metadata.Count},
	{"errs", metadata.Counter, metadata.Count},
	{"dropped", metadata.Counter, metadata.Count},
	{"fifo.errs", metadata.Counter, metadata.Count},
	{"frame.errs", metadata.Counter, metadata.Count},
	{"compressed", metadata.Counter, metadata.Count},
	{"multicast", metadata.Counter, metadata.Count},
	{"bytes", metadata.Counter, metadata.Bytes},
	{"packets", metadata.Counter, metadata.Count},
	{"errs", metadata.Counter, metadata.Count},
	{"dropped", metadata.Counter, metadata.Count},
	{"fifo.errs", metadata.Counter, metadata.Count},
	{"collisions", metadata.Counter, metadata.Count},
	{"carrier.errs", metadata.Counter, metadata.Count},
	{"compressed", metadata.Counter, metadata.Count},
}

var ifstatRE = regexp.MustCompile(`\s+(eth\d+|em\d+_\d+/\d+|em\d+_\d+|em\d+|` +
	`bond\d+|team\d+|` + `p\d+p\d+_\d+/\d+|p\d+p\d+_\d+|p\d+p\d+):(.*)`)

func c_ipcount_linux() (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	v4c := 0
	v6c := 0
	err := util.ReadCommand(func(line string) error {
		tl := strings.TrimSpace(line)
		if strings.HasPrefix(tl, "inet ") {
			v4c++
		}
		if strings.HasPrefix(tl, "inet6 ") {
			v6c++
		}
		return nil
	}, "ip", "addr", "list")
	if err != nil {
		return md, err
	}
	Add(&md, "linux.net.ip_count", v4c, opentsdb.TagSet{"version": "4"}, metadata.Gauge, "IP_Addresses", "")
	Add(&md, "linux.net.ip_count", v6c, opentsdb.TagSet{"version": "6"}, metadata.Gauge, "IP_Addresses", "")
	return md, nil
}

func c_ifstat_linux() (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	direction := func(i int) string {
		if i >= 8 {
			return "out"
		} else {
			return "in"
		}
	}
	err := readLine("/proc/net/dev", func(s string) error {
		m := ifstatRE.FindStringSubmatch(s)
		if m == nil {
			return nil
		}
		intf := m[1]
		stats := strings.Fields(m[2])
		tags := opentsdb.TagSet{"iface": intf}
		var bond_string string
		if strings.HasPrefix(intf, "bond") || strings.HasPrefix(intf, "team") {
			bond_string = "bond."
		}
		// Detect speed of the interface in question
		_ = readLine("/sys/class/net/"+intf+"/speed", func(speed string) error {
			Add(&md, "linux.net."+bond_string+"ifspeed", speed, tags, metadata.Gauge, metadata.Megabit, "")
			Add(&md, "os.net."+bond_string+"ifspeed", speed, tags, metadata.Gauge, metadata.Megabit, "")
			return nil
		})
		for i, v := range stats {
			Add(&md, "linux.net."+bond_string+strings.Replace(netFields[i].key, ".", "_", -1), v, opentsdb.TagSet{
				"iface":     intf,
				"direction": direction(i),
			}, netFields[i].rate, netFields[i].unit, "")
			if i < 4 || (i >= 8 && i < 12) {
				Add(&md, "os.net."+bond_string+strings.Replace(netFields[i].key, ".", "_", -1), v, opentsdb.TagSet{
					"iface":     intf,
					"direction": direction(i),
				}, netFields[i].rate, netFields[i].unit, "")

			}
		}
		return nil
	})
	return md, err
}

const (
	linuxNetBondSlaveIsUpDesc = "The status of the bonded or teamed interface."
	linuxNetBondSlaveCount    = "The number of slaves on the bonded or teamed interface."
)

func c_if_bond_linux() (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	const bondingPath = "/proc/net/bonding"
	bondDevices, err := ioutil.ReadDir(bondingPath)
	if err != nil {
		return md, nil
	}
	for _, fi := range bondDevices {
		var iface string
		var slaveCount int
		if err := readLine(filepath.Join(bondingPath, fi.Name()), func(s string) error {
			f := strings.SplitN(s, ":", 2)
			if len(f) != 2 {
				return nil
			}
			f[0] = strings.TrimSpace(f[0])
			f[1] = strings.TrimSpace(f[1])
			if f[0] == "Slave Interface" {
				iface = f[1]
				slaveCount++
			}
			// TODO: This will probably need to be updated for other types of bonding beside LACP, but I have no examples available to work with at the moment
			if f[0] == "MII Status" && iface != "" {
				var status int
				if f[1] == "up" {
					status = 1
				}
				Add(&md, "linux.net.bond.slave.is_up", status, opentsdb.TagSet{"slave": iface, "bond": fi.Name()}, metadata.Gauge, metadata.Bool, linuxNetBondSlaveIsUpDesc)
			}
			return nil
		}); err != nil {
			return md, err
		}
		Add(&md, "linux.net.bond.slave.count", slaveCount, opentsdb.TagSet{"bond": fi.Name()}, metadata.Gauge, metadata.Count, linuxNetBondSlaveCount)
	}
	return md, nil
}

func c_if_team_linux() (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	getState := func(iname string) (TeamState, error) {
		var ts TeamState
		reader, err := util.Command(time.Second*5, nil, "teamdctl", iname, "state", "dump")
		if err != nil {
			return ts, err
		}
		err = json.NewDecoder(reader).Decode(&ts)
		if err != nil {
			return ts, err
		}
		return ts, nil
	}
	teamdFiles, err := ioutil.ReadDir("/var/run/teamd")
	if err != nil {
		return md, nil
	}
	for _, f := range teamdFiles {
		name := f.Name()
		if strings.HasSuffix(name, ".pid") {
			name = strings.TrimSuffix(name, ".pid")
			ts, err := getState(name)
			if err != nil {
				return md, err
			}
			var slaveCount int
			for portName, port := range ts.TeamPorts {
				slaveCount++
				Add(&md, "linux.net.bond.slave.is_up", port.Link.Up, opentsdb.TagSet{"slave": portName, "bond": name}, metadata.Gauge, metadata.Bool, linuxNetBondSlaveIsUpDesc)
			}
			Add(&md, "linux.net.bond.slave.count", slaveCount, opentsdb.TagSet{"bond": name}, metadata.Gauge, metadata.Count, linuxNetBondSlaveCount)
		}
	}
	return md, nil
}

type TeamState struct {
	TeamPorts map[string]TeamPort `json:"ports"`
	Runner    struct {
		Active       bool    `json:"active"`
		FastRate     bool    `json:"fast_rate"`
		SelectPolicy string  `json:"select_policy"`
		SysPrio      float64 `json:"sys_prio"`
	} `json:"runner"`
	Setup struct {
		Daemonized         bool    `json:"daemonized"`
		DbusEnabled        bool    `json:"dbus_enabled"`
		DebugLevel         float64 `json:"debug_level"`
		KernelTeamModeName string  `json:"kernel_team_mode_name"`
		Pid                float64 `json:"pid"`
		PidFile            string  `json:"pid_file"`
		RunnerName         string  `json:"runner_name"`
		ZmqEnabled         bool    `json:"zmq_enabled"`
	} `json:"setup"`
	TeamDevice struct {
		Ifinfo struct {
			DevAddr    string  `json:"dev_addr"`
			DevAddrLen float64 `json:"dev_addr_len"`
			Ifindex    float64 `json:"ifindex"`
			Ifname     string  `json:"ifname"`
		} `json:"ifinfo"`
	} `json:"team_device"`
}

type TeamPort struct {
	Ifinfo struct {
		DevAddr    string  `json:"dev_addr"`
		DevAddrLen float64 `json:"dev_addr_len"`
		Ifindex    float64 `json:"ifindex"`
		Ifname     string  `json:"ifname"`
	}
	Link struct {
		Duplex string  `json:"duplex"`
		Speed  float64 `json:"speed"`
		Up     bool    `json:"up"`
	} `json:"link"`
	LinkWatches struct {
		List struct {
			LinkWatch0 struct {
				DelayDown float64 `json:"delay_down"`
				DelayUp   float64 `json:"delay_up"`
				Name      string  `json:"name"`
				Up        bool    `json:"up"`
			} `json:"link_watch_0"`
		} `json:"list"`
		Up bool `json:"up"`
	} `json:"link_watches"`
	Runner struct {
		ActorLacpduInfo struct {
			Key            float64 `json:"key"`
			Port           float64 `json:"port"`
			PortPriority   float64 `json:"port_priority"`
			State          float64 `json:"state"`
			System         string  `json:"system"`
			SystemPriority float64 `json:"system_priority"`
		} `json:"actor_lacpdu_info"`
		Aggregator struct {
			ID       float64 `json:"id"`
			Selected bool    `json:"selected"`
		} `json:"aggregator"`
		Key               float64 `json:"key"`
		PartnerLacpduInfo struct {
			Key            float64 `json:"key"`
			Port           float64 `json:"port"`
			PortPriority   float64 `json:"port_priority"`
			State          float64 `json:"state"`
			System         string  `json:"system"`
			SystemPriority float64 `json:"system_priority"`
		} `json:"partner_lacpdu_info"`
		Prio     float64 `json:"prio"`
		Selected bool    `json:"selected"`
		State    string  `json:"state"`
	} `json:"runner"`
}
<commit_msg>cmd/scollector: Collect ifspeed metric and master metadata for linux teams<commit_after>package collectors

import (
	"encoding/json"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"bosun.org/metadata"
	"bosun.org/opentsdb"
	"bosun.org/util"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_ifstat_linux})
	collectors = append(collectors, &IntervalCollector{F: c_ipcount_linux})
	collectors = append(collectors, &IntervalCollector{F: c_if_team_linux})
	collectors = append(collectors, &IntervalCollector{F: c_if_bond_linux})
}

var netFields = []struct {
	key  string
	rate metadata.RateType
	unit metadata.Unit
}{
	{"bytes", metadata.Counter, metadata.Bytes},
	{"packets", metadata.Counter, metadata.Count},
	{"errs", metadata.Counter, metadata.Count},
	{"dropped", metadata.Counter, metadata.Count},
	{"fifo.errs", metadata.Counter, metadata.Count},
	{"frame.errs", metadata.Counter, metadata.Count},
	{"compressed", metadata.Counter, metadata.Count},
	{"multicast", metadata.Counter, metadata.Count},
	{"bytes", metadata.Counter, metadata.Bytes},
	{"packets", metadata.Counter, metadata.Count},
	{"errs", metadata.Counter, metadata.Count},
	{"dropped", metadata.Counter, metadata.Count},
	{"fifo.errs", metadata.Counter, metadata.Count},
	{"collisions", metadata.Counter, metadata.Count},
	{"carrier.errs", metadata.Counter, metadata.Count},
	{"compressed", metadata.Counter, metadata.Count},
}

var ifstatRE = regexp.MustCompile(`\s+(eth\d+|em\d+_\d+/\d+|em\d+_\d+|em\d+|` +
	`bond\d+|team\d+|` + `p\d+p\d+_\d+/\d+|p\d+p\d+_\d+|p\d+p\d+):(.*)`)

func c_ipcount_linux() (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	v4c := 0
	v6c := 0
	err := util.ReadCommand(func(line string) error {
		tl := strings.TrimSpace(line)
		if strings.HasPrefix(tl, "inet ") {
			v4c++
		}
		if strings.HasPrefix(tl, "inet6 ") {
			v6c++
		}
		return nil
	}, "ip", "addr", "list")
	if err != nil {
		return md, err
	}
	Add(&md, "linux.net.ip_count", v4c, opentsdb.TagSet{"version": "4"}, metadata.Gauge, "IP_Addresses", "")
	Add(&md, "linux.net.ip_count", v6c, opentsdb.TagSet{"version": "6"}, metadata.Gauge, "IP_Addresses", "")
	return md, nil
}

func c_ifstat_linux() (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	direction := func(i int) string {
		if i >= 8 {
			return "out"
		} else {
			return "in"
		}
	}
	err := readLine("/proc/net/dev", func(s string) error {
		m := ifstatRE.FindStringSubmatch(s)
		if m == nil {
			return nil
		}
		intf := m[1]
		stats := strings.Fields(m[2])
		tags := opentsdb.TagSet{"iface": intf}
		var bond_string string
		if strings.HasPrefix(intf, "bond") || strings.HasPrefix(intf, "team") {
			bond_string = "bond."
		}
		// Detect speed of the interface in question
		_ = readLine("/sys/class/net/"+intf+"/speed", func(speed string) error {
			Add(&md, "linux.net."+bond_string+"ifspeed", speed, tags, metadata.Gauge, metadata.Megabit, "")
			Add(&md, "os.net."+bond_string+"ifspeed", speed, tags, metadata.Gauge, metadata.Megabit, "")
			return nil
		})
		for i, v := range stats {
			Add(&md, "linux.net."+bond_string+strings.Replace(netFields[i].key, ".", "_", -1), v, opentsdb.TagSet{
				"iface":     intf,
				"direction": direction(i),
			}, netFields[i].rate, netFields[i].unit, "")
			if i < 4 || (i >= 8 && i < 12) {
				Add(&md, "os.net."+bond_string+strings.Replace(netFields[i].key, ".", "_", -1), v, opentsdb.TagSet{
					"iface":     intf,
					"direction": direction(i),
				}, netFields[i].rate, netFields[i].unit, "")

			}
		}
		return nil
	})
	return md, err
}

const (
	linuxNetBondSlaveIsUpDesc = "The status of the bonded or teamed interface."
	linuxNetBondSlaveCount    = "The number of slaves on the bonded or teamed interface."
)

func c_if_bond_linux() (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	const bondingPath = "/proc/net/bonding"
	bondDevices, err := ioutil.ReadDir(bondingPath)
	if err != nil {
		return md, nil
	}
	for _, fi := range bondDevices {
		var iface string
		var slaveCount int
		if err := readLine(filepath.Join(bondingPath, fi.Name()), func(s string) error {
			f := strings.SplitN(s, ":", 2)
			if len(f) != 2 {
				return nil
			}
			f[0] = strings.TrimSpace(f[0])
			f[1] = strings.TrimSpace(f[1])
			if f[0] == "Slave Interface" {
				iface = f[1]
				slaveCount++
			}
			// TODO: This will probably need to be updated for other types of bonding beside LACP, but I have no examples available to work with at the moment
			if f[0] == "MII Status" && iface != "" {
				var status int
				if f[1] == "up" {
					status = 1
				}
				Add(&md, "linux.net.bond.slave.is_up", status, opentsdb.TagSet{"slave": iface, "bond": fi.Name()}, metadata.Gauge, metadata.Bool, linuxNetBondSlaveIsUpDesc)
			}
			return nil
		}); err != nil {
			return md, err
		}
		Add(&md, "linux.net.bond.slave.count", slaveCount, opentsdb.TagSet{"bond": fi.Name()}, metadata.Gauge, metadata.Count, linuxNetBondSlaveCount)
	}
	return md, nil
}

func c_if_team_linux() (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	getState := func(iname string) (TeamState, error) {
		var ts TeamState
		reader, err := util.Command(time.Second*5, nil, "teamdctl", iname, "state", "dump")
		if err != nil {
			return ts, err
		}
		err = json.NewDecoder(reader).Decode(&ts)
		if err != nil {
			return ts, err
		}
		return ts, nil
	}
	teamdFiles, err := ioutil.ReadDir("/var/run/teamd")
	if err != nil {
		return md, nil
	}
	for _, f := range teamdFiles {
		name := f.Name()
		if strings.HasSuffix(name, ".pid") {
			name = strings.TrimSuffix(name, ".pid")
			ts, err := getState(name)
			if err != nil {
				return md, err
			}
			var slaveCount int
			var speed int64
			for portName, port := range ts.TeamPorts {
				slaveCount++
				speed += int64(port.Link.Speed)
				metadata.AddMeta("", opentsdb.TagSet{"iface": portName}, "master", name, true)
				Add(&md, "linux.net.bond.slave.is_up", port.Link.Up, opentsdb.TagSet{"slave": portName, "bond": name}, metadata.Gauge, metadata.Bool, linuxNetBondSlaveIsUpDesc)
			}
			Add(&md, "os.net.bond.ifspeed", speed, opentsdb.TagSet{"bond": name}, metadata.Gauge, metadata.Megabit, osNetIfSpeedDesc)
			Add(&md, "linux.net.bond.slave.count", slaveCount, opentsdb.TagSet{"bond": name}, metadata.Gauge, metadata.Count, linuxNetBondSlaveCount)
		}
	}
	return md, nil
}

type TeamState struct {
	TeamPorts map[string]TeamPort `json:"ports"`
	Runner    struct {
		Active       bool    `json:"active"`
		FastRate     bool    `json:"fast_rate"`
		SelectPolicy string  `json:"select_policy"`
		SysPrio      float64 `json:"sys_prio"`
	} `json:"runner"`
	Setup struct {
		Daemonized         bool    `json:"daemonized"`
		DbusEnabled        bool    `json:"dbus_enabled"`
		DebugLevel         float64 `json:"debug_level"`
		KernelTeamModeName string  `json:"kernel_team_mode_name"`
		Pid                float64 `json:"pid"`
		PidFile            string  `json:"pid_file"`
		RunnerName         string  `json:"runner_name"`
		ZmqEnabled         bool    `json:"zmq_enabled"`
	} `json:"setup"`
	TeamDevice struct {
		Ifinfo struct {
			DevAddr    string  `json:"dev_addr"`
			DevAddrLen float64 `json:"dev_addr_len"`
			Ifindex    float64 `json:"ifindex"`
			Ifname     string  `json:"ifname"`
		} `json:"ifinfo"`
	} `json:"team_device"`
}

type TeamPort struct {
	Ifinfo struct {
		DevAddr    string  `json:"dev_addr"`
		DevAddrLen float64 `json:"dev_addr_len"`
		Ifindex    float64 `json:"ifindex"`
		Ifname     string  `json:"ifname"`
	}
	Link struct {
		Duplex string  `json:"duplex"`
		Speed  float64 `json:"speed"`
		Up     bool    `json:"up"`
	} `json:"link"`
	LinkWatches struct {
		List struct {
			LinkWatch0 struct {
				DelayDown float64 `json:"delay_down"`
				DelayUp   float64 `json:"delay_up"`
				Name      string  `json:"name"`
				Up        bool    `json:"up"`
			} `json:"link_watch_0"`
		} `json:"list"`
		Up bool `json:"up"`
	} `json:"link_watches"`
	Runner struct {
		ActorLacpduInfo struct {
			Key            float64 `json:"key"`
			Port           float64 `json:"port"`
			PortPriority   float64 `json:"port_priority"`
			State          float64 `json:"state"`
			System         string  `json:"system"`
			SystemPriority float64 `json:"system_priority"`
		} `json:"actor_lacpdu_info"`
		Aggregator struct {
			ID       float64 `json:"id"`
			Selected bool    `json:"selected"`
		} `json:"aggregator"`
		Key               float64 `json:"key"`
		PartnerLacpduInfo struct {
			Key            float64 `json:"key"`
			Port           float64 `json:"port"`
			PortPriority   float64 `json:"port_priority"`
			State          float64 `json:"state"`
			System         string  `json:"system"`
			SystemPriority float64 `json:"system_priority"`
		} `json:"partner_lacpdu_info"`
		Prio     float64 `json:"prio"`
		Selected bool    `json:"selected"`
		State    string  `json:"state"`
	} `json:"runner"`
}
<|endoftext|>