<commit_before>package agent

import (
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/hashicorp/nomad/client"
	clientconfig "github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/nomad"
	"github.com/hashicorp/nomad/nomad/structs"
)

// Agent is a long running daemon that is used to run both
// clients and servers. Servers are responsible for managing
// state and making scheduling decisions. Clients can be
// scheduled to, and are responsible for interfacing with
// servers to run allocations.
type Agent struct {
	config    *Config
	logger    *log.Logger
	logOutput io.Writer

	server *nomad.Server
	client *client.Client

	shutdown     bool
	shutdownCh   chan struct{}
	shutdownLock sync.Mutex
}

// NewAgent is used to create a new agent with the given configuration
func NewAgent(config *Config, logOutput io.Writer) (*Agent, error) {
	// Ensure we have a log sink
	if logOutput == nil {
		logOutput = os.Stderr
	}

	a := &Agent{
		config:     config,
		logger:     log.New(logOutput, "", log.LstdFlags),
		logOutput:  logOutput,
		shutdownCh: make(chan struct{}),
	}

	if err := a.setupServer(); err != nil {
		return nil, err
	}
	if err := a.setupClient(); err != nil {
		return nil, err
	}
	if a.client == nil && a.server == nil {
		return nil, fmt.Errorf("must have at least client or server mode enabled")
	}
	return a, nil
}

// serverConfig is used to generate a new server configuration struct
// for initializing a nomad server.
func (a *Agent) serverConfig() (*nomad.Config, error) {
	conf := a.config.NomadConfig
	if conf == nil {
		conf = nomad.DefaultConfig()
	}
	conf.LogOutput = a.logOutput
	conf.DevMode = a.config.DevMode
	conf.Build = fmt.Sprintf("%s%s", a.config.Version, a.config.VersionPrerelease)
	if a.config.Region != "" {
		conf.Region = a.config.Region
	}
	if a.config.Datacenter != "" {
		conf.Datacenter = a.config.Datacenter
	}
	if a.config.NodeName != "" {
		conf.NodeName = a.config.NodeName
	}
	if a.config.Server.BootstrapExpect > 0 {
		if a.config.Server.BootstrapExpect == 1 {
			conf.Bootstrap = true
		} else {
			conf.BootstrapExpect = a.config.Server.BootstrapExpect
		}
	}
	if a.config.DataDir != "" {
		conf.DataDir = filepath.Join(a.config.DataDir, "server")
	}
	if a.config.Server.DataDir != "" {
		conf.DataDir = a.config.Server.DataDir
	}
	if a.config.Server.ProtocolVersion != 0 {
		conf.ProtocolVersion = uint8(a.config.Server.ProtocolVersion)
	}
	if a.config.Server.NumSchedulers != 0 {
		conf.NumSchedulers = a.config.Server.NumSchedulers
	}
	if len(a.config.Server.EnabledSchedulers) != 0 {
		conf.EnabledSchedulers = a.config.Server.EnabledSchedulers
	}

	// Set up the advertise addrs
	if addr := a.config.AdvertiseAddrs.Serf; addr != "" {
		serfAddr, err := net.ResolveTCPAddr("tcp", addr)
		if err != nil {
			return nil, fmt.Errorf("error resolving serf advertise address: %s", err)
		}
		conf.SerfConfig.MemberlistConfig.AdvertiseAddr = serfAddr.IP.String()
		conf.SerfConfig.MemberlistConfig.AdvertisePort = serfAddr.Port
	}
	if addr := a.config.AdvertiseAddrs.RPC; addr != "" {
		rpcAddr, err := net.ResolveTCPAddr("tcp", addr)
		if err != nil {
			return nil, fmt.Errorf("error resolving rpc advertise address: %s", err)
		}
		conf.RPCAdvertise = rpcAddr
	}

	// Set up the bind addresses
	if addr := a.config.BindAddr; addr != "" {
		conf.RPCAddr.IP = net.ParseIP(addr)
		conf.SerfConfig.MemberlistConfig.BindAddr = addr
	}
	if addr := a.config.Addresses.RPC; addr != "" {
		conf.RPCAddr.IP = net.ParseIP(addr)
	}
	if addr := a.config.Addresses.Serf; addr != "" {
		conf.SerfConfig.MemberlistConfig.BindAddr = addr
	}

	// Set up the ports
	if port := a.config.Ports.RPC; port != 0 {
		conf.RPCAddr.Port = port
	}
	if port := a.config.Ports.Serf; port != 0 {
		conf.SerfConfig.MemberlistConfig.BindPort = port
	}

	if gcThreshold := a.config.Server.NodeGCThreshold; gcThreshold != "" {
		dur, err := time.ParseDuration(gcThreshold)
		if err != nil {
			return nil, err
		}
		conf.NodeGCThreshold = dur
	}

	return conf, nil
}

// setupServer is used to setup the server if enabled
func (a *Agent) setupServer() error {
	if !a.config.Server.Enabled {
		return nil
	}

	// Setup the configuration
	conf, err := a.serverConfig()
	if err != nil {
		return fmt.Errorf("server config setup failed: %s", err)
	}

	// Create the server
	server, err := nomad.NewServer(conf)
	if err != nil {
		return fmt.Errorf("server setup failed: %v", err)
	}

	a.server = server
	return nil
}

// setupClient is used to setup the client if enabled
func (a *Agent) setupClient() error {
	if !a.config.Client.Enabled {
		return nil
	}

	// Setup the configuration
	conf := a.config.ClientConfig
	if conf == nil {
		conf = client.DefaultConfig()
	}
	if a.server != nil {
		conf.RPCHandler = a.server
	}
	conf.LogOutput = a.logOutput
	conf.DevMode = a.config.DevMode
	if a.config.Region != "" {
		conf.Region = a.config.Region
	}
	if a.config.DataDir != "" {
		conf.StateDir = filepath.Join(a.config.DataDir, "client")
		conf.AllocDir = filepath.Join(a.config.DataDir, "alloc")
	}
	if a.config.Client.StateDir != "" {
		conf.StateDir = a.config.Client.StateDir
	}
	if a.config.Client.AllocDir != "" {
		conf.AllocDir = a.config.Client.AllocDir
	}
	conf.Servers = a.config.Client.Servers
	if a.config.Client.NetworkInterface != "" {
		conf.NetworkInterface = a.config.Client.NetworkInterface
	}
	conf.Options = a.config.Client.Options
	if a.config.Client.NetworkSpeed != 0 {
		conf.NetworkSpeed = a.config.Client.NetworkSpeed
	}
	if a.config.Client.MaxKillTimeout != "" {
		dur, err := time.ParseDuration(a.config.Client.MaxKillTimeout)
		if err != nil {
			return fmt.Errorf("Error parsing retry interval: %s", err)
		}
		conf.MaxKillTimeout = dur
	}
	conf.ClientMaxPort = a.config.Client.ClientMaxPort
	conf.ClientMinPort = a.config.Client.ClientMinPort

	// Setup the node
	conf.Node = new(structs.Node)
	conf.Node.Datacenter = a.config.Datacenter
	conf.Node.Name = a.config.NodeName
	conf.Node.Meta = a.config.Client.Meta
	conf.Node.NodeClass = a.config.Client.NodeClass
	httpAddr := fmt.Sprintf("%s:%d", a.config.BindAddr, a.config.Ports.HTTP)
	if a.config.Addresses.HTTP != "" && a.config.AdvertiseAddrs.HTTP == "" {
		addr, err := net.ResolveTCPAddr("tcp", a.config.Addresses.HTTP)
		if err != nil {
			return fmt.Errorf("error resolving http addr: %v:", err)
		}
		httpAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
	} else if a.config.AdvertiseAddrs.HTTP != "" {
		addr, err := net.ResolveTCPAddr("tcp", a.config.AdvertiseAddrs.HTTP)
		if err != nil {
			return fmt.Errorf("error resolving advertise http addr: %v", err)
		}
		httpAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
	}
	conf.Node.HTTPAddr = httpAddr

	// Reserve some ports for the plugins
	if err := a.reservePortsForClient(conf); err != nil {
		return err
	}

	// Create the client
	client, err := client.NewClient(conf)
	if err != nil {
		return fmt.Errorf("client setup failed: %v", err)
	}
	a.client = client
	return nil
}

func (a *Agent) reservePortsForClient(conf *clientconfig.Config) error {
	deviceName, addr, mask, err := a.findLoopbackDevice()
	if err != nil {
		return fmt.Errorf("error finding the device name for loopback: %v", err)
	}
	var nr *structs.NetworkResource
	if conf.Node.Reserved == nil {
		conf.Node.Reserved = &structs.Resources{}
	}
	for _, n := range conf.Node.Reserved.Networks {
		if n.Device == deviceName {
			nr = n
		}
	}
	if nr == nil {
		nr = &structs.NetworkResource{
			Device:        deviceName,
			IP:            addr,
			CIDR:          mask,
			ReservedPorts: make([]structs.Port, 0),
		}
	}
	for i := conf.ClientMinPort; i <= conf.ClientMaxPort; i++ {
		nr.ReservedPorts = append(nr.ReservedPorts, structs.Port{Label: fmt.Sprintf("plugin-%d", i), Value: int(i)})
	}
	conf.Node.Reserved.Networks = append(conf.Node.Reserved.Networks, nr)
	return nil
}

func (a *Agent) findLoopbackDevice() (string, string, string, error) {
	var ifcs []net.Interface
	var err error
	var deviceName string
	ifcs, err = net.Interfaces()
	if err != nil {
		return "", "", "", err
	}
	for _, ifc := range ifcs {
		addrs, err := ifc.Addrs()
		if err != nil {
			return deviceName, "", "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip.IsLoopback() {
				return ifc.Name, ip.String(), addr.String(), nil
			}
		}
	}

	return deviceName, "", "", err
}

// Leave is used gracefully exit. Clients will inform servers
// of their departure so that allocations can be rescheduled.
func (a *Agent) Leave() error {
	if a.client != nil {
		if err := a.client.Leave(); err != nil {
			a.logger.Printf("[ERR] agent: client leave failed: %v", err)
		}
	}
	if a.server != nil {
		if err := a.server.Leave(); err != nil {
			a.logger.Printf("[ERR] agent: server leave failed: %v", err)
		}
	}
	return nil
}

// Shutdown is used to terminate the agent.
func (a *Agent) Shutdown() error {
	a.shutdownLock.Lock()
	defer a.shutdownLock.Unlock()

	if a.shutdown {
		return nil
	}

	a.logger.Println("[INFO] agent: requesting shutdown")
	if a.client != nil {
		if err := a.client.Shutdown(); err != nil {
			a.logger.Printf("[ERR] agent: client shutdown failed: %v", err)
		}
	}
	if a.server != nil {
		if err := a.server.Shutdown(); err != nil {
			a.logger.Printf("[ERR] agent: server shutdown failed: %v", err)
		}
	}

	a.logger.Println("[INFO] agent: shutdown complete")
	a.shutdown = true
	close(a.shutdownCh)
	return nil
}

// RPC is used to make an RPC call to the Nomad servers
func (a *Agent) RPC(method string, args interface{}, reply interface{}) error {
	if a.server != nil {
		return a.server.RPC(method, args, reply)
	}
	return a.client.RPC(method, args, reply)
}

// Client returns the configured client or nil
func (a *Agent) Client() *client.Client {
	return a.client
}

// Server returns the configured server or nil
func (a *Agent) Server() *nomad.Server {
	return a.server
}

// Stats is used to return statistics for debugging and insight
// for various sub-systems
func (a *Agent) Stats() map[string]map[string]string {
	stats := make(map[string]map[string]string)
	if a.server != nil {
		subStat := a.server.Stats()
		for k, v := range subStat {
			stats[k] = v
		}
	}
	if a.client != nil {
		subStat := a.client.Stats()
		for k, v := range subStat {
			stats[k] = v
		}
	}
	return stats
}
<commit_msg>Adding comments<commit_after>package agent

import (
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/hashicorp/nomad/client"
	clientconfig "github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/nomad"
	"github.com/hashicorp/nomad/nomad/structs"
)

// Agent is a long running daemon that is used to run both
// clients and servers. Servers are responsible for managing
// state and making scheduling decisions. Clients can be
// scheduled to, and are responsible for interfacing with
// servers to run allocations.
type Agent struct {
	config    *Config
	logger    *log.Logger
	logOutput io.Writer

	server *nomad.Server
	client *client.Client

	shutdown     bool
	shutdownCh   chan struct{}
	shutdownLock sync.Mutex
}

// NewAgent is used to create a new agent with the given configuration
func NewAgent(config *Config, logOutput io.Writer) (*Agent, error) {
	// Ensure we have a log sink
	if logOutput == nil {
		logOutput = os.Stderr
	}

	a := &Agent{
		config:     config,
		logger:     log.New(logOutput, "", log.LstdFlags),
		logOutput:  logOutput,
		shutdownCh: make(chan struct{}),
	}

	if err := a.setupServer(); err != nil {
		return nil, err
	}
	if err := a.setupClient(); err != nil {
		return nil, err
	}
	if a.client == nil && a.server == nil {
		return nil, fmt.Errorf("must have at least client or server mode enabled")
	}
	return a, nil
}

