<commit_before>package commands

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/machine/cli"
	"github.com/docker/machine/commands/mcndirs"
	"github.com/docker/machine/drivers/errdriver"
	"github.com/docker/machine/libmachine/cert"
	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/drivers/plugin/localbinary"
	"github.com/docker/machine/libmachine/drivers/rpc"
	"github.com/docker/machine/libmachine/host"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/persist"
)

var (
	ErrUnknownShell       = errors.New("Error: Unknown shell")
	ErrNoMachineSpecified = errors.New("Error: Expected to get one or more machine names as arguments")
	ErrExpectedOneMachine = errors.New("Error: Expected one machine name as an argument")
)

// CommandLine contains all the information passed to the commands on the command line.
type CommandLine interface {
	ShowHelp()

	Application() *cli.App

	Args() cli.Args

	Bool(name string) bool

	String(name string) string

	StringSlice(name string) []string

	GlobalString(name string) string

	FlagNames() (names []string)

	Generic(name string) interface{}
}

type contextCommandLine struct {
	*cli.Context
}

func (c *contextCommandLine) ShowHelp() {
	cli.ShowCommandHelp(c.Context, c.Command.Name)
}

func (c *contextCommandLine) Application() *cli.App {
	return c.App
}

func newPluginDriver(driverName string, rawContent []byte) (drivers.Driver, error) {
	d, err := rpcdriver.NewRPCClientDriver(rawContent, driverName)
	if err != nil {
		// Not being able to find a driver binary is a "known error"
		if _, ok := err.(localbinary.ErrPluginBinaryNotFound); ok {
			return errdriver.NewDriver(driverName), nil
		}
		return nil, err
	}

	if driverName == "virtualbox" {
		return drivers.NewSerialDriver(d), nil
	}

	return d, nil
}

func fatalOnError(command func(commandLine CommandLine) error) func(context *cli.Context) {
	return func(context *cli.Context) {
		if err := command(&contextCommandLine{context}); err != nil {
			log.Fatal(err)
		}
	}
}

func confirmInput(msg string) (bool, error) {
	fmt.Printf("%s (y/n): ", msg)

	var resp string
	_, err := fmt.Scanln(&resp)
	if err != nil {
		return false, err
	}

	confirmed := strings.Index(strings.ToLower(resp), "y") == 0
	return confirmed, nil
}

func getStore(c CommandLine) persist.Store {
	certInfo := getCertPathInfoFromContext(c)
	return &persist.Filestore{
		Path:             c.GlobalString("storage-path"),
		CaCertPath:       certInfo.CaCertPath,
		CaPrivateKeyPath: certInfo.CaPrivateKeyPath,
	}
}

func listHosts(store persist.Store) ([]*host.Host, error) {
	cliHosts := []*host.Host{}

	hosts, err := store.List()
	if err != nil {
		return nil, fmt.Errorf("Error attempting to list hosts from store: %s", err)
	}

	for _, h := range hosts {
		d, err := newPluginDriver(h.DriverName, h.RawDriver)
		if err != nil {
			return nil, fmt.Errorf("Error attempting to invoke binary for plugin '%s': %s", h.DriverName, err)
		}

		h.Driver = d

		cliHosts = append(cliHosts, h)
	}

	return cliHosts, nil
}

func loadHost(store persist.Store, hostName string) (*host.Host, error) {
	h, err := store.Load(hostName)
	if err != nil {
		return nil, fmt.Errorf("Loading host from store failed: %s", err)
	}

	d, err := newPluginDriver(h.DriverName, h.RawDriver)
	if err != nil {
		return nil, fmt.Errorf("Error attempting to invoke binary for plugin: %s", err)
	}

	h.Driver = d

	return h, nil
}

func saveHost(store persist.Store, h *host.Host) error {
	if err := store.Save(h); err != nil {
		return fmt.Errorf("Error attempting to save host to store: %s", err)
	}

	return nil
}

func getFirstArgHost(c CommandLine) (*host.Host, error) {
	store := getStore(c)
	hostName := c.Args().First()

	h, err := loadHost(store, hostName)
	if err != nil {
		return nil, fmt.Errorf("Error trying to get host %q: %s", hostName, err)
	}

	return h, nil
}

func getHostsFromContext(c CommandLine) ([]*host.Host, error) {
	store := getStore(c)
	hosts := []*host.Host{}

	for _, hostName := range c.Args() {
		h, err := loadHost(store, hostName)
		if err != nil {
			return nil, fmt.Errorf("Could not load host %q: %s", hostName, err)
		}
		hosts = append(hosts, h)
	}

	return hosts, nil
}

var Commands = []cli.Command{
	{
		Name:   "active",
		Usage:  "Print which machine is active",
		Action: fatalOnError(cmdActive),
	},
	{
		Name:        "config",
		Usage:       "Print the connection config for machine",
		Description: "Argument is a machine name.",
		Action:      fatalOnError(cmdConfig),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "swarm",
				Usage: "Display the Swarm config instead of the Docker daemon",
			},
		},
	},
	{
		Flags:           sharedCreateFlags,
		Name:            "create",
		Usage:           fmt.Sprintf("Create a machine.\n\nRun '%s create --driver name' to include the create flags for that driver in the help text.", os.Args[0]),
		Action:          fatalOnError(cmdCreateOuter),
		SkipFlagParsing: true,
	},
	{
		Name:        "env",
		Usage:       "Display the commands to set up the environment for the Docker client",
		Description: "Argument is a machine name.",
		Action:      fatalOnError(cmdEnv),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "swarm",
				Usage: "Display the Swarm config instead of the Docker daemon",
			},
			cli.StringFlag{
				Name:  "shell",
				Usage: "Force environment to be configured for specified shell",
			},
			cli.BoolFlag{
				Name:  "unset, u",
				Usage: "Unset variables instead of setting them",
			},
			cli.BoolFlag{
				Name:  "no-proxy",
				Usage: "Add machine IP to NO_PROXY environment variable",
			},
		},
	},
	{
		Name:        "inspect",
		Usage:       "Inspect information about a machine",
		Description: "Argument is a machine name.",
		Action:      fatalOnError(cmdInspect),
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "format, f",
				Usage: "Format the output using the given go template.",
				Value: "",
			},
		},
	},
	{
		Name:        "ip",
		Usage:       "Get the IP address of a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdIP),
	},
	{
		Name:        "kill",
		Usage:       "Kill a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdKill),
	},
	{
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "quiet, q",
				Usage: "Enable quiet mode",
			},
			cli.StringSliceFlag{
				Name:  "filter",
				Usage: "Filter output based on conditions provided",
				Value: &cli.StringSlice{},
			},
		},
		Name:   "ls",
		Usage:  "List machines",
		Action: fatalOnError(cmdLs),
	},
	{
		Name:        "regenerate-certs",
		Usage:       "Regenerate TLS Certificates for a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdRegenerateCerts),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "force, f",
				Usage: "Force rebuild and do not prompt",
			},
		},
	},
	{
		Name:        "restart",
		Usage:       "Restart a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdRestart),
	},
	{
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "force, f",
				Usage: "Remove local configuration even if machine cannot be removed",
			},
		},
		Name:        "rm",
		Usage:       "Remove a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdRm),
	},
	{
		Name:            "ssh",
		Usage:           "Log into or run a command on a machine with SSH.",
		Description:     "Arguments are [machine-name] [command]",
		Action:          fatalOnError(cmdSSH),
		SkipFlagParsing: true,
	},
	{
		Name:        "scp",
		Usage:       "Copy files between machines",
		Description: "Arguments are [machine:][path] [machine:][path].",
		Action:      fatalOnError(cmdScp),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "recursive, r",
				Usage: "Copy files recursively (required to copy directories)",
			},
		},
	},
	{
		Name:        "start",
		Usage:       "Start a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdStart),
	},
	{
		Name:        "status",
		Usage:       "Get the status of a machine",
		Description: "Argument is a machine name.",
		Action:      fatalOnError(cmdStatus),
	},
	{
		Name:        "stop",
		Usage:       "Stop a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdStop),
	},
	{
		Name:        "upgrade",
		Usage:       "Upgrade a machine to the latest version of Docker",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdUpgrade),
	},
	{
		Name:        "url",
		Usage:       "Get the URL of a machine",
		Description: "Argument is a machine name.",
		Action:      fatalOnError(cmdURL),
	},
}

func printIP(h *host.Host) func() error {
	return func() error {
		ip, err := h.Driver.GetIP()
		if err != nil {
			return fmt.Errorf("Error getting IP address: %s", err)
		}
		fmt.Println(ip)
		return nil
	}
}

// machineCommand maps the command name to the corresponding machine command.
// We run commands concurrently and communicate back an error if there was one.
func machineCommand(actionName string, host *host.Host, errorChan chan<- error) {
	// TODO: These actions should have their own type.
	commands := map[string](func() error){
		"configureAuth": host.ConfigureAuth,
		"start":         host.Start,
		"stop":          host.Stop,
		"restart":       host.Restart,
		"kill":          host.Kill,
		"upgrade":       host.Upgrade,
		"ip":            printIP(host),
	}

	log.Debugf("command=%s machine=%s", actionName, host.Name)

	errorChan <- commands[actionName]()
}

// runActionForeachMachine will run the command across multiple machines
func runActionForeachMachine(actionName string, machines []*host.Host) []error {
	var (
		numConcurrentActions = 0
		errorChan            = make(chan error)
		errs                 = []error{}
	)

	for _, machine := range machines {
		numConcurrentActions++
		go machineCommand(actionName, machine, errorChan)
	}

	// TODO: We should probably only do 5-10 of these
	// at a time, since otherwise cloud providers might
	// rate limit us.
	for i := 0; i < numConcurrentActions; i++ {
		if err := <-errorChan; err != nil {
			errs = append(errs, err)
		}
	}

	close(errorChan)

	return errs
}

func consolidateErrs(errs []error) error {
	finalErr := ""
	for _, err := range errs {
		finalErr = fmt.Sprintf("%s\n%s", finalErr, err)
	}

	return errors.New(strings.TrimSpace(finalErr))
}

func runActionWithContext(actionName string, c CommandLine) error {
	store := getStore(c)

	hosts, err := getHostsFromContext(c)
	if err != nil {
		return err
	}

	if len(hosts) == 0 {
		return ErrNoMachineSpecified
	}

	if errs := runActionForeachMachine(actionName, hosts); len(errs) > 0 {
		return consolidateErrs(errs)
	}

	for _, h := range hosts {
		if err := saveHost(store, h); err != nil {
			return fmt.Errorf("Error saving host to store: %s", err)
		}
	}

	return nil
}

// Returns the cert paths.
// codegangsta/cli will not set the cert paths if the storage-path is set to
// something different so we cannot use the paths in the global options. le
// sigh.
func getCertPathInfoFromContext(c CommandLine) cert.PathInfo {
	caCertPath := c.GlobalString("tls-ca-cert")
	if caCertPath == "" {
		caCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca.pem")
	}

	caKeyPath := c.GlobalString("tls-ca-key")
	if caKeyPath == "" {
		caKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca-key.pem")
	}

	clientCertPath := c.GlobalString("tls-client-cert")
	if clientCertPath == "" {
		clientCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "cert.pem")
	}

	clientKeyPath := c.GlobalString("tls-client-key")
	if clientKeyPath == "" {
		clientKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "key.pem")
	}

	return cert.PathInfo{
		CaCertPath:       caCertPath,
		CaPrivateKeyPath: caKeyPath,
		ClientCertPath:   clientCertPath,
		ClientKeyPath:    clientKeyPath,
	}
}
<commit_msg>Fix create usage & description in cli<commit_after>package commands

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/machine/cli"
	"github.com/docker/machine/commands/mcndirs"
	"github.com/docker/machine/drivers/errdriver"
	"github.com/docker/machine/libmachine/cert"
	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/drivers/plugin/localbinary"
	"github.com/docker/machine/libmachine/drivers/rpc"
	"github.com/docker/machine/libmachine/host"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/persist"
)

var (
	ErrUnknownShell       = errors.New("Error: Unknown shell")
	ErrNoMachineSpecified = errors.New("Error: Expected to get one or more machine names as arguments")
	ErrExpectedOneMachine = errors.New("Error: Expected one machine name as an argument")
)

