<commit_before>package command

import (
	"flag"
	"os"
	"os/signal"
	"syscall"

	"github.com/mredivo/pulldeploy/deployment"
	"github.com/mredivo/pulldeploy/logging"
	"github.com/mredivo/pulldeploy/pdconfig"
	"github.com/mredivo/pulldeploy/signaller"
	"github.com/mredivo/pulldeploy/storage"
)

// pulldeploy daemon ...
type Daemon struct {
	result     *Result
	pdcfg      pdconfig.PDConfig
	envName    string
	logFile    string
	lw         *logging.Writer
	stg        storage.Storage
	hr         *signaller.Registry
	myHostname string
	canary     map[string]int
}

func (cmd *Daemon) CheckArgs(cmdName string, pdcfg pdconfig.PDConfig, osArgs []string) *Result {

	var envName, logFile string
	cmd.result = NewResult(cmdName)
	cmd.pdcfg = pdcfg

	cmdFlags := flag.NewFlagSet(cmdName, flag.ExitOnError)
	cmdFlags.StringVar(&envName, "env", "", "environment to be monitored")
	cmdFlags.StringVar(&logFile, "logfile", "", "name of log file (default stdout)")
	cmdFlags.Parse(osArgs)

	if envName == "" {
		cmd.result.Errorf("env is a mandatory argument")
	} else {
		cmd.envName = envName
	}
	cmd.logFile = logFile
	cmd.canary = make(map[string]int)

	return cmd.result
}

func (cmd *Daemon) Exec() *Result {

	// Set up reload and termination signal handlers.
	var sigterm = make(chan os.Signal, 1)
	signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM)
	var sighup = make(chan os.Signal, 1)
	signal.Notify(sighup, syscall.SIGHUP)
	var sigusr1 = make(chan os.Signal, 1)
	signal.Notify(sigusr1, syscall.SIGUSR1)

	// Open the logger.
	logger := logging.New("pulldeploy", cmd.logFile, true)
	defer logger.Close()
	cmd.lw = logger.GetWriter("", cmd.pdcfg.GetLogLevel())
	cmd.lw.Info(cmd.pdcfg.GetVersionInfo().OneLine())

	// Instantiate the signaller that tells us when apps need attention.
	sgnlr := signaller.New(cmd.pdcfg.GetSignallerConfig(), cmd.lw)
	appEvent := sgnlr.Open()
	defer sgnlr.Close()
	cmd.hr = sgnlr.GetRegistry()

	// Get access to the repo storage.
	stgcfg := cmd.pdcfg.GetStorageConfig()
	if stg, err := storage.New(storage.AccessMethod(stgcfg.AccessMethod), stgcfg.Params); err == nil {
		cmd.stg = stg
	} else {
		cmd.result.AppendError(err)
		return cmd.result
	}

	// Determine the local hostname.
	cmd.myHostname, _ = os.Hostname()
	cmd.lw.Info("Host name: %q", cmd.myHostname)

	// Get the set of applications to monitor.
	appList := cmd.pdcfg.GetAppList()

	var registerAppHosts = func() {
		for appName, _ := range appList {

			// Retrieve the app definition.
			appCfg, err := cmd.pdcfg.GetAppConfig(appName)
			if err != nil {
				cmd.lw.Error("Error getting configuration for %q: %s", appName, err.Error())
				continue
			}

			// Instantiate the deployment object for this application.
			dplmt, err := deployment.New(appName, cmd.pdcfg, appCfg)
			if err != nil {
				cmd.lw.Error("Error in deployment for %q: %s", appName, err.Error())
				continue
			}

			// Register with current version, and ask for notifications.
			cmd.hr.Register(cmd.envName, appName, cmd.myHostname,
				dplmt.GetCurrentLink(), dplmt.GetDeployedVersions())
			sgnlr.Monitor(cmd.envName, appName)
		}
	}

	var unregisterAppHosts = func() {
		for appName, _ := range appList {
			cmd.hr.Unregister(cmd.envName, appName, cmd.myHostname)
		}
	}

	var synchronize = func() {
		for appName, _ := range appList {
			cmd.synchronize(signaller.Notification{Source: signaller.KNS_FORCED, Appname: appName})
		}
	}

	// Register the host for each application in this environment.
	registerAppHosts()
	cmd.lw.Info("Startup complete")

	// Perform the initial synchronization with the repo.
	synchronize()

	// Processing loop.
	repeat := true
	for repeat {
		select {

		case appNotification := <-appEvent:
			// Make the local deploy/release state of the app match the repo index.
			cmd.synchronize(appNotification)

		case <-sigusr1:
			// Close and re-open the logfile.
			cmd.lw.Info("Received SIGUSR1")
			logger.OnRotate()

		case <-sighup:
			// Refresh the set of applications to monitor.
			cmd.lw.Info("Received SIGHUP")
			// Unregister and stop monitoring.
			unregisterAppHosts()
			cmd.pdcfg.RefreshAppList()
			appList = cmd.pdcfg.GetAppList()
			// Re-register and restart monitoring.
			registerAppHosts()
			synchronize()

		case <-sigterm:
			// Gracefully shut down.
			cmd.lw.Info("Received SIGINT/SIGTERM")
			repeat = false // Break loop.
		}
	}

	unregisterAppHosts()
	cmd.lw.Info("Termination complete")

	return cmd.result
}

func (cmd *Daemon) synchronize(an signaller.Notification) {

	cmd.lw.Info("Synchronizing %q in %q (%s)", an.Appname, cmd.envName, an.Source)

	// Retrieve the app definition.
	appCfg, err := cmd.pdcfg.GetAppConfig(an.Appname)
	if err != nil {
		cmd.lw.Error("Error getting configuration for %q: %s", an.Appname, err.Error())
		return
	}

	// Get the extension for the artifact type.
	var extension string
	if ac, err := cmd.pdcfg.GetArtifactConfig(appCfg.ArtifactType); err == nil {
		extension = ac.Extension
	} else {
		cmd.lw.Error("Invalid ArtifactType for %q: %q", an.Appname, appCfg.ArtifactType)
		return
	}

	// Instantiate the deployment object for this application.
	dplmt, err := deployment.New(an.Appname, cmd.pdcfg, appCfg)
	if err != nil {
		cmd.lw.Error("Error in deployment for %q: %s", an.Appname, err.Error())
		return
	}

	// Retrieve the repository index.
	if ri, err := getRepoIndex(cmd.stg, an.Appname); err == nil {

		// Retrieve the environment.
		if env, err := ri.GetEnv(cmd.envName); err != nil {
			cmd.lw.Error("Error getting %q environment for %q: %s", cmd.envName, an.Appname, err.Error())
			return
		} else {

			// First check whether the repository has changed since we last looked.
			if canary, found := cmd.canary[an.Appname]; found && canary == ri.Canary {
				cmd.lw.Debug("Canary unchanged for %q: %d", an.Appname, canary)
				// Returning here misses the case where the repository has not changed,
				// but the local filesystem has. Log the message, but keep going.
				//return
			}

			// Determine whether any new versions have been deployed since we last checked.
			localVersionList := dplmt.GetDeployedVersions()
			var deployedVersionList []string
			for _, v := range env.Deployed {
				deployedVersionList = append(deployedVersionList, v.Version)
			}
			newDeployments := subtractArray(deployedVersionList, localVersionList)
			cmd.lw.Debug("Deployments for %s in %s: local=%v, repo=%v new=%v",
				an.Appname, cmd.envName, localVersionList, deployedVersionList, newDeployments)

			// Fetch and unpack all new deployments.
			for _, version := range newDeployments {

				// Determine the base filename.
				filename := ri.ArtifactFilename(version, extension)

				// Retrieve the artifact for that filename.
				if !dplmt.ArtifactPresent(version) {
					if art, err := cmd.stg.GetReader(ri.ArtifactPath(filename)); err == nil {
						if err := dplmt.WriteArtifact(version, art); err == nil {
							cmd.lw.Debug("Fetched artifact %q for %s in %s",
								ri.ArtifactPath(filename), cmd.envName, an.Appname)
						} else {
							cmd.lw.Error("Error writing artifact %q for %s in %s: %s",
								ri.ArtifactPath(filename), cmd.envName, an.Appname, err.Error())
							continue
						}
					} else {
						cmd.lw.Error("Error getting artifact %q for %s in %s: %s",
							ri.ArtifactPath(filename), cmd.envName, an.Appname, err.Error())
						continue
					}
				}

				// Retrieve the HMAC for that filename.
				if !dplmt.HMACPresent(version) {
					if hmac, err := cmd.stg.Get(ri.HMACPath(filename)); err == nil {
						if err := dplmt.WriteHMAC(version, hmac); err == nil {
							cmd.lw.Debug("Fetched HMAC %q for %s in %s",
								ri.HMACPath(filename), cmd.envName, an.Appname)
						} else {
							cmd.lw.Error("Error writing HMAC %q for %s in %s: %s",
								ri.HMACPath(filename), cmd.envName, an.Appname, err.Error())
							continue
						}
					} else {
						cmd.lw.Error("Error getting HMAC %q for %s in %s: %s",
							ri.HMACPath(filename), cmd.envName, an.Appname, err.Error())
						continue
					}
				}

				// Compare the calculated HMAC with the retrieved HMAC.
				if err := dplmt.CheckHMAC(version); err == nil {
					cmd.lw.Debug("HMAC comparison succeeded for %s in %s, version %q",
						an.Appname, cmd.envName, version)
				} else {
					cmd.lw.Error("HMAC comparison FAILED for %s in %s, version %q",
						an.Appname, cmd.envName, version)
					continue
				}

				// Extract the artifact to the release directory.
				if err := dplmt.Extract(version); err == nil {
					cmd.lw.Debug("Extracted version %q for %s in %s",
						version, cmd.envName, an.Appname)
				} else {
					cmd.lw.Error("Extract FAILED for %s in %s, version %q: %s",
						an.Appname, cmd.envName, version, err.Error())
					continue
				}

				// Execute the post-deploy command.
				cmd.logPostCommand(dplmt.PostDeploy(version))
				cmd.hr.Register(cmd.envName, an.Appname, cmd.myHostname,
					dplmt.GetCurrentLink(), dplmt.GetDeployedVersions())
			}

			// Determine the currently released version on the local host, and
			// update if necessary.
			localRelease := dplmt.GetCurrentLink()
			currentRelease := env.GetCurrentVersion(cmd.myHostname)
			cmd.lw.Debug("Current release: local=%q, repo=%q", localRelease, currentRelease)
			if localRelease != currentRelease && currentRelease != "" {
				if err := dplmt.Link(currentRelease); err == nil {
					cmd.lw.Info("Current release for %s in %s set to %q",
						an.Appname, cmd.envName, currentRelease)
					// Execute the post-release command.
					cmd.logPostCommand(dplmt.PostRelease(currentRelease))
					cmd.hr.Register(cmd.envName, an.Appname, cmd.myHostname,
						dplmt.GetCurrentLink(), dplmt.GetDeployedVersions())
				} else {
					cmd.lw.Error("Error setting current release for %s in %s to %q: %s",
						an.Appname, cmd.envName, currentRelease, err.Error())
				}
			}

			// Note that the local host is in sync with the index.
			cmd.canary[an.Appname] = ri.Canary
		}

	} else {
		cmd.lw.Error("Error getting repo index for %q: %s", an.Appname, err.Error())
	}
}

func (cmd *Daemon) logPostCommand(cmdline string, err error) {
	if cmdline != "" {
		cmd.lw.Info(cmdline)
	}
	if err != nil {
		cmd.lw.Warn(err.Error())
	}
}
<commit_msg>Do not fetch disabled versions when not present locally<commit_after>package command

import (
	"flag"
	"os"
	"os/signal"
	"syscall"

	"github.com/mredivo/pulldeploy/deployment"
	"github.com/mredivo/pulldeploy/logging"
	"github.com/mredivo/pulldeploy/pdconfig"
	"github.com/mredivo/pulldeploy/signaller"
	"github.com/mredivo/pulldeploy/storage"
)

// pulldeploy daemon ...
type Daemon struct {
	result     *Result
	pdcfg      pdconfig.PDConfig
	envName    string
	logFile    string
	lw         *logging.Writer
	stg        storage.Storage
	hr         *signaller.Registry
	myHostname string
	canary     map[string]int
}