// serverConfig is used to generate a new server configuration struct
// for initializing a nomad server.
func (a *Agent) serverConfig() (*nomad.Config, error) {
	conf := a.config.NomadConfig
	if conf == nil {
		conf = nomad.DefaultConfig()
	}
	conf.LogOutput = a.logOutput
	conf.DevMode = a.config.DevMode
	conf.Build = fmt.Sprintf("%s%s", a.config.Version, a.config.VersionPrerelease)
	if a.config.Region != "" {
		conf.Region = a.config.Region
	}
	if a.config.Datacenter != "" {
		conf.Datacenter = a.config.Datacenter
	}
	if a.config.NodeName != "" {
		conf.NodeName = a.config.NodeName
	}
	if a.config.Server.BootstrapExpect > 0 {
		if a.config.Server.BootstrapExpect == 1 {
			conf.Bootstrap = true
		} else {
			conf.BootstrapExpect = a.config.Server.BootstrapExpect
		}
	}
	if a.config.DataDir != "" {
		conf.DataDir = filepath.Join(a.config.DataDir, "server")
	}
	if a.config.Server.DataDir != "" {
		conf.DataDir = a.config.Server.DataDir
	}
	if a.config.Server.ProtocolVersion != 0 {
		conf.ProtocolVersion = uint8(a.config.Server.ProtocolVersion)
	}
	if a.config.Server.NumSchedulers != 0 {
		conf.NumSchedulers = a.config.Server.NumSchedulers
	}
	if len(a.config.Server.EnabledSchedulers) != 0 {
		conf.EnabledSchedulers = a.config.Server.EnabledSchedulers
	}

	// Set up the advertise addrs
	if addr := a.config.AdvertiseAddrs.Serf; addr != "" {
		serfAddr, err := net.ResolveTCPAddr("tcp", addr)
		if err != nil {
			return nil, fmt.Errorf("error resolving serf advertise address: %s", err)
		}
		conf.SerfConfig.MemberlistConfig.AdvertiseAddr = serfAddr.IP.String()
		conf.SerfConfig.MemberlistConfig.AdvertisePort = serfAddr.Port
	}
	if addr := a.config.AdvertiseAddrs.RPC; addr != "" {
		rpcAddr, err := net.ResolveTCPAddr("tcp", addr)
		if err != nil {
			return nil, fmt.Errorf("error resolving rpc advertise address: %s", err)
		}
		conf.RPCAdvertise = rpcAddr
	}

	// Set up the bind addresses
	if addr := a.config.BindAddr; addr != "" {
		conf.RPCAddr.IP = net.ParseIP(addr)
		conf.SerfConfig.MemberlistConfig.BindAddr = addr
	}
	if addr := a.config.Addresses.RPC; addr != "" {
		conf.RPCAddr.IP = net.ParseIP(addr)
	}
	if addr := a.config.Addresses.Serf; addr != "" {
		conf.SerfConfig.MemberlistConfig.BindAddr = addr
	}

	// Set up the ports
	if port := a.config.Ports.RPC; port != 0 {
		conf.RPCAddr.Port = port
	}
	if port := a.config.Ports.Serf; port != 0 {
		conf.SerfConfig.MemberlistConfig.BindPort = port
	}

	if gcThreshold := a.config.Server.NodeGCThreshold; gcThreshold != "" {
		dur, err := time.ParseDuration(gcThreshold)
		if err != nil {
			return nil, err
		}
		conf.NodeGCThreshold = dur
	}

	return conf, nil
}

// setupServer is used to setup the server if enabled
func (a *Agent) setupServer() error {
	if !a.config.Server.Enabled {
		return nil
	}

	// Setup the configuration
	conf, err := a.serverConfig()
	if err != nil {
		return fmt.Errorf("server config setup failed: %s", err)
	}

	// Create the server
	server, err := nomad.NewServer(conf)
	if err != nil {
		return fmt.Errorf("server setup failed: %v", err)
	}

	a.server = server
	return nil
}

// setupClient is used to setup the client if enabled
func (a *Agent) setupClient() error {
	if !a.config.Client.Enabled {
		return nil
	}

	// Setup the configuration
	conf := a.config.ClientConfig
	if conf == nil {
		conf = client.DefaultConfig()
	}
	if a.server != nil {
		conf.RPCHandler = a.server
	}
	conf.LogOutput = a.logOutput
	conf.DevMode = a.config.DevMode
	if a.config.Region != "" {
		conf.Region = a.config.Region
	}
	if a.config.DataDir != "" {
		conf.StateDir = filepath.Join(a.config.DataDir, "client")
		conf.AllocDir = filepath.Join(a.config.DataDir, "alloc")
	}
	if a.config.Client.StateDir != "" {
		conf.StateDir = a.config.Client.StateDir
	}
	if a.config.Client.AllocDir != "" {
		conf.AllocDir = a.config.Client.AllocDir
	}
	conf.Servers = a.config.Client.Servers
	if a.config.Client.NetworkInterface != "" {
		conf.NetworkInterface = a.config.Client.NetworkInterface
	}
	conf.Options = a.config.Client.Options
	if a.config.Client.NetworkSpeed != 0 {
		conf.NetworkSpeed = a.config.Client.NetworkSpeed
	}
	if a.config.Client.MaxKillTimeout != "" {
		dur, err := time.ParseDuration(a.config.Client.MaxKillTimeout)
		if err != nil {
			return fmt.Errorf("Error parsing retry interval: %s", err)
		}
		conf.MaxKillTimeout = dur
	}
	conf.ClientMaxPort = a.config.Client.ClientMaxPort
	conf.ClientMinPort = a.config.Client.ClientMinPort

	// Setup the node
	conf.Node = new(structs.Node)
	conf.Node.Datacenter = a.config.Datacenter
	conf.Node.Name = a.config.NodeName
	conf.Node.Meta = a.config.Client.Meta
	conf.Node.NodeClass = a.config.Client.NodeClass
	httpAddr := fmt.Sprintf("%s:%d", a.config.BindAddr, a.config.Ports.HTTP)
	if a.config.Addresses.HTTP != "" && a.config.AdvertiseAddrs.HTTP == "" {
		addr, err := net.ResolveTCPAddr("tcp", a.config.Addresses.HTTP)
		if err != nil {
			return fmt.Errorf("error resolving http addr: %v:", err)
		}
		httpAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
	} else if a.config.AdvertiseAddrs.HTTP != "" {
		addr, err := net.ResolveTCPAddr("tcp", a.config.AdvertiseAddrs.HTTP)
		if err != nil {
			return fmt.Errorf("error resolving advertise http addr: %v", err)
		}
		httpAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
	}
	conf.Node.HTTPAddr = httpAddr

	// Reserve some ports for the plugins
	if err := a.reservePortsForClient(conf); err != nil {
		return err
	}

	// Create the client
	client, err := client.NewClient(conf)
	if err != nil {
		return fmt.Errorf("client setup failed: %v", err)
	}
	a.client = client
	return nil
}

// reservePortsForClient reservers a range of ports for the client to use when
// it creates various plugins for log collection, executors, drivers, etc
func (a *Agent) reservePortsForClient(conf *clientconfig.Config) error {
	// finding the device name for loopback
	deviceName, addr, mask, err := a.findLoopbackDevice()
	if err != nil {
		return fmt.Errorf("error finding the device name for loopback: %v", err)
	}

	// seeing if the user has already reserved some resources on this device
	var nr *structs.NetworkResource
	if conf.Node.Reserved == nil {
		conf.Node.Reserved = &structs.Resources{}
	}
	for _, n := range conf.Node.Reserved.Networks {
		if n.Device == deviceName {
			nr = n
		}
	}
	// If the user hasn't already created the device, we create it
	if nr == nil {
		nr = &structs.NetworkResource{
			Device:        deviceName,
			IP:            addr,
			CIDR:          mask,
			ReservedPorts: make([]structs.Port, 0),
		}
	}
	// appending the port ranges we want to use for the client to the list of
	// reserved ports for this device
	for i := conf.ClientMinPort; i <= conf.ClientMaxPort; i++ {
		nr.ReservedPorts = append(nr.ReservedPorts, structs.Port{Label: fmt.Sprintf("plugin-%d", i), Value: int(i)})
	}
	conf.Node.Reserved.Networks = append(conf.Node.Reserved.Networks, nr)
	return nil
}

// findLoopbackDevice iterates through all the interfaces on a machine and
// returns the ip addr, mask of the loopback device
func (a *Agent) findLoopbackDevice() (string, string, string, error) {
	var ifcs []net.Interface
	var err error
	ifcs, err = net.Interfaces()
	if err != nil {
		return "", "", "", err
	}
	for _, ifc := range ifcs {
		addrs, err := ifc.Addrs()
		if err != nil {
			return "", "", "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip.IsLoopback() {
				if ip.To4() == nil {
					continue
				}
				return ifc.Name, ip.String(), addr.String(), nil
			}
		}
	}

	return "", "", "", fmt.Errorf("no loopback devices with IPV4 addr found")
}

// Leave is used gracefully exit. Clients will inform servers
// of their departure so that allocations can be rescheduled.
func (a *Agent) Leave() error {
	if a.client != nil {
		if err := a.client.Leave(); err != nil {
			a.logger.Printf("[ERR] agent: client leave failed: %v", err)
		}
	}
	if a.server != nil {
		if err := a.server.Leave(); err != nil {
			a.logger.Printf("[ERR] agent: server leave failed: %v", err)
		}
	}
	return nil
}

// Shutdown is used to terminate the agent.
func (a *Agent) Shutdown() error {
	a.shutdownLock.Lock()
	defer a.shutdownLock.Unlock()

	if a.shutdown {
		return nil
	}

	a.logger.Println("[INFO] agent: requesting shutdown")
	if a.client != nil {
		if err := a.client.Shutdown(); err != nil {
			a.logger.Printf("[ERR] agent: client shutdown failed: %v", err)
		}
	}
	if a.server != nil {
		if err := a.server.Shutdown(); err != nil {
			a.logger.Printf("[ERR] agent: server shutdown failed: %v", err)
		}
	}

	a.logger.Println("[INFO] agent: shutdown complete")
	a.shutdown = true
	close(a.shutdownCh)
	return nil
}

// RPC is used to make an RPC call to the Nomad servers
func (a *Agent) RPC(method string, args interface{}, reply interface{}) error {
	if a.server != nil {
		return a.server.RPC(method, args, reply)
	}
	return a.client.RPC(method, args, reply)
}

// Client returns the configured client or nil
func (a *Agent) Client() *client.Client {
	return a.client
}

// Server returns the configured server or nil
func (a *Agent) Server() *nomad.Server {
	return a.server
}

// Stats is used to return statistics for debugging and insight
// for various sub-systems
func (a *Agent) Stats() map[string]map[string]string {
	stats := make(map[string]map[string]string)
	if a.server != nil {
		subStat := a.server.Stats()
		for k, v := range subStat {
			stats[k] = v
		}
	}
	if a.client != nil {
		subStat := a.client.Stats()
		for k, v := range subStat {
			stats[k] = v
		}
	}
	return stats
}
<|endoftext|>


<commit_before>package agent

import (
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"time"

	"github.com/hashicorp/nomad/client"
	"github.com/hashicorp/nomad/nomad"
	"github.com/hashicorp/nomad/nomad/structs"
)

// Agent is a long running daemon that is used to run both
// clients and servers. Servers are responsible for managing
// state and making scheduling decisions. Clients can be
// scheduled to, and are responsible for interfacing with
// servers to run allocations.
type Agent struct {
	config    *Config
	logger    *log.Logger
	logOutput io.Writer

	server *nomad.Server
	client *client.Client

	shutdown     bool
	shutdownCh   chan struct{}
	shutdownLock sync.Mutex
}

// NewAgent is used to create a new agent with the given configuration
func NewAgent(config *Config, logOutput io.Writer) (*Agent, error) {
	// Ensure we have a log sink
	if logOutput == nil {
		logOutput = os.Stderr
	}

	a := &Agent{
		config:     config,
		logger:     log.New(logOutput, "", log.LstdFlags),
		logOutput:  logOutput,
		shutdownCh: make(chan struct{}),
	}

	if err := a.setupServer(); err != nil {
		return nil, err
	}
	if err := a.setupClient(); err != nil {
		return nil, err
	}
	if a.client == nil && a.server == nil {
		return nil, fmt.Errorf("must have at least client or server mode enabled")
	}
	return a, nil
}

// serverConfig is used to generate a new server configuration struct
// for initializing a nomad server.
func (a *Agent) serverConfig() (*nomad.Config, error) {
	conf := a.config.NomadConfig
	if conf == nil {
		conf = nomad.DefaultConfig()
	}
	conf.LogOutput = a.logOutput
	conf.DevMode = a.config.DevMode
	conf.Build = fmt.Sprintf("%s%s", a.config.Version, a.config.VersionPrerelease)
	if a.config.Region != "" {
		conf.Region = a.config.Region
	}
	if a.config.Datacenter != "" {
		conf.Datacenter = a.config.Datacenter
	}
	if a.config.NodeName != "" {
		conf.NodeName = a.config.NodeName
	}
	if a.config.Server.BootstrapExpect > 0 {
		if a.config.Server.BootstrapExpect == 1 {
			conf.Bootstrap = true
		} else {
			conf.BootstrapExpect = a.config.Server.BootstrapExpect
		}
	}
	if a.config.DataDir != "" {
		conf.DataDir = filepath.Join(a.config.DataDir, "server")
	}
	if a.config.Server.DataDir != "" {
		conf.DataDir = a.config.Server.DataDir
	}
	if a.config.Server.ProtocolVersion != 0 {
		conf.ProtocolVersion = uint8(a.config.Server.ProtocolVersion)
	}
	if a.config.Server.NumSchedulers != 0 {
		conf.NumSchedulers = a.config.Server.NumSchedulers
	}
	if len(a.config.Server.EnabledSchedulers) != 0 {
		conf.EnabledSchedulers = a.config.Server.EnabledSchedulers
	}

	// Set up the advertise addrs
	if addr := a.config.AdvertiseAddrs.Serf; addr != "" {
		serfAddr, err := net.ResolveTCPAddr("tcp", addr)
		if err != nil {
			return nil, fmt.Errorf("error resolving serf advertise address: %s", err)
		}
		conf.SerfConfig.MemberlistConfig.AdvertiseAddr = serfAddr.IP.String()
		conf.SerfConfig.MemberlistConfig.AdvertisePort = serfAddr.Port
	}
	if addr := a.config.AdvertiseAddrs.RPC; addr != "" {
		rpcAddr, err := net.ResolveTCPAddr("tcp", addr)
		if err != nil {
			return nil, fmt.Errorf("error resolving rpc advertise address: %s", err)
		}
		conf.RPCAdvertise = rpcAddr
	}

	// Set up the bind addresses
	if addr := a.config.BindAddr; addr != "" {
		conf.RPCAddr.IP = net.ParseIP(addr)
		conf.SerfConfig.MemberlistConfig.BindAddr = addr
	}
	if addr := a.config.Addresses.RPC; addr != "" {
		conf.RPCAddr.IP = net.ParseIP(addr)
	}
	if addr := a.config.Addresses.Serf; addr != "" {
		conf.SerfConfig.MemberlistConfig.BindAddr = addr
	}

	// Set up the ports
	if port := a.config.Ports.RPC; port != 0 {
		conf.RPCAddr.Port = port
	}
	if port := a.config.Ports.Serf; port != 0 {
		conf.SerfConfig.MemberlistConfig.BindPort = port
	}

	if gcThreshold := a.config.Server.NodeGCThreshold; gcThreshold != "" {
		dur, err := time.ParseDuration(gcThreshold)
		if err != nil {
			return nil, err
		}
		conf.NodeGCThreshold = dur
	}

	return conf, nil
}