// CommandLine contains all the information passed to the commands on the command line.
type CommandLine interface {
	ShowHelp()

	Application() *cli.App

	Args() cli.Args

	Bool(name string) bool

	String(name string) string

	StringSlice(name string) []string

	GlobalString(name string) string

	FlagNames() (names []string)

	Generic(name string) interface{}
}

type contextCommandLine struct {
	*cli.Context
}

func (c *contextCommandLine) ShowHelp() {
	cli.ShowCommandHelp(c.Context, c.Command.Name)
}

func (c *contextCommandLine) Application() *cli.App {
	return c.App
}

func newPluginDriver(driverName string, rawContent []byte) (drivers.Driver, error) {
	d, err := rpcdriver.NewRPCClientDriver(rawContent, driverName)
	if err != nil {
		// Not being able to find a driver binary is a "known error"
		if _, ok := err.(localbinary.ErrPluginBinaryNotFound); ok {
			return errdriver.NewDriver(driverName), nil
		}
		return nil, err
	}

	if driverName == "virtualbox" {
		return drivers.NewSerialDriver(d), nil
	}

	return d, nil
}

func fatalOnError(command func(commandLine CommandLine) error) func(context *cli.Context) {
	return func(context *cli.Context) {
		if err := command(&contextCommandLine{context}); err != nil {
			log.Fatal(err)
		}
	}
}

func confirmInput(msg string) (bool, error) {
	fmt.Printf("%s (y/n): ", msg)

	var resp string
	_, err := fmt.Scanln(&resp)
	if err != nil {
		return false, err
	}

	confirmed := strings.Index(strings.ToLower(resp), "y") == 0
	return confirmed, nil
}

func getStore(c CommandLine) persist.Store {
	certInfo := getCertPathInfoFromContext(c)
	return &persist.Filestore{
		Path:             c.GlobalString("storage-path"),
		CaCertPath:       certInfo.CaCertPath,
		CaPrivateKeyPath: certInfo.CaPrivateKeyPath,
	}
}

func listHosts(store persist.Store) ([]*host.Host, error) {
	cliHosts := []*host.Host{}

	hosts, err := store.List()
	if err != nil {
		return nil, fmt.Errorf("Error attempting to list hosts from store: %s", err)
	}

	for _, h := range hosts {
		d, err := newPluginDriver(h.DriverName, h.RawDriver)
		if err != nil {
			return nil, fmt.Errorf("Error attempting to invoke binary for plugin '%s': %s", h.DriverName, err)
		}

		h.Driver = d

		cliHosts = append(cliHosts, h)
	}

	return cliHosts, nil
}

func loadHost(store persist.Store, hostName string) (*host.Host, error) {
	h, err := store.Load(hostName)
	if err != nil {
		return nil, fmt.Errorf("Loading host from store failed: %s", err)
	}

	d, err := newPluginDriver(h.DriverName, h.RawDriver)
	if err != nil {
		return nil, fmt.Errorf("Error attempting to invoke binary for plugin: %s", err)
	}

	h.Driver = d

	return h, nil
}

func saveHost(store persist.Store, h *host.Host) error {
	if err := store.Save(h); err != nil {
		return fmt.Errorf("Error attempting to save host to store: %s", err)
	}

	return nil
}

func getFirstArgHost(c CommandLine) (*host.Host, error) {
	store := getStore(c)
	hostName := c.Args().First()

	h, err := loadHost(store, hostName)
	if err != nil {
		return nil, fmt.Errorf("Error trying to get host %q: %s", hostName, err)
	}

	return h, nil
}

func getHostsFromContext(c CommandLine) ([]*host.Host, error) {
	store := getStore(c)
	hosts := []*host.Host{}

	for _, hostName := range c.Args() {
		h, err := loadHost(store, hostName)
		if err != nil {
			return nil, fmt.Errorf("Could not load host %q: %s", hostName, err)
		}
		hosts = append(hosts, h)
	}

	return hosts, nil
}

var Commands = []cli.Command{
	{
		Name:   "active",
		Usage:  "Print which machine is active",
		Action: fatalOnError(cmdActive),
	},
	{
		Name:        "config",
		Usage:       "Print the connection config for machine",
		Description: "Argument is a machine name.",
		Action:      fatalOnError(cmdConfig),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "swarm",
				Usage: "Display the Swarm config instead of the Docker daemon",
			},
		},
	},
	{
		Flags:           sharedCreateFlags,
		Name:            "create",
		Usage:           "Create a machine",
		Description:     fmt.Sprintf("Run '%s create --driver name' to include the create flags for that driver in the help text.", os.Args[0]),
		Action:          fatalOnError(cmdCreateOuter),
		SkipFlagParsing: true,
	},
	{
		Name:        "env",
		Usage:       "Display the commands to set up the environment for the Docker client",
		Description: "Argument is a machine name.",
		Action:      fatalOnError(cmdEnv),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "swarm",
				Usage: "Display the Swarm config instead of the Docker daemon",
			},
			cli.StringFlag{
				Name:  "shell",
				Usage: "Force environment to be configured for specified shell",
			},
			cli.BoolFlag{
				Name:  "unset, u",
				Usage: "Unset variables instead of setting them",
			},
			cli.BoolFlag{
				Name:  "no-proxy",
				Usage: "Add machine IP to NO_PROXY environment variable",
			},
		},
	},
	{
		Name:        "inspect",
		Usage:       "Inspect information about a machine",
		Description: "Argument is a machine name.",
		Action:      fatalOnError(cmdInspect),
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "format, f",
				Usage: "Format the output using the given go template.",
				Value: "",
			},
		},
	},
	{
		Name:        "ip",
		Usage:       "Get the IP address of a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdIP),
	},
	{
		Name:        "kill",
		Usage:       "Kill a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdKill),
	},
	{
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "quiet, q",
				Usage: "Enable quiet mode",
			},
			cli.StringSliceFlag{
				Name:  "filter",
				Usage: "Filter output based on conditions provided",
				Value: &cli.StringSlice{},
			},
		},
		Name:   "ls",
		Usage:  "List machines",
		Action: fatalOnError(cmdLs),
	},
	{
		Name:        "regenerate-certs",
		Usage:       "Regenerate TLS Certificates for a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdRegenerateCerts),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "force, f",
				Usage: "Force rebuild and do not prompt",
			},
		},
	},
	{
		Name:        "restart",
		Usage:       "Restart a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdRestart),
	},
	{
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "force, f",
				Usage: "Remove local configuration even if machine cannot be removed",
			},
		},
		Name:        "rm",
		Usage:       "Remove a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdRm),
	},
	{
		Name:            "ssh",
		Usage:           "Log into or run a command on a machine with SSH.",
		Description:     "Arguments are [machine-name] [command]",
		Action:          fatalOnError(cmdSSH),
		SkipFlagParsing: true,
	},
	{
		Name:        "scp",
		Usage:       "Copy files between machines",
		Description: "Arguments are [machine:][path] [machine:][path].",
		Action:      fatalOnError(cmdScp),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "recursive, r",
				Usage: "Copy files recursively (required to copy directories)",
			},
		},
	},
	{
		Name:        "start",
		Usage:       "Start a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdStart),
	},
	{
		Name:        "status",
		Usage:       "Get the status of a machine",
		Description: "Argument is a machine name.",
		Action:      fatalOnError(cmdStatus),
	},
	{
		Name:        "stop",
		Usage:       "Stop a machine",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdStop),
	},
	{
		Name:        "upgrade",
		Usage:       "Upgrade a machine to the latest version of Docker",
		Description: "Argument(s) are one or more machine names.",
		Action:      fatalOnError(cmdUpgrade),
	},
	{
		Name:        "url",
		Usage:       "Get the URL of a machine",
		Description: "Argument is a machine name.",
		Action:      fatalOnError(cmdURL),
	},
}

func printIP(h *host.Host) func() error {
	return func() error {
		ip, err := h.Driver.GetIP()
		if err != nil {
			return fmt.Errorf("Error getting IP address: %s", err)
		}
		fmt.Println(ip)
		return nil
	}
}

// machineCommand maps the command name to the corresponding machine command.
// We run commands concurrently and communicate back an error if there was one.
func machineCommand(actionName string, host *host.Host, errorChan chan<- error) {
	// TODO: These actions should have their own type.
	commands := map[string](func() error){
		"configureAuth": host.ConfigureAuth,
		"start":         host.Start,
		"stop":          host.Stop,
		"restart":       host.Restart,
		"kill":          host.Kill,
		"upgrade":       host.Upgrade,
		"ip":            printIP(host),
	}

	log.Debugf("command=%s machine=%s", actionName, host.Name)

	errorChan <- commands[actionName]()
}

// runActionForeachMachine will run the command across multiple machines
func runActionForeachMachine(actionName string, machines []*host.Host) []error {
	var (
		numConcurrentActions = 0
		errorChan            = make(chan error)
		errs                 = []error{}
	)

	for _, machine := range machines {
		numConcurrentActions++
		go machineCommand(actionName, machine, errorChan)
	}

	// TODO: We should probably only do 5-10 of these
	// at a time, since otherwise cloud providers might
	// rate limit us.
	for i := 0; i < numConcurrentActions; i++ {
		if err := <-errorChan; err != nil {
			errs = append(errs, err)
		}
	}

	close(errorChan)

	return errs
}

func consolidateErrs(errs []error) error {
	finalErr := ""
	for _, err := range errs {
		finalErr = fmt.Sprintf("%s\n%s", finalErr, err)
	}

	return errors.New(strings.TrimSpace(finalErr))
}

func runActionWithContext(actionName string, c CommandLine) error {
	store := getStore(c)

	hosts, err := getHostsFromContext(c)
	if err != nil {
		return err
	}

	if len(hosts) == 0 {
		return ErrNoMachineSpecified
	}

	if errs := runActionForeachMachine(actionName, hosts); len(errs) > 0 {
		return consolidateErrs(errs)
	}

	for _, h := range hosts {
		if err := saveHost(store, h); err != nil {
			return fmt.Errorf("Error saving host to store: %s", err)
		}
	}

	return nil
}

// Returns the cert paths.
// codegangsta/cli will not set the cert paths if the storage-path is set to
// something different so we cannot use the paths in the global options. le
// sigh.
func getCertPathInfoFromContext(c CommandLine) cert.PathInfo {
	caCertPath := c.GlobalString("tls-ca-cert")
	if caCertPath == "" {
		caCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca.pem")
	}

	caKeyPath := c.GlobalString("tls-ca-key")
	if caKeyPath == "" {
		caKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca-key.pem")
	}

	clientCertPath := c.GlobalString("tls-client-cert")
	if clientCertPath == "" {
		clientCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "cert.pem")
	}

	clientKeyPath := c.GlobalString("tls-client-key")
	if clientKeyPath == "" {
		clientKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "key.pem")
	}

	return cert.PathInfo{
		CaCertPath:       caCertPath,
		CaPrivateKeyPath: caKeyPath,
		ClientCertPath:   clientCertPath,
		ClientKeyPath:    clientKeyPath,
	}
}
<|endoftext|>


<commit_before>package hof

import "testing"
import "reflect"