func (cmd *Daemon) CheckArgs(cmdName string, pdcfg pdconfig.PDConfig, osArgs []string) *Result {

	var envName, logFile string
	cmd.result = NewResult(cmdName)
	cmd.pdcfg = pdcfg

	cmdFlags := flag.NewFlagSet(cmdName, flag.ExitOnError)
	cmdFlags.StringVar(&envName, "env", "", "environment to be monitored")
	cmdFlags.StringVar(&logFile, "logfile", "", "name of log file (default stdout)")
	cmdFlags.Parse(osArgs)

	if envName == "" {
		cmd.result.Errorf("env is a mandatory argument")
	} else {
		cmd.envName = envName
	}
	cmd.logFile = logFile
	cmd.canary = make(map[string]int)

	return cmd.result
}

func (cmd *Daemon) Exec() *Result {

	// Set up reload and termination signal handlers.
	var sigterm = make(chan os.Signal, 1)
	signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM)
	var sighup = make(chan os.Signal, 1)
	signal.Notify(sighup, syscall.SIGHUP)
	var sigusr1 = make(chan os.Signal, 1)
	signal.Notify(sigusr1, syscall.SIGUSR1)

	// Open the logger.
	logger := logging.New("pulldeploy", cmd.logFile, true)
	defer logger.Close()
	cmd.lw = logger.GetWriter("", cmd.pdcfg.GetLogLevel())
	cmd.lw.Info(cmd.pdcfg.GetVersionInfo().OneLine())

	// Instantiate the signaller that tells us when apps need attention.
	sgnlr := signaller.New(cmd.pdcfg.GetSignallerConfig(), cmd.lw)
	appEvent := sgnlr.Open()
	defer sgnlr.Close()
	cmd.hr = sgnlr.GetRegistry()

	// Get access to the repo storage.
	stgcfg := cmd.pdcfg.GetStorageConfig()
	if stg, err := storage.New(storage.AccessMethod(stgcfg.AccessMethod), stgcfg.Params); err == nil {
		cmd.stg = stg
	} else {
		cmd.result.AppendError(err)
		return cmd.result
	}

	// Determine the local hostname.
	cmd.myHostname, _ = os.Hostname()
	cmd.lw.Info("Host name: %q", cmd.myHostname)

	// Get the set of applications to monitor.
	appList := cmd.pdcfg.GetAppList()

	var registerAppHosts = func() {
		for appName, _ := range appList {

			// Retrieve the app definition.
			appCfg, err := cmd.pdcfg.GetAppConfig(appName)
			if err != nil {
				cmd.lw.Error("Error getting configuration for %q: %s", appName, err.Error())
				continue
			}

			// Instantiate the deployment object for this application.
			dplmt, err := deployment.New(appName, cmd.pdcfg, appCfg)
			if err != nil {
				cmd.lw.Error("Error in deployment for %q: %s", appName, err.Error())
				continue
			}

			// Register with current version, and ask for notifications.
			cmd.hr.Register(cmd.envName, appName, cmd.myHostname,
				dplmt.GetCurrentLink(), dplmt.GetDeployedVersions())
			sgnlr.Monitor(cmd.envName, appName)
		}
	}

	var unregisterAppHosts = func() {
		for appName, _ := range appList {
			cmd.hr.Unregister(cmd.envName, appName, cmd.myHostname)
		}
	}

	var synchronize = func() {
		for appName, _ := range appList {
			cmd.synchronize(signaller.Notification{Source: signaller.KNS_FORCED, Appname: appName})
		}
	}

	// Register the host for each application in this environment.
	registerAppHosts()
	cmd.lw.Info("Startup complete")

	// Perform the initial synchronization with the repo.
	synchronize()

	// Processing loop.
	repeat := true
	for repeat {
		select {

		case appNotification := <-appEvent:
			// Make the local deploy/release state of the app match the repo index.
			cmd.synchronize(appNotification)

		case <-sigusr1:
			// Close and re-open the logfile.
			cmd.lw.Info("Received SIGUSR1")
			logger.OnRotate()

		case <-sighup:
			// Refresh the set of applications to monitor.
			cmd.lw.Info("Received SIGHUP")
			// Unregister and stop monitoring.
			unregisterAppHosts()
			cmd.pdcfg.RefreshAppList()
			appList = cmd.pdcfg.GetAppList()
			// Re-register and restart monitoring.
			registerAppHosts()
			synchronize()

		case <-sigterm:
			// Gracefully shut down.
			cmd.lw.Info("Received SIGINT/SIGTERM")
			repeat = false // Break loop.
		}
	}

	unregisterAppHosts()
	cmd.lw.Info("Termination complete")

	return cmd.result
}

func (cmd *Daemon) synchronize(an signaller.Notification) {

	cmd.lw.Info("Synchronizing %q in %q (%s)", an.Appname, cmd.envName, an.Source)

	// Retrieve the app definition.
	appCfg, err := cmd.pdcfg.GetAppConfig(an.Appname)
	if err != nil {
		cmd.lw.Error("Error getting configuration for %q: %s", an.Appname, err.Error())
		return
	}

	// Get the extension for the artifact type.
	var extension string
	if ac, err := cmd.pdcfg.GetArtifactConfig(appCfg.ArtifactType); err == nil {
		extension = ac.Extension
	} else {
		cmd.lw.Error("Invalid ArtifactType for %q: %q", an.Appname, appCfg.ArtifactType)
		return
	}

	// Instantiate the deployment object for this application.
	dplmt, err := deployment.New(an.Appname, cmd.pdcfg, appCfg)
	if err != nil {
		cmd.lw.Error("Error in deployment for %q: %s", an.Appname, err.Error())
		return
	}

	// Retrieve the repository index.
	if ri, err := getRepoIndex(cmd.stg, an.Appname); err == nil {

		// Retrieve the environment.
		if env, err := ri.GetEnv(cmd.envName); err != nil {
			cmd.lw.Error("Error getting %q environment for %q: %s", cmd.envName, an.Appname, err.Error())
			return
		} else {

			// First check whether the repository has changed since we last looked.
			if canary, found := cmd.canary[an.Appname]; found && canary == ri.Canary {
				cmd.lw.Debug("Canary unchanged for %q: %d", an.Appname, canary)
				// Returning here misses the case where the repository has not changed,
				// but the local filesystem has. Log the message, but keep going.
				//return
			}

			// Determine whether any new versions have been deployed since we last checked.
			localVersionList := dplmt.GetDeployedVersions()
			var deployedVersionList []string
			for _, v := range env.Deployed {
				if vers, err := ri.GetVersion(v.Version); err == nil && vers.Enabled {
					deployedVersionList = append(deployedVersionList, v.Version)
				}
			}
			newDeployments := subtractArray(deployedVersionList, localVersionList)
			cmd.lw.Debug("Deployments for %s in %s: local=%v, repo=%v new=%v",
				an.Appname, cmd.envName, localVersionList, deployedVersionList, newDeployments)

			// Fetch and unpack all new deployments.
			for _, version := range newDeployments {

				// Determine the base filename.
				filename := ri.ArtifactFilename(version, extension)

				// Retrieve the artifact for that filename.
				if !dplmt.ArtifactPresent(version) {
					if art, err := cmd.stg.GetReader(ri.ArtifactPath(filename)); err == nil {
						if err := dplmt.WriteArtifact(version, art); err == nil {
							cmd.lw.Debug("Fetched artifact %q for %s in %s",
								ri.ArtifactPath(filename), cmd.envName, an.Appname)
						} else {
							cmd.lw.Error("Error writing artifact %q for %s in %s: %s",
								ri.ArtifactPath(filename), cmd.envName, an.Appname, err.Error())
							continue
						}
					} else {
						cmd.lw.Error("Error getting artifact %q for %s in %s: %s",
							ri.ArtifactPath(filename), cmd.envName, an.Appname, err.Error())
						continue
					}
				}

				// Retrieve the HMAC for that filename.
				if !dplmt.HMACPresent(version) {
					if hmac, err := cmd.stg.Get(ri.HMACPath(filename)); err == nil {
						if err := dplmt.WriteHMAC(version, hmac); err == nil {
							cmd.lw.Debug("Fetched HMAC %q for %s in %s",
								ri.HMACPath(filename), cmd.envName, an.Appname)
						} else {
							cmd.lw.Error("Error writing HMAC %q for %s in %s: %s",
								ri.HMACPath(filename), cmd.envName, an.Appname, err.Error())
							continue
						}
					} else {
						cmd.lw.Error("Error getting HMAC %q for %s in %s: %s",
							ri.HMACPath(filename), cmd.envName, an.Appname, err.Error())
						continue
					}
				}

				// Compare the calculated HMAC with the retrieved HMAC.
				if err := dplmt.CheckHMAC(version); err == nil {
					cmd.lw.Debug("HMAC comparison succeeded for %s in %s, version %q",
						an.Appname, cmd.envName, version)
				} else {
					cmd.lw.Error("HMAC comparison FAILED for %s in %s, version %q",
						an.Appname, cmd.envName, version)
					continue
				}

				// Extract the artifact to the release directory.
				if err := dplmt.Extract(version); err == nil {
					cmd.lw.Debug("Extracted version %q for %s in %s",
						version, cmd.envName, an.Appname)
				} else {
					cmd.lw.Error("Extract FAILED for %s in %s, version %q: %s",
						an.Appname, cmd.envName, version, err.Error())
					continue
				}

				// Execute the post-deploy command.
				cmd.logPostCommand(dplmt.PostDeploy(version))
				cmd.hr.Register(cmd.envName, an.Appname, cmd.myHostname,
					dplmt.GetCurrentLink(), dplmt.GetDeployedVersions())
			}

			// Determine the currently released version on the local host, and
			// update if necessary.
			localRelease := dplmt.GetCurrentLink()
			currentRelease := env.GetCurrentVersion(cmd.myHostname)
			cmd.lw.Debug("Current release: local=%q, repo=%q", localRelease, currentRelease)
			if localRelease != currentRelease && currentRelease != "" {
				if err := dplmt.Link(currentRelease); err == nil {
					cmd.lw.Info("Current release for %s in %s set to %q",
						an.Appname, cmd.envName, currentRelease)
					// Execute the post-release command.
					cmd.logPostCommand(dplmt.PostRelease(currentRelease))
					cmd.hr.Register(cmd.envName, an.Appname, cmd.myHostname,
						dplmt.GetCurrentLink(), dplmt.GetDeployedVersions())
				} else {
					cmd.lw.Error("Error setting current release for %s in %s to %q: %s",
						an.Appname, cmd.envName, currentRelease, err.Error())
				}
			}

			// Note that the local host is in sync with the index.
			cmd.canary[an.Appname] = ri.Canary
		}

	} else {
		cmd.lw.Error("Error getting repo index for %q: %s", an.Appname, err.Error())
	}
}

func (cmd *Daemon) logPostCommand(cmdline string, err error) {
	if cmdline != "" {
		cmd.lw.Info(cmdline)
	}
	if err != nil {
		cmd.lw.Warn(err.Error())
	}
}
<|endoftext|>


<commit_before>package command

import (
	"encoding/json"
	"fmt"

	"github.com/ondevice/ondevice/config"
	"github.com/ondevice/ondevice/control"
	"github.com/ondevice/ondevice/logg"
)

// StatusCmd -- implements 'ondevice status'
type StatusCmd struct{}

const _longStatusHelp = `ondevice status [--json]

Print status and version information on the local ondevice client/device

Options:
--json
  Prints JSON formatted instead of human readable output

Exit Codes:
0: daemon up and running
1: on error
2: daemon running but not online
3: daemon not running (or unreachable)
`

func (s *StatusCmd) args() string {
	return "[--json]"
}

func (s *StatusCmd) longHelp() string {
	return _longStatusHelp
}

func (s *StatusCmd) shortHelp() string {
	return "Prints the client and local device status"
}

// Run -- implements 'ondevice status'
func (s *StatusCmd) Run(args []string) int {
	if len(args) == 0 {
		return print()
	} else if len(args) != 1 || args[0] != "--json" {
		logg.Fatal("'ondevice status' expects either no arguments or a single '--json'")
		return 1
	} else {
		return printJSON()
	}
}

func print() int {
	state := getState()

	if state.Device != nil {
		fmt.Println("Device:")
		fmt.Println("  devID: ", state.Device["devId"])
		fmt.Println("  state: ", state.Device["state"])
		fmt.Println("  version: ", state.Version)
		fmt.Println("")
	}

	fmt.Println("Client:")
	fmt.Println("  version: ", state.Client["version"])

	return getReturnCode(state)
}

func printJSON() int {
	state := getState()

	buff, _ := json.MarshalIndent(state, "", "  ")
	fmt.Println(string(buff))

	return getReturnCode(state)
}

func getReturnCode(state control.DeviceState) int {
	if state.Device == nil {
		return 3 // missing "device" status -> daemon not running
	}

	if ds, ok := state.Device["state"]; ok {
		if ds == "online" {
			return 0
		}
	}
	return 1
}