// setupServer is used to setup the server if enabled
func (a *Agent) setupServer() error {
	if !a.config.Server.Enabled {
		return nil
	}

	// Setup the configuration
	conf, err := a.serverConfig()
	if err != nil {
		return fmt.Errorf("server config setup failed: %s", err)
	}

	// Create the server
	server, err := nomad.NewServer(conf)
	if err != nil {
		return fmt.Errorf("server setup failed: %v", err)
	}

	a.server = server
	return nil
}

// setupClient is used to setup the client if enabled
func (a *Agent) setupClient() error {
	if !a.config.Client.Enabled {
		return nil
	}

	// Setup the configuration
	conf := a.config.ClientConfig
	if conf == nil {
		conf = client.DefaultConfig()
	}
	if a.server != nil {
		conf.RPCHandler = a.server
	}
	conf.LogOutput = a.logOutput
	conf.DevMode = a.config.DevMode
	if a.config.Region != "" {
		conf.Region = a.config.Region
	}
	if a.config.DataDir != "" {
		conf.StateDir = filepath.Join(a.config.DataDir, "client")
		conf.AllocDir = filepath.Join(a.config.DataDir, "alloc")
	}
	if a.config.Client.StateDir != "" {
		conf.StateDir = a.config.Client.StateDir
	}
	if a.config.Client.AllocDir != "" {
		conf.AllocDir = a.config.Client.AllocDir
	}
	conf.Servers = a.config.Client.Servers
	if a.config.Client.NetworkInterface != "" {
		conf.NetworkInterface = a.config.Client.NetworkInterface
	}
	conf.Options = a.config.Client.Options
	if a.config.Client.NetworkSpeed != 0 {
		conf.NetworkSpeed = a.config.Client.NetworkSpeed
	}
	if a.config.Client.MaxKillTimeout != "" {
		dur, err := time.ParseDuration(a.config.Client.MaxKillTimeout)
		if err != nil {
			return fmt.Errorf("Error parsing retry interval: %s", err)
		}
		conf.MaxKillTimeout = dur
	}
	conf.ClientMaxPort = a.config.Client.ClientMaxPort
	conf.ClientMinPort = a.config.Client.ClientMinPort

	// Setup the node
	conf.Node = new(structs.Node)
	conf.Node.Datacenter = a.config.Datacenter
	conf.Node.Name = a.config.NodeName
	conf.Node.Meta = a.config.Client.Meta
	conf.Node.NodeClass = a.config.Client.NodeClass
	httpAddr := fmt.Sprintf("%s:%d", a.config.BindAddr, a.config.Ports.HTTP)
	if a.config.Addresses.HTTP != "" && a.config.AdvertiseAddrs.HTTP == "" {
		addr, err := net.ResolveTCPAddr("tcp", a.config.Addresses.HTTP)
		if err != nil {
			return fmt.Errorf("error resolving http addr: %v:", err)
		}
		httpAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
	} else if a.config.AdvertiseAddrs.HTTP != "" {
		addr, err := net.ResolveTCPAddr("tcp", a.config.AdvertiseAddrs.HTTP)
		if err != nil {
			return fmt.Errorf("error resolving advertise http addr: %v", err)
		}
		httpAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
	}
	conf.Node.HTTPAddr = httpAddr

	// Reserve some ports for the plugins
	if runtime.GOOS == "windows" {
		deviceName, err := a.findLoopbackDevice()
		if err != nil {
			return fmt.Errorf("error finding the device name for loopback: %v", err)
		}
		var nr *structs.NetworkResource
		for _, n := range conf.Node.Reserved.Networks {
			if n.Device == deviceName {
				nr = n
			}
		}
		if nr == nil {
			nr = &structs.NetworkResource{
				Device:        deviceName,
				ReservedPorts: make([]structs.Port, 0),
			}
		}
		for i := conf.ClientMinPort; i <= conf.ClientMaxPort; i++ {
			nr.ReservedPorts = append(nr.ReservedPorts, structs.Port{Label: fmt.Sprintf("plugin-%d", i), Value: int(i)})
		}
	}

	// Create the client
	client, err := client.NewClient(conf)
	if err != nil {
		return fmt.Errorf("client setup failed: %v", err)
	}
	a.client = client
	return nil
}

func (a *Agent) findLoopbackDevice() (string, error) {
	var ifcs []net.Interface
	var err error
	var deviceName string
	ifcs, err = net.Interfaces()
	if err != nil {
		return "", err
	}
	for _, ifc := range ifcs {
		addrs, err := ifc.Addrs()
		if err != nil {
			return deviceName, err
		}
		for _, addr := range addrs {
			if net.ParseIP(addr.String()).IsLoopback() {
				return ifc.Name, nil
			}
		}
	}
	return deviceName, err
}

// Leave is used gracefully exit. Clients will inform servers
// of their departure so that allocations can be rescheduled.
func (a *Agent) Leave() error {
	if a.client != nil {
		if err := a.client.Leave(); err != nil {
			a.logger.Printf("[ERR] agent: client leave failed: %v", err)
		}
	}
	if a.server != nil {
		if err := a.server.Leave(); err != nil {
			a.logger.Printf("[ERR] agent: server leave failed: %v", err)
		}
	}
	return nil
}

// Shutdown is used to terminate the agent.
func (a *Agent) Shutdown() error {
	a.shutdownLock.Lock()
	defer a.shutdownLock.Unlock()

	if a.shutdown {
		return nil
	}

	a.logger.Println("[INFO] agent: requesting shutdown")
	if a.client != nil {
		if err := a.client.Shutdown(); err != nil {
			a.logger.Printf("[ERR] agent: client shutdown failed: %v", err)
		}
	}
	if a.server != nil {
		if err := a.server.Shutdown(); err != nil {
			a.logger.Printf("[ERR] agent: server shutdown failed: %v", err)
		}
	}

	a.logger.Println("[INFO] agent: shutdown complete")
	a.shutdown = true
	close(a.shutdownCh)
	return nil
}

// RPC is used to make an RPC call to the Nomad servers
func (a *Agent) RPC(method string, args interface{}, reply interface{}) error {
	if a.server != nil {
		return a.server.RPC(method, args, reply)
	}
	return a.client.RPC(method, args, reply)
}

// Client returns the configured client or nil
func (a *Agent) Client() *client.Client {
	return a.client
}

// Server returns the configured server or nil
func (a *Agent) Server() *nomad.Server {
	return a.server
}

// Stats is used to return statistics for debugging and insight
// for various sub-systems
func (a *Agent) Stats() map[string]map[string]string {
	stats := make(map[string]map[string]string)
	if a.server != nil {
		subStat := a.server.Stats()
		for k, v := range subStat {
			stats[k] = v
		}
	}
	if a.client != nil {
		subStat := a.client.Stats()
		for k, v := range subStat {
			stats[k] = v
		}
	}
	return stats
}
<commit_msg>Reserving ports for client in every platform<commit_after>package agent

import (
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/hashicorp/nomad/client"
	clientconfig "github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/nomad"
	"github.com/hashicorp/nomad/nomad/structs"
)

// Agent is a long running daemon that is used to run both
// clients and servers. Servers are responsible for managing
// state and making scheduling decisions. Clients can be
// scheduled to, and are responsible for interfacing with
// servers to run allocations.
type Agent struct {
	config    *Config
	logger    *log.Logger
	logOutput io.Writer

	server *nomad.Server
	client *client.Client

	shutdown     bool
	shutdownCh   chan struct{}
	shutdownLock sync.Mutex
}

// NewAgent is used to create a new agent with the given configuration
func NewAgent(config *Config, logOutput io.Writer) (*Agent, error) {
	// Ensure we have a log sink
	if logOutput == nil {
		logOutput = os.Stderr
	}

	a := &Agent{
		config:     config,
		logger:     log.New(logOutput, "", log.LstdFlags),
		logOutput:  logOutput,
		shutdownCh: make(chan struct{}),
	}

	if err := a.setupServer(); err != nil {
		return nil, err
	}
	if err := a.setupClient(); err != nil {
		return nil, err
	}
	if a.client == nil && a.server == nil {
		return nil, fmt.Errorf("must have at least client or server mode enabled")
	}
	return a, nil
}

// serverConfig is used to generate a new server configuration struct
// for initializing a nomad server.
func (a *Agent) serverConfig() (*nomad.Config, error) {
	conf := a.config.NomadConfig
	if conf == nil {
		conf = nomad.DefaultConfig()
	}
	conf.LogOutput = a.logOutput
	conf.DevMode = a.config.DevMode
	conf.Build = fmt.Sprintf("%s%s", a.config.Version, a.config.VersionPrerelease)
	if a.config.Region != "" {
		conf.Region = a.config.Region
	}
	if a.config.Datacenter != "" {
		conf.Datacenter = a.config.Datacenter
	}
	if a.config.NodeName != "" {
		conf.NodeName = a.config.NodeName
	}
	if a.config.Server.BootstrapExpect > 0 {
		if a.config.Server.BootstrapExpect == 1 {
			conf.Bootstrap = true
		} else {
			conf.BootstrapExpect = a.config.Server.BootstrapExpect
		}
	}
	if a.config.DataDir != "" {
		conf.DataDir = filepath.Join(a.config.DataDir, "server")
	}
	if a.config.Server.DataDir != "" {
		conf.DataDir = a.config.Server.DataDir
	}
	if a.config.Server.ProtocolVersion != 0 {
		conf.ProtocolVersion = uint8(a.config.Server.ProtocolVersion)
	}
	if a.config.Server.NumSchedulers != 0 {
		conf.NumSchedulers = a.config.Server.NumSchedulers
	}
	if len(a.config.Server.EnabledSchedulers) != 0 {
		conf.EnabledSchedulers = a.config.Server.EnabledSchedulers
	}

	// Set up the advertise addrs
	if addr := a.config.AdvertiseAddrs.Serf; addr != "" {
		serfAddr, err := net.ResolveTCPAddr("tcp", addr)
		if err != nil {
			return nil, fmt.Errorf("error resolving serf advertise address: %s", err)
		}
		conf.SerfConfig.MemberlistConfig.AdvertiseAddr = serfAddr.IP.String()
		conf.SerfConfig.MemberlistConfig.AdvertisePort = serfAddr.Port
	}
	if addr := a.config.AdvertiseAddrs.RPC; addr != "" {
		rpcAddr, err := net.ResolveTCPAddr("tcp", addr)
		if err != nil {
			return nil, fmt.Errorf("error resolving rpc advertise address: %s", err)
		}
		conf.RPCAdvertise = rpcAddr
	}

	// Set up the bind addresses
	if addr := a.config.BindAddr; addr != "" {
		conf.RPCAddr.IP = net.ParseIP(addr)
		conf.SerfConfig.MemberlistConfig.BindAddr = addr
	}
	if addr := a.config.Addresses.RPC; addr != "" {
		conf.RPCAddr.IP = net.ParseIP(addr)
	}
	if addr := a.config.Addresses.Serf; addr != "" {
		conf.SerfConfig.MemberlistConfig.BindAddr = addr
	}

	// Set up the ports
	if port := a.config.Ports.RPC; port != 0 {
		conf.RPCAddr.Port = port
	}
	if port := a.config.Ports.Serf; port != 0 {
		conf.SerfConfig.MemberlistConfig.BindPort = port
	}

	if gcThreshold := a.config.Server.NodeGCThreshold; gcThreshold != "" {
		dur, err := time.ParseDuration(gcThreshold)
		if err != nil {
			return nil, err
		}
		conf.NodeGCThreshold = dur
	}

	return conf, nil
}

// setupServer is used to setup the server if enabled
func (a *Agent) setupServer() error {
	if !a.config.Server.Enabled {
		return nil
	}

	// Setup the configuration
	conf, err := a.serverConfig()
	if err != nil {
		return fmt.Errorf("server config setup failed: %s", err)
	}

	// Create the server
	server, err := nomad.NewServer(conf)
	if err != nil {
		return fmt.Errorf("server setup failed: %v", err)
	}

	a.server = server
	return nil
}