func TestIntMap(t *testing.T) {
	var mapper func(func(int) int, []int) []int
	MakeMapFunc(&mapper)

	in := []int{1, 2, 3, 4, 5}
	f := func(x int) int { return x * 2 }
	exp := []int{2, 4, 6, 8, 10}

	out := mapper(f, in)
	if !reflect.DeepEqual(exp, out) {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestStringIntMap(t *testing.T) {
	var mapper func(func(string) int, []string) []int
	MakeMapFunc(&mapper)

	in := []string{"try", "this", "thing"}
	f := func(x string) int { return len(x) }
	exp := []int{3, 4, 5}

	out := mapper(f, in)

	if !reflect.DeepEqual(exp, out) {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestIntStringMap(t *testing.T) {
	var mapper func(func(int) string, []int) []string
	MakeMapFunc(&mapper)

	in := []int{1, 2, 3}
	exp := []string{"x", "xx", "xxx"}
	f := func(x int) string {
		out := ""
		for i := 0; i < x; i++ {
			out += "x"
		}
		return out
	}

	out := mapper(f, in)

	if !reflect.DeepEqual(exp, out) {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestEmptyMap(t *testing.T) {
	var mapper func(func(int) int, []int) []int
	MakeMapFunc(&mapper)

	in := []int{}
	f := func(x int) int { return x * 2 }
	exp := []int{}

	out := mapper(f, in)
	if !reflect.DeepEqual(exp, out) {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestFilter(t *testing.T) {
	var filter func(func(int) bool, []int) []int
	MakeFilterFunc(&filter)

	in := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	f := func(x int) bool { return x%2 == 0 }
	exp := []int{2, 4, 6, 8, 10}

	out := filter(f, in)
	if !reflect.DeepEqual(exp, out) {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestReduce(t *testing.T) {
	var reduce func(func(int, int) int, []int) int
	MakeReduceFunc(&reduce)

	in := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	f := func(x, y int) int { return x + y }
	exp := 55

	out := reduce(f, in)
	if exp != out {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestReduceInit(t *testing.T) {
	var reduce func(func(int, int) int, []int, int) int
	MakeReduceFunc(&reduce)

	in := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	f := func(x, y int) int { return x + y }
	init := 45
	exp := 100

	out := reduce(f, in, init)
	if exp != out {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestReduceTwoTypes(t *testing.T) {
	var reduce func(func(string, int) string, []int, string) string
	MakeReduceFunc(&reduce)

	in := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	f := func(x string, y int) string { return x + string('0'+y) }
	init := ""
	exp := "0123456789"

	out := reduce(f, in, init)
	if exp != out {
		t.Fatal("expected", exp, ", got", out)
	}
}

func sliceRange(n int) []int {
	out := make([]int, n, n)
	for i := 0; i < n; i++ {
		out[i] = i
	}
	return out
}

var benchmarkIn = sliceRange(100)

func BenchmarkForMap(b *testing.B) {
	for n := 0; n < b.N; n++ {
		l := len(benchmarkIn)
		out := make([]int, l, l)
		for i := 0; i < l; i++ {
			out[i] = benchmarkIn[i] * 2
		}
		if out[1] != 2 {
			panic("wrong result")
		}
	}
}

func BenchmarkMakeMapFunc(b *testing.B) {
	var mapper func(func(int) int, []int) []int
	MakeMapFunc(&mapper)

	for n := 0; n < b.N; n++ {
		f := func(x int) int { return x * 2 }
		out := mapper(f, benchmarkIn)
		if out[1] != 2 {
			panic("wrong result")
		}
	}
}

func interfaceMapper(f func(interface{}) interface{}, in []interface{}) []interface{} {
	out := make([]interface{}, len(in), len(in))
	for i, v := range in {
		out[i] = f(v)
	}
	return out
}

func BenchmarkInterfaceMapFunc(b *testing.B) {
	for n := 0; n < b.N; n++ {
		l := len(benchmarkIn)

		interfaceIn := make([]interface{}, l, l)
		for i, x := range benchmarkIn {
			interfaceIn[i] = x
		}

		f := func(x interface{}) interface{} {
			return x.(int) * 2
		}

		interfaceOut := interfaceMapper(f, interfaceIn)

		out := make([]int, l, l)
		for i, n := range interfaceOut {
			out[i] = n.(int)
		}
	}
}
<commit_msg>add another benchmark that calls a function to get the next mapped value.<commit_after>package hof

import "testing"
import "reflect"

func TestIntMap(t *testing.T) {
	var mapper func(func(int) int, []int) []int
	MakeMapFunc(&mapper)

	in := []int{1, 2, 3, 4, 5}
	f := func(x int) int { return x * 2 }
	exp := []int{2, 4, 6, 8, 10}

	out := mapper(f, in)
	if !reflect.DeepEqual(exp, out) {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestStringIntMap(t *testing.T) {
	var mapper func(func(string) int, []string) []int
	MakeMapFunc(&mapper)

	in := []string{"try", "this", "thing"}
	f := func(x string) int { return len(x) }
	exp := []int{3, 4, 5}

	out := mapper(f, in)

	if !reflect.DeepEqual(exp, out) {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestIntStringMap(t *testing.T) {
	var mapper func(func(int) string, []int) []string
	MakeMapFunc(&mapper)

	in := []int{1, 2, 3}
	exp := []string{"x", "xx", "xxx"}
	f := func(x int) string {
		out := ""
		for i := 0; i < x; i++ {
			out += "x"
		}
		return out
	}

	out := mapper(f, in)

	if !reflect.DeepEqual(exp, out) {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestEmptyMap(t *testing.T) {
	var mapper func(func(int) int, []int) []int
	MakeMapFunc(&mapper)

	in := []int{}
	f := func(x int) int { return x * 2 }
	exp := []int{}

	out := mapper(f, in)
	if !reflect.DeepEqual(exp, out) {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestFilter(t *testing.T) {
	var filter func(func(int) bool, []int) []int
	MakeFilterFunc(&filter)

	in := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	f := func(x int) bool { return x%2 == 0 }
	exp := []int{2, 4, 6, 8, 10}

	out := filter(f, in)
	if !reflect.DeepEqual(exp, out) {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestReduce(t *testing.T) {
	var reduce func(func(int, int) int, []int) int
	MakeReduceFunc(&reduce)

	in := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	f := func(x, y int) int { return x + y }
	exp := 55

	out := reduce(f, in)
	if exp != out {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestReduceInit(t *testing.T) {
	var reduce func(func(int, int) int, []int, int) int
	MakeReduceFunc(&reduce)

	in := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	f := func(x, y int) int { return x + y }
	init := 45
	exp := 100

	out := reduce(f, in, init)
	if exp != out {
		t.Fatal("expected", exp, ", got", out)
	}
}

func TestReduceTwoTypes(t *testing.T) {
	var reduce func(func(string, int) string, []int, string) string
	MakeReduceFunc(&reduce)

	in := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	f := func(x string, y int) string { return x + string('0'+y) }
	init := ""
	exp := "0123456789"

	out := reduce(f, in, init)
	if exp != out {
		t.Fatal("expected", exp, ", got", out)
	}
}

func sliceRange(n int) []int {
	out := make([]int, n, n)
	for i := 0; i < n; i++ {
		out[i] = i
	}
	return out
}

var benchmarkIn = sliceRange(100)

func BenchmarkForMap(b *testing.B) {
	for n := 0; n < b.N; n++ {
		l := len(benchmarkIn)
		out := make([]int, l, l)
		for i := 0; i < l; i++ {
			out[i] = benchmarkIn[i] * 2
		}
		if out[1] != 2 {
			panic("wrong result")
		}
	}
}

func BenchmarkForFuncMap(b *testing.B) {
	for n := 0; n < b.N; n++ {
		l := len(benchmarkIn)
		out := make([]int, l, l)
		f := func(x int) int { return x * 2 }
		for i := 0; i < l; i++ {
			out[i] = f(benchmarkIn[i])
		}
		if out[1] != 2 {
			panic("wrong result")
		}
	}
}

func interfaceMapper(f func(interface{}) interface{}, in []interface{}) []interface{} {
	out := make([]interface{}, len(in), len(in))
	for i, v := range in {
		out[i] = f(v)
	}
	return out
}

func BenchmarkInterfaceMapFunc(b *testing.B) {
	for n := 0; n < b.N; n++ {
		l := len(benchmarkIn)

		interfaceIn := make([]interface{}, l, l)
		for i, x := range benchmarkIn {
			interfaceIn[i] = x
		}

		f := func(x interface{}) interface{} {
			return x.(int) * 2
		}

		interfaceOut := interfaceMapper(f, interfaceIn)

		out := make([]int, l, l)
		for i, n := range interfaceOut {
			out[i] = n.(int)
		}
		if out[1] != 2 {
			panic("wrong result")
		}
	}
}

func BenchmarkMakeMapFunc(b *testing.B) {
	var mapper func(func(int) int, []int) []int
	MakeMapFunc(&mapper)

	for n := 0; n < b.N; n++ {
		f := func(x int) int { return x * 2 }
		out := mapper(f, benchmarkIn)
		if out[1] != 2 {
			panic("wrong result")
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>flatten conditionals<commit_after><|endoftext|>


<commit_before>// Copyright 2012 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package hopwatch

import (
	"code.google.com/p/go.net/websocket"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"runtime/debug"
	"strconv"
	"strings"
	"sync"
)

// command is used to transport message to and from the debugger.
type command struct {
	Action     string
	Parameters map[string]string
}

// addParam adds a key,value string pair to the command ; no check on overwrites.
func (self *command) addParam(key, value string) {
	if self.Parameters == nil {
		self.Parameters = map[string]string{}
	}
	self.Parameters[key] = value
}

var hopwatchHostParam = flag.String("hopwatch.host", "localhost", "HTTP host the debugger is listening on")
var hopwatchPortParam = flag.Int("hopwatch.port", 2346, "HTTP port the debugger is listening on")
var hopwatchParam = flag.Bool("hopwatch", true, "controls whether hopwatch agent is started")
var hopwatchOpenParam = flag.Bool("hopwatch.open", true, "controls whether a browser page is opened on the hopwatch page")

var hopwatchEnabled = true
var hopwatchOpenEnabled = true
var hopwatchHost = "localhost"
var hopwatchPort int64 = 23456

var currentWebsocket *websocket.Conn
var toBrowserChannel = make(chan command)
var fromBrowserChannel = make(chan command)
var connectChannel = make(chan command)
var debuggerMutex = sync.Mutex{}

func init() {
	// check any command line params. (needed when programs do not call flag.Parse() )
	for i, arg := range os.Args {
		if arg == "-hopwatch" && i < len(os.Args) && os.Args[i+1] == "false" {
			log.Printf("[hopwatch] disabled.\n")
			hopwatchEnabled = false
			return
		}
		if arg == "-hopwatch.open" && i < len(os.Args) && os.Args[i+1] == "false" {
			log.Printf("[hopwatch] auto open debugger disabled.\n")
			hopwatchOpenEnabled = false
		}
		if arg == "-hopwatch.host" && i < len(os.Args) {
			hopwatchHost = os.Args[i+1]
		}
		if arg == "-hopwatch.port" && i < len(os.Args) {
			port, err := strconv.ParseInt(os.Args[i+1], 10, 8)
			if err != nil {
				log.Panicf("[hopwatch] illegal port parameter:%v", err)
			}
			hopwatchPort = port
		}
	}
	http.HandleFunc("/hopwatch.html", html)
	http.HandleFunc("/hopwatch.css", css)
	http.HandleFunc("/hopwatch.js", js)
	http.HandleFunc("/gosource", gosource)
	http.Handle("/hopwatch", websocket.Handler(connectHandler))
	go listen()
	go sendLoop()
}

// Open calls the OS default program for uri
func open(uri string) error {
	var run string
	switch {
	case "windows" == runtime.GOOS:
		run = "start"
	case "darwin" == runtime.GOOS:
		run = "open"
	case "linux" == runtime.GOOS:
		run = "xdg-open"
	default:
		return fmt.Errorf("Unable to open uri:%v on:%v", uri, runtime.GOOS)
	}
	return exec.Command(run, uri).Start()
}

// serve a (source) file for displaying in the debugger
func gosource(w http.ResponseWriter, req *http.Request) {
	fileName := req.FormValue("file")
	// should check for permission?
	http.ServeFile(w, req, fileName)
}

// listen starts a Http Server on a fixed port.
// listen is run in parallel to the initialization process such that it does not block.
func listen() {
	hostPort := fmt.Sprintf("%s:%d", hopwatchHost, hopwatchPort)
	if hopwatchOpenEnabled {
		log.Printf("[hopwatch] opening http://%v/hopwatch.html ...\n", hostPort)
		go open(fmt.Sprintf("http://%v/hopwatch.html", hostPort))
	} else {
		log.Printf("[hopwatch] open http://%v/hopwatch.html ...\n", hostPort)
	}
	if err := http.ListenAndServe(hostPort, nil); err != nil {
		log.Printf("[hopwatch] failed to start listener:%v", err.Error())
	}
}

// connectHandler is a Http handler and is called on loading the debugger in a browser.
// As soon as a command is received the receiveLoop is started.
func connectHandler(ws *websocket.Conn) {
	if currentWebsocket != nil {
		log.Printf("[hopwatch] already connected to a debugger; Ignore this\n")
		return
	}
	log.Printf("[hopwatch] begin accepting commands ...\n")
	// remember the connection for the sendLoop
	currentWebsocket = ws
	var cmd command
	if err := websocket.JSON.Receive(currentWebsocket, &cmd); err != nil {
		log.Printf("[hopwatch] connectHandler.JSON.Receive failed:%v", err)
	} else {
		log.Printf("[hopwatch] connected to browser. ready to hop")
		connectChannel <- cmd
		receiveLoop()
	}
	log.Printf("[hopwatch] end accepting commands.\n")
}

// receiveLoop reads commands from the websocket and puts them onto a channel.
func receiveLoop() {
	for {
		var cmd command
		if err := websocket.JSON.Receive(currentWebsocket, &cmd); err != nil {
			log.Printf("[hopwatch] receiveLoop.JSON.Receive failed:%v", err)
			fromBrowserChannel <- command{Action: "quit"}
			break
		}
		if "quit" == cmd.Action {
			hopwatchEnabled = false
			log.Printf("[hopwatch] browser requests disconnect.\n")
			fromBrowserChannel <- cmd
			currentWebsocket.Close() // TODO is not detected by Chrome
			currentWebsocket = nil
			break
		} else {
			fromBrowserChannel <- cmd
		}
	}
}

// sendLoop takes commands from a channel to send to the browser (debugger).
// If no connection is available then wait for it.
// If the command action is quit then abort the loop.
func sendLoop() {
	if currentWebsocket == nil {
		log.Print("[hopwatch] no browser connection, wait for it ...")
		cmd := <-connectChannel
		if "quit" == cmd.Action {
			return
		}
	}
	for {
		next := <-toBrowserChannel
		if "quit" == next.Action {
			break
		}
		if currentWebsocket == nil {
			log.Print("[hopwatch] no browser connection, wait for it ...")
			cmd := <-connectChannel
			if "quit" == cmd.Action {
				break
			}
		}
		websocket.JSON.Send(currentWebsocket, &next)
	}
}

// watchpoint is a helper to provide a fluent style api.
// This allows for statements like hopwatch.Display("var",value).Break()
type Watchpoint struct {
	disabled bool
	offset   int
}

// Printf formats according to a format specifier and writes to the debugger screen.
// It returns a new Watchpoint to send more or break.
func Printf(format string, params ...interface{}) *Watchpoint {
	wp := &Watchpoint{offset: 2}
	return wp.Printf(format, params...)
}

// Display sends variable name,value pairs to the debugger.
// The parameter nameValuePairs must be even sized.
func Display(nameValuePairs ...interface{}) *Watchpoint {
	wp := &Watchpoint{offset: 2}
	return wp.Display(nameValuePairs...)
}

// Break suspends the execution of the program and waits for an instruction from the debugger (e.g. Resume).
// Break is only effective if all (if any) conditions are true. The program will resume otherwise.
func Break(conditions ...bool) {
	suspend(2, conditions...)
}

// CallerOffset (default=2) allows you to change the file indicator in hopwatch.
// Use this method when you wrap the .CallerOffset(..).Display(..).Break() in your own function.
func CallerOffset(offset int) *Watchpoint {
	wp := &Watchpoint{offset: offset}
	if offset < 0 {
		log.Panicf("[hopwatch] ERROR: illegal caller offset:%v . watchpoint is disabled.\n", offset)
		wp.disabled = true
	}
	return wp
}

// Printf formats according to a format specifier and writes to the debugger screen.
func (self *Watchpoint) Printf(format string, params ...interface{}) *Watchpoint {
	_, file, line, ok := runtime.Caller(self.offset)
	cmd := command{Action: "print"}
	if ok {
		cmd.addParam("go.file", file)
		cmd.addParam("go.line", fmt.Sprint(line))
	}
	if len(params) == 0 {
		cmd.addParam("line", format)
	} else {
		cmd.addParam("line", fmt.Sprintf(format, params...))
	}
	channelExchangeCommands(cmd)
	return self
}

// Display sends variable name,value pairs to the debugger. Values are formatted using %#v.
// The parameter nameValuePairs must be even sized.
func (self *Watchpoint) Display(nameValuePairs ...interface{}) *Watchpoint {
	_, file, line, ok := runtime.Caller(self.offset)
	cmd := command{Action: "display"}
	if ok {
		cmd.addParam("go.file", file)
		cmd.addParam("go.line", fmt.Sprint(line))
	}
	if len(nameValuePairs)%2 == 0 {
		for i := 0; i < len(nameValuePairs); i += 2 {
			k := nameValuePairs[i]
			v := nameValuePairs[i+1]
			cmd.addParam(fmt.Sprint(k), fmt.Sprintf("%#v", v))
		}
	} else {
		log.Printf("[hopwatch] WARN: missing variable for Display(...) in: %v:%v\n", file, line)
		self.disabled = true
		return self
	}
	channelExchangeCommands(cmd)
	return self
}

// Break halts the execution of the program and waits for an instruction from the debugger (e.g. Resume).
// Break is only effective if all (if any) conditions are true. The program will resume otherwise.
func (self Watchpoint) Break(conditions ...bool) {
	suspend(self.offset, conditions...)
}

// suspend will create a new Command and send it to the browser.
// callerOffset controls from which stackframe the go source file and linenumber must be read.
func suspend(callerOffset int, conditions ...bool) {
	for _, condition := range conditions {
		if !condition {
			return
		}
	}
	_, file, line, ok := runtime.Caller(callerOffset)
	cmd := command{Action: "break"}
	if ok {
		cmd.addParam("go.file", file)
		cmd.addParam("go.line", fmt.Sprint(line))
		cmd.addParam("go.stack", trimStack(string(debug.Stack())))
	}
	channelExchangeCommands(cmd)
}

// Peel off the part of the stack that lives in hopwatch
func trimStack(stack string) string {
	lines := strings.Split(stack, "\n")
	c := 0
	for _, line := range lines {
		if strings.Index(line, "/hopwatch") == -1 { // means no function in this package
			break
		}
		c++
	}
	return strings.Join(lines[c:], "\n")
}

// Put a command on the browser channel and wait for the reply command
func channelExchangeCommands(toCmd command) {
	if !hopwatchEnabled {
		return
	}
	// synchronize command exchange ; break only one goroutine at a time
	debuggerMutex.Lock()
	toBrowserChannel <- toCmd
	_ = <-fromBrowserChannel
	debuggerMutex.Unlock()
}
<commit_msg>fixed handling of hopwatch options<commit_after>// Copyright 2012 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package hopwatch

import (
	"code.google.com/p/go.net/websocket"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"runtime/debug"
	"strconv"
	"strings"
	"sync"
)

// command is used to transport message to and from the debugger.
type command struct {
	Action     string
	Parameters map[string]string
}

// addParam adds a key,value string pair to the command ; no check on overwrites.
func (self *command) addParam(key, value string) {
	if self.Parameters == nil {
		self.Parameters = map[string]string{}
	}
	self.Parameters[key] = value
}

var hopwatchHostParam = flag.String("hopwatch.host", "localhost", "HTTP host the debugger is listening on")
var hopwatchPortParam = flag.Int("hopwatch.port", 2346, "HTTP port the debugger is listening on")
var hopwatchParam = flag.Bool("hopwatch", true, "controls whether hopwatch agent is started")
var hopwatchOpenParam = flag.Bool("hopwatch.open", true, "controls whether a browser page is opened on the hopwatch page")

var hopwatchEnabled = true
var hopwatchOpenEnabled = true
var hopwatchHost = "localhost"
var hopwatchPort int64 = 23456

var currentWebsocket *websocket.Conn
var toBrowserChannel = make(chan command)
var fromBrowserChannel = make(chan command)
var connectChannel = make(chan command)
var debuggerMutex = sync.Mutex{}

func init() {
	// check any command line params. (needed when programs do not call flag.Parse() )
	for i, arg := range os.Args {
		if strings.HasPrefix(arg,"-hopwatch") {
			if strings.HasSuffix(arg,"false") {
				log.Printf("[hopwatch] disabled.\n")
				hopwatchEnabled = false
				return
			}
		}
		if strings.HasPrefix(arg,"-hopwatch.open") {
			if strings.HasSuffix(arg,"false") {
				log.Printf("[hopwatch] auto open debugger disabled.\n")
				hopwatchOpenEnabled = false
			}
		}
		if strings.HasPrefix(arg,"-hopwatch.host") {
			if eq := strings.Index(arg,"="); eq != -1 {
				hopwatchHost = arg[eq+1:]
			} else if i < len(os.Args) {
				hopwatchHost = os.Args[i+1]
			}
		}
		if strings.HasPrefix(arg,"-hopwatch.port") {
			portString := ""
			if eq := strings.Index(arg,"="); eq != -1 {
				portString = arg[eq+1:]
			} else if i < len(os.Args) {
				portString = os.Args[i+1]
			}
			port, err := strconv.ParseInt(portString, 10, 8)
			if err != nil {
				log.Panicf("[hopwatch] illegal port parameter:%v", err)
			}
			hopwatchPort = port
		}
	}
	http.HandleFunc("/hopwatch.html", html)
	http.HandleFunc("/hopwatch.css", css)
	http.HandleFunc("/hopwatch.js", js)
	http.HandleFunc("/gosource", gosource)
	http.Handle("/hopwatch", websocket.Handler(connectHandler))
	go listen()
	go sendLoop()
}

// Open calls the OS default program for uri
func open(uri string) error {
	var run string
	switch {
	case "windows" == runtime.GOOS:
		run = "start"
	case "darwin" == runtime.GOOS:
		run = "open"
	case "linux" == runtime.GOOS:
		run = "xdg-open"
	default:
		return fmt.Errorf("Unable to open uri:%v on:%v", uri, runtime.GOOS)
	}
	return exec.Command(run, uri).Start()
}

// serve a (source) file for displaying in the debugger
func gosource(w http.ResponseWriter, req *http.Request) {
	fileName := req.FormValue("file")
	// should check for permission?
	http.ServeFile(w, req, fileName)
}

// listen starts a Http Server on a fixed port.
// listen is run in parallel to the initialization process such that it does not block.
func listen() {
	hostPort := fmt.Sprintf("%s:%d", hopwatchHost, hopwatchPort)
	if hopwatchOpenEnabled {
		log.Printf("[hopwatch] opening http://%v/hopwatch.html ...\n", hostPort)
		go open(fmt.Sprintf("http://%v/hopwatch.html", hostPort))
	} else {
		log.Printf("[hopwatch] open http://%v/hopwatch.html ...\n", hostPort)
	}
	if err := http.ListenAndServe(hostPort, nil); err != nil {
		log.Printf("[hopwatch] failed to start listener:%v", err.Error())
	}
}

// connectHandler is a Http handler and is called on loading the debugger in a browser.
// As soon as a command is received the receiveLoop is started.
func connectHandler(ws *websocket.Conn) {
	if currentWebsocket != nil {
		log.Printf("[hopwatch] already connected to a debugger; Ignore this\n")
		return
	}
	log.Printf("[hopwatch] begin accepting commands ...\n")
	// remember the connection for the sendLoop
	currentWebsocket = ws
	var cmd command
	if err := websocket.JSON.Receive(currentWebsocket, &cmd); err != nil {
		log.Printf("[hopwatch] connectHandler.JSON.Receive failed:%v", err)
	} else {
		log.Printf("[hopwatch] connected to browser. ready to hop")
		connectChannel <- cmd
		receiveLoop()
	}
	log.Printf("[hopwatch] end accepting commands.\n")
}

// receiveLoop reads commands from the websocket and puts them onto a channel.
func receiveLoop() {
	for {
		var cmd command
		if err := websocket.JSON.Receive(currentWebsocket, &cmd); err != nil {
			log.Printf("[hopwatch] receiveLoop.JSON.Receive failed:%v", err)
			fromBrowserChannel <- command{Action: "quit"}
			break
		}
		if "quit" == cmd.Action {
			hopwatchEnabled = false
			log.Printf("[hopwatch] browser requests disconnect.\n")
			fromBrowserChannel <- cmd
			currentWebsocket.Close() // TODO is not detected by Chrome
			currentWebsocket = nil
			break
		} else {
			fromBrowserChannel <- cmd
		}
	}
}

// sendLoop takes commands from a channel to send to the browser (debugger).
// If no connection is available then wait for it.
// If the command action is quit then abort the loop.
func sendLoop() {
	if currentWebsocket == nil {
		log.Print("[hopwatch] no browser connection, wait for it ...")
		cmd := <-connectChannel
		if "quit" == cmd.Action {
			return
		}
	}
	for {
		next := <-toBrowserChannel
		if "quit" == next.Action {
			break
		}
		if currentWebsocket == nil {
			log.Print("[hopwatch] no browser connection, wait for it ...")
			cmd := <-connectChannel
			if "quit" == cmd.Action {
				break
			}
		}
		websocket.JSON.Send(currentWebsocket, &next)
	}
}

// watchpoint is a helper to provide a fluent style api.
// This allows for statements like hopwatch.Display("var",value).Break()
type Watchpoint struct {
	disabled bool
	offset   int
}

// Printf formats according to a format specifier and writes to the debugger screen.
// It returns a new Watchpoint to send more or break.
func Printf(format string, params ...interface{}) *Watchpoint {
	wp := &Watchpoint{offset: 2}
	return wp.Printf(format, params...)
}

// Display sends variable name,value pairs to the debugger.
// The parameter nameValuePairs must be even sized.
func Display(nameValuePairs ...interface{}) *Watchpoint {
	wp := &Watchpoint{offset: 2}
	return wp.Display(nameValuePairs...)
}

// Break suspends the execution of the program and waits for an instruction from the debugger (e.g. Resume).
// Break is only effective if all (if any) conditions are true. The program will resume otherwise.
func Break(conditions ...bool) {
	suspend(2, conditions...)
}

// CallerOffset (default=2) allows you to change the file indicator in hopwatch.
// Use this method when you wrap the .CallerOffset(..).Display(..).Break() in your own function.
func CallerOffset(offset int) *Watchpoint {
	wp := &Watchpoint{offset: offset}
	if offset < 0 {
		log.Panicf("[hopwatch] ERROR: illegal caller offset:%v . watchpoint is disabled.\n", offset)
		wp.disabled = true
	}
	return wp
}

// Printf formats according to a format specifier and writes to the debugger screen.
func (self *Watchpoint) Printf(format string, params ...interface{}) *Watchpoint {
	_, file, line, ok := runtime.Caller(self.offset)
	cmd := command{Action: "print"}
	if ok {
		cmd.addParam("go.file", file)
		cmd.addParam("go.line", fmt.Sprint(line))
	}
	if len(params) == 0 {
		cmd.addParam("line", format)
	} else {
		cmd.addParam("line", fmt.Sprintf(format, params...))
	}
	channelExchangeCommands(cmd)
	return self
}

// Display sends variable name,value pairs to the debugger. Values are formatted using %#v.
// The parameter nameValuePairs must be even sized.
func (self *Watchpoint) Display(nameValuePairs ...interface{}) *Watchpoint {
	_, file, line, ok := runtime.Caller(self.offset)
	cmd := command{Action: "display"}
	if ok {
		cmd.addParam("go.file", file)
		cmd.addParam("go.line", fmt.Sprint(line))
	}
	if len(nameValuePairs)%2 == 0 {
		for i := 0; i < len(nameValuePairs); i += 2 {
			k := nameValuePairs[i]
			v := nameValuePairs[i+1]
			cmd.addParam(fmt.Sprint(k), fmt.Sprintf("%#v", v))
		}
	} else {
		log.Printf("[hopwatch] WARN: missing variable for Display(...) in: %v:%v\n", file, line)
		self.disabled = true
		return self
	}
	channelExchangeCommands(cmd)
	return self
}

// Break halts the execution of the program and waits for an instruction from the debugger (e.g. Resume).
// Break is only effective if all (if any) conditions are true. The program will resume otherwise.
func (self Watchpoint) Break(conditions ...bool) {
	suspend(self.offset, conditions...)
}

// suspend will create a new Command and send it to the browser.
// callerOffset controls from which stackframe the go source file and linenumber must be read.
func suspend(callerOffset int, conditions ...bool) {
	for _, condition := range conditions {
		if !condition {
			return
		}
	}
	_, file, line, ok := runtime.Caller(callerOffset)
	cmd := command{Action: "break"}
	if ok {
		cmd.addParam("go.file", file)
		cmd.addParam("go.line", fmt.Sprint(line))
		cmd.addParam("go.stack", trimStack(string(debug.Stack())))
	}
	channelExchangeCommands(cmd)
}

// Peel off the part of the stack that lives in hopwatch
func trimStack(stack string) string {
	lines := strings.Split(stack, "\n")
	c := 0
	for _, line := range lines {
		if strings.Index(line, "/hopwatch") == -1 { // means no function in this package
			break
		}
		c++
	}
	return strings.Join(lines[c:], "\n")
}

// Put a command on the browser channel and wait for the reply command
func channelExchangeCommands(toCmd command) {
	if !hopwatchEnabled {
		return
	}
	// synchronize command exchange ; break only one goroutine at a time
	debuggerMutex.Lock()
	toBrowserChannel <- toCmd
	_ = <-fromBrowserChannel
	debuggerMutex.Unlock()
}
<|endoftext|>


<commit_before>package hostess

import (
	"errors"
	"fmt"
	"net"
	"sort"
)

// ErrInvalidVersionArg is raised when a function expects IPv 4 or 6 but is
// passed a value not 4 or 6.
var ErrInvalidVersionArg = errors.New("Version argument must be 4 or 6")

// Hostlist is an ordered set of Hostnames. When in a Hostlist, Hostnames must
// follow some rules:
//
// 	- Hostlist may contain IPv4 AND IPv6 ("IP version" or "IPv") Hostnames.
// 	- Names are only allowed to overlap if IP version is different.
// 	- Adding a Hostname for an existing name will replace the old one.
//
// See docs for the Sort and Add for more details.
type Hostlist []*Hostname

// NewHostlist initializes a new Hostlist
func NewHostlist() *Hostlist {
	return &Hostlist{}
}

// Len returns the number of Hostnames in the list, part of sort.Interface
func (h Hostlist) Len() int {
	return len(h)
}

// Less determines the sort order of two Hostnames, part of sort.Interface
func (h Hostlist) Less(i, j int) bool {
	// Sort 127.0.0.1, 127.0.1.1 and "localhost" at the top
	if h[i].Domain == "localhost" {
		return true
	}
	if h[j].Domain == "localhost" {
		return false
	}

	// Sort IPv4 before IPv6
	if h[i].IPv6 && !h[j].IPv6 {
		return false
	}
	if !h[i].IPv6 && h[j].IPv6 {
		return true
	}

	// Compare the the IP addresses (byte array)
	if !h[i].IP.Equal(h[j].IP) {
		for c := range h[i].IP {
			if h[i].IP[c] < h[j].IP[c] {
				return true
			} else if h[i].IP[c] > h[j].IP[c] {
				return false
			}
		}
	}

	// Prep for domain sorting
	ilen := len(h[i].Domain)
	jlen := len(h[j].Domain)
	max := ilen
	if jlen > max {
		max = jlen
	}

	// Sort domains alphabetically
	// TODO: This works best if domains are lowercased. However, we do not
	// enforce lowercase because of UTF-8 domain names, which may be broken by
	// case folding. There is a way to do this correctly but it's complicated
	// so I'm not going to do it right now.
	for c := 0; c < max; c++ {
		if c >= ilen {
			return true
		}
		if c >= jlen {
			return false
		}
		if h[i].Domain[c] < h[j].Domain[c] {
			return true
		}
		if h[i].Domain[c] > h[j].Domain[c] {
			return false
		}
	}

	// Seems like everything was the same, so it can't be Less. Also since we
	// can't Add something twice we should never end up here. Just in case...
	return false
}

// Swap changes the position of two Hostnames, part of sort.Interface
func (h Hostlist) Swap(i, j int) {
	h[i], h[j] = h[j], h[i]
}

// Sort this list of Hostnames, according to Hostlist sorting rules:
//
// 	1. localhost comes before other domains
// 	2. IPv4 comes before IPv6
// 	3. IPs are sorted in numerical order
// 	4. domains are sorted in alphabetical
func (h *Hostlist) Sort() {
	sort.Sort(*h)
}

// Contains returns true if this Hostlist has the specified Hostname
func (h *Hostlist) Contains(b *Hostname) bool {
	for _, a := range *h {
		if a.Equal(b) {
			return true
		}
	}
	return false
}

// ContainsDomain returns true if a Hostname in this Hostlist matches domain
func (h *Hostlist) ContainsDomain(domain string) bool {
	for _, hostname := range *h {
		if hostname.Domain == domain {
			return true
		}
	}
	return false
}

// ContainsIP returns true if a Hostname in this Hostlist matches IP
func (h *Hostlist) ContainsIP(IP net.IP) bool {
	for _, hostname := range *h {
		if hostname.EqualIP(IP) {
			return true
		}
	}
	return false
}

// Add a new Hostname to this hostlist. If a Hostname with the same domain name
// and IP version is found, it will be replaced and an error will be returned.
// If you try to add an identical Hostname, an error will be returned.
// Note that in normal operation, you will sometimes expect an error, and the
// error data is mainly to alert you that you mis-entered information, not that
// the application has a problem.
func (h *Hostlist) Add(host *Hostname) error {
	for _, found := range *h {
		if found.Equal(host) {
			return fmt.Errorf("Duplicate hostname entry for %s -> %s",
				host.Domain, host.IP)
		} else if found.Domain == host.Domain && found.IPv6 == host.IPv6 {
			return fmt.Errorf("Conflicting hostname entries for %s -> %s and -> %s",
				host.Domain, host.IP, found.IP)
		}
	}
	*h = append(*h, host)
	h.Sort()
	return nil
}

// IndexOf will indicate the index of a Hostname in Hostlist, or -1 if it is
// not found.
func (h *Hostlist) IndexOf(host *Hostname) int {
	for index, found := range *h {
		if found.Equal(host) {
			return index
		}
	}
	return -1
}

// IndexOfDomainV will indicate the index of a Hostname in Hostlist that has
// the same domain and IP version, or -1 if it is not found.
//
// This function will panic if IP version is not 4 or 6.
func (h *Hostlist) IndexOfDomainV(domain string, version int) int {
	if version != 4 && version != 6 {
		panic(ErrInvalidVersionArg)
	}
	for index, hostname := range *h {
		if hostname.Domain == domain && hostname.IPv6 == (version == 6) {
			return index
		}
	}
	return -1
}

// Remove will delete the Hostname at the specified index. If index is out of
// bounds (i.e. -1), Remove silently no-ops.
func (h *Hostlist) Remove(index int) {
	if index > -1 && index < len(*h) {
		*h = append((*h)[:index], (*h)[index+1:]...)
	}
}

// RemoveDomain removes both IPv4 and IPv6 Hostname entries matching domain.
func (h *Hostlist) RemoveDomain(domain string) {
	h.Remove(h.IndexOfDomainV(domain, 4))
	h.Remove(h.IndexOfDomainV(domain, 6))
}

// RemoveDomainV removes a Hostname entry matching the domain and IP version.
//
// This function will panic if IP version is not 4 or 6.
func (h *Hostlist) RemoveDomainV(domain string, version int) {
	if version != 4 && version != 6 {
		panic(ErrInvalidVersionArg)
	}
	h.Remove(h.IndexOfDomainV(domain, version))
}

// Enable will change any Hostnames matching domain to be enabled.
func (h *Hostlist) Enable(domain string) {
	for _, hostname := range *h {
		if hostname.Domain == domain {
			hostname.Enabled = true
		}
	}
}

// EnableV will change a Hostname matching domain and IP version to be enabled.
//
// This function will panic if IP version is not 4 or 6.
func (h *Hostlist) EnableV(domain string, version int) {
	if version != 4 && version != 6 {
		panic(ErrInvalidVersionArg)
	}
	for _, hostname := range *h {
		if hostname.Domain == domain && hostname.IPv6 == (version == 6) {
			hostname.Enabled = true
		}
	}
}

// Disable will change any Hostnames matching domain to be disabled.
func (h *Hostlist) Disable(domain string) {
	for _, hostname := range *h {
		if hostname.Domain == domain {
			hostname.Enabled = false
		}
	}
}

// DisableV will change any Hostnames matching domain and IP version to be disabled.
//
// This function will panic if IP version is not 4 or 6.
func (h *Hostlist) DisableV(domain string, version int) {
	if version != 4 && version != 6 {
		panic(ErrInvalidVersionArg)
	}
	for _, hostname := range *h {
		if hostname.Domain == domain && hostname.IPv6 == (version == 6) {
			hostname.Enabled = false
		}
	}
}

// FilterByIP filters the list of hostnames by IP address.
func (h *Hostlist) FilterByIP(IP net.IP) (hostnames []*Hostname) {
	for _, hostname := range *h {
		if hostname.IP.Equal(IP) {
			hostnames = append(hostnames, hostname)
		}
	}
	return
}

// FilterByDomain filters the list of hostnames by Domain.
func (h *Hostlist) FilterByDomain(domain string) (hostnames []*Hostname) {
	for _, hostname := range *h {
		if hostname.Domain == domain {
			hostnames = append(hostnames, hostname)
		}
	}
	return
}

// FilterByDomainV filters the list of hostnames by domain and IPv4 or IPv6.
// This should never contain more than one item, but returns a list for
// consistency with other filter functions.
func (h *Hostlist) FilterByDomainV(domain string, version int) (hostnames []*Hostname) {
	for _, hostname := range *h {
		if hostname.Domain == domain && hostname.IPv6 == (version == 6) {
			hostnames = append(hostnames, hostname)
		}
	}
	return
}

// Format takes the current list of Hostnames in this Hostfile and turns it
// into a string suitable for use as an /etc/hosts file.
// Sorting uses the following logic:
//
// 1. List is sorted by IP address
// 2. Commented items are sorted displayed
// 3. 127.* appears at the top of the list (so boot resolvers don't break)
// 4. When present, "localhost" will always appear first in the domain list
func (h *Hostlist) Format() []byte {
	h.Sort()
	var out []byte
	for _, hostname := range *h {
		out = append(out, []byte(hostname.Format())...)
		out = append(out, []byte("\n")...)
	}
	return out
}
<commit_msg>Renamed a bunch of variables in Less so they're easier to follow in the code<commit_after>package hostess

import (
	"errors"
	"fmt"
	"net"
	"sort"
)

// ErrInvalidVersionArg is raised when a function expects IPv 4 or 6 but is
// passed a value not 4 or 6.
var ErrInvalidVersionArg = errors.New("Version argument must be 4 or 6")

// Hostlist is an ordered set of Hostnames. When in a Hostlist, Hostnames must
// follow some rules:
//
// 	- Hostlist may contain IPv4 AND IPv6 ("IP version" or "IPv") Hostnames.
// 	- Names are only allowed to overlap if IP version is different.
// 	- Adding a Hostname for an existing name will replace the old one.
//
// See docs for the Sort and Add for more details.
type Hostlist []*Hostname

// NewHostlist initializes a new Hostlist
func NewHostlist() *Hostlist {
	return &Hostlist{}
}

// Len returns the number of Hostnames in the list, part of sort.Interface
func (h Hostlist) Len() int {
	return len(h)
}

// Less determines the sort order of two Hostnames, part of sort.Interface
func (h Hostlist) Less(A, B int) bool {
	// Sort 127.0.0.1, 127.0.1.1 and "localhost" at the top
	if h[A].Domain == "localhost" {
		return true
	}
	if h[B].Domain == "localhost" {
		return false
	}

	// Sort IPv4 before IPv6
	// A is IPv4 and B is IPv6. A wins!
	if !h[A].IPv6 && h[B].IPv6 {
		return true
	}
	// A is IPv6 but B is IPv4. A loses!
	if h[A].IPv6 && !h[B].IPv6 {
		return false
	}

	// Compare the the IP addresses (byte array)
	if !h[A].IP.Equal(h[B].IP) {
		for charIndex := range h[A].IP {
			// A and B's IPs differ at this index, and A is less. A wins!
			if h[A].IP[charIndex] < h[B].IP[charIndex] {
				return true
			}
			// A and B's IPs differ at this index, and B is less. A loses!
			if h[A].IP[charIndex] > h[B].IP[charIndex] {
				return false
			}
		}
	}

	// Prep for domain sorting
	aLength := len(h[A].Domain)
	bLength := len(h[B].Domain)
	max := aLength
	if bLength > max {
		max = bLength
	}

	// Sort domains alphabetically
	// TODO: This works best if domains are lowercased. However, we do not
	// enforce lowercase because of UTF-8 domain names, which may be broken by
	// case folding. There is a way to do this correctly but it's complicated
	// so I'm not going to do it right now.
	for charIndex := 0; charIndex < max; charIndex++ {
		// This index is longer than A, so A is shorter. A wins!
		if charIndex >= aLength {
			return true
		}
		// This index is longer than B, so B is shorter. A loses!
		if charIndex >= bLength {
			return false
		}
		// A and B differ at this index and A is less. A wins!
		if h[A].Domain[charIndex] < h[B].Domain[charIndex] {
			return true
		}
		// A and B differ at this index and B is less. A loses!
		if h[A].Domain[charIndex] > h[B].Domain[charIndex] {
			return false
		}
	}

	// Seems like everything was the same, so it can't be Less. Also since we
	// can't Add something twice we should never end up here. Just in case...
	return false
}

// Swap changes the position of two Hostnames, part of sort.Interface
func (h Hostlist) Swap(i, j int) {
	h[i], h[j] = h[j], h[i]
}

// Sort this list of Hostnames, according to Hostlist sorting rules:
//
// 	1. localhost comes before other domains
// 	2. IPv4 comes before IPv6
// 	3. IPs are sorted in numerical order
// 	4. domains are sorted in alphabetical
func (h *Hostlist) Sort() {
	sort.Sort(*h)
}

// Contains returns true if this Hostlist has the specified Hostname
func (h *Hostlist) Contains(b *Hostname) bool {
	for _, a := range *h {
		if a.Equal(b) {
			return true
		}
	}
	return false
}

// ContainsDomain returns true if a Hostname in this Hostlist matches domain
func (h *Hostlist) ContainsDomain(domain string) bool {
	for _, hostname := range *h {
		if hostname.Domain == domain {
			return true
		}
	}
	return false
}

// ContainsIP returns true if a Hostname in this Hostlist matches IP
func (h *Hostlist) ContainsIP(IP net.IP) bool {
	for _, hostname := range *h {
		if hostname.EqualIP(IP) {
			return true
		}
	}
	return false
}

// Add a new Hostname to this hostlist. If a Hostname with the same domain name
// and IP version is found, it will be replaced and an error will be returned.
// If you try to add an identical Hostname, an error will be returned.
// Note that in normal operation, you will sometimes expect an error, and the
// error data is mainly to alert you that you mis-entered information, not that
// the application has a problem.
func (h *Hostlist) Add(host *Hostname) error {
	for _, found := range *h {
		if found.Equal(host) {
			return fmt.Errorf("Duplicate hostname entry for %s -> %s",
				host.Domain, host.IP)
		} else if found.Domain == host.Domain && found.IPv6 == host.IPv6 {
			return fmt.Errorf("Conflicting hostname entries for %s -> %s and -> %s",
				host.Domain, host.IP, found.IP)
		}
	}
	*h = append(*h, host)
	h.Sort()
	return nil
}

// IndexOf will indicate the index of a Hostname in Hostlist, or -1 if it is
// not found.
func (h *Hostlist) IndexOf(host *Hostname) int {
	for index, found := range *h {
		if found.Equal(host) {
			return index
		}
	}
	return -1
}

// IndexOfDomainV will indicate the index of a Hostname in Hostlist that has
// the same domain and IP version, or -1 if it is not found.
//
// This function will panic if IP version is not 4 or 6.
func (h *Hostlist) IndexOfDomainV(domain string, version int) int {
	if version != 4 && version != 6 {
		panic(ErrInvalidVersionArg)
	}
	for index, hostname := range *h {
		if hostname.Domain == domain && hostname.IPv6 == (version == 6) {
			return index
		}
	}
	return -1
}

// Remove will delete the Hostname at the specified index. If index is out of
// bounds (i.e. -1), Remove silently no-ops.
func (h *Hostlist) Remove(index int) {
	if index > -1 && index < len(*h) {
		*h = append((*h)[:index], (*h)[index+1:]...)
	}
}

// RemoveDomain removes both IPv4 and IPv6 Hostname entries matching domain.
func (h *Hostlist) RemoveDomain(domain string) {
	h.Remove(h.IndexOfDomainV(domain, 4))
	h.Remove(h.IndexOfDomainV(domain, 6))
}

// RemoveDomainV removes a Hostname entry matching the domain and IP version.
//
// This function will panic if IP version is not 4 or 6.
func (h *Hostlist) RemoveDomainV(domain string, version int) {
	if version != 4 && version != 6 {
		panic(ErrInvalidVersionArg)
	}
	h.Remove(h.IndexOfDomainV(domain, version))
}

// Enable will change any Hostnames matching domain to be enabled.
func (h *Hostlist) Enable(domain string) {
	for _, hostname := range *h {
		if hostname.Domain == domain {
			hostname.Enabled = true
		}
	}
}

// EnableV will change a Hostname matching domain and IP version to be enabled.
//
// This function will panic if IP version is not 4 or 6.
func (h *Hostlist) EnableV(domain string, version int) {
	if version != 4 && version != 6 {
		panic(ErrInvalidVersionArg)
	}
	for _, hostname := range *h {
		if hostname.Domain == domain && hostname.IPv6 == (version == 6) {
			hostname.Enabled = true
		}
	}
}

// Disable will change any Hostnames matching domain to be disabled.
func (h *Hostlist) Disable(domain string) {
	for _, hostname := range *h {
		if hostname.Domain == domain {
			hostname.Enabled = false
		}
	}
}

// DisableV will change any Hostnames matching domain and IP version to be disabled.
//
// This function will panic if IP version is not 4 or 6.
func (h *Hostlist) DisableV(domain string, version int) {
	if version != 4 && version != 6 {
		panic(ErrInvalidVersionArg)
	}
	for _, hostname := range *h {
		if hostname.Domain == domain && hostname.IPv6 == (version == 6) {
			hostname.Enabled = false
		}
	}
}

// FilterByIP filters the list of hostnames by IP address.
func (h *Hostlist) FilterByIP(IP net.IP) (hostnames []*Hostname) {
	for _, hostname := range *h {
		if hostname.IP.Equal(IP) {
			hostnames = append(hostnames, hostname)
		}
	}
	return
}

// FilterByDomain filters the list of hostnames by Domain.
func (h *Hostlist) FilterByDomain(domain string) (hostnames []*Hostname) {
	for _, hostname := range *h {
		if hostname.Domain == domain {
			hostnames = append(hostnames, hostname)
		}
	}
	return
}

// FilterByDomainV filters the list of hostnames by domain and IPv4 or IPv6.
// This should never contain more than one item, but returns a list for
// consistency with other filter functions.
func (h *Hostlist) FilterByDomainV(domain string, version int) (hostnames []*Hostname) {
	for _, hostname := range *h {
		if hostname.Domain == domain && hostname.IPv6 == (version == 6) {
			hostnames = append(hostnames, hostname)
		}
	}
	return
}

// Format takes the current list of Hostnames in this Hostfile and turns it
// into a string suitable for use as an /etc/hosts file.
// Sorting uses the following logic:
//
// 1. List is sorted by IP address
// 2. Commented items are sorted displayed
// 3. 127.* appears at the top of the list (so boot resolvers don't break)
// 4. When present, "localhost" will always appear first in the domain list
func (h *Hostlist) Format() []byte {
	h.Sort()
	var out []byte
	for _, hostname := range *h {
		out = append(out, []byte(hostname.Format())...)
		out = append(out, []byte("\n")...)
	}
	return out
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"
	"os"

	"golang.org/x/net/context"

	"github.com/codegangsta/cli"
	"github.com/olekukonko/tablewriter"
)

var cmdZonesList = cli.Command{
	Name:  "list",
	Usage: "lists zones",
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:  "list",
			Usage: "if true only prints ids",
		},
	},
	Action: func(c *cli.Context) {
		zones, err := client(c).Zones.List(context.Background())
		if err != nil {
			log.Fatal(err)
		}

		table := tablewriter.NewWriter(os.Stdout)
		table.SetHeader([]string{
			"ID",
			"Name",
			"Paused",
			"Status",
		})

		for _, zone := range zones {
			if c.Bool("list") {
				fmt.Println(zone.ID)
			} else {
				table.Append([]string{
					zone.ID,
					zone.Name,
					yesOrNo(zone.Paused),
					zone.Status,
				})
			}
		}

		if !c.Bool("list") {
			table.Render()
		}
	},
}
<commit_msg>Flag for listing zones domains<commit_after>package cmd

import (
	"fmt"
	"log"
	"os"

	"golang.org/x/net/context"

	"github.com/codegangsta/cli"
	"github.com/olekukonko/tablewriter"
)

var cmdZonesList = cli.Command{
	Name:  "list",
	Usage: "lists zones",
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:  "list",
			Usage: "if true only prints ids",
		},
		cli.BoolFlag{
			Name:  "domains",
			Usage: "(only with --list) if true only prints domains",
		},
	},
	Action: func(c *cli.Context) {
		zones, err := client(c).Zones.List(context.Background())
		if err != nil {
			log.Fatal(err)
		}

		table := tablewriter.NewWriter(os.Stdout)
		table.SetHeader([]string{
			"ID",
			"Name",
			"Paused",
			"Status",
		})

		for _, zone := range zones {
			if c.Bool("list") {
				if c.Bool("domains") {
					fmt.Println(zone.Name)
				} else {
					fmt.Println(zone.ID)
				}
			} else {
				table.Append([]string{
					zone.ID,
					zone.Name,
					yesOrNo(zone.Paused),
					zone.Status,
				})
			}
		}

		if !c.Bool("list") {
			table.Render()
		}
	},
}
<|endoftext|>


<commit_before>/*
   Copyright 2014 CoreOS, Inc.

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

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

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

package validate

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/coreos/coreos-cloudinit/config"

	"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/gopkg.in/yaml.v1"
)

var (
	yamlLineError = regexp.MustCompile(`^YAML error: line (?P<line>[[:digit:]]+): (?P<msg>.*)$`)
	yamlError     = regexp.MustCompile(`^YAML error: (?P<msg>.*)$`)
)

// Validate runs a series of validation tests against the given userdata and
// returns a report detailing all of the issues. Presently, only cloud-configs
// can be validated.
func Validate(userdataBytes []byte) (Report, error) {
	switch {
	case config.IsScript(string(userdataBytes)):
		return Report{}, nil
	case config.IsCloudConfig(string(userdataBytes)):
		return validateCloudConfig(userdataBytes, Rules)
	default:
		return Report{entries: []Entry{
			Entry{kind: entryError, message: `must be "#cloud-config" or begin with "#!"`},
		}}, nil
	}
}

// validateCloudConfig runs all of the validation rules in Rules and returns
// the resulting report and any errors encountered.
func validateCloudConfig(config []byte, rules []rule) (report Report, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("%v", r)
		}
	}()

	c, err := parseCloudConfig(config, &report)
	if err != nil {
		return report, err
	}

	c = normalizeNodeNames(c, &report)
	for _, r := range rules {
		r(c, &report)
	}
	return report, nil
}

// parseCloudConfig parses the provided config into a node structure and logs
// any parsing issues into the provided report. Unrecoverable errors are
// returned as an error.
func parseCloudConfig(config []byte, report *Report) (n node, err error) {
	var raw map[interface{}]interface{}
	if err := yaml.Unmarshal(config, &raw); err != nil {
		matches := yamlLineError.FindStringSubmatch(err.Error())
		if len(matches) == 3 {
			line, err := strconv.Atoi(matches[1])
			if err != nil {
				return n, err
			}
			msg := matches[2]
			report.Error(line, msg)
			return n, nil
		}

		matches = yamlError.FindStringSubmatch(err.Error())
		if len(matches) == 2 {
			report.Error(1, matches[1])
			return n, nil
		}

		return n, errors.New("couldn't parse yaml error")
	}

	return NewNode(raw, NewContext(config)), nil
}

// normalizeNodeNames replaces all occurences of '-' with '_' within key names
// and makes a note of each replacement in the report.
func normalizeNodeNames(node node, report *Report) node {
	if strings.Contains(node.name, "-") {
		report.Info(node.line, fmt.Sprintf("%q uses '-' instead of '_'", node.name))
		node.name = strings.Replace(node.name, "-", "_", -1)
	}
	for i := range node.children {
		node.children[i] = normalizeNodeNames(node.children[i], report)
	}
	return node
}
<commit_msg>config/validate: fix line number for header check<commit_after>/*
   Copyright 2014 CoreOS, Inc.

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

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

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

package validate

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/coreos/coreos-cloudinit/config"

	"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/gopkg.in/yaml.v1"
)

var (
	yamlLineError = regexp.MustCompile(`^YAML error: line (?P<line>[[:digit:]]+): (?P<msg>.*)$`)
	yamlError     = regexp.MustCompile(`^YAML error: (?P<msg>.*)$`)
)

// Validate runs a series of validation tests against the given userdata and
// returns a report detailing all of the issues. Presently, only cloud-configs
// can be validated.
func Validate(userdataBytes []byte) (Report, error) {
	switch {
	case config.IsScript(string(userdataBytes)):
		return Report{}, nil
	case config.IsCloudConfig(string(userdataBytes)):
		return validateCloudConfig(userdataBytes, Rules)
	default:
		return Report{entries: []Entry{
			Entry{kind: entryError, message: `must be "#cloud-config" or begin with "#!"`, line: 1},
		}}, nil
	}
}

// validateCloudConfig runs all of the validation rules in Rules and returns
// the resulting report and any errors encountered.
func validateCloudConfig(config []byte, rules []rule) (report Report, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("%v", r)
		}
	}()

	c, err := parseCloudConfig(config, &report)
	if err != nil {
		return report, err
	}

	c = normalizeNodeNames(c, &report)
	for _, r := range rules {
		r(c, &report)
	}
	return report, nil
}

// parseCloudConfig parses the provided config into a node structure and logs
// any parsing issues into the provided report. Unrecoverable errors are
// returned as an error.
func parseCloudConfig(config []byte, report *Report) (n node, err error) {
	var raw map[interface{}]interface{}
	if err := yaml.Unmarshal(config, &raw); err != nil {
		matches := yamlLineError.FindStringSubmatch(err.Error())
		if len(matches) == 3 {
			line, err := strconv.Atoi(matches[1])
			if err != nil {
				return n, err
			}
			msg := matches[2]
			report.Error(line, msg)
			return n, nil
		}

		matches = yamlError.FindStringSubmatch(err.Error())
		if len(matches) == 2 {
			report.Error(1, matches[1])
			return n, nil
		}

		return n, errors.New("couldn't parse yaml error")
	}

	return NewNode(raw, NewContext(config)), nil
}

// normalizeNodeNames replaces all occurences of '-' with '_' within key names
// and makes a note of each replacement in the report.
func normalizeNodeNames(node node, report *Report) node {
	if strings.Contains(node.name, "-") {
		report.Info(node.line, fmt.Sprintf("%q uses '-' instead of '_'", node.name))
		node.name = strings.Replace(node.name, "-", "_", -1)
	}
	for i := range node.children {
		node.children[i] = normalizeNodeNames(node.children[i], report)
	}
	return node
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"flag"
	"log"
	"net/http"
	"time"
)

var (
	edgeHost      = flag.String("edgeHost", "www.gov.uk", "Hostname of edge")
	originPort    = flag.Int("originPort", 8080, "Origin port to listen on for requests")
	skipVerifyTLS = flag.Bool("skipVerifyTLS", false, "Skip TLS cert verification if set")
)

// These consts and vars are available to all tests.
const requestTimeout = time.Second * 5

var (
	client       *http.Transport
	originServer *CDNServeMux
)

// Setup clients and servers.
func init() {

	flag.Parse()

	tlsOptions := &tls.Config{}
	if *skipVerifyTLS {
		tlsOptions.InsecureSkipVerify = true
	}

	client = &http.Transport{
		ResponseHeaderTimeout: requestTimeout,
		TLSClientConfig:       tlsOptions,
	}
	originServer = StartServer(*originPort)

	log.Println("Confirming that CDN is healthy")
	err := confirmEdgeIsHealthy(originServer, *edgeHost)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Add caching for edgeHost DNS lookup<commit_after>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"time"
)

var (
	edgeHost      = flag.String("edgeHost", "www.gov.uk", "Hostname of edge")
	originPort    = flag.Int("originPort", 8080, "Origin port to listen on for requests")
	skipVerifyTLS = flag.Bool("skipVerifyTLS", false, "Skip TLS cert verification if set")
)

// These consts and vars are available to all tests.
const requestTimeout = time.Second * 5

var (
	client       *http.Transport
	originServer *CDNServeMux
)

var hardCachedEdgeHostIp string

// Setup clients and servers.
func init() {

	flag.Parse()

	tlsOptions := &tls.Config{}
	if *skipVerifyTLS {
		tlsOptions.InsecureSkipVerify = true
	}

	client = &http.Transport{
		ResponseHeaderTimeout: requestTimeout,
		TLSClientConfig:       tlsOptions,
		Dial:                  HardCachedHostDial,
	}
	originServer = StartServer(*originPort)

	log.Println("Confirming that CDN is healthy")
	err := confirmEdgeIsHealthy(originServer, *edgeHost)
	if err != nil {
		log.Fatal(err)
	}
}

func CachedHostIpAddress(host string) string {
	if hardCachedEdgeHostIp == "" {
		ipAddresses, err := net.LookupHost(host)
		if err != nil {
			log.Fatal(err)
		}
		hardCachedEdgeHostIp = ipAddresses[0]
	}
	return hardCachedEdgeHostIp
}

func HardCachedHostDial(network, addr string) (net.Conn, error) {
	host, port, err := net.SplitHostPort(addr)
	if err != nil {
		log.Fatal(err)
	}
	if host == "localhost" {
		return net.Dial(network, addr)
	}
	ipAddr := CachedHostIpAddress(host)
	return net.Dial(network, fmt.Sprintf("%s:%s", ipAddr, port))
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package samples

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os/exec"
	"path"
	"sync"

	"github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

// A struct that implements common behavior needed by tests in the samples/
// directory where the file system is mounted by a subprocess. Use it as an
// embedded field in your test fixture, calling its SetUp method from your
// SetUp method after setting the MountType and MountFlags fields.
type SubprocessTest struct {
	// The type of the file system to mount. Must be recognized by mount_sample.
	MountType string

	// Additional flags to be passed to the mount_sample tool.
	MountFlags []string

	// A context object that can be used for long-running operations.
	Ctx context.Context

	// The directory at which the file system is mounted.
	Dir string

	// Anothing non-nil in this slice will be closed by TearDown. The test will
	// fail if closing fails.
	ToClose []io.Closer

	mountCmd    *exec.Cmd
	mountStdout bytes.Buffer
	mountStderr bytes.Buffer
}

// Mount the file system and initialize the other exported fields of the
// struct. Panics on error.
//
// REQUIRES: t.FileSystem has been set.
func (t *SubprocessTest) SetUp(ti *ogletest.TestInfo) {
	err := t.initialize()
	if err != nil {
		panic(err)
	}
}

// Set by buildMountSample.
var mountSamplePath string
var mountSampleErr error
var mountSampleOnce sync.Once

// Build the mount_sample tool if it has not yet been built for this process.
// Return a path to the binary.
func buildMountSample() (toolPath string, err error) {
	// Build if we haven't yet.
	mountSampleOnce.Do(func() {
		// Create a temporary directory.
		tempDir, err := ioutil.TempDir("", "")
		if err != nil {
			mountSampleErr = fmt.Errorf("TempDir: %v", err)
			return
		}

		mountSamplePath = path.Join(tempDir, "mount_sample")

		// Build the command.
		cmd := exec.Command(
			"go",
			"build",
			"-o",
			mountSamplePath,
			"github.com/jacobsa/fuse/samples/mount_sample")

		output, err := cmd.CombinedOutput()
		if err != nil {
			mountSampleErr = fmt.Errorf(
				"mount_sample exited with %v, output:\n%s",
				err,
				string(output))

			return
		}
	})

	if mountSampleErr != nil {
		err = mountSampleErr
		return
	}

	toolPath = mountSamplePath
	return
}

// Like SetUp, but doens't panic.
func (t *SubprocessTest) initialize() (err error) {
	// Initialize the context.
	t.Ctx = context.Background()

	// Set up a temporary directory.
	t.Dir, err = ioutil.TempDir("", "sample_test")
	if err != nil {
		err = fmt.Errorf("TempDir: %v", err)
		return
	}

	// Build the mount_sample tool.
	toolPath, err := buildMountSample()
	if err != nil {
		err = fmt.Errorf("buildMountSample: %v", err)
		return
	}

	// Set up a command.
	args := []string{
		toolPath,
		"--type",
		t.MountType,
	}

	args = append(args, t.MountFlags...)

	t.mountCmd = exec.Command(toolPath, args...)
	t.mountCmd.Stdout = &t.mountStdout
	t.mountCmd.Stderr = &t.mountStderr

	// Start it.
	if err = t.mountCmd.Start(); err != nil {
		err = fmt.Errorf("mountCmd.Start: %v", err)
		return
	}

	// TODO(jacobsa): Probably need some sort of signalling (on stderr? write to
	// a flag-controlled file?) when WaitForReady has returned.

	return
}

// Unmount the file system and clean up. Panics on error.
func (t *SubprocessTest) TearDown() {
	err := t.destroy()
	if err != nil {
		panic(err)
	}
}

// Like TearDown, but doesn't panic.
func (t *SubprocessTest) destroy() (err error) {
	// Close what is necessary.
	for _, c := range t.ToClose {
		if c == nil {
			continue
		}

		ogletest.ExpectEq(nil, c.Close())
	}

	// If we didn't try to mount the file system, there's nothing further to do.
	if t.mountCmd == nil {
		return
	}

	// In the background, initiate an unmount.
	unmountErrChan := make(chan error)
	go func() {
		unmountErrChan <- unmount(t.Dir)
	}()

	// Make sure we wait for the unmount, even if we've already returned early in
	// error. Return its error if we haven't seen any other error.
	defer func() {
		unmountErr := <-unmountErrChan
		if unmountErr != nil {
			if err != nil {
				log.Println("unmount:", unmountErr)
				return
			}

			err = fmt.Errorf("unmount: %v", unmountErr)
		}
	}()

	// Wait for the subprocess.
	if err = t.mountCmd.Wait(); err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			err = fmt.Errorf(
				"mount_sample exited with %v. Stderr:\n%s",
				exitErr,
				t.mountStderr.String())

			return
		}

		err = fmt.Errorf("mountCmd.Wait: %v", err)
		return
	}

	return
}
<commit_msg>Fixed an invocation bug.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package samples

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os/exec"
	"path"
	"sync"

	"github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

// A struct that implements common behavior needed by tests in the samples/
// directory where the file system is mounted by a subprocess. Use it as an
// embedded field in your test fixture, calling its SetUp method from your
// SetUp method after setting the MountType and MountFlags fields.
type SubprocessTest struct {
	// The type of the file system to mount. Must be recognized by mount_sample.
	MountType string

	// Additional flags to be passed to the mount_sample tool.
	MountFlags []string

	// A context object that can be used for long-running operations.
	Ctx context.Context

	// The directory at which the file system is mounted.
	Dir string

	// Anothing non-nil in this slice will be closed by TearDown. The test will
	// fail if closing fails.
	ToClose []io.Closer

	mountCmd    *exec.Cmd
	mountStdout bytes.Buffer
	mountStderr bytes.Buffer
}

// Mount the file system and initialize the other exported fields of the
// struct. Panics on error.
//
// REQUIRES: t.FileSystem has been set.
func (t *SubprocessTest) SetUp(ti *ogletest.TestInfo) {
	err := t.initialize()
	if err != nil {
		panic(err)
	}
}

// Set by buildMountSample.
var mountSamplePath string
var mountSampleErr error
var mountSampleOnce sync.Once

// Build the mount_sample tool if it has not yet been built for this process.
// Return a path to the binary.
func buildMountSample() (toolPath string, err error) {
	// Build if we haven't yet.
	mountSampleOnce.Do(func() {
		// Create a temporary directory.
		tempDir, err := ioutil.TempDir("", "")
		if err != nil {
			mountSampleErr = fmt.Errorf("TempDir: %v", err)
			return
		}

		mountSamplePath = path.Join(tempDir, "mount_sample")

		// Build the command.
		cmd := exec.Command(
			"go",
			"build",
			"-o",
			mountSamplePath,
			"github.com/jacobsa/fuse/samples/mount_sample")

		output, err := cmd.CombinedOutput()
		if err != nil {
			mountSampleErr = fmt.Errorf(
				"mount_sample exited with %v, output:\n%s",
				err,
				string(output))

			return
		}
	})

	if mountSampleErr != nil {
		err = mountSampleErr
		return
	}

	toolPath = mountSamplePath
	return
}

// Like SetUp, but doens't panic.
func (t *SubprocessTest) initialize() (err error) {
	// Initialize the context.
	t.Ctx = context.Background()

	// Set up a temporary directory.
	t.Dir, err = ioutil.TempDir("", "sample_test")
	if err != nil {
		err = fmt.Errorf("TempDir: %v", err)
		return
	}

	// Build the mount_sample tool.
	toolPath, err := buildMountSample()
	if err != nil {
		err = fmt.Errorf("buildMountSample: %v", err)
		return
	}

	// Set up a command.
	args := []string{
		"--type",
		t.MountType,
	}

	args = append(args, t.MountFlags...)

	t.mountCmd = exec.Command(toolPath, args...)
	t.mountCmd.Stdout = &t.mountStdout
	t.mountCmd.Stderr = &t.mountStderr

	// Start it.
	if err = t.mountCmd.Start(); err != nil {
		err = fmt.Errorf("mountCmd.Start: %v", err)
		return
	}

	// TODO(jacobsa): Probably need some sort of signalling (on stderr? write to
	// a flag-controlled file?) when WaitForReady has returned.

	return
}

// Unmount the file system and clean up. Panics on error.
func (t *SubprocessTest) TearDown() {
	err := t.destroy()
	if err != nil {
		panic(err)
	}
}

// Like TearDown, but doesn't panic.
func (t *SubprocessTest) destroy() (err error) {
	// Close what is necessary.
	for _, c := range t.ToClose {
		if c == nil {
			continue
		}

		ogletest.ExpectEq(nil, c.Close())
	}

	// If we didn't try to mount the file system, there's nothing further to do.
	if t.mountCmd == nil {
		return
	}

	// In the background, initiate an unmount.
	unmountErrChan := make(chan error)
	go func() {
		unmountErrChan <- unmount(t.Dir)
	}()

	// Make sure we wait for the unmount, even if we've already returned early in
	// error. Return its error if we haven't seen any other error.
	defer func() {
		unmountErr := <-unmountErrChan
		if unmountErr != nil {
			if err != nil {
				log.Println("unmount:", unmountErr)
				return
			}

			err = fmt.Errorf("unmount: %v", unmountErr)
		}
	}()

	// Wait for the subprocess.
	if err = t.mountCmd.Wait(); err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			err = fmt.Errorf(
				"mount_sample exited with %v. Stderr:\n%s",
				exitErr,
				t.mountStderr.String())

			return
		}

		err = fmt.Errorf("mountCmd.Wait: %v", err)
		return
	}

	return
}
<|endoftext|>


<commit_before>package espsdk

import (
	"encoding/json"
	"log"
)

// GetKeywords requests suggestions from the Getty controlled vocabulary
// for the keywords provided.
func GetKeywords(client *Client) []byte { return client.get(Keywords) }

// GetPersonalities requests suggestions from the Getty controlled vocabulary
// for the famous personalities provided.
func GetPersonalities(client *Client) []byte { return client.get(Personalities) }

// GetControlledValues returns complete lists of values and descriptions for
// fields with controlled vocabularies, grouped by submission type.
func GetControlledValues(client *Client) []byte { return client.get(ControlledValues) }

// GetTranscoderMappings lists acceptable transcoder mapping values
// for Getty and iStock video.
func GetTranscoderMappings(client *Client) []byte { return client.get(TranscoderMappings) }

// GetCompositions lists all possible composition values.
func GetCompositions(client *Client) []byte { return client.get(Compositions) }

type PeopleMetadata struct {
	Term     string `json:"term,omitempty"`
	TermID   int    `json:"term_id,omitempty"`
	ImageURI string `json:"image_uri,omitempty"`
	HelpText string `json:"help_text,omitempty"`
}

type PeopleMetadataList []PeopleMetadata

// Marshal serializes PeopleMetadata into a byte slice of indented JSON.
func (m PeopleMetadataList) Marshal() ([]byte, error) { return indentedJSON(m) }

// Unmarshal attempts to deserialize the provided JSON payload into a
// representation of people metadata.
func (m PeopleMetadataList) Unmarshal(payload []byte) PeopleMetadataList {
	var items PeopleMetadataList
	if err := json.Unmarshal(payload, &items); err != nil {
		log.Fatal(err)
	}
	return items
}

// GetNumberOfPeople lists all possible values for Number of People.
func (m PeopleMetadataList) GetNumberOfPeople(client *Client) PeopleMetadataList {
	return m.Unmarshal(client.get(NumberOfPeople))
}

// GetExpressions lists all possible facial expression values.
func (m PeopleMetadataList) GetExpressions(client *Client) PeopleMetadataList {
	return m.Unmarshal(client.get(Expressions))
}

// GetCompositions lists all possible composition values.
func (m PeopleMetadataList) GetCompositions(client *Client) PeopleMetadataList {
	return m.Unmarshal(client.get(Compositions))
}

// PrettyPrint returns a human-readable serialized JSON representation of
// the provided object.
func (m PeopleMetadataList) PrettyPrint() string { return prettyPrint(m) }
<commit_msg>improve naming<commit_after>package espsdk

import (
	"encoding/json"
	"log"
)

// GetKeywords requests suggestions from the Getty controlled vocabulary
// for the keywords provided.
//
// TODO: not implemented (keywords and personalities need a new struct type)
func GetKeywords(client *Client) []byte { return client.get(Keywords) }

// GetPersonalities requests suggestions from the Getty controlled vocabulary
// for the famous personalities provided.
//
// TODO: not implemented (keywords and personalities need a new struct type)
func GetPersonalities(client *Client) []byte { return client.get(Personalities) }

// GetControlledValues returns complete lists of values and descriptions for
// fields with controlled vocabularies, grouped by submission type.
//
// TODO: not implemented (needs new struct type)
func GetControlledValues(client *Client) []byte { return client.get(ControlledValues) }

// GetTranscoderMappings lists acceptable transcoder mapping values
// for Getty and iStock video.
//
// TODO: not implemented (needs new struct type)
func GetTranscoderMappings(client *Client) []byte { return client.get(TranscoderMappings) }

type TermItem struct {
	Term     string `json:"term,omitempty"`
	TermID   int    `json:"term_id,omitempty"`
	ImageURI string `json:"image_uri,omitempty"`
	HelpText string `json:"help_text,omitempty"`
}

type TermList []TermItem

// Marshal serializes a TermList into a byte slice of indented JSON.
func (m TermList) Marshal() ([]byte, error) { return indentedJSON(m) }

// Unmarshal attempts to deserialize the provided JSON payload into a
// representation of people metadata.
func (m TermList) Unmarshal(payload []byte) TermList {
	var items TermList
	if err := json.Unmarshal(payload, &items); err != nil {
		log.Fatal(err)
	}
	return items
}

// GetNumberOfPeople lists all possible values for Number of People.
func (m TermList) GetNumberOfPeople(client *Client) TermList {
	return m.Unmarshal(client.get(NumberOfPeople))
}

// GetExpressions lists all possible facial expression values.
func (m TermList) GetExpressions(client *Client) TermList {
	return m.Unmarshal(client.get(Expressions))
}

// GetCompositions lists all possible composition values.
func (m TermList) GetCompositions(client *Client) TermList {
	return m.Unmarshal(client.get(Compositions))
}

// PrettyPrint returns a human-readable serialized JSON representation of
// the provided object.
func (m TermList) PrettyPrint() string { return prettyPrint(m) }
<|endoftext|>