func getState() control.DeviceState {
	rc, err := control.GetState()
	if err != nil {
		// can't query device socket -> assume daemon is not running
		// TODO make me a debug message
		logg.Warning("Couldn't query device state: ", err)
		rc = control.DeviceState{}
	}

	rc.Client = map[string]string{
		"version": config.GetVersion(),
	}

	return rc
}
<commit_msg>Decreased log verbosity of ‘ondevice status’ (not printing a warning just because the daemon isn’t running)<commit_after>package command

import (
	"encoding/json"
	"fmt"

	"github.com/ondevice/ondevice/config"
	"github.com/ondevice/ondevice/control"
	"github.com/ondevice/ondevice/logg"
)

// StatusCmd -- implements 'ondevice status'
type StatusCmd struct{}

const _longStatusHelp = `ondevice status [--json]

Print status and version information on the local ondevice client/device

Options:
--json
  Prints JSON formatted instead of human readable output

Exit Codes:
0: daemon up and running
1: on error
2: daemon running but not online
3: daemon not running (or unreachable)
`

func (s *StatusCmd) args() string {
	return "[--json]"
}

func (s *StatusCmd) longHelp() string {
	return _longStatusHelp
}

func (s *StatusCmd) shortHelp() string {
	return "Prints the client and local device status"
}

// Run -- implements 'ondevice status'
func (s *StatusCmd) Run(args []string) int {
	if len(args) == 0 {
		return print()
	} else if len(args) != 1 || args[0] != "--json" {
		logg.Fatal("'ondevice status' expects either no arguments or a single '--json'")
		return 1
	} else {
		return printJSON()
	}
}

func print() int {
	state := getState()

	if state.Device != nil {
		fmt.Println("Device:")
		fmt.Println("  devID: ", state.Device["devId"])
		fmt.Println("  state: ", state.Device["state"])
		fmt.Println("  version: ", state.Version)
		fmt.Println("")
	}

	fmt.Println("Client:")
	fmt.Println("  version: ", state.Client["version"])

	return getReturnCode(state)
}

func printJSON() int {
	state := getState()

	buff, _ := json.MarshalIndent(state, "", "  ")
	fmt.Println(string(buff))

	return getReturnCode(state)
}

func getReturnCode(state control.DeviceState) int {
	if state.Device == nil {
		return 3 // missing "device" status -> daemon not running
	}

	if ds, ok := state.Device["state"]; ok {
		if ds == "online" {
			return 0
		}
	}
	return 1
}

func getState() control.DeviceState {
	rc, err := control.GetState()
	if err != nil {
		// can't query device socket -> assume daemon is not running
		logg.Debug("Couldn't query device state: ", err)
		rc = control.DeviceState{}
	}

	rc.Client = map[string]string{
		"version": config.GetVersion(),
	}

	return rc
}
<|endoftext|>


<commit_before>// Code generated by "stringer -type Command"; DO NOT EDIT

package medtronic

import "fmt"

const _Command_name = "AckNakSetClockSetAbsoluteTempBasalSuspendWakeupSetPercentTempBasalClockPumpIdBatteryReservoirHistoryPageCarbUnitsGlucoseUnitsCarbRatiosInsulinSensitivitiesModelBasalRatesBasalPatternABasalPatternBTempBasalLastHistoryPageGlucoseTargetsSettingsStatus"

var _Command_map = map[Command]string{
	6:   _Command_name[0:3],
	21:  _Command_name[3:6],
	64:  _Command_name[6:14],
	76:  _Command_name[14:34],
	77:  _Command_name[34:41],
	93:  _Command_name[41:47],
	105: _Command_name[47:66],
	112: _Command_name[66:71],
	113: _Command_name[71:77],
	114: _Command_name[77:84],
	115: _Command_name[84:93],
	128: _Command_name[93:104],
	136: _Command_name[104:113],
	137: _Command_name[113:125],
	138: _Command_name[125:135],
	139: _Command_name[135:155],
	141: _Command_name[155:160],
	146: _Command_name[160:170],
	147: _Command_name[170:183],
	148: _Command_name[183:196],
	152: _Command_name[196:205],
	157: _Command_name[205:220],
	159: _Command_name[220:234],
	192: _Command_name[234:242],
	206: _Command_name[242:248],
}

func (i Command) String() string {
	if str, ok := _Command_map[i]; ok {
		return str
	}
	return fmt.Sprintf("Command(%d)", i)
}
<commit_msg>Check in file generated by stringer tool<commit_after>// Code generated by "stringer -type Command"; DO NOT EDIT

package medtronic

import "fmt"

const _Command_name = "acknaksetClocksetAbsoluteTempBasalsuspendwakeupsetPercentTempBasalclockpumpIDbatteryreservoirhistoryPagerfRemoteSrfRemoteACTrfRemoteBglucoseUnitscarbRatiosinsulinSensitivitiesmodelbasalRatesbasalPatternAbasalPatternBtempBasallastHistoryPageglucoseTargetssettingsstatus"

var _Command_map = map[Command]string{
	6:   _Command_name[0:3],
	21:  _Command_name[3:6],
	64:  _Command_name[6:14],
	76:  _Command_name[14:34],
	77:  _Command_name[34:41],
	93:  _Command_name[41:47],
	105: _Command_name[47:66],
	112: _Command_name[66:71],
	113: _Command_name[71:77],
	114: _Command_name[77:84],
	115: _Command_name[84:93],
	128: _Command_name[93:104],
	129: _Command_name[104:113],
	134: _Command_name[113:124],
	136: _Command_name[124:133],
	137: _Command_name[133:145],
	138: _Command_name[145:155],
	139: _Command_name[155:175],
	141: _Command_name[175:180],
	146: _Command_name[180:190],
	147: _Command_name[190:203],
	148: _Command_name[203:216],
	152: _Command_name[216:225],
	157: _Command_name[225:240],
	159: _Command_name[240:254],
	192: _Command_name[254:262],
	206: _Command_name[262:268],
}

func (i Command) String() string {
	if str, ok := _Command_map[i]; ok {
		return str
	}
	return fmt.Sprintf("Command(%d)", i)
}
<|endoftext|>


<commit_before>package engine

// Directory Index

import (
	"bytes"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/go-gcfg/gcfg"
	log "github.com/sirupsen/logrus"
	"github.com/xyproto/algernon/themes"
	"github.com/xyproto/algernon/utils"
)

var (
	// List of filenames that should be displayed instead of a directory listing
	indexFilenames = []string{"index.lua", "index.html", "index.md", "index.txt", "index.pongo2", "index.tmpl", "index.po2", "index.amber", "index.happ", "index.hyper", "index.hyper.js", "index.hyper.jsx"}
)

const (
	dotSlash        = "." + utils.Pathsep           /* ./ */
	doubleP         = utils.Pathsep + utils.Pathsep /* // */
	dirconfFilename = ".algernon"
)

// DirConfig keeps a directory listing configuration
type DirConfig struct {
	Main struct {
		Title string
		Theme string
	}
}

// DirectoryListing serves the given directory as a web page with links the the contents
func (ac *Config) DirectoryListing(w http.ResponseWriter, req *http.Request, rootdir, dirname, theme string) {
	var (
		buf          bytes.Buffer
		fullFilename string
		URLpath      string
		title        = dirname
	)

	// Remove a trailing slash after the root directory, if present
	rootdir = strings.TrimSuffix(rootdir, "/")

	// Fill the coming HTML body with a list of all the filenames in `dirname`
	for _, filename := range utils.GetFilenames(dirname) {

		if filename == dirconfFilename {
			// Skip
			continue
		}

		// Find the full name
		fullFilename = dirname

		// Add a "/" after the directory name, if missing
		if !strings.HasSuffix(fullFilename, utils.Pathsep) {
			fullFilename += utils.Pathsep
		}

		// Add the filename at the end
		fullFilename += filename

		// Remove the root directory from the link path
		URLpath = fullFilename[len(rootdir)+1:]

		// Output different entries for files and directories
		buf.WriteString(themes.HTMLLink(filename, URLpath, ac.fs.IsDir(fullFilename)))
	}

	// Read directory configuration, if present
	fullDirConfFilename := filepath.Join(dirname, dirconfFilename)
	if ac.fs.Exists(fullDirConfFilename) {
		var dirConf DirConfig
		if err := gcfg.ReadFileInto(&dirConf, fullDirConfFilename); err == nil { // if no error
			if dirConf.Main.Title != "" {
				title = dirConf.Main.Title
			}
			if dirConf.Main.Theme != "" {
				theme = dirConf.Main.Theme
			}
		}
	} else {
		// Strip the leading "./" from the current directory
		title = strings.TrimPrefix(title, dotSlash)

		// Replace "//" with just "/"
		title = strings.Replace(title, doubleP, utils.Pathsep, utils.EveryInstance)
	}

	// Check if the current page contents are empty
	if buf.Len() == 0 {
		buf.WriteString("Empty directory")
	}

	htmldata := themes.MessagePageBytes(title, buf.Bytes(), theme)

	// If the auto-refresh feature has been enabled
	if ac.autoRefresh {
		// Insert JavaScript for refreshing the page into the generated HTML
		htmldata = ac.InsertAutoRefresh(req, htmldata)
	}

	// Serve the page
	w.Header().Add("Content-Type", "text/html;charset=utf-8")
	ac.DataToClient(w, req, dirname, htmldata)
}

// DirPage serves a directory, using index.* files, if present.
// The directory must exist.
// rootdir is the base directory (can be ".")
// dirname is the specific directory that is to be served (should never be ".")
func (ac *Config) DirPage(w http.ResponseWriter, req *http.Request, rootdir, dirname, theme string) {

	// Check if we are instructed to quit after serving the first file
	if ac.quitAfterFirstRequest {
		go ac.quitSoon("Quit after first request", defaultSoonDuration)
	}

	// If the URL does not end with a slash, redirect to an URL that does
	if !strings.HasSuffix(req.URL.Path, "/") {
		if req.Method == "POST" {
			log.Warn("Redirecting a POST request: " + req.URL.Path + " -> " + req.URL.Path + "/.")
			log.Warn("Header data may be lost! Please add the missing slash.")
		}
		http.Redirect(w, req, req.URL.Path+"/", http.StatusMovedPermanently)
		return
	}

	// Handle the serving of index files, if needed
	var filename string
	for _, indexfile := range indexFilenames {
		filename = filepath.Join(dirname, indexfile)
		if ac.fs.Exists(filename) {
			ac.FilePage(w, req, filename, ac.defaultLuaDataFilename)
			return
		}
	}

	// Serve a directory listing if no index file is found
	ac.DirectoryListing(w, req, rootdir, dirname, theme)
}
<commit_msg>URL encode links when listing directories<commit_after>package engine

// Directory Index

import (
	"bytes"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/go-gcfg/gcfg"
	log "github.com/sirupsen/logrus"
	"github.com/xyproto/algernon/themes"
	"github.com/xyproto/algernon/utils"
)

var (
	// List of filenames that should be displayed instead of a directory listing
	indexFilenames = []string{"index.lua", "index.html", "index.md", "index.txt", "index.pongo2", "index.tmpl", "index.po2", "index.amber", "index.happ", "index.hyper", "index.hyper.js", "index.hyper.jsx"}
)

const (
	dotSlash        = "." + utils.Pathsep           /* ./ */
	doubleP         = utils.Pathsep + utils.Pathsep /* // */
	dirconfFilename = ".algernon"
)

// DirConfig keeps a directory listing configuration
type DirConfig struct {
	Main struct {
		Title string
		Theme string
	}
}

// DirectoryListing serves the given directory as a web page with links the the contents
func (ac *Config) DirectoryListing(w http.ResponseWriter, req *http.Request, rootdir, dirname, theme string) {
	var (
		buf          bytes.Buffer
		fullFilename string
		URLpath      string
		title        = dirname
	)

	// Remove a trailing slash after the root directory, if present
	rootdir = strings.TrimSuffix(rootdir, "/")

	// Fill the coming HTML body with a list of all the filenames in `dirname`
	for _, filename := range utils.GetFilenames(dirname) {

		if filename == dirconfFilename {
			// Skip
			continue
		}

		// Find the full name
		fullFilename = dirname

		// Add a "/" after the directory name, if missing
		if !strings.HasSuffix(fullFilename, utils.Pathsep) {
			fullFilename += utils.Pathsep
		}

		// Add the filename at the end
		fullFilename += filename

		// Remove the root directory from the link path
		URLpath = url.QueryEscape(fullFilename[len(rootdir)+1:])

		// Output different entries for files and directories
		buf.WriteString(themes.HTMLLink(filename, URLpath, ac.fs.IsDir(fullFilename)))
	}

	// Read directory configuration, if present
	fullDirConfFilename := filepath.Join(dirname, dirconfFilename)
	if ac.fs.Exists(fullDirConfFilename) {
		var dirConf DirConfig
		if err := gcfg.ReadFileInto(&dirConf, fullDirConfFilename); err == nil { // if no error
			if dirConf.Main.Title != "" {
				title = dirConf.Main.Title
			}
			if dirConf.Main.Theme != "" {
				theme = dirConf.Main.Theme
			}
		}
	} else {
		// Strip the leading "./" from the current directory
		title = strings.TrimPrefix(title, dotSlash)

		// Replace "//" with just "/"
		title = strings.Replace(title, doubleP, utils.Pathsep, utils.EveryInstance)
	}

	// Check if the current page contents are empty
	if buf.Len() == 0 {
		buf.WriteString("Empty directory")
	}

	htmldata := themes.MessagePageBytes(title, buf.Bytes(), theme)

	// If the auto-refresh feature has been enabled
	if ac.autoRefresh {
		// Insert JavaScript for refreshing the page into the generated HTML
		htmldata = ac.InsertAutoRefresh(req, htmldata)
	}

	// Serve the page
	w.Header().Add("Content-Type", "text/html;charset=utf-8")
	ac.DataToClient(w, req, dirname, htmldata)
}