// setupClient is used to setup the client if enabled
func (a *Agent) setupClient() error {
	if !a.config.Client.Enabled {
		return nil
	}

	// Setup the configuration
	conf := a.config.ClientConfig
	if conf == nil {
		conf = client.DefaultConfig()
	}
	if a.server != nil {
		conf.RPCHandler = a.server
	}
	conf.LogOutput = a.logOutput
	conf.DevMode = a.config.DevMode
	if a.config.Region != "" {
		conf.Region = a.config.Region
	}
	if a.config.DataDir != "" {
		conf.StateDir = filepath.Join(a.config.DataDir, "client")
		conf.AllocDir = filepath.Join(a.config.DataDir, "alloc")
	}
	if a.config.Client.StateDir != "" {
		conf.StateDir = a.config.Client.StateDir
	}
	if a.config.Client.AllocDir != "" {
		conf.AllocDir = a.config.Client.AllocDir
	}
	conf.Servers = a.config.Client.Servers
	if a.config.Client.NetworkInterface != "" {
		conf.NetworkInterface = a.config.Client.NetworkInterface
	}
	conf.Options = a.config.Client.Options
	if a.config.Client.NetworkSpeed != 0 {
		conf.NetworkSpeed = a.config.Client.NetworkSpeed
	}
	if a.config.Client.MaxKillTimeout != "" {
		dur, err := time.ParseDuration(a.config.Client.MaxKillTimeout)
		if err != nil {
			return fmt.Errorf("Error parsing retry interval: %s", err)
		}
		conf.MaxKillTimeout = dur
	}
	conf.ClientMaxPort = a.config.Client.ClientMaxPort
	conf.ClientMinPort = a.config.Client.ClientMinPort

	// Setup the node
	conf.Node = new(structs.Node)
	conf.Node.Datacenter = a.config.Datacenter
	conf.Node.Name = a.config.NodeName
	conf.Node.Meta = a.config.Client.Meta
	conf.Node.NodeClass = a.config.Client.NodeClass
	httpAddr := fmt.Sprintf("%s:%d", a.config.BindAddr, a.config.Ports.HTTP)
	if a.config.Addresses.HTTP != "" && a.config.AdvertiseAddrs.HTTP == "" {
		addr, err := net.ResolveTCPAddr("tcp", a.config.Addresses.HTTP)
		if err != nil {
			return fmt.Errorf("error resolving http addr: %v:", err)
		}
		httpAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
	} else if a.config.AdvertiseAddrs.HTTP != "" {
		addr, err := net.ResolveTCPAddr("tcp", a.config.AdvertiseAddrs.HTTP)
		if err != nil {
			return fmt.Errorf("error resolving advertise http addr: %v", err)
		}
		httpAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
	}
	conf.Node.HTTPAddr = httpAddr

	// Reserve some ports for the plugins
	if err := a.reservePortsForClient(conf); err != nil {
		return err
	}

	// Create the client
	client, err := client.NewClient(conf)
	if err != nil {
		return fmt.Errorf("client setup failed: %v", err)
	}
	a.client = client
	return nil
}

func (a *Agent) reservePortsForClient(conf *clientconfig.Config) error {
	deviceName, addr, mask, err := a.findLoopbackDevice()
	if err != nil {
		return fmt.Errorf("error finding the device name for loopback: %v", err)
	}
	var nr *structs.NetworkResource
	if conf.Node.Reserved == nil {
		conf.Node.Reserved = &structs.Resources{}
	}
	for _, n := range conf.Node.Reserved.Networks {
		if n.Device == deviceName {
			nr = n
		}
	}
	if nr == nil {
		nr = &structs.NetworkResource{
			Device:        deviceName,
			IP:            addr,
			CIDR:          mask,
			ReservedPorts: make([]structs.Port, 0),
		}
	}
	for i := conf.ClientMinPort; i <= conf.ClientMaxPort; i++ {
		nr.ReservedPorts = append(nr.ReservedPorts, structs.Port{Label: fmt.Sprintf("plugin-%d", i), Value: int(i)})
	}
	conf.Node.Reserved.Networks = append(conf.Node.Reserved.Networks, nr)
	return nil
}

func (a *Agent) findLoopbackDevice() (string, string, string, error) {
	var ifcs []net.Interface
	var err error
	var deviceName string
	ifcs, err = net.Interfaces()
	if err != nil {
		return "", "", "", err
	}
	for _, ifc := range ifcs {
		addrs, err := ifc.Addrs()
		if err != nil {
			return deviceName, "", "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip.IsLoopback() {
				return ifc.Name, ip.String(), addr.String(), nil
			}
		}
	}

	return deviceName, "", "", err
}

// Leave is used gracefully exit. Clients will inform servers
// of their departure so that allocations can be rescheduled.
func (a *Agent) Leave() error {
	if a.client != nil {
		if err := a.client.Leave(); err != nil {
			a.logger.Printf("[ERR] agent: client leave failed: %v", err)
		}
	}
	if a.server != nil {
		if err := a.server.Leave(); err != nil {
			a.logger.Printf("[ERR] agent: server leave failed: %v", err)
		}
	}
	return nil
}

// Shutdown is used to terminate the agent.
func (a *Agent) Shutdown() error {
	a.shutdownLock.Lock()
	defer a.shutdownLock.Unlock()

	if a.shutdown {
		return nil
	}

	a.logger.Println("[INFO] agent: requesting shutdown")
	if a.client != nil {
		if err := a.client.Shutdown(); err != nil {
			a.logger.Printf("[ERR] agent: client shutdown failed: %v", err)
		}
	}
	if a.server != nil {
		if err := a.server.Shutdown(); err != nil {
			a.logger.Printf("[ERR] agent: server shutdown failed: %v", err)
		}
	}

	a.logger.Println("[INFO] agent: shutdown complete")
	a.shutdown = true
	close(a.shutdownCh)
	return nil
}

// RPC is used to make an RPC call to the Nomad servers
func (a *Agent) RPC(method string, args interface{}, reply interface{}) error {
	if a.server != nil {
		return a.server.RPC(method, args, reply)
	}
	return a.client.RPC(method, args, reply)
}

// Client returns the configured client or nil
func (a *Agent) Client() *client.Client {
	return a.client
}

// Server returns the configured server or nil
func (a *Agent) Server() *nomad.Server {
	return a.server
}

// Stats is used to return statistics for debugging and insight
// for various sub-systems
func (a *Agent) Stats() map[string]map[string]string {
	stats := make(map[string]map[string]string)
	if a.server != nil {
		subStat := a.server.Stats()
		for k, v := range subStat {
			stats[k] = v
		}
	}
	if a.client != nil {
		subStat := a.client.Stats()
		for k, v := range subStat {
			stats[k] = v
		}
	}
	return stats
}
<|endoftext|>


<commit_before>package sysfs

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strconv"
	"strings"

	"github.com/prometheus/procfs/internal/util"
)

type PowerSupply struct {
	Name                     string // Power Supply Name
	Authentic                *int64 `fileName:"authentic"`                   // /sys/class/power_suppy/<Name>/authentic
	Calibrate                *int64 `fileName:"calibrate"`                   // /sys/class/power_suppy/<Name>/calibrate
	Capacity                 *int64 `fileName:"capacity"`                    // /sys/class/power_suppy/<Name>/capacity
	CapacityAlertMax         *int64 `fileName:"capacity_alert_max"`          // /sys/class/power_suppy/<Name>/capacity_alert_max
	CapacityAlertMin         *int64 `fileName:"capacity_alert_min"`          // /sys/class/power_suppy/<Name>/capacity_alert_min
	CapacityLevel            string `fileName:"capacity_level"`              // /sys/class/power_suppy/<Name>/capacity_level
	ChargeAvg                *int64 `fileName:"charge_avg"`                  // /sys/class/power_suppy/<Name>/charge_avg
	ChargeEmpty              *int64 `fileName:"charge_empty"`                // /sys/class/power_suppy/<Name>/charge_empty
	ChargeFull               *int64 `fileName:"charge_full"`                 // /sys/class/power_suppy/<Name>/charge_full
	ChargeNow                *int64 `fileName:"charge_now"`                  // /sys/class/power_suppy/<Name>/charge_now
	ChargeControlLimit       *int64 `fileName:"charge_control_limit"`        // /sys/class/power_suppy/<Name>/charge_control_limit
	ChargeControlLimitMax    *int64 `fileName:"charge_control_limit_max"`    // /sys/class/power_suppy/<Name>/charge_control_limit_max
	ChargeCounter            *int64 `fileName:"charge_counter"`              // /sys/class/power_suppy/<Name>/charge_counter
	ChargeEmptyDesign        *int64 `fileName:"charge_empty_design"`         // /sys/class/power_suppy/<Name>/charge_empty_design
	ChargeFullDesign         *int64 `fileName:"charge_full_design"`          // /sys/class/power_suppy/<Name>/charge_full_design
	ChargeTermCurrent        *int64 `fileName:"charge_term_current"`         // /sys/class/power_suppy/<Name>/charge_term_current
	ChargeType               string `fileName:"charge_type"`                 // /sys/class/power_supply/<Name>/charge_type
	ConstantChargeCurrent    *int64 `fileName:"constant_charge_current"`     // /sys/class/power_suppy/<Name>/constant_charge_current
	ConstantChargeCurrentMax *int64 `fileName:"constant_charge_current_max"` // /sys/class/power_suppy/<Name>/constant_charge_current_max
	ConstantChargeVoltage    *int64 `fileName:"constant_charge_voltage"`     // /sys/class/power_suppy/<Name>/constant_charge_voltage
	ConstantChargeVoltageMax *int64 `fileName:"constant_charge_voltage_max"` // /sys/class/power_suppy/<Name>/constant_charge_voltage_max
	CurrentBoot              *int64 `fileName:"current_boot"`                // /sys/class/power_suppy/<Name>/current_boot
	CurrentAvg               *int64 `fileName:"current_avg"`                 // /sys/class/power_suppy/<Name>/current_avg
	CurrentMax               *int64 `fileName:"current_max"`                 // /sys/class/power_suppy/<Name>/current_max
	CurrentNow               *int64 `fileName:"current_now"`                 // /sys/class/power_suppy/<Name>/current_now
	CycleCount               *int64 `fileName:"cycle_count"`                 // /sys/class/power_suppy/<Name>/cycle_count
	EnergyEmptyDesign        *int64 `fileName:"energy_empty_design"`         // /sys/class/power_suppy/<Name>/energy_empty_design
	EnergyFullDesign         *int64 `fileName:"energy_full_design"`          // /sys/class/power_suppy/<Name>/energy_full_design
	EnergyNow                *int64 `fileName:"energy_now"`                  // /sys/class/power_supply/<Name>/energy_now
	EnergyEmpty              *int64 `fileName:"energy_empty"`                // /sys/class/power_suppy/<Name>/energy_empty
	EnergyFull               *int64 `fileName:"energy_full"`                 // /sys/class/power_suppy/<Name>/energy_full
	EnergyAvg                *int64 `fileName:"energy_avg"`                  // /sys/class/power_supply/<Name>/energy_avg
	Health                   string `fileName:"health"`                      // /sys/class/power_suppy/<Name>/health
	InputCurrentLimit        *int64 `fileName:"input_current_limit"`         // /sys/class/power_suppy/<Name>/input_current_limit
	Manufacturer             string `fileName:"manufacturer"`                // /sys/class/power_suppy/<Name>/manufacturer
	ModelName                string `fileName:"model_name"`                  // /sys/class/power_suppy/<Name>/model_name
	Online                   *int64 `fileName:"online"`                      // /sys/class/power_suppy/<Name>/online
	PowerAvg                 *int64 `fileName:"power_avg"`                   // /sys/class/power_suppy/<Name>/power_avg
	PowerNow                 *int64 `fileName:"power_now"`                   // /sys/class/power_suppy/<Name>/power_now
	PrechargeCurrent         *int64 `fileName:"precharge_current"`           // /sys/class/power_suppy/<Name>/precharge_current
	Present                  *int64 `fileName:"power_avg"`                   // /sys/class/power_suppy/<Name>/present
	Scope                    *int64 `fileName:"scope"`                       // /sys/class/power_suppy/<Name>/scope
	SerialNumber             string `fileName:"serial_number"`               // /sys/class/power_suppy/<Name>/serial_number
	Status                   string `fileName:"status"`                      // /sys/class/power_supply/<Name>/status
	Technology               string `fileName:"technology"`                  // /sys/class/power_suppy/<Name>/technology
	Temp                     *int64 `fileName:"temp"`                        // /sys/class/power_suppy/<Name>/temp
	TempAlertMax             *int64 `fileName:"temp_alert_max"`              // /sys/class/power_suppy/<Name>/temp_alert_max
	TempAlertMin             *int64 `fileName:"temp_alert_min"`              // /sys/class/power_suppy/<Name>/temp_alert_min
	TempAmbient              *int64 `fileName:"temp_ambient"`                // /sys/class/power_suppy/<Name>/temp_ambient
	TempAmbientMax           *int64 `fileName:"temp_ambient_max"`            // /sys/class/power_suppy/<Name>/temp_ambient_max
	TempAmbientMin           *int64 `fileName:"temp_ambient_min"`            // /sys/class/power_suppy/<Name>/temp_ambient_min
	TempMax                  *int64 `fileName:"temp_max"`                    // /sys/class/power_suppy/<Name>/temp_max
	TempMin                  *int64 `fileName:"temp_min"`                    // /sys/class/power_suppy/<Name>/temp_min
	TimeToEmptyAvg           *int64 `fileName:"time_to_empty_avg"`           // /sys/class/power_suppy/<Name>/time_to_empty_avg
	TimeToFullAvg            *int64 `fileName:"time_to_full_avg"`            // /sys/class/power_suppy/<Name>/time_to_full_avg
	TimeToEmptyNow           *int64 `fileName:"time_to_empty_now"`           // /sys/class/power_suppy/<Name>/time_to_empty_now
	TimeToFullNow            *int64 `fileName:"time_to_full_now"`            // /sys/class/power_suppy/<Name>/time_to_full_now
	Type                     string `fileName:"type"`                        // /sys/class/power_supply/<Name>/type
	UsbType                  string `fileName:"usb_type"`                    // /sys/class/power_supply/<Name>/usb_type
	VoltageBoot              *int64 `fileName:"voltage_boot"`                // /sys/class/power_suppy/<Name>/voltage_boot
	VoltageMax               *int64 `fileName:"voltage_max"`                 // /sys/class/power_suppy/<Name>/voltage_max
	VoltageMaxDesign         *int64 `fileName:"voltage_max_design"`          // /sys/class/power_suppy/<Name>/voltage_max_design
	VoltageMin               *int64 `fileName:"voltage_min"`                 // /sys/class/power_suppy/<Name>/voltage_min
	VoltageMinDesign         *int64 `fileName:"voltage_min_design"`          // /sys/class/power_suppy/<Name>/voltage_min_design
	VoltageNow               *int64 `fileName:"voltage_now"`                 // /sys/class/power_supply/<Name>/voltage_now
	VoltageAvg               *int64 `fileName:"voltage_avg"`                 // /sys/class/power_supply/<Name>/voltage_avg
	VoltageOCV               *int64 `fileName:"voltage_ocv"`                 // /sys/class/power_suppy/<Name>/voltage_ocv
}

type PowerSupplyClass map[string]PowerSupply

func NewPowerSupplyClass() (PowerSupplyClass, error) {
	fs, err := NewFS(DefaultMountPoint)
	if err != nil {
		return nil, err
	}

	return fs.NewPowerSupplyClass()
}

func (fs FS) NewPowerSupplyClass() (PowerSupplyClass, error) {
	path := fs.Path("class/power_supply")

	powerSupplyDirs, err := ioutil.ReadDir(path)
	if err != nil {
		return PowerSupplyClass{}, fmt.Errorf("cannot access %s dir %s", path, err)
	}

	powerSupplyClass := PowerSupplyClass{}
	for _, powerSupplyDir := range powerSupplyDirs {
		powerSupply, err := powerSupplyClass.parsePowerSupply(path + "/" + powerSupplyDir.Name())
		if err != nil {
			return nil, err
		}
		powerSupply.Name = powerSupplyDir.Name()
		powerSupplyClass[powerSupplyDir.Name()] = *powerSupply
	}
	return powerSupplyClass, nil
}

func (psc PowerSupplyClass) parsePowerSupply(powerSupplyPath string) (*PowerSupply, error) {
	powerSupply := PowerSupply{}
	powerSupplyElem := reflect.ValueOf(&powerSupply).Elem()
	powerSupplyType := reflect.TypeOf(powerSupply)

	//start from 1 - skip the Name field
	for i := 1; i < powerSupplyElem.NumField(); i++ {
		fieldType := powerSupplyType.Field(i)
		fieldValue := powerSupplyElem.Field(i)

		if fieldType.Tag.Get("fileName") == "" {
			panic(fmt.Errorf("field %s does not have a filename tag", fieldType.Name))
		}

		value, err := util.SysReadFile(powerSupplyPath + "/" + fieldType.Tag.Get("fileName"))

		if err != nil {
			if os.IsNotExist(err) || err.Error() == "operation not supported" || err.Error() == "invalid argument" {
				continue
			}
			return nil, fmt.Errorf("could not access file %s: %s", fieldType.Tag.Get("fileName"), err)
		}

		switch fieldValue.Kind() {
		case reflect.String:
			fieldValue.SetString(value)
		case reflect.Ptr:
			var int64ptr *int64
			switch fieldValue.Type() {
			case reflect.TypeOf(int64ptr):
				var intValue int64
				if strings.HasPrefix(value, "0x") {
					intValue, err = strconv.ParseInt(value[2:], 16, 64)
					if err != nil {
						return nil, fmt.Errorf("expected hex value for %s, got: %s", fieldType.Name, value)
					}
				} else {
					intValue, err = strconv.ParseInt(value, 10, 64)
					if err != nil {
						return nil, fmt.Errorf("expected Uint64 value for %s, got: %s", fieldType.Name, value)
					}
				}
				fieldValue.Set(reflect.ValueOf(&intValue))
			default:
				return nil, fmt.Errorf("unhandled pointer type %q", fieldValue.Type())
			}
		default:
			return nil, fmt.Errorf("unhandled type %q", fieldValue.Kind())
		}
	}

	return &powerSupply, nil
}
<commit_msg>Sort power supply attributes<commit_after>package sysfs

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strconv"
	"strings"

	"github.com/prometheus/procfs/internal/util"
)

type PowerSupply struct {
	Name                     string // Power Supply Name
	Authentic                *int64 `fileName:"authentic"`                   // /sys/class/power_suppy/<Name>/authentic
	Calibrate                *int64 `fileName:"calibrate"`                   // /sys/class/power_suppy/<Name>/calibrate
	Capacity                 *int64 `fileName:"capacity"`                    // /sys/class/power_suppy/<Name>/capacity
	CapacityAlertMax         *int64 `fileName:"capacity_alert_max"`          // /sys/class/power_suppy/<Name>/capacity_alert_max
	CapacityAlertMin         *int64 `fileName:"capacity_alert_min"`          // /sys/class/power_suppy/<Name>/capacity_alert_min
	CapacityLevel            string `fileName:"capacity_level"`              // /sys/class/power_suppy/<Name>/capacity_level
	ChargeAvg                *int64 `fileName:"charge_avg"`                  // /sys/class/power_suppy/<Name>/charge_avg
	ChargeControlLimit       *int64 `fileName:"charge_control_limit"`        // /sys/class/power_suppy/<Name>/charge_control_limit
	ChargeControlLimitMax    *int64 `fileName:"charge_control_limit_max"`    // /sys/class/power_suppy/<Name>/charge_control_limit_max
	ChargeCounter            *int64 `fileName:"charge_counter"`              // /sys/class/power_suppy/<Name>/charge_counter
	ChargeEmpty              *int64 `fileName:"charge_empty"`                // /sys/class/power_suppy/<Name>/charge_empty
	ChargeEmptyDesign        *int64 `fileName:"charge_empty_design"`         // /sys/class/power_suppy/<Name>/charge_empty_design
	ChargeFull               *int64 `fileName:"charge_full"`                 // /sys/class/power_suppy/<Name>/charge_full
	ChargeFullDesign         *int64 `fileName:"charge_full_design"`          // /sys/class/power_suppy/<Name>/charge_full_design
	ChargeNow                *int64 `fileName:"charge_now"`                  // /sys/class/power_suppy/<Name>/charge_now
	ChargeTermCurrent        *int64 `fileName:"charge_term_current"`         // /sys/class/power_suppy/<Name>/charge_term_current
	ChargeType               string `fileName:"charge_type"`                 // /sys/class/power_supply/<Name>/charge_type
	ConstantChargeCurrent    *int64 `fileName:"constant_charge_current"`     // /sys/class/power_suppy/<Name>/constant_charge_current
	ConstantChargeCurrentMax *int64 `fileName:"constant_charge_current_max"` // /sys/class/power_suppy/<Name>/constant_charge_current_max
	ConstantChargeVoltage    *int64 `fileName:"constant_charge_voltage"`     // /sys/class/power_suppy/<Name>/constant_charge_voltage
	ConstantChargeVoltageMax *int64 `fileName:"constant_charge_voltage_max"` // /sys/class/power_suppy/<Name>/constant_charge_voltage_max
	CurrentAvg               *int64 `fileName:"current_avg"`                 // /sys/class/power_suppy/<Name>/current_avg
	CurrentBoot              *int64 `fileName:"current_boot"`                // /sys/class/power_suppy/<Name>/current_boot
	CurrentMax               *int64 `fileName:"current_max"`                 // /sys/class/power_suppy/<Name>/current_max
	CurrentNow               *int64 `fileName:"current_now"`                 // /sys/class/power_suppy/<Name>/current_now
	CycleCount               *int64 `fileName:"cycle_count"`                 // /sys/class/power_suppy/<Name>/cycle_count
	EnergyAvg                *int64 `fileName:"energy_avg"`                  // /sys/class/power_supply/<Name>/energy_avg
	EnergyEmpty              *int64 `fileName:"energy_empty"`                // /sys/class/power_suppy/<Name>/energy_empty
	EnergyEmptyDesign        *int64 `fileName:"energy_empty_design"`         // /sys/class/power_suppy/<Name>/energy_empty_design
	EnergyFull               *int64 `fileName:"energy_full"`                 // /sys/class/power_suppy/<Name>/energy_full
	EnergyFullDesign         *int64 `fileName:"energy_full_design"`          // /sys/class/power_suppy/<Name>/energy_full_design
	EnergyNow                *int64 `fileName:"energy_now"`                  // /sys/class/power_supply/<Name>/energy_now
	Health                   string `fileName:"health"`                      // /sys/class/power_suppy/<Name>/health
	InputCurrentLimit        *int64 `fileName:"input_current_limit"`         // /sys/class/power_suppy/<Name>/input_current_limit
	Manufacturer             string `fileName:"manufacturer"`                // /sys/class/power_suppy/<Name>/manufacturer
	ModelName                string `fileName:"model_name"`                  // /sys/class/power_suppy/<Name>/model_name
	Online                   *int64 `fileName:"online"`                      // /sys/class/power_suppy/<Name>/online
	PowerAvg                 *int64 `fileName:"power_avg"`                   // /sys/class/power_suppy/<Name>/power_avg
	PowerNow                 *int64 `fileName:"power_now"`                   // /sys/class/power_suppy/<Name>/power_now
	PrechargeCurrent         *int64 `fileName:"precharge_current"`           // /sys/class/power_suppy/<Name>/precharge_current
	Present                  *int64 `fileName:"power_avg"`                   // /sys/class/power_suppy/<Name>/present
	Scope                    *int64 `fileName:"scope"`                       // /sys/class/power_suppy/<Name>/scope
	SerialNumber             string `fileName:"serial_number"`               // /sys/class/power_suppy/<Name>/serial_number
	Status                   string `fileName:"status"`                      // /sys/class/power_supply/<Name>/status
	Technology               string `fileName:"technology"`                  // /sys/class/power_suppy/<Name>/technology
	Temp                     *int64 `fileName:"temp"`                        // /sys/class/power_suppy/<Name>/temp
	TempAlertMax             *int64 `fileName:"temp_alert_max"`              // /sys/class/power_suppy/<Name>/temp_alert_max
	TempAlertMin             *int64 `fileName:"temp_alert_min"`              // /sys/class/power_suppy/<Name>/temp_alert_min
	TempAmbient              *int64 `fileName:"temp_ambient"`                // /sys/class/power_suppy/<Name>/temp_ambient
	TempAmbientMax           *int64 `fileName:"temp_ambient_max"`            // /sys/class/power_suppy/<Name>/temp_ambient_max
	TempAmbientMin           *int64 `fileName:"temp_ambient_min"`            // /sys/class/power_suppy/<Name>/temp_ambient_min
	TempMax                  *int64 `fileName:"temp_max"`                    // /sys/class/power_suppy/<Name>/temp_max
	TempMin                  *int64 `fileName:"temp_min"`                    // /sys/class/power_suppy/<Name>/temp_min
	TimeToEmptyAvg           *int64 `fileName:"time_to_empty_avg"`           // /sys/class/power_suppy/<Name>/time_to_empty_avg
	TimeToEmptyNow           *int64 `fileName:"time_to_empty_now"`           // /sys/class/power_suppy/<Name>/time_to_empty_now
	TimeToFullAvg            *int64 `fileName:"time_to_full_avg"`            // /sys/class/power_suppy/<Name>/time_to_full_avg
	TimeToFullNow            *int64 `fileName:"time_to_full_now"`            // /sys/class/power_suppy/<Name>/time_to_full_now
	Type                     string `fileName:"type"`                        // /sys/class/power_supply/<Name>/type
	UsbType                  string `fileName:"usb_type"`                    // /sys/class/power_supply/<Name>/usb_type
	VoltageAvg               *int64 `fileName:"voltage_avg"`                 // /sys/class/power_supply/<Name>/voltage_avg
	VoltageBoot              *int64 `fileName:"voltage_boot"`                // /sys/class/power_suppy/<Name>/voltage_boot
	VoltageMax               *int64 `fileName:"voltage_max"`                 // /sys/class/power_suppy/<Name>/voltage_max
	VoltageMaxDesign         *int64 `fileName:"voltage_max_design"`          // /sys/class/power_suppy/<Name>/voltage_max_design
	VoltageMin               *int64 `fileName:"voltage_min"`                 // /sys/class/power_suppy/<Name>/voltage_min
	VoltageMinDesign         *int64 `fileName:"voltage_min_design"`          // /sys/class/power_suppy/<Name>/voltage_min_design
	VoltageNow               *int64 `fileName:"voltage_now"`                 // /sys/class/power_supply/<Name>/voltage_now
	VoltageOCV               *int64 `fileName:"voltage_ocv"`                 // /sys/class/power_suppy/<Name>/voltage_ocv
}

type PowerSupplyClass map[string]PowerSupply

func NewPowerSupplyClass() (PowerSupplyClass, error) {
	fs, err := NewFS(DefaultMountPoint)
	if err != nil {
		return nil, err
	}

	return fs.NewPowerSupplyClass()
}

func (fs FS) NewPowerSupplyClass() (PowerSupplyClass, error) {
	path := fs.Path("class/power_supply")

	powerSupplyDirs, err := ioutil.ReadDir(path)
	if err != nil {
		return PowerSupplyClass{}, fmt.Errorf("cannot access %s dir %s", path, err)
	}

	powerSupplyClass := PowerSupplyClass{}
	for _, powerSupplyDir := range powerSupplyDirs {
		powerSupply, err := powerSupplyClass.parsePowerSupply(path + "/" + powerSupplyDir.Name())
		if err != nil {
			return nil, err
		}
		powerSupply.Name = powerSupplyDir.Name()
		powerSupplyClass[powerSupplyDir.Name()] = *powerSupply
	}
	return powerSupplyClass, nil
}