// DirPage serves a directory, using index.* files, if present.
// The directory must exist.
// rootdir is the base directory (can be ".")
// dirname is the specific directory that is to be served (should never be ".")
func (ac *Config) DirPage(w http.ResponseWriter, req *http.Request, rootdir, dirname, theme string) {

	// Check if we are instructed to quit after serving the first file
	if ac.quitAfterFirstRequest {
		go ac.quitSoon("Quit after first request", defaultSoonDuration)
	}

	// If the URL does not end with a slash, redirect to an URL that does
	if !strings.HasSuffix(req.URL.Path, "/") {
		if req.Method == "POST" {
			log.Warn("Redirecting a POST request: " + req.URL.Path + " -> " + req.URL.Path + "/.")
			log.Warn("Header data may be lost! Please add the missing slash.")
		}
		http.Redirect(w, req, req.URL.Path+"/", http.StatusMovedPermanently)
		return
	}

	// Handle the serving of index files, if needed
	var filename string
	for _, indexfile := range indexFilenames {
		filename = filepath.Join(dirname, indexfile)
		if ac.fs.Exists(filename) {
			ac.FilePage(w, req, filename, ac.defaultLuaDataFilename)
			return
		}
	}

	// Serve a directory listing if no index file is found
	ac.DirectoryListing(w, req, rootdir, dirname, theme)
}
<|endoftext|>


<commit_before>package etcdng

import (
	"crypto/tls"
	"crypto/x509"
	"io/ioutil"

	log "github.com/sirupsen/logrus"
)

func NewTLSConfig(opt Options) *tls.Config {
	var cfg *tls.Config = nil

	if opt.CertFile != "" && opt.KeyFile != "" {
		var rpool *x509.CertPool = nil
		if opt.CaFile != "" {
			if pemBytes, err := ioutil.ReadFile(opt.CaFile); err == nil {
				rpool = x509.NewCertPool()
				rpool.AppendCertsFromPEM(pemBytes)
			} else {
				log.WithError(err).Errorf("Error reading etcd cert CA File")
			}
		}

		if tlsCert, err := tls.LoadX509KeyPair(opt.CertFile, opt.KeyFile); err == nil {
			cfg = &tls.Config{
				RootCAs:            rpool,
				Certificates:       []tls.Certificate{tlsCert},
				InsecureSkipVerify: opt.InsecureSkipVerify,
			}
		} else {
			log.WithError(err).Errorf("Error loading KeyPair for TLS client")
		}
	}

	// If InsecureSkipVerify is provided, assume TLS
	if (opt.EnableTLS || opt.InsecureSkipVerify) && cfg == nil {
		cfg = &tls.Config{
			InsecureSkipVerify: opt.InsecureSkipVerify,
		}
	}
	return cfg
}
<commit_msg>Respect etcdCaFile in absence of key/cert pair<commit_after>package etcdng

import (
	"crypto/tls"
	"crypto/x509"
	"io/ioutil"

	log "github.com/sirupsen/logrus"
)

func NewTLSConfig(opt Options) *tls.Config {
	if opt.CertFile == "" && opt.KeyFile == "" && opt.CaFile == "" && !opt.EnableTLS && !opt.InsecureSkipVerify {
		return nil
	}

	cfg := tls.Config{
		InsecureSkipVerify: opt.InsecureSkipVerify,
	}

	if opt.CaFile != "" {
		if pemBytes, err := ioutil.ReadFile(opt.CaFile); err == nil {
			cfg.RootCAs = x509.NewCertPool()
			cfg.RootCAs.AppendCertsFromPEM(pemBytes)
		} else {
			log.WithError(err).Errorf("Error reading etcd cert CA File")
		}
	}

	if opt.CertFile != "" && opt.KeyFile != "" {
		if tlsCert, err := tls.LoadX509KeyPair(opt.CertFile, opt.KeyFile); err == nil {
			cfg.Certificates = []tls.Certificate{tlsCert}
		} else {
			log.WithError(err).Errorf("Error loading KeyPair for TLS client")
		}
	}

	return &cfg
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"text/template"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
)

// stack - holds all meaningful information about a particular stack.
type stack struct {
	name         string
	stackname    string
	template     string
	dependsOn    []string
	dependents   []interface{}
	stackoutputs *cloudformation.DescribeStacksOutput
	parameters   []*cloudformation.Parameter
	output       *cloudformation.DescribeStacksOutput
	policy       string
}

// setStackName - sets the stackname with struct
func (s *stack) setStackName() {
	s.stackname = fmt.Sprintf("%s-%s", config.Project, s.name)
}

func (s *stack) deploy(session *session.Session) error {

	err := s.deployTimeParser()
	if err != nil {
		return err
	}

	Log(fmt.Sprintf("Updated Template:\n%s", s.template), level.debug)

	svc := cloudformation.New(session)

	createParams := &cloudformation.CreateStackInput{
		StackName:       aws.String(s.stackname),
		DisableRollback: aws.Bool(!job.rollback),
		TemplateBody:    aws.String(s.template),
	}

	// NOTE: Add parameters flag here if params set
	if len(s.parameters) > 0 {
		createParams.Parameters = s.parameters
	}

	// If IAM is bening touched, add Capabilities
	if strings.Contains(s.template, "AWS::IAM") {
		createParams.Capabilities = []*string{
			aws.String(cloudformation.CapabilityCapabilityIam),
			aws.String(cloudformation.CapabilityCapabilityNamedIam),
		}
	}

	Log(fmt.Sprintln("Calling [CreateStack] with parameters:", createParams), level.debug)
	if _, err := svc.CreateStack(createParams); err != nil {
		return errors.New(fmt.Sprintln("Deploying failed: ", err.Error()))

	}

	go verbose(s.stackname, "CREATE", session)
	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [WaitUntilStackCreateComplete] with parameters:", describeStacksInput), level.debug)
	if err := svc.WaitUntilStackCreateComplete(describeStacksInput); err != nil {
		return err
	}

	Log(fmt.Sprintf("Deployment successful: [%s]", s.stackname), "info")

	return nil
}

func (s *stack) update(session *session.Session) error {
	svc := cloudformation.New(session)
	capability := "CAPABILITY_IAM"
	updateParams := &cloudformation.UpdateStackInput{
		StackName:    aws.String(s.stackname),
		TemplateBody: aws.String(s.template),
		Capabilities: []*string{&capability},
	}

	// NOTE: Add parameters flag here if params set
	if len(s.parameters) > 0 {
		updateParams.Parameters = s.parameters
	}

	if s.stackExists(session) {
		Log("Stack exists, updating...", "info")

		Log(fmt.Sprintln("Calling [UpdateStack] with parameters:", updateParams), level.debug)
		_, err := svc.UpdateStack(updateParams)

		if err != nil {
			return errors.New(fmt.Sprintln("Update failed: ", err))
		}

		go verbose(s.stackname, "UPDATE", session)

		describeStacksInput := &cloudformation.DescribeStacksInput{
			StackName: aws.String(s.stackname),
		}
		Log(fmt.Sprintln("Calling [WaitUntilStackUpdateComplete] with parameters:", describeStacksInput), level.debug)
		if err := svc.WaitUntilStackUpdateComplete(describeStacksInput); err != nil {
			return err
		}

		Log(fmt.Sprintf("Stack update successful: [%s]", s.stackname), "info")

	}
	return nil
}

func (s *stack) terminate(session *session.Session) error {
	svc := cloudformation.New(session)

	params := &cloudformation.DeleteStackInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [DeleteStack] with parameters:", params), level.debug)
	_, err := svc.DeleteStack(params)

	go verbose(s.stackname, "DELETE", session)

	if err != nil {
		return errors.New(fmt.Sprintln("Deleting failed: ", err))
	}

	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [WaitUntilStackDeleteComplete] with parameters:", describeStacksInput), level.debug)
	if err := svc.WaitUntilStackDeleteComplete(describeStacksInput); err != nil {
		return err
	}

	Log(fmt.Sprintf("Deletion successful: [%s]", s.stackname), "info")
	return nil
}

func (s *stack) stackExists(session *session.Session) bool {
	svc := cloudformation.New(session)

	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [DescribeStacks] with parameters:", describeStacksInput), level.debug)
	_, err := svc.DescribeStacks(describeStacksInput)

	if err == nil {
		return true
	}

	return false
}

func (s *stack) status(session *session.Session) error {
	svc := cloudformation.New(session)

	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [UpdateStack] with parameters:", describeStacksInput), level.debug)
	status, err := svc.DescribeStacks(describeStacksInput)

	if err != nil {
		if strings.Contains(strings.ToLower(err.Error()), "exist") {
			fmt.Printf("create_pending -> %s [%s]"+"\n", s.name, s.stackname)
			return nil
		}
		return err
	}

	// Define time flag
	stat := *status.Stacks[0].StackStatus
	var timeflag time.Time
	switch strings.Split(stat, "_")[0] {
	case "UPDATE":
		timeflag = *status.Stacks[0].LastUpdatedTime
	default:
		timeflag = *status.Stacks[0].CreationTime
	}

	// Print Status
	fmt.Printf(
		"%s%s - %s --> %s - [%s]"+"\n",
		colorString(`@`, "magenta"),
		timeflag.Format(time.RFC850),
		strings.ToLower(colorMap(*status.Stacks[0].StackStatus)),
		s.name,
		s.stackname,
	)

	return nil
}

func (s *stack) state(session *session.Session) (string, error) {
	svc := cloudformation.New(session)

	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [DescribeStacks] with parameters: ", describeStacksInput), level.debug)
	status, err := svc.DescribeStacks(describeStacksInput)
	if err != nil {
		if strings.Contains(err.Error(), "not exist") {
			return state.pending, nil
		}
		return "", err
	}

	if strings.Contains(strings.ToLower(status.GoString()), "complete") {
		return state.complete, nil
	} else if strings.Contains(strings.ToLower(status.GoString()), "fail") {
		return state.failed, nil
	}
	return "", nil
}