func (psc PowerSupplyClass) parsePowerSupply(powerSupplyPath string) (*PowerSupply, error) {
	powerSupply := PowerSupply{}
	powerSupplyElem := reflect.ValueOf(&powerSupply).Elem()
	powerSupplyType := reflect.TypeOf(powerSupply)

	//start from 1 - skip the Name field
	for i := 1; i < powerSupplyElem.NumField(); i++ {
		fieldType := powerSupplyType.Field(i)
		fieldValue := powerSupplyElem.Field(i)

		if fieldType.Tag.Get("fileName") == "" {
			panic(fmt.Errorf("field %s does not have a filename tag", fieldType.Name))
		}

		value, err := util.SysReadFile(powerSupplyPath + "/" + fieldType.Tag.Get("fileName"))

		if err != nil {
			if os.IsNotExist(err) || err.Error() == "operation not supported" || err.Error() == "invalid argument" {
				continue
			}
			return nil, fmt.Errorf("could not access file %s: %s", fieldType.Tag.Get("fileName"), err)
		}

		switch fieldValue.Kind() {
		case reflect.String:
			fieldValue.SetString(value)
		case reflect.Ptr:
			var int64ptr *int64
			switch fieldValue.Type() {
			case reflect.TypeOf(int64ptr):
				var intValue int64
				if strings.HasPrefix(value, "0x") {
					intValue, err = strconv.ParseInt(value[2:], 16, 64)
					if err != nil {
						return nil, fmt.Errorf("expected hex value for %s, got: %s", fieldType.Name, value)
					}
				} else {
					intValue, err = strconv.ParseInt(value, 10, 64)
					if err != nil {
						return nil, fmt.Errorf("expected Uint64 value for %s, got: %s", fieldType.Name, value)
					}
				}
				fieldValue.Set(reflect.ValueOf(&intValue))
			default:
				return nil, fmt.Errorf("unhandled pointer type %q", fieldValue.Type())
			}
		default:
			return nil, fmt.Errorf("unhandled type %q", fieldValue.Kind())
		}
	}

	return &powerSupply, nil
}
<|endoftext|>


<commit_before>// +build !race

package command

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/hashicorp/vault/command/server"
	"github.com/hashicorp/vault/meta"
	"github.com/mitchellh/cli"
)

var (
	basehcl = `
disable_mlock = true

listener "tcp" {
  address = "127.0.0.1:8200"
  tls_disable = "true"
}
`

	consulhcl = `
backend "consul" {
    prefix = "foo/"
    advertise_addr = "http://127.0.0.1:8200"
}
`
	haconsulhcl = `
ha_backend "consul" {
    prefix = "bar/"
    advertise_addr = "http://127.0.0.1:8200"
}
`

	badhaconsulhcl = `
ha_backend "file" {
    path = "/dev/null"
}
`

	reloadhcl = `
backend "file" {
    path = "/dev/null"
}

disable_mlock = true

listener "tcp" {
    address = "127.0.0.1:8203"
    tls_cert_file = "TMPDIR/reload_FILE.pem"
    tls_key_file = "TMPDIR/reload_FILE.key"
}
`
)

// The following tests have a go-metrics/exp manager race condition
func TestServer_CommonHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: meta.Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}

	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	if !strings.Contains(ui.OutputWriter.String(), "(HA available)") {
		t.Fatalf("did not find HA available: %s", ui.OutputWriter.String())
	}
}

func TestServer_GoodSeparateHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: meta.Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl + haconsulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}

	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	if !strings.Contains(ui.OutputWriter.String(), "HA Backend:") {
		t.Fatalf("did not find HA Backend: %s", ui.OutputWriter.String())
	}
}

func TestServer_BadSeparateHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: meta.Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl + badhaconsulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name()}

	if code := c.Run(args); code == 0 {
		t.Fatalf("bad: should have gotten an error on a bad HA config")
	}
}

func TestServer_ReloadListener(t *testing.T) {
	wd, _ := os.Getwd()
	wd += "/server/test-fixtures/reload/"

	td, err := ioutil.TempDir("", fmt.Sprintf("vault-test-%d", rand.New(rand.NewSource(time.Now().Unix())).Int63))
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(td)

	wg := &sync.WaitGroup{}

	// Setup initial certs
	inBytes, _ := ioutil.ReadFile(wd + "reload_foo.pem")
	ioutil.WriteFile(td+"/reload_foo.pem", inBytes, 0777)
	inBytes, _ = ioutil.ReadFile(wd + "reload_foo.key")
	ioutil.WriteFile(td+"/reload_foo.key", inBytes, 0777)
	inBytes, _ = ioutil.ReadFile(wd + "reload_bar.pem")
	ioutil.WriteFile(td+"/reload_bar.pem", inBytes, 0777)
	inBytes, _ = ioutil.ReadFile(wd + "reload_bar.key")
	ioutil.WriteFile(td+"/reload_bar.key", inBytes, 0777)

	relhcl := strings.Replace(strings.Replace(reloadhcl, "TMPDIR", td, -1), "FILE", "foo", -1)
	ioutil.WriteFile(td+"/reload.hcl", []byte(relhcl), 0777)

	inBytes, _ = ioutil.ReadFile(wd + "reload_ca.pem")
	certPool := x509.NewCertPool()
	ok := certPool.AppendCertsFromPEM(inBytes)
	if !ok {
		t.Fatal("not ok when appending CA cert")
	}

	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: meta.Meta{
			Ui: ui,
		},
		ShutdownCh:  MakeShutdownCh(),
		SighupCh:    MakeSighupCh(),
		ReloadFuncs: map[string][]server.ReloadFunc{},
	}

	finished := false
	finishedMutex := sync.Mutex{}

	wg.Add(1)
	args := []string{"-config", td + "/reload.hcl"}
	go func() {
		if code := c.Run(args); code != 0 {
			t.Error("got a non-zero exit status")
		}
		finishedMutex.Lock()
		finished = true
		finishedMutex.Unlock()
		wg.Done()
	}()

	checkFinished := func() {
		finishedMutex.Lock()
		if finished {
			t.Fatal(fmt.Sprintf("finished early; relhcl was\n%s\nstdout was\n%s\nstderr was\n%s\n", relhcl, ui.OutputWriter.String(), ui.ErrorWriter.String()))
		}
		finishedMutex.Unlock()
	}

	testCertificateName := func(cn string) error {
		conn, err := tls.Dial("tcp", "127.0.0.1:8203", &tls.Config{
			RootCAs: certPool,
		})
		if err != nil {
			return err
		}
		defer conn.Close()
		if err = conn.Handshake(); err != nil {
			return err
		}
		servName := conn.ConnectionState().PeerCertificates[0].Subject.CommonName
		if servName != cn {
			return fmt.Errorf("expected %s, got %s", cn, servName)
		}
		return nil
	}

	checkFinished()
	time.Sleep(2 * time.Second)
	checkFinished()

	if err := testCertificateName("foo.example.com"); err != nil {
		t.Fatalf("certificate name didn't check out: %s", err)
	}

	relhcl = strings.Replace(strings.Replace(reloadhcl, "TMPDIR", td, -1), "FILE", "bar", -1)
	ioutil.WriteFile(td+"/reload.hcl", []byte(relhcl), 0777)

	c.SighupCh <- struct{}{}
	checkFinished()
	time.Sleep(2 * time.Second)
	checkFinished()

	if err := testCertificateName("bar.example.com"); err != nil {
		t.Fatalf("certificate name didn't check out: %s", err)
	}

	c.ShutdownCh <- struct{}{}

	wg.Wait()
}
<commit_msg>Disable service registration for consul HA tests<commit_after>// +build !race

package command

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/hashicorp/vault/command/server"
	"github.com/hashicorp/vault/meta"
	"github.com/mitchellh/cli"
)

var (
	basehcl = `
disable_mlock = true

listener "tcp" {
  address = "127.0.0.1:8200"
  tls_disable = "true"
}
`

	consulhcl = `
backend "consul" {
    prefix = "foo/"
    advertise_addr = "http://127.0.0.1:8200"
    disable_registration = "true"
}
`
	haconsulhcl = `
ha_backend "consul" {
    prefix = "bar/"
    advertise_addr = "http://127.0.0.1:8200"
    disable_registration = "true"
}
`

	badhaconsulhcl = `
ha_backend "file" {
    path = "/dev/null"
}
`

	reloadhcl = `
backend "file" {
    path = "/dev/null"
}

disable_mlock = true

listener "tcp" {
    address = "127.0.0.1:8203"
    tls_cert_file = "TMPDIR/reload_FILE.pem"
    tls_key_file = "TMPDIR/reload_FILE.key"
}
`
)

// The following tests have a go-metrics/exp manager race condition
func TestServer_CommonHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: meta.Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}

	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	if !strings.Contains(ui.OutputWriter.String(), "(HA available)") {
		t.Fatalf("did not find HA available: %s", ui.OutputWriter.String())
	}
}

func TestServer_GoodSeparateHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: meta.Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl + haconsulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}

	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	if !strings.Contains(ui.OutputWriter.String(), "HA Backend:") {
		t.Fatalf("did not find HA Backend: %s", ui.OutputWriter.String())
	}
}

func TestServer_BadSeparateHA(t *testing.T) {
	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: meta.Meta{
			Ui: ui,
		},
	}

	tmpfile, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("error creating temp dir: %v", err)
	}

	tmpfile.WriteString(basehcl + consulhcl + badhaconsulhcl)
	tmpfile.Close()
	defer os.Remove(tmpfile.Name())

	args := []string{"-config", tmpfile.Name()}

	if code := c.Run(args); code == 0 {
		t.Fatalf("bad: should have gotten an error on a bad HA config")
	}
}

func TestServer_ReloadListener(t *testing.T) {
	wd, _ := os.Getwd()
	wd += "/server/test-fixtures/reload/"

	td, err := ioutil.TempDir("", fmt.Sprintf("vault-test-%d", rand.New(rand.NewSource(time.Now().Unix())).Int63))
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(td)

	wg := &sync.WaitGroup{}

	// Setup initial certs
	inBytes, _ := ioutil.ReadFile(wd + "reload_foo.pem")
	ioutil.WriteFile(td+"/reload_foo.pem", inBytes, 0777)
	inBytes, _ = ioutil.ReadFile(wd + "reload_foo.key")
	ioutil.WriteFile(td+"/reload_foo.key", inBytes, 0777)
	inBytes, _ = ioutil.ReadFile(wd + "reload_bar.pem")
	ioutil.WriteFile(td+"/reload_bar.pem", inBytes, 0777)
	inBytes, _ = ioutil.ReadFile(wd + "reload_bar.key")
	ioutil.WriteFile(td+"/reload_bar.key", inBytes, 0777)

	relhcl := strings.Replace(strings.Replace(reloadhcl, "TMPDIR", td, -1), "FILE", "foo", -1)
	ioutil.WriteFile(td+"/reload.hcl", []byte(relhcl), 0777)

	inBytes, _ = ioutil.ReadFile(wd + "reload_ca.pem")
	certPool := x509.NewCertPool()
	ok := certPool.AppendCertsFromPEM(inBytes)
	if !ok {
		t.Fatal("not ok when appending CA cert")
	}

	ui := new(cli.MockUi)
	c := &ServerCommand{
		Meta: meta.Meta{
			Ui: ui,
		},
		ShutdownCh:  MakeShutdownCh(),
		SighupCh:    MakeSighupCh(),
		ReloadFuncs: map[string][]server.ReloadFunc{},
	}

	finished := false
	finishedMutex := sync.Mutex{}

	wg.Add(1)
	args := []string{"-config", td + "/reload.hcl"}
	go func() {
		if code := c.Run(args); code != 0 {
			t.Error("got a non-zero exit status")
		}
		finishedMutex.Lock()
		finished = true
		finishedMutex.Unlock()
		wg.Done()
	}()

	checkFinished := func() {
		finishedMutex.Lock()
		if finished {
			t.Fatal(fmt.Sprintf("finished early; relhcl was\n%s\nstdout was\n%s\nstderr was\n%s\n", relhcl, ui.OutputWriter.String(), ui.ErrorWriter.String()))
		}
		finishedMutex.Unlock()
	}

	testCertificateName := func(cn string) error {
		conn, err := tls.Dial("tcp", "127.0.0.1:8203", &tls.Config{
			RootCAs: certPool,
		})
		if err != nil {
			return err
		}
		defer conn.Close()
		if err = conn.Handshake(); err != nil {
			return err
		}
		servName := conn.ConnectionState().PeerCertificates[0].Subject.CommonName
		if servName != cn {
			return fmt.Errorf("expected %s, got %s", cn, servName)
		}
		return nil
	}

	checkFinished()
	time.Sleep(2 * time.Second)
	checkFinished()

	if err := testCertificateName("foo.example.com"); err != nil {
		t.Fatalf("certificate name didn't check out: %s", err)
	}

	relhcl = strings.Replace(strings.Replace(reloadhcl, "TMPDIR", td, -1), "FILE", "bar", -1)
	ioutil.WriteFile(td+"/reload.hcl", []byte(relhcl), 0777)

	c.SighupCh <- struct{}{}
	checkFinished()
	time.Sleep(2 * time.Second)
	checkFinished()

	if err := testCertificateName("bar.example.com"); err != nil {
		t.Fatalf("certificate name didn't check out: %s", err)
	}

	c.ShutdownCh <- struct{}{}

	wg.Wait()
}
<|endoftext|>


<commit_before>package fetcheddit

import (
	"code.leeclagett.com/grokeddit"
	"reflect"
	"testing"
)

func TestThingList(t *testing.T) {
	type Input struct {
		list          []string
		initialAnchor *AnchorPoint
	}

	type TestData struct {
		input  Input
		output []*grokeddit.Thing
	}

	// not sure how much how like this ... but I want to keep the two above
	// structs hidden to this file, not sure of any other way in Go
	validateThingList := func(
		t *testing.T, expected []*grokeddit.Thing, input thingList) {

		for input.hasNext() {

			result, error := input.getNext()

			if len(expected) == 0 {
				t.Error("Expected less")
			} else { // have another expected element

				if expected[0] == nil {
					if error == nil {
						t.Error("Expected non-nil error")
					}
				} else { // expected element
					if error != nil {
						t.Error("Expected non-nil error, has next!")
					}

					if !reflect.DeepEqual(result, *expected[0]) {
						t.Error("Mis-match on expected data")
					}
				}

				expected = expected[1:len(expected)]
			}
		}

		if len(expected) != 0 {
			t.Errorf("Expected %d more", len(expected))
		}

		_, error := input.getNext()
		if error == nil {
			t.Error("Expected non-nil error, reached EOF")
		}
	}

	tests := []TestData{
		// Test errors on first retrieval
		{Input{[]string{}, nil}, []*grokeddit.Thing{}},
		{
			Input{
				[]string{},
				&AnchorPoint{grokeddit.GlobalId{}, Previous},
			},
			[]*grokeddit.Thing{},
		},
		{
			Input{
				[]string{},
				&AnchorPoint{grokeddit.GlobalId{}, Next},
			},
			[]*grokeddit.Thing{},
		},
		// Test errors on second retrieval
		{
			Input{[]string{listingForward}, nil},
			[]*grokeddit.Thing{
				&listingOutputForward.Children[0],
				&listingOutputForward.Children[1],
				nil,
			},
		},
		{
			Input{
				[]string{listingReverse},
				&AnchorPoint{grokeddit.GlobalId{}, Previous},
			},
			[]*grokeddit.Thing{
				&listingOutputReverse.Children[1],
				&listingOutputReverse.Children[0],
				nil,
			},
		},
		{
			Input{
				[]string{listingForward},
				&AnchorPoint{grokeddit.GlobalId{}, Next},
			},
			[]*grokeddit.Thing{
				&listingOutputForward.Children[0],
				&listingOutputForward.Children[1],
				nil,
			},
		},
		// Test complete retrieval
		{
			Input{
				[]string{
					listingForward,
					listingForward,
					listingReverse,
				},
				nil,
			},
			[]*grokeddit.Thing{
				&listingOutputForward.Children[0],
				&listingOutputForward.Children[1],
				&listingOutputForward.Children[0],
				&listingOutputForward.Children[1],
				&listingOutputReverse.Children[0],
				&listingOutputReverse.Children[1],
			},
		},
		{
			Input{
				[]string{
					listingReverse,
					listingReverse,
					listingForward,
				},
				&AnchorPoint{grokeddit.GlobalId{}, Previous},
			},
			[]*grokeddit.Thing{
				&listingOutputReverse.Children[1],
				&listingOutputReverse.Children[0],
				&listingOutputReverse.Children[1],
				&listingOutputReverse.Children[0],
				&listingOutputForward.Children[1],
				&listingOutputForward.Children[0],
			},
		},
		{
			Input{
				[]string{
					listingForward,
					listingForward,
					listingReverse,
				},
				&AnchorPoint{grokeddit.GlobalId{}, Next},
				},
			[]*grokeddit.Thing{
				&listingOutputForward.Children[0],
				&listingOutputForward.Children[1],
				&listingOutputForward.Children[0],
				&listingOutputForward.Children[1],
				&listingOutputReverse.Children[0],
				&listingOutputReverse.Children[1],
			},
		},
	}

	for _, test := range tests {
		path, error := CreatePath("test_path")
		if error != nil {
			t.Error("Couldn't create path!")
		}

		thingList, error := fetchThingList(
			path.FetchGrokedListing,
			CreateTestFetch(test.input.list),
			test.input.initialAnchor)

		expectedList := test.output
		if error != nil {
			if len(expectedList) != 0 {
				t.Error("Unexpected error when creating thing list")
			}
		} else { // no error retrieving first block
			validateThingList(t, expectedList, thingList)
		}
	}
}
<commit_msg>ThingList tests now verify that the path retrieved is correct.<commit_after>package fetcheddit

import (
	"code.leeclagett.com/grokeddit"
	"reflect"
	"testing"
)

func TestThingList(t *testing.T) {
	type Input struct {
		list          []string
		initialAnchor *AnchorPoint
	}

	type Expected struct {
		output    []*grokeddit.Thing
		requested []string
	}

	type TestData struct {
		input    Input
		expected Expected
	}

	// not sure how much how like this ... but I want to keep the two above
	// structs hidden to this file, not sure of any other way in Go
	validateThingList := func(
		t *testing.T, expected []*grokeddit.Thing, actual thingList) {

		for actual.hasNext() {

			result, error := actual.getNext()

			if len(expected) == 0 {
				t.Error("Expected less")
			} else { // have another expected element

				if expected[0] == nil {
					if error == nil {
						t.Error("Expected non-nil error")
					}
				} else { // expected element
					if error != nil {
						t.Error("Expected non-nil error, has next!")
					}

					if !reflect.DeepEqual(result, *(expected[0])) {
						t.Error("Mis-match on expected data")
					}
				}

				expected = expected[1:len(expected)]
			}
		}

		if len(expected) != 0 {
			t.Errorf("Expected %d more", len(expected))
		}

		_, error := actual.getNext()
		if error == nil {
			t.Error("Expected non-nil error, reached EOF")
		}
	}

	validateFetched := func(t *testing.T, expected []string, actual *TestFetch) {
		for _, expectedPath := range expected {
			actualPath, error := actual.GetNextFetchLocation()
			if error != nil {
				t.Error("Expected another path")
			} else {
				if expectedPath != actualPath {
					t.Errorf(
						"Expected retrieval path \"%s\" but got \"%s\"",
						expectedPath,
						actualPath)
				}
			}
		}

		_, error := actual.Fetch("blah")
		if error == nil {
			t.Errorf("Did not fetch all paths")
		}

		leftoverPath, error := actual.GetNextFetchLocation()
		if error == nil {
			t.Errorf("Expected another path retrieval \"%s\"", leftoverPath)
		}
	}

	tests := []TestData{
		// Test errors on first retrieval
		{Input{[]string{}, nil}, Expected{[]*grokeddit.Thing{}, []string{}}},
		{
			Input{
				[]string{},
				&AnchorPoint{grokeddit.GlobalId{}, Previous},
			},
			Expected{[]*grokeddit.Thing{}, []string{}},
		},
		{
			Input{
				[]string{},
				&AnchorPoint{grokeddit.GlobalId{}, Next},
			},
			Expected{[]*grokeddit.Thing{}, []string{}},
		},
		// Test errors on second retrieval
		{
			Input{[]string{listingForward}, nil},
			Expected{
				[]*grokeddit.Thing{
					&listingOutputForward.Children[0],
					&listingOutputForward.Children[1],
					nil,
				},
				[]string{"test_path.json?"},
			},
		},
		{
			Input{
				[]string{listingReverse},
				&AnchorPoint{grokeddit.GlobalId{}, Previous},
			},
			Expected{
				[]*grokeddit.Thing{
					&listingOutputReverse.Children[1],
					&listingOutputReverse.Children[0],
					nil,
				},
				[]string{"test_path.json?before=t1_0"},
			},
		},
		{
			Input{
				[]string{listingForward},
				&AnchorPoint{grokeddit.GlobalId{}, Next},
			},
			Expected{
				[]*grokeddit.Thing{
					&listingOutputForward.Children[0],
					&listingOutputForward.Children[1],
					nil,
				},
				[]string{"test_path.json?after=t1_0"},
			},
		},
		// Test complete retrieval
		{
			Input{
				[]string{
					listingForward,
					listingForward,
					listingReverse,
				},
				nil,
			},
			Expected{
				[]*grokeddit.Thing{
					&listingOutputForward.Children[0],
					&listingOutputForward.Children[1],
					&listingOutputForward.Children[0],
					&listingOutputForward.Children[1],
					&listingOutputReverse.Children[0],
					&listingOutputReverse.Children[1],
				},
				[]string{
					"test_path.json?",
					"test_path.json?after=t3_20d5ol",
					"test_path.json?after=t3_20d5ol",
				},
			},
		},
		{
			Input{
				[]string{
					listingReverse,
					listingReverse,
					listingForward,
				},
				&AnchorPoint{grokeddit.GlobalId{}, Previous},
			},
			Expected{
				[]*grokeddit.Thing{
					&listingOutputReverse.Children[1],
					&listingOutputReverse.Children[0],
					&listingOutputReverse.Children[1],
					&listingOutputReverse.Children[0],
					&listingOutputForward.Children[1],
					&listingOutputForward.Children[0],
				},
				[]string{
					"test_path.json?before=t1_0",
					"test_path.json?before=t3_20d5ol",
					"test_path.json?before=t3_20d5ol",
				},
			},
		},
		{
			Input{
				[]string{
					listingForward,
					listingForward,
					listingReverse,
				},
				&AnchorPoint{grokeddit.GlobalId{}, Next},
			},
			Expected{
				[]*grokeddit.Thing{
					&listingOutputForward.Children[0],
					&listingOutputForward.Children[1],
					&listingOutputForward.Children[0],
					&listingOutputForward.Children[1],
					&listingOutputReverse.Children[0],
					&listingOutputReverse.Children[1],
				},
				[]string{
					"test_path.json?after=t1_0",
					"test_path.json?after=t3_20d5ol",
					"test_path.json?after=t3_20d5ol",
				},
			},
		},
	}

	for _, test := range tests {

		path, error := CreatePath("test_path")
		if error != nil {
			t.Error("Couldn't create path!")
		}

		testFetch := CreateTestFetch(test.input.list)
		thingList, error := fetchThingList(
			path.FetchGrokedListing,
			testFetch,
			test.input.initialAnchor)

		if error != nil {
			if len(test.expected.output) != 0 {
				t.Error("Unexpected error when creating thing list")
			}
		} else { // no error retrieving first block
			validateThingList(t, test.expected.output, thingList)
		}

		validateFetched(t, test.expected.requested, testFetch)
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"github.com/centurylinkcloud/clc-go-cli/base"
	"github.com/centurylinkcloud/clc-go-cli/models"
	"github.com/centurylinkcloud/clc-go-cli/models/datacenter"
	"github.com/centurylinkcloud/clc-go-cli/models/group"
)

const (
	BaseURL = "https://api.ctl.io"
)

type GroupList struct {
	CommandBase
}

func NewGroupList(info CommandExcInfo) *GroupList {
	g := GroupList{}
	g.ExcInfo = info
	return &g
}

func (g *GroupList) Execute(cn base.Connection) error {
	var err error

	g.Output, err = GetGroups(cn)
	if err != nil {
		return err
	}
	return nil
}

func GetGroups(cn base.Connection) ([]group.Entity, error) {
	var err error
	var groups []group.Entity

	datacenters := []datacenter.GetRes{}
	dcURL := fmt.Sprintf("%s/v2/datacenters/{accountAlias}", BaseURL)
	err = cn.ExecuteRequest("GET", dcURL, nil, &datacenters)
	if err != nil {
		return nil, err
	}

	for _, ref := range datacenters {
		// Get detailed DC info.
		d := datacenter.GetRes{}
		dcURL = fmt.Sprintf("%s%s?groupLinks=true", BaseURL, GetLink(ref.Links, "self"))
		err = cn.ExecuteRequest("GET", dcURL, nil, &d)
		if err != nil {
			return nil, err
		}
		// Get the root group of the given DC.
		g := group.Entity{}
		gURL := fmt.Sprintf("%s%s", BaseURL, GetLink(d.Links, "group"))
		err = cn.ExecuteRequest("GET", gURL, nil, &g)
		if err != nil {
			return nil, err
		}
		groups = append(groups, g)
	}
	return groups, nil
}

func GetLink(links []models.LinkEntity, resource string) string {
	for _, link := range links {
		if link.Rel == resource {
			return link.Href
		}
	}
	panic(fmt.Sprintf("No %s link found", resource))
}
<commit_msg>Rewrite GetGroups using goroutines in order to achieve robust performance<commit_after>package commands

import (
	"fmt"
	"github.com/centurylinkcloud/clc-go-cli/base"
	"github.com/centurylinkcloud/clc-go-cli/models"
	"github.com/centurylinkcloud/clc-go-cli/models/datacenter"
	"github.com/centurylinkcloud/clc-go-cli/models/group"
	"time"
)

const (
	BaseURL          = "https://api.ctl.io"
	GroupListTimeout = 200
)

type GroupList struct {
	CommandBase
}

func NewGroupList(info CommandExcInfo) *GroupList {
	g := GroupList{}
	g.ExcInfo = info
	return &g
}

func (g *GroupList) Execute(cn base.Connection) error {
	var err error

	g.Output, err = GetGroups(cn)
	if err != nil {
		return err
	}
	return nil
}

func GetGroups(cn base.Connection) ([]group.Entity, error) {
	datacenters := []datacenter.GetRes{}
	dcURL := fmt.Sprintf("%s/v2/datacenters/{accountAlias}", BaseURL)
	err := cn.ExecuteRequest("GET", dcURL, nil, &datacenters)
	if err != nil {
		return nil, err
	}

	done := make(chan error)
	groups := make([]group.Entity, len(datacenters))
	for i, ref := range datacenters {
		go loadGroups(ref, groups, i, cn, done)
	}

	received := 0
	for {
		select {
		case err := <-done:
			if err != nil {
				return nil, err
			}
			received += 1
			if received == len(datacenters) {
				return groups, nil
			}
		case <-time.After(time.Second * GroupListTimeout):
			return nil, fmt.Errorf("Request timeout error.")
		}
	}
}

func GetLink(links []models.LinkEntity, resource string) string {
	for _, link := range links {
		if link.Rel == resource {
			return link.Href
		}
	}
	panic(fmt.Sprintf("No %s link found", resource))
}

func loadGroups(ref datacenter.GetRes, groups []group.Entity, dcnumber int, cn base.Connection, done chan<- error) {
	// Get detailed DC info.
	d := datacenter.GetRes{}
	dcURL := fmt.Sprintf("%s%s?groupLinks=true", BaseURL, GetLink(ref.Links, "self"))
	err := cn.ExecuteRequest("GET", dcURL, nil, &d)
	if err != nil {
		done <- err
		return
	}
	// Get the root group of the given DC.
	g := group.Entity{}
	gURL := fmt.Sprintf("%s%s", BaseURL, GetLink(d.Links, "group"))
	err = cn.ExecuteRequest("GET", gURL, nil, &g)
	if err != nil {
		done <- err
		return
	}
	groups[dcnumber] = g
	done <- nil
}
<|endoftext|>


<commit_before>// Copyright 2011 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.

// copied from the http package in the standard library
package main

import (
	"bytes"
)

// The algorithm uses at most sniffLen bytes to make its decision.
const sniffLen = 512

// DetectContentType implements the algorithm described
// at http://mimesniff.spec.whatwg.org/ to determine the
// Content-Type of the given data.  It considers at most the
// first 512 bytes of data.  DetectContentType always returns
// a valid MIME type: if it cannot determine a more specific one, it
// returns "application/octet-stream".
func DetectContentType(data []byte) string {
	if len(data) > sniffLen {
		data = data[:sniffLen]
	}

	// Index of the first non-whitespace byte in data.
	firstNonWS := 0
	for ; firstNonWS < len(data) && isWS(data[firstNonWS]); firstNonWS++ {
	}

	for _, sig := range sniffSignatures {
		if ct := sig.match(data, firstNonWS); ct != "" {
			return ct
		}
	}

	return "application/octet-stream" // fallback
}

func isWS(b byte) bool {
	switch b {
	case '\t', '\n', '\x0c', '\r', ' ':
		return true
	}
	return false
}

type sniffSig interface {
	// match returns the MIME type of the data, or "" if unknown.
	match(data []byte, firstNonWS int) string
}

// Data matching the table in section 6.
var sniffSignatures = []sniffSig{
	htmlSig("<!DOCTYPE HTML"),
	htmlSig("<HTML"),
	htmlSig("<HEAD"),
	htmlSig("<SCRIPT"),
	htmlSig("<IFRAME"),
	htmlSig("<H1"),
	htmlSig("<DIV"),
	htmlSig("<FONT"),
	htmlSig("<TABLE"),
	htmlSig("<A"),
	htmlSig("<STYLE"),
	htmlSig("<TITLE"),
	htmlSig("<B"),
	htmlSig("<BODY"),
	htmlSig("<BR"),
	htmlSig("<P"),
	htmlSig("<!--"),

	&maskedSig{mask: []byte("\xFF\xFF\xFF\xFF\xFF"), pat: []byte("<?xml"), skipWS: true, ct: "text/xml; charset=utf-8"},

	&exactSig{[]byte("%PDF-"), "application/pdf"},
	&exactSig{[]byte("%!PS-Adobe-"), "application/postscript"},

	// UTF BOMs.
	&maskedSig{mask: []byte("\xFF\xFF\x00\x00"), pat: []byte("\xFE\xFF\x00\x00"), ct: "text/plain; charset=utf-16be"},
	&maskedSig{mask: []byte("\xFF\xFF\x00\x00"), pat: []byte("\xFF\xFE\x00\x00"), ct: "text/plain; charset=utf-16le"},
	&maskedSig{mask: []byte("\xFF\xFF\xFF\x00"), pat: []byte("\xEF\xBB\xBF\x00"), ct: "text/plain; charset=utf-8"},

	&exactSig{[]byte("GIF87a"), "image/gif"},
	&exactSig{[]byte("GIF89a"), "image/gif"},
	&exactSig{[]byte("\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"), "image/png"},
	&exactSig{[]byte("\xFF\xD8\xFF"), "image/jpeg"},
	&exactSig{[]byte("BM"), "image/bmp"},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WEBPVP"),
		ct:   "image/webp",
	},
	&exactSig{[]byte("\x00\x00\x01\x00"), "image/vnd.microsoft.icon"},
	&exactSig{[]byte("\x4F\x67\x67\x53\x00"), "application/ogg"},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WAVE"),
		ct:   "audio/wave",
	},
	&exactSig{[]byte("\x1A\x45\xDF\xA3"), "video/webm"},
	&exactSig{[]byte("\x52\x61\x72\x20\x1A\x07\x00"), "application/x-rar-compressed"},
	&exactSig{[]byte("\x50\x4B\x03\x04"), "application/zip"},
	&exactSig{[]byte("\x1F\x8B\x08"), "application/x-gzip"},

	// TODO(dsymonds): Re-enable this when the spec is sorted w.r.t. MP4.
	//mp4Sig(0),

	textSig(0), // should be last
}