func (s *stack) change(session *session.Session, req string) error {
	svc := cloudformation.New(session)

	switch req {

	case "create":
		// Resolve Deploy-Time functions
		err := s.deployTimeParser()
		if err != nil {
			return err
		}

		params := &cloudformation.CreateChangeSetInput{
			StackName:     aws.String(s.stackname),
			ChangeSetName: aws.String(job.changeName),
		}

		Log(fmt.Sprintf("Updated Template:\n%s", s.template), level.debug)

		params.TemplateBody = aws.String(s.template)

		// If IAM is bening touched, add Capabilities
		if strings.Contains(s.template, "AWS::IAM") {
			params.Capabilities = []*string{
				aws.String(cloudformation.CapabilityCapabilityIam),
				aws.String(cloudformation.CapabilityCapabilityNamedIam),
			}
		}

		if _, err = svc.CreateChangeSet(params); err != nil {
			return err
		}

		describeParams := &cloudformation.DescribeChangeSetInput{
			StackName:     aws.String(s.stackname),
			ChangeSetName: aws.String(job.changeName),
		}

		for {
			// Waiting for PENDING state to change
			resp, err := svc.DescribeChangeSet(describeParams)
			if err != nil {
				return err
			}

			Log(fmt.Sprintf("Creating Change-Set: [%s] - %s - %s", job.changeName, colorMap(*resp.Status), s.stackname), level.info)

			if *resp.Status == "CREATE_COMPLETE" || *resp.Status == "FAILED" {
				break
			}

			time.Sleep(time.Second * 1)
		}

	case "rm":
		params := &cloudformation.DeleteChangeSetInput{
			ChangeSetName: aws.String(job.changeName),
			StackName:     aws.String(s.stackname),
		}

		if _, err := svc.DeleteChangeSet(params); err != nil {
			return err
		}

		Log(fmt.Sprintf("Change-Set: [%s] deleted", job.changeName), level.info)

	case "list":
		params := &cloudformation.ListChangeSetsInput{
			StackName: aws.String(s.stackname),
		}

		resp, err := svc.ListChangeSets(params)
		if err != nil {
			return err
		}

		// if strings.Contains(resp.GoString(), "Summaries:") {
		for _, i := range resp.Summaries {
			Log(fmt.Sprintf("%s%s - Change-Set: [%s] - Status: [%s]", colorString("@", "magenta"), i.CreationTime.Format(time.RFC850), *i.ChangeSetName, *i.ExecutionStatus), level.info)
		}
		// }

	case "execute":
		params := &cloudformation.ExecuteChangeSetInput{
			StackName:     aws.String(s.stackname),
			ChangeSetName: aws.String(job.changeName),
		}

		if _, err := svc.ExecuteChangeSet(params); err != nil {
			return err
		}

		describeStacksInput := &cloudformation.DescribeStacksInput{
			StackName: aws.String(s.stackname),
		}

		go verbose(s.stackname, "UPDATE", session)

		Log(fmt.Sprintln("Calling [WaitUntilStackUpdateComplete] with parameters:", describeStacksInput), level.debug)
		if err := svc.WaitUntilStackUpdateComplete(describeStacksInput); err != nil {
			return err
		}

	case "desc":
		params := &cloudformation.DescribeChangeSetInput{
			ChangeSetName: aws.String(job.changeName),
			StackName:     aws.String(s.stackname),
		}

		resp, err := svc.DescribeChangeSet(params)
		if err != nil {
			return err
		}

		o, err := json.MarshalIndent(resp, "", "  ")
		if err != nil {
			return err
		}

		fmt.Printf("%s\n", o)
	}

	return nil
}

func (s *stack) check(session *session.Session) error {
	svc := cloudformation.New(session)

	params := &cloudformation.ValidateTemplateInput{
		TemplateBody: aws.String(s.template),
	}

	Log(fmt.Sprintf("Calling [ValidateTemplate] with parameters:\n%s"+"\n--\n", params), level.debug)
	resp, err := svc.ValidateTemplate(params)
	if err != nil {
		return err
	}

	fmt.Printf(
		"%s\n\n%s"+"\n",
		colorString("Valid!", "green"),
		resp.GoString(),
	)

	return nil
}

func (s *stack) outputs(session *session.Session) error {

	svc := cloudformation.New(session)
	outputParams := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [DescribeStacks] with parameters:", outputParams), level.debug)
	outputs, err := svc.DescribeStacks(outputParams)
	if err != nil {
		return errors.New(fmt.Sprintln("Unable to reach stack", err.Error()))
	}

	// set stack outputs property
	s.output = outputs

	return nil
}

// deployTimeParser - Parses templates during deployment to resolve specfic Dependency functions like stackout...
func (s *stack) deployTimeParser() error {

	// Create template
	t, err := template.New("deploy-template").Delims("<<", ">>").Funcs(deployTimeFunctions).Parse(s.template)
	if err != nil {
		return err
	}

	// so that we can write to string
	var doc bytes.Buffer
	t.Execute(&doc, config.vars())
	s.template = doc.String()
	Log(fmt.Sprintf("Deploy Time Template Generate:\n%s", s.template), level.debug)

	return nil
}
<commit_msg>added stackPolicy method and add stack policy param values to deploy method<commit_after>package commands

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"text/template"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
)

// stack - holds all meaningful information about a particular stack.
type stack struct {
	name         string
	stackname    string
	template     string
	dependsOn    []string
	dependents   []interface{}
	stackoutputs *cloudformation.DescribeStacksOutput
	parameters   []*cloudformation.Parameter
	output       *cloudformation.DescribeStacksOutput
	policy       string
}

// setStackName - sets the stackname with struct
func (s *stack) setStackName() {
	s.stackname = fmt.Sprintf("%s-%s", config.Project, s.name)
}

func (s *stack) deploy(session *session.Session) error {

	err := s.deployTimeParser()
	if err != nil {
		return err
	}

	Log(fmt.Sprintf("Updated Template:\n%s", s.template), level.debug)

	svc := cloudformation.New(session)

	createParams := &cloudformation.CreateStackInput{
		StackName:       aws.String(s.stackname),
		DisableRollback: aws.Bool(!job.rollback),
		TemplateBody:    aws.String(s.template),
	}

	if s.policy != "" {
		if strings.HasPrefix(s.policy, "http://") || strings.HasPrefix(s.policy, "https://") {
			createParams.StackPolicyURL = &s.policy
		} else {
			createParams.StackPolicyBody = &s.policy
		}
	}

	// NOTE: Add parameters flag here if params set
	if len(s.parameters) > 0 {
		createParams.Parameters = s.parameters
	}

	// If IAM is bening touched, add Capabilities
	if strings.Contains(s.template, "AWS::IAM") {
		createParams.Capabilities = []*string{
			aws.String(cloudformation.CapabilityCapabilityIam),
			aws.String(cloudformation.CapabilityCapabilityNamedIam),
		}
	}

	Log(fmt.Sprintln("Calling [CreateStack] with parameters:", createParams), level.debug)
	if _, err := svc.CreateStack(createParams); err != nil {
		return errors.New(fmt.Sprintln("Deploying failed: ", err.Error()))

	}

	go verbose(s.stackname, "CREATE", session)
	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [WaitUntilStackCreateComplete] with parameters:", describeStacksInput), level.debug)
	if err := svc.WaitUntilStackCreateComplete(describeStacksInput); err != nil {
		return err
	}

	Log(fmt.Sprintf("Deployment successful: [%s]", s.stackname), "info")

	return nil
}

func (s *stack) update(session *session.Session) error {
	svc := cloudformation.New(session)
	capability := "CAPABILITY_IAM"
	updateParams := &cloudformation.UpdateStackInput{
		StackName:    aws.String(s.stackname),
		TemplateBody: aws.String(s.template),
		Capabilities: []*string{&capability},
	}

	// NOTE: Add parameters flag here if params set
	if len(s.parameters) > 0 {
		updateParams.Parameters = s.parameters
	}

	if s.stackExists(session) {
		Log("Stack exists, updating...", "info")

		Log(fmt.Sprintln("Calling [UpdateStack] with parameters:", updateParams), level.debug)
		_, err := svc.UpdateStack(updateParams)

		if err != nil {
			return errors.New(fmt.Sprintln("Update failed: ", err))
		}

		go verbose(s.stackname, "UPDATE", session)

		describeStacksInput := &cloudformation.DescribeStacksInput{
			StackName: aws.String(s.stackname),
		}
		Log(fmt.Sprintln("Calling [WaitUntilStackUpdateComplete] with parameters:", describeStacksInput), level.debug)
		if err := svc.WaitUntilStackUpdateComplete(describeStacksInput); err != nil {
			return err
		}

		Log(fmt.Sprintf("Stack update successful: [%s]", s.stackname), "info")

	}
	return nil
}

func (s *stack) terminate(session *session.Session) error {
	svc := cloudformation.New(session)

	params := &cloudformation.DeleteStackInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [DeleteStack] with parameters:", params), level.debug)
	_, err := svc.DeleteStack(params)

	go verbose(s.stackname, "DELETE", session)

	if err != nil {
		return errors.New(fmt.Sprintln("Deleting failed: ", err))
	}

	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [WaitUntilStackDeleteComplete] with parameters:", describeStacksInput), level.debug)
	if err := svc.WaitUntilStackDeleteComplete(describeStacksInput); err != nil {
		return err
	}

	Log(fmt.Sprintf("Deletion successful: [%s]", s.stackname), "info")
	return nil
}

func (s *stack) stackExists(session *session.Session) bool {
	svc := cloudformation.New(session)

	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [DescribeStacks] with parameters:", describeStacksInput), level.debug)
	_, err := svc.DescribeStacks(describeStacksInput)

	if err == nil {
		return true
	}

	return false
}

func (s *stack) status(session *session.Session) error {
	svc := cloudformation.New(session)

	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [UpdateStack] with parameters:", describeStacksInput), level.debug)
	status, err := svc.DescribeStacks(describeStacksInput)

	if err != nil {
		if strings.Contains(strings.ToLower(err.Error()), "exist") {
			fmt.Printf("create_pending -> %s [%s]"+"\n", s.name, s.stackname)
			return nil
		}
		return err
	}

	// Define time flag
	stat := *status.Stacks[0].StackStatus
	var timeflag time.Time
	switch strings.Split(stat, "_")[0] {
	case "UPDATE":
		timeflag = *status.Stacks[0].LastUpdatedTime
	default:
		timeflag = *status.Stacks[0].CreationTime
	}

	// Print Status
	fmt.Printf(
		"%s%s - %s --> %s - [%s]"+"\n",
		colorString(`@`, "magenta"),
		timeflag.Format(time.RFC850),
		strings.ToLower(colorMap(*status.Stacks[0].StackStatus)),
		s.name,
		s.stackname,
	)

	return nil
}

func (s *stack) state(session *session.Session) (string, error) {
	svc := cloudformation.New(session)

	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [DescribeStacks] with parameters: ", describeStacksInput), level.debug)
	status, err := svc.DescribeStacks(describeStacksInput)
	if err != nil {
		if strings.Contains(err.Error(), "not exist") {
			return state.pending, nil
		}
		return "", err
	}

	if strings.Contains(strings.ToLower(status.GoString()), "complete") {
		return state.complete, nil
	} else if strings.Contains(strings.ToLower(status.GoString()), "fail") {
		return state.failed, nil
	}
	return "", nil
}

func (s *stack) change(session *session.Session, req string) error {
	svc := cloudformation.New(session)

	switch req {

	case "create":
		// Resolve Deploy-Time functions
		err := s.deployTimeParser()
		if err != nil {
			return err
		}

		params := &cloudformation.CreateChangeSetInput{
			StackName:     aws.String(s.stackname),
			ChangeSetName: aws.String(job.changeName),
		}

		Log(fmt.Sprintf("Updated Template:\n%s", s.template), level.debug)

		params.TemplateBody = aws.String(s.template)

		// If IAM is bening touched, add Capabilities
		if strings.Contains(s.template, "AWS::IAM") {
			params.Capabilities = []*string{
				aws.String(cloudformation.CapabilityCapabilityIam),
				aws.String(cloudformation.CapabilityCapabilityNamedIam),
			}
		}

		if _, err = svc.CreateChangeSet(params); err != nil {
			return err
		}

		describeParams := &cloudformation.DescribeChangeSetInput{
			StackName:     aws.String(s.stackname),
			ChangeSetName: aws.String(job.changeName),
		}

		for {
			// Waiting for PENDING state to change
			resp, err := svc.DescribeChangeSet(describeParams)
			if err != nil {
				return err
			}

			Log(fmt.Sprintf("Creating Change-Set: [%s] - %s - %s", job.changeName, colorMap(*resp.Status), s.stackname), level.info)

			if *resp.Status == "CREATE_COMPLETE" || *resp.Status == "FAILED" {
				break
			}

			time.Sleep(time.Second * 1)
		}

	case "rm":
		params := &cloudformation.DeleteChangeSetInput{
			ChangeSetName: aws.String(job.changeName),
			StackName:     aws.String(s.stackname),
		}

		if _, err := svc.DeleteChangeSet(params); err != nil {
			return err
		}

		Log(fmt.Sprintf("Change-Set: [%s] deleted", job.changeName), level.info)

	case "list":
		params := &cloudformation.ListChangeSetsInput{
			StackName: aws.String(s.stackname),
		}

		resp, err := svc.ListChangeSets(params)
		if err != nil {
			return err
		}

		// if strings.Contains(resp.GoString(), "Summaries:") {
		for _, i := range resp.Summaries {
			Log(fmt.Sprintf("%s%s - Change-Set: [%s] - Status: [%s]", colorString("@", "magenta"), i.CreationTime.Format(time.RFC850), *i.ChangeSetName, *i.ExecutionStatus), level.info)
		}
		// }

	case "execute":
		params := &cloudformation.ExecuteChangeSetInput{
			StackName:     aws.String(s.stackname),
			ChangeSetName: aws.String(job.changeName),
		}

		if _, err := svc.ExecuteChangeSet(params); err != nil {
			return err
		}

		describeStacksInput := &cloudformation.DescribeStacksInput{
			StackName: aws.String(s.stackname),
		}

		go verbose(s.stackname, "UPDATE", session)

		Log(fmt.Sprintln("Calling [WaitUntilStackUpdateComplete] with parameters:", describeStacksInput), level.debug)
		if err := svc.WaitUntilStackUpdateComplete(describeStacksInput); err != nil {
			return err
		}

	case "desc":
		params := &cloudformation.DescribeChangeSetInput{
			ChangeSetName: aws.String(job.changeName),
			StackName:     aws.String(s.stackname),
		}

		resp, err := svc.DescribeChangeSet(params)
		if err != nil {
			return err
		}

		o, err := json.MarshalIndent(resp, "", "  ")
		if err != nil {
			return err
		}

		fmt.Printf("%s\n", o)
	}

	return nil
}