type exactSig struct {
	sig []byte
	ct  string
}

func (e *exactSig) match(data []byte, firstNonWS int) string {
	if bytes.HasPrefix(data, e.sig) {
		return e.ct
	}
	return ""
}

type maskedSig struct {
	mask, pat []byte
	skipWS    bool
	ct        string
}

func (m *maskedSig) match(data []byte, firstNonWS int) string {
	if m.skipWS {
		data = data[firstNonWS:]
	}
	if len(data) < len(m.mask) {
		return ""
	}
	for i, mask := range m.mask {
		db := data[i] & mask
		if db != m.pat[i] {
			return ""
		}
	}
	return m.ct
}

type htmlSig []byte

func (h htmlSig) match(data []byte, firstNonWS int) string {
	data = data[firstNonWS:]
	if len(data) < len(h)+1 {
		return ""
	}
	for i, b := range h {
		db := data[i]
		if 'A' <= b && b <= 'Z' {
			db &= 0xDF
		}
		if b != db {
			return ""
		}
	}
	// Next byte must be space or right angle bracket.
	if db := data[len(h)]; db != ' ' && db != '>' {
		return ""
	}
	return "text/html; charset=utf-8"
}

type textSig int

func (textSig) match(data []byte, firstNonWS int) string {
	// c.f. section 5, step 4.
	for _, b := range data[firstNonWS:] {
		switch {
		case 0x00 <= b && b <= 0x08,
			b == 0x0B,
			0x0E <= b && b <= 0x1A,
			0x1C <= b && b <= 0x1F:
			return ""
		}
	}
	return "text/plain; charset=utf-8"
}
<commit_msg>Update sniff.go<commit_after>// Copyright 2011 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.

package main

import (
	"bytes"
	"encoding/binary"
)

// The algorithm uses at most sniffLen bytes to make its decision.
const sniffLen = 512

// DetectContentType implements the algorithm described
// at http://mimesniff.spec.whatwg.org/ to determine the
// Content-Type of the given data. It considers at most the
// first 512 bytes of data. DetectContentType always returns
// a valid MIME type: if it cannot determine a more specific one, it
// returns "application/octet-stream".
func DetectContentType(data []byte) string {
	if len(data) > sniffLen {
		data = data[:sniffLen]
	}

	// Index of the first non-whitespace byte in data.
	firstNonWS := 0
	for ; firstNonWS < len(data) && isWS(data[firstNonWS]); firstNonWS++ {
	}

	for _, sig := range sniffSignatures {
		if ct := sig.match(data, firstNonWS); ct != "" {
			return ct
		}
	}

	return "application/octet-stream" // fallback
}

func isWS(b byte) bool {
	switch b {
	case '\t', '\n', '\x0c', '\r', ' ':
		return true
	}
	return false
}

type sniffSig interface {
	// match returns the MIME type of the data, or "" if unknown.
	match(data []byte, firstNonWS int) string
}

// Data matching the table in section 6.
var sniffSignatures = []sniffSig{
	htmlSig("<!DOCTYPE HTML"),
	htmlSig("<HTML"),
	htmlSig("<HEAD"),
	htmlSig("<SCRIPT"),
	htmlSig("<IFRAME"),
	htmlSig("<H1"),
	htmlSig("<DIV"),
	htmlSig("<FONT"),
	htmlSig("<TABLE"),
	htmlSig("<A"),
	htmlSig("<STYLE"),
	htmlSig("<TITLE"),
	htmlSig("<B"),
	htmlSig("<BODY"),
	htmlSig("<BR"),
	htmlSig("<P"),
	htmlSig("<!--"),

	&maskedSig{mask: []byte("\xFF\xFF\xFF\xFF\xFF"), pat: []byte("<?xml"), skipWS: true, ct: "text/xml; charset=utf-8"},

	&exactSig{[]byte("%PDF-"), "application/pdf"},
	&exactSig{[]byte("%!PS-Adobe-"), "application/postscript"},

	// UTF BOMs.
	&maskedSig{mask: []byte("\xFF\xFF\x00\x00"), pat: []byte("\xFE\xFF\x00\x00"), ct: "text/plain; charset=utf-16be"},
	&maskedSig{mask: []byte("\xFF\xFF\x00\x00"), pat: []byte("\xFF\xFE\x00\x00"), ct: "text/plain; charset=utf-16le"},
	&maskedSig{mask: []byte("\xFF\xFF\xFF\x00"), pat: []byte("\xEF\xBB\xBF\x00"), ct: "text/plain; charset=utf-8"},

	&exactSig{[]byte("GIF87a"), "image/gif"},
	&exactSig{[]byte("GIF89a"), "image/gif"},
	&exactSig{[]byte("\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"), "image/png"},
	&exactSig{[]byte("\xFF\xD8\xFF"), "image/jpeg"},
	&exactSig{[]byte("BM"), "image/bmp"},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WEBPVP"),
		ct:   "image/webp",
	},
	&exactSig{[]byte("\x00\x00\x01\x00"), "image/vnd.microsoft.icon"},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WAVE"),
		ct:   "audio/wave",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("FORM\x00\x00\x00\x00AIFF"),
		ct:   "audio/aiff",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF"),
		pat:  []byte(".snd"),
		ct:   "audio/basic",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("OggS\x00"),
		ct:   "application/ogg",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("MThd\x00\x00\x00\x06"),
		ct:   "audio/midi",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF"),
		pat:  []byte("ID3"),
		ct:   "audio/mpeg",
	},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00AVI "),
		ct:   "video/avi",
	},
	&exactSig{[]byte("\x1A\x45\xDF\xA3"), "video/webm"},
	&exactSig{[]byte("\x52\x61\x72\x20\x1A\x07\x00"), "application/x-rar-compressed"},
	&exactSig{[]byte("\x50\x4B\x03\x04"), "application/zip"},
	&exactSig{[]byte("\x1F\x8B\x08"), "application/x-gzip"},

	mp4Sig{},

	textSig{}, // should be last
}

type exactSig struct {
	sig []byte
	ct  string
}

func (e *exactSig) match(data []byte, firstNonWS int) string {
	if bytes.HasPrefix(data, e.sig) {
		return e.ct
	}
	return ""
}

type maskedSig struct {
	mask, pat []byte
	skipWS    bool
	ct        string
}

func (m *maskedSig) match(data []byte, firstNonWS int) string {
	// pattern matching algorithm section 6
	// https://mimesniff.spec.whatwg.org/#pattern-matching-algorithm

	if m.skipWS {
		data = data[firstNonWS:]
	}
	if len(m.pat) != len(m.mask) {
		return ""
	}
	if len(data) < len(m.mask) {
		return ""
	}
	for i, mask := range m.mask {
		db := data[i] & mask
		if db != m.pat[i] {
			return ""
		}
	}
	return m.ct
}

type htmlSig []byte

func (h htmlSig) match(data []byte, firstNonWS int) string {
	data = data[firstNonWS:]
	if len(data) < len(h)+1 {
		return ""
	}
	for i, b := range h {
		db := data[i]
		if 'A' <= b && b <= 'Z' {
			db &= 0xDF
		}
		if b != db {
			return ""
		}
	}
	// Next byte must be space or right angle bracket.
	if db := data[len(h)]; db != ' ' && db != '>' {
		return ""
	}
	return "text/html; charset=utf-8"
}

var mp4ftype = []byte("ftyp")
var mp4 = []byte("mp4")

type mp4Sig struct{}

func (mp4Sig) match(data []byte, firstNonWS int) string {
	// https://mimesniff.spec.whatwg.org/#signature-for-mp4
	// c.f. section 6.2.1
	if len(data) < 12 {
		return ""
	}
	boxSize := int(binary.BigEndian.Uint32(data[:4]))
	if boxSize%4 != 0 || len(data) < boxSize {
		return ""
	}
	if !bytes.Equal(data[4:8], mp4ftype) {
		return ""
	}
	for st := 8; st < boxSize; st += 4 {
		if st == 12 {
			// minor version number
			continue
		}
		if bytes.Equal(data[st:st+3], mp4) {
			return "video/mp4"
		}
	}
	return ""
}

type textSig struct{}

func (textSig) match(data []byte, firstNonWS int) string {
	// c.f. section 5, step 4.
	for _, b := range data[firstNonWS:] {
		switch {
		case b <= 0x08,
			b == 0x0B,
			0x0E <= b && b <= 0x1A,
			0x1C <= b && b <= 0x1F:
			return ""
		}
	}
	return "text/plain; charset=utf-8"
}
<|endoftext|>