func (s *stack) check(session *session.Session) error {
	svc := cloudformation.New(session)

	params := &cloudformation.ValidateTemplateInput{
		TemplateBody: aws.String(s.template),
	}

	Log(fmt.Sprintf("Calling [ValidateTemplate] with parameters:\n%s"+"\n--\n", params), level.debug)
	resp, err := svc.ValidateTemplate(params)
	if err != nil {
		return err
	}

	fmt.Printf(
		"%s\n\n%s"+"\n",
		colorString("Valid!", "green"),
		resp.GoString(),
	)

	return nil
}

func (s *stack) outputs(session *session.Session) error {

	svc := cloudformation.New(session)
	outputParams := &cloudformation.DescribeStacksInput{
		StackName: aws.String(s.stackname),
	}

	Log(fmt.Sprintln("Calling [DescribeStacks] with parameters:", outputParams), level.debug)
	outputs, err := svc.DescribeStacks(outputParams)
	if err != nil {
		return errors.New(fmt.Sprintln("Unable to reach stack", err.Error()))
	}

	// set stack outputs property
	s.output = outputs

	return nil
}

func (s *stack) stackPolicy(session *session.Session) error {

	if s.policy == "" {
		return fmt.Errorf("Empty Stack Policy value detected...")
	}

	svc := cloudformation.New(session)

	params := &cloudformation.SetStackPolicyInput{
		StackName: &s.stackname,
	}

	// Check if source is a URL
	if strings.HasPrefix(s.policy, `http://`) || strings.HasPrefix(s.policy, `https://`) {
		params.StackPolicyURL = &s.policy
	} else {
		params.StackPolicyBody = &s.policy
	}

	Log(fmt.Sprintln("Calling SetStackPolicy with params: ", params), level.debug)
	resp, err := svc.SetStackPolicy(params)
	if err != nil {
		return err
	}

	Log(fmt.Sprintf("Stack Policy applied: [%s] - %s", s.stackname, resp.GoString()), level.info)

	return nil
}

// deployTimeParser - Parses templates during deployment to resolve specfic Dependency functions like stackout...
func (s *stack) deployTimeParser() error {

	// Create template
	t, err := template.New("deploy-template").Delims("<<", ">>").Funcs(deployTimeFunctions).Parse(s.template)
	if err != nil {
		return err
	}

	// so that we can write to string
	var doc bytes.Buffer
	t.Execute(&doc, config.vars())
	s.template = doc.String()
	Log(fmt.Sprintf("Deploy Time Template Generate:\n%s", s.template), level.debug)

	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package commands

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"text/tabwriter"

	"github.com/spf13/cobra"
)

var storeAddress string

const defaultStore = "https://cdn.rawgit.com/openfaas/store/master/store.json"

type storeItem struct {
	Title       string `json:"title"`
	Description string `json:"description"`
	Image       string `json:"image"`
	Name        string `json:"name"`
	Fprocess    string `json:"fprocess"`
	Network     string `json:"network"`
	RepoURL     string `json:"repo_url"`
}

func init() {
	// Setup flags that are used by multiple commands (variables defined in faas.go)
	storeListCmd.Flags().StringVarP(&storeAddress, "store", "g", defaultStore, "Store URL starting with http(s)://")
	storeInspectCmd.Flags().StringVarP(&storeAddress, "store", "g", defaultStore, "Store URL starting with http(s)://")

	storeCmd.AddCommand(storeListCmd)
	storeCmd.AddCommand(storeInspectCmd)
	faasCmd.AddCommand(storeCmd)
}

var storeCmd = &cobra.Command{
	Use:   `store`,
	Short: "OpenFaaS store commands",
	Long:  "Allows browsing and deploying OpenFaaS store functions"
}

var storeListCmd = &cobra.Command{
	Use:     `list [--store STORE_URL]`,
	Short:   "List OpenFaaS store items",
	Long:    "Lists the available items in OpenFaas store",
	Example: `  faas-cli store list --store https://domain:port`,
	RunE:    runStoreList,
}

var storeInspectCmd = &cobra.Command{
	Use:     `inspect FUNCTION_NAME [--store STORE_URL]`,
	Short:   "Show OpenFaaS store function details",
	Long:    "Prints the detailed informations of the specified OpenFaaS function",
	Example: `  faas-cli store inspect NodeInfo --store https://domain:port`,
	RunE:    runStoreInspect,
}

func runStoreList(cmd *cobra.Command, args []string) error {
	items, err := storeList(storeAddress)
	if err != nil {
		return err
	}

	if len(items) == 0 {
		fmt.Printf("The store is empty.")
		return nil
	}

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
	fmt.Fprintln(w)
	fmt.Fprintln(w, "FUNCTION\tDESCRIPTION")
	fmt.Fprintln(w, "--------\t-----------")

	for _, item := range items {
		fmt.Fprintf(w, "%s\t%s\n", item.Title, item.Description)
	}

	fmt.Fprintln(w)
	w.Flush()

	return nil
}

func runStoreInspect(cmd *cobra.Command, args []string) error {
	if len(args) < 1 {
		return fmt.Errorf("please provide the function name")
	}

	functionName := args[0]

	items, err := storeList(storeAddress)
	if err != nil {
		return err
	}

	for _, item := range items {
		if item.Title == functionName {
			w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
			fmt.Fprintln(w)
			fmt.Fprintln(w, "FUNCTION\tDESCRIPTION\tIMAGE\tFUNCTION PROCESS\tREPO")
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
				item.Title,
				item.Description,
				item.Image,
				item.Fprocess,
				item.RepoURL,
			)

			fmt.Fprintln(w)
			w.Flush()
		}
	}

	return fmt.Errorf("function not found")
}

func storeList(store string) ([]storeItem, error) {
	var results []storeItem

	store = strings.TrimRight(store, "/")

	timeout := 60 * time.Second
	client := makeHTTPClient(&timeout)

	getRequest, err := http.NewRequest(http.MethodGet, store, nil)
	if err != nil {
		return nil, fmt.Errorf("cannot connect to OpenFaaS store on URL: %s", store)
	}

	res, err := client.Do(getRequest)
	if err != nil {
		return nil, fmt.Errorf("cannot connect to OpenFaaS store on URL: %s", store)
	}

	if res.Body != nil {
		defer res.Body.Close()
	}

	switch res.StatusCode {
	case http.StatusOK:

		bytesOut, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return nil, fmt.Errorf("cannot read result from OpenFaaS store on URL: %s", store)
		}
		jsonErr := json.Unmarshal(bytesOut, &results)
		if jsonErr != nil {
			return nil, fmt.Errorf("cannot parse result from OpenFaaS store on URL: %s\n%s", store, jsonErr.Error())
		}
	default:
		bytesOut, err := ioutil.ReadAll(res.Body)
		if err == nil {
			return nil, fmt.Errorf("server returned unexpected status code: %d - %s", res.StatusCode, string(bytesOut))
		}
	}
	return results, nil
}

func makeHTTPClient(timeout *time.Duration) http.Client {
	if timeout != nil {
		return http.Client{
			Timeout: *timeout,
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
				DialContext: (&net.Dialer{
					Timeout: *timeout,
					// KeepAlive: 0,
				}).DialContext,
				// MaxIdleConns:          1,
				// DisableKeepAlives:     true,
				IdleConnTimeout:       120 * time.Millisecond,
				ExpectContinueTimeout: 1500 * time.Millisecond,
			},
		}
	}

	// This should be used for faas-cli invoke etc.
	return http.Client{}
}
<commit_msg>Code cleanup<commit_after>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package commands

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"text/tabwriter"

	"github.com/spf13/cobra"
)

var storeAddress string

const defaultStore = "https://cdn.rawgit.com/openfaas/store/master/store.json"

type storeItem struct {
	Title       string `json:"title"`
	Description string `json:"description"`
	Image       string `json:"image"`
	Name        string `json:"name"`
	Fprocess    string `json:"fprocess"`
	Network     string `json:"network"`
	RepoURL     string `json:"repo_url"`
}

func init() {
	// Setup flags that are used by multiple commands (variables defined in faas.go)
	storeListCmd.Flags().StringVarP(&storeAddress, "store", "g", defaultStore, "Store URL starting with http(s)://")
	storeInspectCmd.Flags().StringVarP(&storeAddress, "store", "g", defaultStore, "Store URL starting with http(s)://")

	storeCmd.AddCommand(storeListCmd)
	storeCmd.AddCommand(storeInspectCmd)
	faasCmd.AddCommand(storeCmd)
}

var storeCmd = &cobra.Command{
	Use:   `store`,
	Short: "OpenFaaS store commands",
	Long:  "Allows browsing and deploying OpenFaaS store functions"
}

var storeListCmd = &cobra.Command{
	Use:     `list [--store STORE_URL]`,
	Short:   "List OpenFaaS store items",
	Long:    "Lists the available items in OpenFaas store",
	Example: `  faas-cli store list --store https://domain:port`,
	RunE:    runStoreList,
}

var storeInspectCmd = &cobra.Command{
	Use:     `inspect FUNCTION_NAME [--store STORE_URL]`,
	Short:   "Show OpenFaaS store function details",
	Long:    "Prints the detailed informations of the specified OpenFaaS function",
	Example: `  faas-cli store inspect NodeInfo --store https://domain:port`,
	RunE:    runStoreInspect,
}

func runStoreList(cmd *cobra.Command, args []string) error {
	items, err := storeList(storeAddress)
	if err != nil {
		return err
	}

	if len(items) == 0 {
		fmt.Printf("The store is empty.")
		return nil
	}

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
	fmt.Fprintln(w)
	fmt.Fprintln(w, "FUNCTION\tDESCRIPTION")

	for _, item := range items {
		fmt.Fprintf(w, "%s\t%s\n", item.Title, item.Description)
	}

	fmt.Fprintln(w)
	w.Flush()

	return nil
}

func runStoreInspect(cmd *cobra.Command, args []string) error {
	if len(args) < 1 {
		return fmt.Errorf("please provide the function name")
	}

	functionName := args[0]

	items, err := storeList(storeAddress)
	if err != nil {
		return err
	}

	for _, item := range items {
		if item.Title == functionName {
			w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
			fmt.Fprintln(w)
			fmt.Fprintln(w, "FUNCTION\tDESCRIPTION\tIMAGE\tFUNCTION PROCESS\tREPO")
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
				item.Title,
				item.Description,
				item.Image,
				item.Fprocess,
				item.RepoURL,
			)

			fmt.Fprintln(w)
			w.Flush()
		}
	}

	return fmt.Errorf("function not found")
}

func storeList(store string) ([]storeItem, error) {
	var results []storeItem

	store = strings.TrimRight(store, "/")

	timeout := 60 * time.Second
	client := makeHTTPClient(&timeout)

	getRequest, err := http.NewRequest(http.MethodGet, store, nil)
	if err != nil {
		return nil, fmt.Errorf("cannot connect to OpenFaaS store on URL: %s", store)
	}

	res, err := client.Do(getRequest)
	if err != nil {
		return nil, fmt.Errorf("cannot connect to OpenFaaS store on URL: %s", store)
	}

	if res.Body != nil {
		defer res.Body.Close()
	}

	switch res.StatusCode {
	case http.StatusOK:

		bytesOut, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return nil, fmt.Errorf("cannot read result from OpenFaaS store on URL: %s", store)
		}
		jsonErr := json.Unmarshal(bytesOut, &results)
		if jsonErr != nil {
			return nil, fmt.Errorf("cannot parse result from OpenFaaS store on URL: %s\n%s", store, jsonErr.Error())
		}
	default:
		bytesOut, err := ioutil.ReadAll(res.Body)
		if err == nil {
			return nil, fmt.Errorf("server returned unexpected status code: %d - %s", res.StatusCode, string(bytesOut))
		}
	}
	return results, nil
}

func makeHTTPClient(timeout *time.Duration) http.Client {
	if timeout != nil {
		return http.Client{
			Timeout: *timeout,
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
				DialContext: (&net.Dialer{
					Timeout: *timeout,
					// KeepAlive: 0,
				}).DialContext,
				// MaxIdleConns:          1,
				// DisableKeepAlives:     true,
				IdleConnTimeout:       120 * time.Millisecond,
				ExpectContinueTimeout: 1500 * time.Millisecond,
			},
		}
	}

	// This should be used for faas-cli invoke etc.
	return http.Client{}
}
<|endoftext|>


<commit_before>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package commands

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"text/tabwriter"

	"github.com/openfaas/faas-cli/proxy"
	"github.com/openfaas/faas-cli/schema"
	"github.com/spf13/cobra"
)

var (
	storeAddress       string
	verboseDescription bool
	storeDeployFlags   DeployFlags
)

const (
	defaultStore      = "https://cdn.rawgit.com/openfaas/store/master/store.json"
	maxDescriptionLen = 40
)

func init() {
	// Setup flags that are used by multiple commands (variables defined in faas.go)
	storeCmd.Flags().StringVarP(&gateway, "gateway", "g", defaultGateway, "Gateway URL starting with http(s)://")

	// Setup flags used by store command
	storeListCmd.Flags().StringVarP(&storeAddress, "url", "u", defaultStore, "Alternative URL starting with http(s)://")
	storeListCmd.Flags().BoolVarP(&verboseDescription, "verbose", "v", false, "Verbose output for the field values")

	storeInspectCmd.Flags().StringVarP(&storeAddress, "url", "u", defaultStore, "Alternative Store URL starting with http(s)://")
	storeInspectCmd.Flags().BoolVarP(&verboseDescription, "verbose", "v", false, "Verbose output for the field values")

	// Setup flags that are used only by deploy command (variables defined above)
	storeDeployCmd.Flags().StringArrayVarP(&storeDeployFlags.envvarOpts, "env", "e", []string{}, "Adds one or more environment variables to the defined ones by store (ENVVAR=VALUE)")
	storeDeployCmd.Flags().StringArrayVarP(&storeDeployFlags.labelOpts, "label", "l", []string{}, "Set one or more label (LABEL=VALUE)")
	storeDeployCmd.Flags().BoolVar(&storeDeployFlags.replace, "replace", false, "Replace any existing function")
	storeDeployCmd.Flags().BoolVar(&storeDeployFlags.update, "update", true, "Update existing functions")
	storeDeployCmd.Flags().StringArrayVar(&storeDeployFlags.constraints, "constraint", []string{}, "Apply a constraint to the function")
	storeDeployCmd.Flags().StringArrayVar(&storeDeployFlags.secrets, "secret", []string{}, "Give the function access to a secure secret")

	storeCmd.AddCommand(storeListCmd)
	storeCmd.AddCommand(storeInspectCmd)
	storeCmd.AddCommand(storeDeployCmd)

	faasCmd.AddCommand(storeCmd)
}

var storeCmd = &cobra.Command{
	Use:   `store`,
	Short: "OpenFaaS store commands",
	Long:  "Allows browsing and deploying OpenFaaS store functions",
}

var storeListCmd = &cobra.Command{
	Use:     `list [--store STORE_URL]`,
	Short:   "List OpenFaaS store items",
	Long:    "Lists the available items in OpenFaas store",
	Example: `  faas-cli store list --url https://domain:port/store.json`,
	RunE:    runStoreList,
}

var storeInspectCmd = &cobra.Command{
	Use:   `inspect (FUNCTION_NAME|FUNCTION_TITLE) [--url STORE_URL]`,
	Short: "Show OpenFaaS store function details",
	Long:  "Prints the detailed informations of the specified OpenFaaS function",
	Example: `  faas-cli store inspect NodeInfo
  faas-cli store inspect NodeInfo --url https://domain:port/store.json`,
	RunE: runStoreInspect,
}

var storeDeployCmd = &cobra.Command{
	Use: `deploy (FUNCTION_NAME|FUNCTION_TITLE)
							[--gateway GATEWAY_URL]
							[--env ENVVAR=VALUE ...]
							[--label LABEL=VALUE ...]
							[--replace=false]
							[--update=true]
							[--constraint PLACEMENT_CONSTRAINT ...]
							[--secret "SECRET_NAME"]
							[--url STORE_URL]`,

	Short: "Deploy OpenFaaS functions from the store",
	Long:  `Same as faas-cli deploy except pre-loaded with arguments from the store`,
	Example: `  faas-cli store deploy figlet
  faas-cli store deploy figlet \
    --gateway=http://localhost:8080 --lang=python \
    --env=MYVAR=myval`,
	RunE: runStoreDeploy,
}

func runStoreList(cmd *cobra.Command, args []string) error {
	items, err := storeList(storeAddress)
	if err != nil {
		return err
	}

	if len(items) == 0 {
		fmt.Printf("The store is empty.")
		return nil
	}

	content := renderStoreItems(items)
	fmt.Print(content)

	return nil
}

func renderStoreItems(items []schema.StoreItem) string {
	var b bytes.Buffer
	w := tabwriter.NewWriter(&b, 0, 0, 1, ' ', 0)
	fmt.Fprintln(w)
	fmt.Fprintln(w, "FUNCTION\tDESCRIPTION")

	for _, item := range items {
		fmt.Fprintf(w, "%s\t%s\n", item.Title, renderDescription(item.Description))
	}

	fmt.Fprintln(w)
	w.Flush()
	return b.String()
}

func renderDescription(descr string) string {
	if !verboseDescription && len(descr) > maxDescriptionLen {
		return descr[0:maxDescriptionLen-3] + "..."
	}

	return descr
}

func runStoreInspect(cmd *cobra.Command, args []string) error {
	if len(args) < 1 {
		return fmt.Errorf("please provide the function name")
	}

	storeItems, err := storeList(storeAddress)
	if err != nil {
		return err
	}

	item := findFunction(args[0], storeItems)
	if item == nil {
		return fmt.Errorf("function '%s' not found", functionName)
	}

	content := renderStoreItem(item)
	fmt.Print(content)

	return nil
}

func renderStoreItem(item *schema.StoreItem) string {
	var b bytes.Buffer
	w := tabwriter.NewWriter(&b, 0, 0, 1, ' ', 0)
	fmt.Fprintln(w)
	fmt.Fprintln(w, "FUNCTION\tDESCRIPTION\tIMAGE\tPROCESS\tREPO")
	fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
		item.Title,
		renderDescription(item.Description),
		item.Image,
		item.Fprocess,
		item.RepoURL,
	)

	fmt.Fprintln(w)
	w.Flush()
	return b.String()
}

func runStoreDeploy(cmd *cobra.Command, args []string) error {
	if len(args) < 1 {
		return fmt.Errorf("please provide the function name")
	}

	storeItems, err := storeList(storeAddress)
	if err != nil {
		return err
	}

	item := findFunction(args[0], storeItems)
	if item == nil {
		return fmt.Errorf("function '%s' not found", functionName)
	}

	// Add the store environment variables to the provided ones from cmd
	if item.Environment != nil {
		for k, v := range item.Environment {
			env := fmt.Sprintf("%s=%s", k, v)
			storeDeployFlags.envvarOpts = append(storeDeployFlags.envvarOpts, env)
		}
	}

	// Add the store labels to the provided ones from cmd
	if item.Labels != nil {
		for k, v := range item.Labels {
			label := fmt.Sprintf("%s=%s", k, v)
			storeDeployFlags.labelOpts = append(storeDeployFlags.labelOpts, label)
		}
	}

	return RunDeploy(
		args,
		item.Image,
		item.Fprocess,
		item.Name,
		storeDeployFlags,
	)
}

func storeList(store string) ([]schema.StoreItem, error) {
	var results []schema.StoreItem

	store = strings.TrimRight(store, "/")

	timeout := 60 * time.Second
	client := proxy.MakeHTTPClient(&timeout)

	getRequest, err := http.NewRequest(http.MethodGet, store, nil)
	if err != nil {
		return nil, fmt.Errorf("cannot connect to OpenFaaS store on URL: %s", store)
	}

	res, err := client.Do(getRequest)
	if err != nil {
		return nil, fmt.Errorf("cannot connect to OpenFaaS store on URL: %s", store)
	}

	if res.Body != nil {
		defer res.Body.Close()
	}

	switch res.StatusCode {
	case http.StatusOK:

		bytesOut, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return nil, fmt.Errorf("cannot read result from OpenFaaS store on URL: %s", store)
		}
		jsonErr := json.Unmarshal(bytesOut, &results)
		if jsonErr != nil {
			return nil, fmt.Errorf("cannot parse result from OpenFaaS store on URL: %s\n%s", store, jsonErr.Error())
		}
	default:
		bytesOut, err := ioutil.ReadAll(res.Body)
		if err == nil {
			return nil, fmt.Errorf("server returned unexpected status code: %d - %s", res.StatusCode, string(bytesOut))
		}
	}
	return results, nil
}

func findFunction(functionName string, storeItems []schema.StoreItem) *schema.StoreItem {
	var item schema.StoreItem

	for _, item = range storeItems {
		if item.Name == functionName || item.Title == functionName {
			return &item
		}
	}

	return &item
}
<commit_msg>add --gateway to store deploy command & rename -g to -u & remove unused flags in usage add --network to store deploy make --url flag persistent to `store` command fix usage & remove unnecessary var assign<commit_after>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package commands

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"text/tabwriter"

	"github.com/openfaas/faas-cli/proxy"
	"github.com/openfaas/faas-cli/schema"
	"github.com/spf13/cobra"
)

var (
	storeAddress       string
	verboseDescription bool
	storeDeployFlags   DeployFlags
)

const (
	defaultStore      = "https://cdn.rawgit.com/openfaas/store/master/store.json"
	maxDescriptionLen = 40
)

func init() {
	storeCmd.PersistentFlags().StringVarP(&storeAddress, "url", "u", defaultStore, "Alternative URL starting with http(s)://")

	// Setup flags used by store command
	storeListCmd.Flags().BoolVarP(&verboseDescription, "verbose", "v", false, "Verbose output for the field values")

	storeInspectCmd.Flags().BoolVarP(&verboseDescription, "verbose", "v", false, "Verbose output for the field values")

	// Setup flags that are used by multiple commands (variables defined in faas.go)
	storeDeployCmd.Flags().StringVarP(&gateway, "gateway", "g", defaultGateway, "Gateway URL starting with http(s)://")
	storeDeployCmd.Flags().StringVar(&network, "network", "", "Name of the network")
	// Setup flags that are used only by deploy command (variables defined above)
	storeDeployCmd.Flags().StringArrayVarP(&storeDeployFlags.envvarOpts, "env", "e", []string{}, "Adds one or more environment variables to the defined ones by store (ENVVAR=VALUE)")
	storeDeployCmd.Flags().StringArrayVarP(&storeDeployFlags.labelOpts, "label", "l", []string{}, "Set one or more label (LABEL=VALUE)")
	storeDeployCmd.Flags().BoolVar(&storeDeployFlags.replace, "replace", false, "Replace any existing function")
	storeDeployCmd.Flags().BoolVar(&storeDeployFlags.update, "update", true, "Update existing functions")
	storeDeployCmd.Flags().StringArrayVar(&storeDeployFlags.constraints, "constraint", []string{}, "Apply a constraint to the function")
	storeDeployCmd.Flags().StringArrayVar(&storeDeployFlags.secrets, "secret", []string{}, "Give the function access to a secure secret")

	storeCmd.AddCommand(storeListCmd)
	storeCmd.AddCommand(storeInspectCmd)
	storeCmd.AddCommand(storeDeployCmd)

	faasCmd.AddCommand(storeCmd)
}

var storeCmd = &cobra.Command{
	Use:   `store`,
	Short: "OpenFaaS store commands",
	Long:  "Allows browsing and deploying OpenFaaS store functions",
}

var storeListCmd = &cobra.Command{
	Use:     `list [--url STORE_URL]`,
	Short:   "List OpenFaaS store items",
	Long:    "Lists the available items in OpenFaas store",
	Example: `  faas-cli store list --url https://domain:port/store.json`,
	RunE:    runStoreList,
}

var storeInspectCmd = &cobra.Command{
	Use:   `inspect (FUNCTION_NAME|FUNCTION_TITLE) [--url STORE_URL]`,
	Short: "Show OpenFaaS store function details",
	Long:  "Prints the detailed informations of the specified OpenFaaS function",
	Example: `  faas-cli store inspect NodeInfo
  faas-cli store inspect NodeInfo --url https://domain:port/store.json`,
	RunE: runStoreInspect,
}

var storeDeployCmd = &cobra.Command{
	Use: `deploy (FUNCTION_NAME|FUNCTION_TITLE)
                        [--gateway GATEWAY_URL]
                        [--network NETWORK_NAME]
                        [--env ENVVAR=VALUE ...]
                        [--label LABEL=VALUE ...]
                        [--replace=false]
                        [--update=true]
                        [--constraint PLACEMENT_CONSTRAINT ...]
                        [--secret "SECRET_NAME"]
                        [--url STORE_URL]`,

	Short: "Deploy OpenFaaS functions from the store",
	Long:  `Same as faas-cli deploy except pre-loaded with arguments from the store`,
	Example: `  faas-cli store deploy figlet
  faas-cli store deploy figlet \
    --gateway=http://localhost:8080 \
    --env=MYVAR=myval`,
	RunE: runStoreDeploy,
}

func runStoreList(cmd *cobra.Command, args []string) error {
	items, err := storeList(storeAddress)
	if err != nil {
		return err
	}

	if len(items) == 0 {
		fmt.Printf("The store is empty.")
		return nil
	}

	fmt.Print(renderStoreItems(items))

	return nil
}

func renderStoreItems(items []schema.StoreItem) string {
	var b bytes.Buffer
	w := tabwriter.NewWriter(&b, 0, 0, 1, ' ', 0)
	fmt.Fprintln(w)
	fmt.Fprintln(w, "FUNCTION\tDESCRIPTION")

	for _, item := range items {
		fmt.Fprintf(w, "%s\t%s\n", item.Title, renderDescription(item.Description))
	}

	fmt.Fprintln(w)
	w.Flush()
	return b.String()
}

func renderDescription(descr string) string {
	if !verboseDescription && len(descr) > maxDescriptionLen {
		return descr[0:maxDescriptionLen-3] + "..."
	}

	return descr
}

func runStoreInspect(cmd *cobra.Command, args []string) error {
	if len(args) < 1 {
		return fmt.Errorf("please provide the function name")
	}

	storeItems, err := storeList(storeAddress)
	if err != nil {
		return err
	}

	item := findFunction(args[0], storeItems)
	if item == nil {
		return fmt.Errorf("function '%s' not found", functionName)
	}

	content := renderStoreItem(item)
	fmt.Print(content)

	return nil
}

func renderStoreItem(item *schema.StoreItem) string {
	var b bytes.Buffer
	w := tabwriter.NewWriter(&b, 0, 0, 1, ' ', 0)
	fmt.Fprintln(w)
	fmt.Fprintln(w, "FUNCTION\tDESCRIPTION\tIMAGE\tPROCESS\tREPO")
	fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
		item.Title,
		renderDescription(item.Description),
		item.Image,
		item.Fprocess,
		item.RepoURL,
	)

	fmt.Fprintln(w)
	w.Flush()
	return b.String()
}

func runStoreDeploy(cmd *cobra.Command, args []string) error {
	if len(args) < 1 {
		return fmt.Errorf("please provide the function name")
	}

	storeItems, err := storeList(storeAddress)
	if err != nil {
		return err
	}

	item := findFunction(args[0], storeItems)
	if item == nil {
		return fmt.Errorf("function '%s' not found", functionName)
	}

	// Add the store environment variables to the provided ones from cmd
	if item.Environment != nil {
		for k, v := range item.Environment {
			env := fmt.Sprintf("%s=%s", k, v)
			storeDeployFlags.envvarOpts = append(storeDeployFlags.envvarOpts, env)
		}
	}

	// Add the store labels to the provided ones from cmd
	if item.Labels != nil {
		for k, v := range item.Labels {
			label := fmt.Sprintf("%s=%s", k, v)
			storeDeployFlags.labelOpts = append(storeDeployFlags.labelOpts, label)
		}
	}

	// Use the network from manifest if not changed by user
	if !cmd.Flag("network").Changed {
		network = item.Network
	}

	return RunDeploy(
		args,
		item.Image,
		item.Fprocess,
		item.Name,
		storeDeployFlags,
	)
}

func storeList(store string) ([]schema.StoreItem, error) {
	var results []schema.StoreItem

	store = strings.TrimRight(store, "/")

	timeout := 60 * time.Second
	client := proxy.MakeHTTPClient(&timeout)

	getRequest, err := http.NewRequest(http.MethodGet, store, nil)
	if err != nil {
		return nil, fmt.Errorf("cannot connect to OpenFaaS store on URL: %s", store)
	}

	res, err := client.Do(getRequest)
	if err != nil {
		return nil, fmt.Errorf("cannot connect to OpenFaaS store on URL: %s", store)
	}

	if res.Body != nil {
		defer res.Body.Close()
	}

	switch res.StatusCode {
	case http.StatusOK:

		bytesOut, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return nil, fmt.Errorf("cannot read result from OpenFaaS store on URL: %s", store)
		}
		jsonErr := json.Unmarshal(bytesOut, &results)
		if jsonErr != nil {
			return nil, fmt.Errorf("cannot parse result from OpenFaaS store on URL: %s\n%s", store, jsonErr.Error())
		}
	default:
		bytesOut, err := ioutil.ReadAll(res.Body)
		if err == nil {
			return nil, fmt.Errorf("server returned unexpected status code: %d - %s", res.StatusCode, string(bytesOut))
		}
	}
	return results, nil
}

func findFunction(functionName string, storeItems []schema.StoreItem) *schema.StoreItem {
	var item schema.StoreItem

	for _, item = range storeItems {
		if item.Name == functionName || item.Title == functionName {
			return &item
		}
	}

	return &item
}
<|endoftext|>


<commit_before>package commands

import (
	"github.com/spf13/cobra"
)

// getStringSlice works around viper.GetStringSlice's bad behavior.
// viper.GetStringSlice gets the flag with viper.Get() and casts the
// result use spf13.cast.ToStringSlice(). The issue is that viper.Get()
// returns an interface{} which cast.ToStringSlice() converts to
// []string{value}
//
// so, for example, `--arg=foo --arg=bar` using viper.GetStringSlice()
// will return []string{"foo,bar"} which is clearly incorrect.
//
func getStringSlice(cmd *cobra.Command, flag string) []string {
	ss, err := cmd.Flags().GetStringSlice(flag)
	if err != nil {
		return []string{}
	}

	return ss
}
<commit_msg>Add function to get uint64 from viper<commit_after>package commands

import (
	"fmt"
	"reflect"
	"strconv"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// getStringSlice works around viper.GetStringSlice's bad behavior.
// viper.GetStringSlice gets the flag with viper.Get() and casts the
// result use spf13.cast.ToStringSlice(). The issue is that viper.Get()
// returns an interface{} which cast.ToStringSlice() converts to
// []string{value}
//
// so, for example, `--arg=foo --arg=bar` using viper.GetStringSlice()
// will return []string{"foo,bar"} which is clearly incorrect.
//
func getStringSlice(cmd *cobra.Command, flag string) []string {
	ss, err := cmd.Flags().GetStringSlice(flag)
	if err != nil {
		return []string{}
	}

	return ss
}

// viper does have a GetUint64 function. Create one here.
func getUint64(key string) uint64 {
	return toUint64(viper.Get(key))
}

func toUint64(i interface{}) uint64 {
	v, _ := toUint64E(i)
	return v
}

// Modeled after spf13/cast ToInt64E
func toUint64E(i interface{}) (uint64, error) {
	i = indirect(i)

	switch s := i.(type) {
	case uint64:
		return s, nil
	case uint:
		return uint64(s), nil
	case uint32:
		return uint64(s), nil
	case uint16:
		return uint64(s), nil
	case uint8:
		return uint64(s), nil
	case string:
		v, err := strconv.ParseUint(s, 0, 0)
		if err == nil {
			return v, nil
		}

		return 0, fmt.Errorf("Unable to cast %#v to uint64", i)
	case float64:
		return uint64(s), nil
	case bool:
		if bool(s) {
			return uint64(1), nil
		}
		return uint64(0), nil
	case nil:
		return uint64(0), nil
	default:
		return uint64(0), fmt.Errorf("Unable to cast %#v to uint64", i)
	}
}

// From html/template/content.go
// Copyright 2011 The Go Authors. All rights reserved.
// indirect returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil).
func indirect(a interface{}) interface{} {
	if a == nil {
		return nil
	}
	if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
		// Avoid creating a reflect.Value if it's not a pointer.
		return a
	}
	v := reflect.ValueOf(a)
	for v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	}
	return v.Interface()
}
<|endoftext|>


<commit_before>package main

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import (
	"net/url"
	"testing"

	"github.com/apache/incubator-trafficcontrol/traffic_ops/tostructs"
	"github.com/jmoiron/sqlx"

	sqlmock "gopkg.in/DATA-DOG/go-sqlmock.v1"
)

func getTestCdns() []tostructs.Cdn {
	cdns := []tostructs.Cdn{}
	testCdn := tostructs.Cdn{
		DNSSECEnabled: false,
		DomainName:    "domainName",
		ID:            1,
		Name:          "cdn1",
		LastUpdated:   "lastUpdated",
	}
	cdns = append(cdns, testCdn)

	testCdn2 := testCdn
	testCdn2.Name = "cdn2"
	testCdn2.DomainName = "domain.net"
	cdns = append(cdns, testCdn2)

	return cdns
}

func TestGetCdns(t *testing.T) {
	mockDB, mock, err := sqlmock.New()
	defer mockDB.Close()
	db := sqlx.NewDb(mockDB, "sqlmock")
	if err != nil {
		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
	}
	defer db.Close()

	testCdns := getTestCdns()
	cols := ColsFromStructByTag("db", tostructs.Cdn{})
	rows := sqlmock.NewRows(cols)

	//TODO: drichardson - build helper to add these Rows from the struct values
	//                    or by CSV if types get in the way
	for _, ts := range testCdns {
		rows = rows.AddRow(
			ts.DNSSECEnabled,
			ts.DomainName,
			ts.ID,
			ts.LastUpdated,
			ts.Name,
		)
	}
	mock.ExpectQuery("SELECT").WillReturnRows(rows)
	v := url.Values{}
	v.Set("dsId", "1")

	servers, err := getCdns(v, db, PrivLevelAdmin)
	if err != nil {
		t.Errorf("getCdns expected: nil error, actual: %v", err)
	}

	if len(servers) != 2 {
		t.Errorf("getCdns expected: len(servers) == 1, actual: %v", len(servers))
	}

}

type SortableCdns []tostructs.Cdn

func (s SortableCdns) Len() int {
	return len(s)
}
func (s SortableCdns) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s SortableCdns) Less(i, j int) bool {
	return s[i].Name < s[j].Name
}
<commit_msg>fixed test case package reference<commit_after>package main

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import (
	"net/url"
	"testing"

	"github.com/apache/incubator-trafficcontrol/traffic_ops/tostructs"
	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/test"
	"github.com/jmoiron/sqlx"

	sqlmock "gopkg.in/DATA-DOG/go-sqlmock.v1"
)

func getTestCdns() []tostructs.Cdn {
	cdns := []tostructs.Cdn{}
	testCdn := tostructs.Cdn{
		DNSSECEnabled: false,
		DomainName:    "domainName",
		ID:            1,
		Name:          "cdn1",
		LastUpdated:   "lastUpdated",
	}
	cdns = append(cdns, testCdn)

	testCdn2 := testCdn
	testCdn2.Name = "cdn2"
	testCdn2.DomainName = "domain.net"
	cdns = append(cdns, testCdn2)

	return cdns
}

func TestGetCdns(t *testing.T) {
	mockDB, mock, err := sqlmock.New()
	defer mockDB.Close()
	db := sqlx.NewDb(mockDB, "sqlmock")
	if err != nil {
		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
	}
	defer db.Close()

	testCdns := getTestCdns()
	cols := test.ColsFromStructByTag("db", tostructs.Cdn{})
	rows := sqlmock.NewRows(cols)

	//TODO: drichardson - build helper to add these Rows from the struct values
	//                    or by CSV if types get in the way
	for _, ts := range testCdns {
		rows = rows.AddRow(
			ts.DNSSECEnabled,
			ts.DomainName,
			ts.ID,
			ts.LastUpdated,
			ts.Name,
		)
	}
	mock.ExpectQuery("SELECT").WillReturnRows(rows)
	v := url.Values{}
	v.Set("dsId", "1")

	servers, err := getCdns(v, db, PrivLevelAdmin)
	if err != nil {
		t.Errorf("getCdns expected: nil error, actual: %v", err)
	}

	if len(servers) != 2 {
		t.Errorf("getCdns expected: len(servers) == 1, actual: %v", len(servers))
	}

}

type SortableCdns []tostructs.Cdn

func (s SortableCdns) Len() int {
	return len(s)
}
func (s SortableCdns) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s SortableCdns) Less(i, j int) bool {
	return s[i].Name < s[j].Name
}
<|endoftext|>