<commit_before>// terminal
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/decred/dcrd/dcrjson"
	"golang.org/x/crypto/ssh/terminal"
)

func execute(quit chan bool, protected *bool, cfg *config, line string, clear *bool, state *terminal.State) {
	switch line {
	case "h":
		fmt.Printf("[h]elp          print this message\n")
		fmt.Printf("[l]ist          list all available commands\n")
		fmt.Printf("[p]rotect       toggle protected mode (for passwords)\n")
		fmt.Printf("[c]lear         clear command history\n")
		fmt.Printf("[q]uit/ctrl+d   exit\n")
		fmt.Printf("Enter commands with arguments to execute them.\n")
	case "l":
		fallthrough
	case "list":
		listCommands()
	case "q":
		fallthrough
	case "quit":
		quit <- true
	case "p":
		fallthrough
	case "protect":
		if *protected {
			*protected = false
			return
		}
		*protected = true
		return
	case "c":
		fallthrough
	case "clear":
		*clear = true
	default:
		args := strings.Split(line, " ")

		if len(args) < 1 {
			usage("No command specified")
			return
		}

		// Ensure the specified method identifies a valid registered command and
		// is one of the usable types.
		listCmdMessageLocal := "Enter [l]ist to list commands"
		method := args[0]
		usageFlags, err := dcrjson.MethodUsageFlags(method)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unrecognized command '%s'\n", method)
			fmt.Fprintln(os.Stderr, listCmdMessageLocal)
			return
		}
		if usageFlags&unusableFlags != 0 {
			fmt.Fprintf(os.Stderr, "The '%s' command can only be used via "+
				"websockets\n", method)
			fmt.Fprintln(os.Stderr, listCmdMessageLocal)
			return
		}

		// Convert remaining command line args to a slice of interface values
		// to be passed along as parameters to new command creation function.
		//
		// Since some commands, such as submitblock, can involve data which is
		// too large for the Operating System to allow as a normal command line
		// parameter, support using '-' as an argument to allow the argument
		// to be read from a stdin pipe.
		bio := bufio.NewReader(os.Stdin)
		params := make([]interface{}, 0, len(args[1:]))
		for _, arg := range args[1:] {
			if arg == "-" {
				terminal.Restore(int(os.Stdin.Fd()), state)
				param, err := bio.ReadString('\n')
				state, _ = terminal.MakeRaw(int(os.Stdin.Fd()))
				if err != nil && err != io.EOF {
					fmt.Fprintf(os.Stderr, "Failed to read data "+
						"from stdin: %v\n", err)
					return
				}
				if err == io.EOF && len(param) == 0 {
					fmt.Fprintln(os.Stderr, "Not enough lines "+
						"provided on stdin")
					return
				}
				param = strings.TrimRight(param, "\r\n")
				params = append(params, param)
				continue
			}

			params = append(params, arg)
		}

		// Attempt to create the appropriate command using the arguments
		// provided by the user.
		cmd, err := dcrjson.NewCmd(method, params...)
		if err != nil {
			// Show the error along with its error code when it's a
			// dcrjson.Error as it reallistcally will always be since the
			// NewCmd function is only supposed to return errors of that
			// type.
			if jerr, ok := err.(dcrjson.Error); ok {
				fmt.Fprintf(os.Stderr, "%s command: %v (code: %s)\n",
					method, err, jerr.Code)
				commandUsage(method)
				return
			}

			// The error is not a dcrjson.Error and this really should not
			// happen.  Nevertheless, fallback to just showing the error
			// if it should happen due to a bug in the package.
			fmt.Fprintf(os.Stderr, "%s command: %v\n", method, err)
			commandUsage(method)
			return
		}

		// Marshal the command into a JSON-RPC byte slice in preparation for
		// sending it to the RPC server.
		marshalledJSON, err := dcrjson.MarshalCmd(1, cmd)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}

		// Send the JSON-RPC request to the server using the user-specified
		// connection configuration.
		result, err := sendPostRequest(marshalledJSON, cfg)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}

		// Choose how to display the result based on its type.
		strResult := string(result)
		if strings.HasPrefix(strResult, "{") || strings.HasPrefix(strResult, "[") {
			var dst bytes.Buffer
			if err := json.Indent(&dst, result, "", "  "); err != nil {
				fmt.Fprintf(os.Stderr, "Failed to format result: %v",
					err)
				return
			}
			fmt.Println(dst.String())
			return

		} else if strings.HasPrefix(strResult, `"`) {
			var str string
			if err := json.Unmarshal(result, &str); err != nil {
				fmt.Fprintf(os.Stderr, "Failed to unmarshal result: %v",
					err)
				return
			}
			fmt.Println(str)
			return

		} else if strResult != "null" {
			fmt.Println(strResult)
		}
	}
}

func startTerminal(c *config) {
	fmt.Printf("Starting terminal mode.\n")
	fmt.Printf("Enter h for [h]elp.\n")
	fmt.Printf("Enter q for [q]uit.\n")
	done := make(chan bool)
	initState, err := terminal.GetState(int(os.Stdin.Fd()))
	protected := false
	clear := false
	if err != nil {
		fmt.Printf("error getting terminal state: %v\n", err.Error())
		return
	}

	go func() {
		termState, _ := terminal.MakeRaw(int(os.Stdin.Fd()))
		n := terminal.NewTerminal(os.Stdin, "> ")
		for {
			var ln string
			var err error
			if !protected {
				ln, err = n.ReadLine()
				if err != nil {
					done <- true
				}
			} else {
				ln, err = n.ReadPassword(">*")
				if err != nil {
					done <- true
				}
			}
			execute(done, &protected, c, ln, &clear, termState)

			if clear {
				fmt.Println("Clearing history...")
				termState, _ = terminal.MakeRaw(int(os.Stdin.Fd()))
				n = terminal.NewTerminal(os.Stdin, "> ")
				clear = false
			}
		}
	}()
	select {
	case <-done:
		fmt.Printf("exiting...\n")
		terminal.Restore(int(os.Stdin.Fd()), initState)
		close(done)
	}
}
<commit_msg>dcrctl: fix output in --terminal mode (#416)<commit_after>// terminal
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/decred/dcrd/dcrjson"
	"golang.org/x/crypto/ssh/terminal"
)

func execute(protected *bool, cfg *config, line string, clear *bool) bool {
	switch line {
	case "h":
		fmt.Printf("[h]elp          print this message\n")
		fmt.Printf("[l]ist          list all available commands\n")
		fmt.Printf("[p]rotect       toggle protected mode (for passwords)\n")
		fmt.Printf("[c]lear         clear command history\n")
		fmt.Printf("[q]uit/ctrl+d   exit\n")
		fmt.Printf("Enter commands with arguments to execute them.\n")
	case "l":
		fallthrough
	case "list":
		listCommands()
	case "q":
		fallthrough
	case "quit":
		return true
	case "p":
		fallthrough
	case "protect":
		if *protected {
			*protected = false
			return false
		}
		*protected = true
		return false
	case "c":
		fallthrough
	case "clear":
		*clear = true
	default:
		args := strings.Split(line, " ")

		if len(args) < 1 {
			usage("No command specified")
			return false
		}

		// Ensure the specified method identifies a valid registered command and
		// is one of the usable types.
		listCmdMessageLocal := "Enter [l]ist to list commands"
		method := args[0]
		usageFlags, err := dcrjson.MethodUsageFlags(method)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unrecognized command '%s'\n", method)
			fmt.Fprintln(os.Stderr, listCmdMessageLocal)
			return false
		}
		if usageFlags&unusableFlags != 0 {
			fmt.Fprintf(os.Stderr, "The '%s' command can only be used via "+
				"websockets\n", method)
			fmt.Fprintln(os.Stderr, listCmdMessageLocal)
			return false
		}

		// Convert remaining command line args to a slice of interface values
		// to be passed along as parameters to new command creation function.
		//
		// Since some commands, such as submitblock, can involve data which is
		// too large for the Operating System to allow as a normal command line
		// parameter, support using '-' as an argument to allow the argument
		// to be read from a stdin pipe.
		bio := bufio.NewReader(os.Stdin)
		params := make([]interface{}, 0, len(args[1:]))
		for _, arg := range args[1:] {
			if arg == "-" {
				param, err := bio.ReadString('\n')
				if err != nil && err != io.EOF {
					fmt.Fprintf(os.Stderr, "Failed to read data "+
						"from stdin: %v\n", err)
					return false
				}
				if err == io.EOF && len(param) == 0 {
					fmt.Fprintln(os.Stderr, "Not enough lines "+
						"provided on stdin")
					return false
				}
				param = strings.TrimRight(param, "\r\n")
				params = append(params, param)
				continue
			}

			params = append(params, arg)
		}

		// Attempt to create the appropriate command using the arguments
		// provided by the user.
		cmd, err := dcrjson.NewCmd(method, params...)
		if err != nil {
			// Show the error along with its error code when it's a
			// dcrjson.Error as it reallistcally will always be since the
			// NewCmd function is only supposed to return errors of that
			// type.
			if jerr, ok := err.(dcrjson.Error); ok {
				fmt.Fprintf(os.Stderr, "%s command: %v (code: %s)\n",
					method, err, jerr.Code)
				commandUsage(method)
				return false
			}

			// The error is not a dcrjson.Error and this really should not
			// happen.  Nevertheless, fallback to just showing the error
			// if it should happen due to a bug in the package.
			fmt.Fprintf(os.Stderr, "%s command: %v\n", method, err)
			commandUsage(method)
			return false
		}

		// Marshal the command into a JSON-RPC byte slice in preparation for
		// sending it to the RPC server.
		marshalledJSON, err := dcrjson.MarshalCmd(1, cmd)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return false
		}

		// Send the JSON-RPC request to the server using the user-specified
		// connection configuration.
		result, err := sendPostRequest(marshalledJSON, cfg)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return false
		}

		// Choose how to display the result based on its type.
		strResult := string(result)
		if strings.HasPrefix(strResult, "{") || strings.HasPrefix(strResult, "[") {
			var dst bytes.Buffer
			if err := json.Indent(&dst, result, "", "  "); err != nil {
				fmt.Fprintf(os.Stderr, "Failed to format result: %v\n",
					err)
				return false
			}
			fmt.Println(dst.String())
			return false

		} else if strings.HasPrefix(strResult, `"`) {
			var str string
			if err := json.Unmarshal(result, &str); err != nil {
				fmt.Fprintf(os.Stderr, "Failed to unmarshal result: %v\n",
					err)
				return false
			}
			fmt.Println(str)
			return false

		} else if strResult != "null" {
			fmt.Println(strResult)
		}
	}
	return false
}

func startTerminal(c *config) {
	var clear, protected bool

	fmt.Println("Starting terminal mode.")
	fmt.Println("Enter h for [h]elp.")
	fmt.Println("Enter l for [l]ist of commands.")
	fmt.Println("Enter q for [q]uit.")

	termState, err := terminal.MakeRaw(int(os.Stdin.Fd()))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to set raw mode on STDIN: %v\n",
			err)
		return
	}
	n := terminal.NewTerminal(os.Stdin, "> ")
	for {
		var ln string
		var err error
		if !protected {
			ln, err = n.ReadLine()
		} else {
			ln, err = n.ReadPassword(">*")
		}
		terminal.Restore(int(os.Stdin.Fd()), termState)
		if err != nil {
			break
		}

		quit := execute(&protected, c, ln, &clear)
		if quit {
			break
		}

		if clear {
			fmt.Println("Clearing history...")
			termState, err = terminal.MakeRaw(int(os.Stdin.Fd()))
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to set raw "+
					"mode on STDIN: %v\n", err)
				break
			}
			n = terminal.NewTerminal(os.Stdin, "> ")
			clear = false
		} else {
			termState, err = terminal.MakeRaw(int(os.Stdin.Fd()))
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to set raw "+
					"mode on STDIN: %v\n", err)
				break
			}
		}
	}
	fmt.Println("exiting...")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/digitalocean/go-metadata"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	dto "github.com/prometheus/client_model/go"
	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/digitalocean/do-agent/internal/log"
	"github.com/digitalocean/do-agent/internal/process"
	"github.com/digitalocean/do-agent/pkg/clients/tsclient"
	"github.com/digitalocean/do-agent/pkg/collector"
	"github.com/digitalocean/do-agent/pkg/decorate"
	"github.com/digitalocean/do-agent/pkg/decorate/compat"
	"github.com/digitalocean/do-agent/pkg/writer"
)

var (
	config struct {
		targets       map[string]string
		metadataURL   *url.URL
		authURL       *url.URL
		sonarEndpoint string
		stdoutOnly    bool
		debug         bool
		syslog        bool
		noProcesses   bool
		noNode        bool
		kubernetes    string
		dbaas         string
	}

	// additionalParams is a list of extra command line flags to append
	// this is mostly needed for appending node_exporter flags when necessary.
	additionalParams = []string{}

	// disabledCollectors is a hash used by disableCollectors to prevent
	// duplicate entries
	disabledCollectors = map[string]interface{}{}

	kubernetesClusterUUIDUserDataPrefix = "k8saas_cluster_uuid: "
	kubernetesClusterUUIDLabel = "kubernetes_cluster_uuid"

	errClusterUUIDNotFound = errors.New("kubernetes cluster UUID not found")
)

const (
	defaultMetadataURL = "http://169.254.169.254/metadata"
	defaultAuthURL     = "https://sonar.digitalocean.com"
	defaultSonarURL    = ""
	defaultTimeout     = 2 * time.Second
)

func init() {
	kingpin.Flag("auth-host", "Endpoint to use for obtaining droplet app key").
		Default(defaultAuthURL).
		URLVar(&config.authURL)

	kingpin.Flag("metadata-host", "Endpoint to use for obtaining droplet metadata").
		Default(defaultMetadataURL).
		URLVar(&config.metadataURL)

	kingpin.Flag("sonar-host", "Endpoint to use for delivering metrics").
		Default(defaultSonarURL).
		StringVar(&config.sonarEndpoint)

	kingpin.Flag("stdout-only", "write all metrics to stdout only").
		BoolVar(&config.stdoutOnly)

	kingpin.Flag("debug", "display debug information to stdout").
		BoolVar(&config.debug)

	kingpin.Flag("syslog", "enable logging to syslog").
		BoolVar(&config.syslog)

	kingpin.Flag("k8s-metrics-path", "enable DO Kubernetes metrics collection (this must be a DOK8s metrics endpoint)").
		StringVar(&config.kubernetes)

	kingpin.Flag("no-collector.processes", "disable processes cpu/memory collection").Default("false").
		BoolVar(&config.noProcesses)

	kingpin.Flag("no-collector.node", "disable processes node collection").Default("false").
		BoolVar(&config.noNode)

	kingpin.Flag("dbaas-metrics-path", "enable DO DBAAS metrics collection (this must be a DO DBAAS metrics endpoint)").
		StringVar(&config.dbaas)
}

func checkConfig() error {
	var err error
	for name, uri := range config.targets {
		if _, err = url.Parse(uri); err != nil {
			return errors.Wrapf(err, "url for target %q is not valid", name)
		}
	}
	return nil
}

func initWriter() (metricWriter, throttler) {
	if config.stdoutOnly {
		return writer.NewFile(os.Stdout), &constThrottler{wait: 10 * time.Second}
	}

	tsc, err := newTimeseriesClient()
	if err != nil {
		log.Fatal("failed to connect to sonar: %+v", err)
	}
	return writer.NewSonar(tsc), tsc
}

func initDecorator() decorate.Chain {
	return decorate.Chain{
		compat.Names{},
		compat.Disk{},
		compat.CPU{},
		decorate.LowercaseNames{},
	}
}

// WrappedTSClient wraps the tsClient and adds a Name method to it
type WrappedTSClient struct {
	tsclient.Client
}

// Name returns the name of the client
func (m *WrappedTSClient) Name() string { return "tsclient" }

func newTimeseriesClient() (*WrappedTSClient, error) {
	clientOptions := []tsclient.ClientOptFn{
		tsclient.WithUserAgent(fmt.Sprintf("do-agent-%s", version)),
		tsclient.WithRadarEndpoint(config.authURL.String()),
		tsclient.WithMetadataEndpoint(config.metadataURL.String()),
	}

	if config.sonarEndpoint != "" {
		clientOptions = append(clientOptions, tsclient.WithWharfEndpoint(config.sonarEndpoint))
	}

	tsClient := tsclient.New(clientOptions...)
	wrappedTSClient := &WrappedTSClient{tsClient}

	return wrappedTSClient, nil
}

// getKubernetesClusterUUID retrieves the k8s cluster UUID from the droplet metadata
func getKubernetesClusterUUID() (string, error) {
	client := metadata.NewClient(metadata.WithBaseURL(config.metadataURL))
	userData, err := client.UserData()
	if err != nil {
		return "", fmt.Errorf("failed to get user data: %+v", err)
	}
	return parseKubernetesClusterUUID(userData)
}

// parseKubernetesClusterUUID parses the user data and returns the value of the kubernetes cluster UUID
func parseKubernetesClusterUUID(userData string) (string, error) {
	userDataSplit := strings.Split(userData, "\n")
	for _, line := range userDataSplit {
		if strings.HasPrefix(line, kubernetesClusterUUIDUserDataPrefix) {
			return strings.Trim(strings.TrimPrefix(line, kubernetesClusterUUIDUserDataPrefix), "\""), nil
		}
	}
	return "", errClusterUUIDNotFound
}

// initCollectors initializes the prometheus collectors. By default this
// includes node_exporter and buildInfo for each remote target
func initCollectors() []prometheus.Collector {
	// buildInfo provides build information for tracking metrics internally
	cols := []prometheus.Collector{
		buildInfo,
	}

	if !config.noProcesses {
		cols = append(cols, process.NewProcessCollector())
	}

	if config.kubernetes != "" {
		cols = appendKubernetesCollectors(cols)
	}

	if config.dbaas != "" {
		k, err := collector.NewScraper("dodbaas", config.dbaas, nil, dbaasWhitelist, defaultTimeout)
		if err != nil {
			log.Error("Failed to initialize DO DBaaS metrics collector: %+v", err)
		} else {
			cols = append(cols, k)
		}
	}

	// create the default DO agent to collect metrics about
	// this device
	if !config.noNode {
		node, err := collector.NewNodeCollector()
		if err != nil {
			log.Fatal("failed to create DO agent: %+v", err)
		}
		log.Debug("%d node_exporter collectors were registered", len(node.Collectors()))

		for name := range node.Collectors() {
			log.Debug("node_exporter collector registered %q", name)
		}
		cols = append(cols, node)
	}

	return cols
}

// appendKubernetesCollectors appends a kubernetes metrics collector if it can be initialized successfully
func appendKubernetesCollectors(cols []prometheus.Collector) []prometheus.Collector {
	kubernetesClusterUUID, err := getKubernetesClusterUUID()
	if err != nil {
		log.Error("Failed to get cluster UUID when initializing DO Kubernetes metrics: %+v", err)
		return cols
	}
	var kubernetesLabels []*dto.LabelPair
	kubernetesLabels = append(kubernetesLabels, &dto.LabelPair{Name: &kubernetesClusterUUIDLabel, Value: &kubernetesClusterUUID})
	k, err := collector.NewScraper("dokubernetes", config.kubernetes, kubernetesLabels, k8sWhitelist, defaultTimeout)
	if err != nil {
		log.Error("Failed to initialize DO Kubernetes metrics: %+v", err)
		return cols
	}
	cols = append(cols, k)
	return cols
}

// disableCollectors disables collectors by names by adding a list of
// --no-collector.<name> flags to additionalParams
func disableCollectors(names ...string) {
	f := []string{}
	for _, name := range names {
		if _, ok := disabledCollectors[name]; ok {
			// already disabled
			continue
		}

		disabledCollectors[name] = nil
		f = append(f, disableCollectorFlag(name))
	}

	additionalParams = append(additionalParams, f...)
}

// disableCollectorFlag creates the correct cli flag for the given collector name
func disableCollectorFlag(name string) string {
	return fmt.Sprintf("--no-collector.%s", name)
}
<commit_msg>IN-2311: disable process collection (#115)<commit_after>package main

import (
	"fmt"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/digitalocean/go-metadata"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	dto "github.com/prometheus/client_model/go"
	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/digitalocean/do-agent/internal/log"
	"github.com/digitalocean/do-agent/pkg/clients/tsclient"
	"github.com/digitalocean/do-agent/pkg/collector"
	"github.com/digitalocean/do-agent/pkg/decorate"
	"github.com/digitalocean/do-agent/pkg/decorate/compat"
	"github.com/digitalocean/do-agent/pkg/writer"
)

var (
	config struct {
		targets       map[string]string
		metadataURL   *url.URL
		authURL       *url.URL
		sonarEndpoint string
		stdoutOnly    bool
		debug         bool
		syslog        bool
		noProcesses   bool
		noNode        bool
		kubernetes    string
		dbaas         string
	}

	// additionalParams is a list of extra command line flags to append
	// this is mostly needed for appending node_exporter flags when necessary.
	additionalParams = []string{}

	// disabledCollectors is a hash used by disableCollectors to prevent
	// duplicate entries
	disabledCollectors = map[string]interface{}{}

	kubernetesClusterUUIDUserDataPrefix = "k8saas_cluster_uuid: "
	kubernetesClusterUUIDLabel = "kubernetes_cluster_uuid"

	errClusterUUIDNotFound = errors.New("kubernetes cluster UUID not found")
)

const (
	defaultMetadataURL = "http://169.254.169.254/metadata"
	defaultAuthURL     = "https://sonar.digitalocean.com"
	defaultSonarURL    = ""
	defaultTimeout     = 2 * time.Second
)

func init() {
	kingpin.Flag("auth-host", "Endpoint to use for obtaining droplet app key").
		Default(defaultAuthURL).
		URLVar(&config.authURL)

	kingpin.Flag("metadata-host", "Endpoint to use for obtaining droplet metadata").
		Default(defaultMetadataURL).
		URLVar(&config.metadataURL)

	kingpin.Flag("sonar-host", "Endpoint to use for delivering metrics").
		Default(defaultSonarURL).
		StringVar(&config.sonarEndpoint)

	kingpin.Flag("stdout-only", "write all metrics to stdout only").
		BoolVar(&config.stdoutOnly)

	kingpin.Flag("debug", "display debug information to stdout").
		BoolVar(&config.debug)

	kingpin.Flag("syslog", "enable logging to syslog").
		BoolVar(&config.syslog)

	kingpin.Flag("k8s-metrics-path", "enable DO Kubernetes metrics collection (this must be a DOK8s metrics endpoint)").
		StringVar(&config.kubernetes)

	kingpin.Flag("no-collector.processes", "disable processes cpu/memory collection").Default("false").
		BoolVar(&config.noProcesses)

	kingpin.Flag("no-collector.node", "disable processes node collection").Default("false").
		BoolVar(&config.noNode)

	kingpin.Flag("dbaas-metrics-path", "enable DO DBAAS metrics collection (this must be a DO DBAAS metrics endpoint)").
		StringVar(&config.dbaas)
}

func checkConfig() error {
	var err error
	for name, uri := range config.targets {
		if _, err = url.Parse(uri); err != nil {
			return errors.Wrapf(err, "url for target %q is not valid", name)
		}
	}
	return nil
}

func initWriter() (metricWriter, throttler) {
	if config.stdoutOnly {
		return writer.NewFile(os.Stdout), &constThrottler{wait: 10 * time.Second}
	}

	tsc, err := newTimeseriesClient()
	if err != nil {
		log.Fatal("failed to connect to sonar: %+v", err)
	}
	return writer.NewSonar(tsc), tsc
}

func initDecorator() decorate.Chain {
	return decorate.Chain{
		compat.Names{},
		compat.Disk{},
		compat.CPU{},
		decorate.LowercaseNames{},
	}
}

// WrappedTSClient wraps the tsClient and adds a Name method to it
type WrappedTSClient struct {
	tsclient.Client
}

// Name returns the name of the client
func (m *WrappedTSClient) Name() string { return "tsclient" }

func newTimeseriesClient() (*WrappedTSClient, error) {
	clientOptions := []tsclient.ClientOptFn{
		tsclient.WithUserAgent(fmt.Sprintf("do-agent-%s", version)),
		tsclient.WithRadarEndpoint(config.authURL.String()),
		tsclient.WithMetadataEndpoint(config.metadataURL.String()),
	}

	if config.sonarEndpoint != "" {
		clientOptions = append(clientOptions, tsclient.WithWharfEndpoint(config.sonarEndpoint))
	}

	tsClient := tsclient.New(clientOptions...)
	wrappedTSClient := &WrappedTSClient{tsClient}

	return wrappedTSClient, nil
}

// getKubernetesClusterUUID retrieves the k8s cluster UUID from the droplet metadata
func getKubernetesClusterUUID() (string, error) {
	client := metadata.NewClient(metadata.WithBaseURL(config.metadataURL))
	userData, err := client.UserData()
	if err != nil {
		return "", fmt.Errorf("failed to get user data: %+v", err)
	}
	return parseKubernetesClusterUUID(userData)
}

// parseKubernetesClusterUUID parses the user data and returns the value of the kubernetes cluster UUID
func parseKubernetesClusterUUID(userData string) (string, error) {
	userDataSplit := strings.Split(userData, "\n")
	for _, line := range userDataSplit {
		if strings.HasPrefix(line, kubernetesClusterUUIDUserDataPrefix) {
			return strings.Trim(strings.TrimPrefix(line, kubernetesClusterUUIDUserDataPrefix), "\""), nil
		}
	}
	return "", errClusterUUIDNotFound
}

// initCollectors initializes the prometheus collectors. By default this
// includes node_exporter and buildInfo for each remote target
func initCollectors() []prometheus.Collector {
	// buildInfo provides build information for tracking metrics internally
	cols := []prometheus.Collector{
		buildInfo,
	}

	if config.kubernetes != "" {
		cols = appendKubernetesCollectors(cols)
	}

	if config.dbaas != "" {
		k, err := collector.NewScraper("dodbaas", config.dbaas, nil, dbaasWhitelist, defaultTimeout)
		if err != nil {
			log.Error("Failed to initialize DO DBaaS metrics collector: %+v", err)
		} else {
			cols = append(cols, k)
		}
	}

	// create the default DO agent to collect metrics about
	// this device
	if !config.noNode {
		node, err := collector.NewNodeCollector()
		if err != nil {
			log.Fatal("failed to create DO agent: %+v", err)
		}
		log.Debug("%d node_exporter collectors were registered", len(node.Collectors()))

		for name := range node.Collectors() {
			log.Debug("node_exporter collector registered %q", name)
		}
		cols = append(cols, node)
	}

	return cols
}

// appendKubernetesCollectors appends a kubernetes metrics collector if it can be initialized successfully
func appendKubernetesCollectors(cols []prometheus.Collector) []prometheus.Collector {
	kubernetesClusterUUID, err := getKubernetesClusterUUID()
	if err != nil {
		log.Error("Failed to get cluster UUID when initializing DO Kubernetes metrics: %+v", err)
		return cols
	}
	var kubernetesLabels []*dto.LabelPair
	kubernetesLabels = append(kubernetesLabels, &dto.LabelPair{Name: &kubernetesClusterUUIDLabel, Value: &kubernetesClusterUUID})
	k, err := collector.NewScraper("dokubernetes", config.kubernetes, kubernetesLabels, k8sWhitelist, defaultTimeout)
	if err != nil {
		log.Error("Failed to initialize DO Kubernetes metrics: %+v", err)
		return cols
	}
	cols = append(cols, k)
	return cols
}

// disableCollectors disables collectors by names by adding a list of
// --no-collector.<name> flags to additionalParams
func disableCollectors(names ...string) {
	f := []string{}
	for _, name := range names {
		if _, ok := disabledCollectors[name]; ok {
			// already disabled
			continue
		}

		disabledCollectors[name] = nil
		f = append(f, disableCollectorFlag(name))
	}

	additionalParams = append(additionalParams, f...)
}

// disableCollectorFlag creates the correct cli flag for the given collector name
func disableCollectorFlag(name string) string {
	return fmt.Sprintf("--no-collector.%s", name)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strconv"

	"github.com/codegangsta/cli"
	"github.com/twpayne/go-doarama"
)

func baseDoaramaOptions(c *cli.Context) []doarama.Option {
	return []doarama.Option{
		doarama.APIURL(c.GlobalString("apiurl")),
		doarama.APIName(c.GlobalString("apiname")),
		doarama.APIKey(c.GlobalString("apikey")),
	}
}

func newDoaramaClient(c *cli.Context) *doarama.Client {
	options := baseDoaramaOptions(c)
	return doarama.NewClient(options...)
}

func newAuthenticatedDoaramaOptions(c *cli.Context) ([]doarama.Option, error) {
	options := baseDoaramaOptions(c)
	userID := c.GlobalString("userid")
	userKey := c.GlobalString("userkey")
	switch {
	case userID != "" && userKey == "":
		options = append(options, doarama.Anonymous(userID))
	case userID == "" && userKey != "":
		options = append(options, doarama.Delegate(userKey))
	default:
		return nil, errors.New("exactly one of -userid and -userkey must be specified")
	}
	return options, nil
}

func newAuthenticatedDoaramaClient(c *cli.Context) (*doarama.Client, error) {
	options, err := newAuthenticatedDoaramaOptions(c)
	if err != nil {
		return nil, err
	}
	return doarama.NewClient(options...), nil
}

func newVisualisationURLOptions(c *cli.Context) *doarama.VisualisationURLOptions {
	var vuo doarama.VisualisationURLOptions
	if c.StringSlice("name") != nil {
		vuo.Names = c.StringSlice("name")
	}
	if c.StringSlice("avatar") != nil {
		vuo.Avatars = c.StringSlice("avatar")
	}
	if c.String("avatarbaseurl") != "" {
		vuo.AvatarBaseURL = c.String("avatarbaseurl")
	}
	if c.Bool("fixedaspect") {
		vuo.FixedAspect = c.Bool("fixedaspect")
	}
	if c.Bool("minimalview") {
		vuo.MinimalView = c.Bool("minimalview")
	}
	if c.String("dzml") != "" {
		vuo.DZML = c.String("dzml")
	}
	return &vuo
}

func activityCreateOne(client *doarama.Client, filename string) (*doarama.Activity, error) {
	gpsTrack, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer gpsTrack.Close()
	return client.CreateActivity(filepath.Base(filename), gpsTrack)
}

func activityCreate(c *cli.Context) error {
	client, err := newAuthenticatedDoaramaClient(c)
	if err != nil {
		return err
	}
	activityType, err := doarama.DefaultActivityTypes.Find(c.String("activitytype"))
	if err != nil {
		return err
	}
	for _, arg := range c.Args() {
		a, err := activityCreateOne(client, arg)
		if err != nil {
			log.Print(err)
			continue
		}
		fmt.Printf("ActivityId: %d\n", a.ID)
		if err := a.SetInfo(&doarama.ActivityInfo{
			TypeID: activityType.ID,
		}); err != nil {
			log.Print(err)
			continue
		}
	}
	return nil
}

func activityDelete(c *cli.Context) error {
	client, err := newAuthenticatedDoaramaClient(c)
	if err != nil {
		return err
	}
	var ids []int
	for _, arg := range c.Args() {
		id64, err := strconv.ParseInt(arg, 10, 0)
		if err != nil {
			return err
		}
		ids = append(ids, int(id64))
	}
	for _, id := range ids {
		a := client.Activity(id)
		if err := a.Delete(); err != nil {
			log.Print(err)
			continue
		}
	}
	return nil
}

func create(c *cli.Context) error {
	client, err := newAuthenticatedDoaramaClient(c)
	if err != nil {
		return err
	}
	activityType, err := doarama.DefaultActivityTypes.Find(c.String("activitytype"))
	if err != nil {
		return err
	}
	var as []*doarama.Activity
	for _, arg := range c.Args() {
		var a *doarama.Activity
		a, err = activityCreateOne(client, arg)
		if err != nil {
			break
		}
		err = a.SetInfo(&doarama.ActivityInfo{
			TypeID: activityType.ID,
		})
		if err != nil {
			break
		}
		fmt.Printf("ActivityId: %d\n", a.ID)
		as = append(as, a)
	}
	if err != nil {
		for _, a := range as {
			a.Delete()
		}
		return err
	}
	if len(as) == 0 {
		return errors.New("no activitiess specified")
	}
	v, err := client.CreateVisualisation(as)
	if err != nil {
		return err
	}
	fmt.Printf("VisualisationKey: %s\n", v.Key)
	vuo := newVisualisationURLOptions(c)
	fmt.Printf("VisualisationURL: %s\n", v.URL(vuo))
	return nil
}

type byName doarama.ActivityTypes

func (ats byName) Len() int           { return len(ats) }
func (ats byName) Less(i, j int) bool { return ats[i].Name < ats[j].Name }
func (ats byName) Swap(i, j int)      { ats[i], ats[j] = ats[j], ats[i] }

func queryActivityTypes(c *cli.Context) error {
	client := newDoaramaClient(c)
	ats, err := client.ActivityTypes()
	if err != nil {
		return err
	}
	sort.Sort(byName(ats))
	for _, at := range ats {
		fmt.Printf("%s: %d\n", at.Name, at.ID)
	}
	return nil
}

func visualisationCreate(c *cli.Context) error {
	client, err := newAuthenticatedDoaramaClient(c)
	if err != nil {
		return err
	}
	var as []*doarama.Activity
	for _, arg := range c.Args() {
		var id64 int64
		id64, err = strconv.ParseInt(arg, 10, 0)
		if err != nil {
			return err
		}
		a := client.Activity(int(id64))
		as = append(as, a)
	}
	v, err := client.CreateVisualisation(as)
	if err != nil {
		return err
	}
	fmt.Printf("VisualisationKey: %s\n", v.Key)
	return nil
}

func visualisationURL(c *cli.Context) error {
	client := newDoaramaClient(c)
	vuo := newVisualisationURLOptions(c)
	for _, arg := range c.Args() {
		v := client.Visualisation(arg)
		fmt.Printf("VisualisationURL: %s\n", v.URL(vuo))
	}
	return nil
}

func logError(f func(*cli.Context) error) func(*cli.Context) {
	return func(c *cli.Context) {
		if err := f(c); err != nil {
			log.Fatal(err)
		}
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "doarama"
	app.Usage = "A command line interface to doarama.com"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "apiurl",
			Value:  doarama.DefaultAPIURL,
			Usage:  "Doarama API URL",
			EnvVar: "DOARAMA_API_URL",
		},
		cli.StringFlag{
			Name:   "apikey",
			Usage:  "Doarama API key",
			EnvVar: "DOARAMA_API_KEY",
		},
		cli.StringFlag{
			Name:   "apiname",
			Usage:  "Doarama API name",
			EnvVar: "DOARAMA_API_NAME",
		},
		cli.StringFlag{
			Name:   "userid",
			Usage:  "Doarama user ID",
			EnvVar: "DOARAMA_USER_ID",
		},
		cli.StringFlag{
			Name:   "userkey",
			Usage:  "Doarama user key",
			EnvVar: "DOARAMA_USER_KEY",
		},
	}
	activityTypeFlag := cli.StringFlag{
		Name:  "activitytype",
		Usage: "activity type",
	}
	nameFlag := cli.StringSliceFlag{
		Name:  "name",
		Usage: "name",
	}
	avatarFlag := cli.StringSliceFlag{
		Name:  "avatar",
		Usage: "avatar",
	}
	avatarBaseURLFlag := cli.StringFlag{
		Name:  "avatarbaseurl",
		Usage: "avatar base URL",
	}
	fixedAspectFlag := cli.BoolFlag{
		Name:  "fixedaspect",
		Usage: "fixed aspect",
	}
	minimalViewFlag := cli.BoolFlag{
		Name:  "minimalview",
		Usage: "minimal view",
	}
	dzmlFlag := cli.StringFlag{
		Name:  "dzml",
		Usage: "DZML",
	}
	app.Commands = []cli.Command{
		{
			Name:    "activity",
			Aliases: []string{"a"},
			Usage:   "Manages activities",
			Subcommands: []cli.Command{
				{
					Name:    "create",
					Aliases: []string{"c"},
					Usage:   "Creates an activity from one or more tracklogs",
					Action:  logError(activityCreate),
					Flags: []cli.Flag{
						activityTypeFlag,
					},
				},
				{
					Name:    "delete",
					Aliases: []string{"d"},
					Usage:   "Deletes one or more activities by id",
					Action:  logError(activityDelete),
				},
			},
		},
		{
			Name:    "create",
			Aliases: []string{"c"},
			Usage:   "Creates a visualisation URL from one or more tracklogs",
			Action:  logError(create),
			Flags: []cli.Flag{
				activityTypeFlag,
				nameFlag,
				avatarFlag,
				avatarBaseURLFlag,
				fixedAspectFlag,
				minimalViewFlag,
				dzmlFlag,
			},
		},
		{
			Name:    "query-activity-types",
			Aliases: []string{"qat"},
			Usage:   "Queries activity types",
			Action:  logError(queryActivityTypes),
		},
		{
			Name:    "visualisation",
			Aliases: []string{"v"},
			Usage:   "Manages visualisations",
			Subcommands: []cli.Command{
				{
					Name:    "create",
					Aliases: []string{"c"},
					Usage:   "Creates a visualisation from a list of activities",
					Action:  logError(visualisationCreate),
				},
				{
					Name:    "url",
					Aliases: []string{"u"},
					Usage:   "Creates a visualisation URL from a visualisation key",
					Action:  logError(visualisationURL),
					Flags: []cli.Flag{
						nameFlag,
						avatarFlag,
						avatarBaseURLFlag,
						fixedAspectFlag,
						minimalViewFlag,
						dzmlFlag,
					},
				},
			},
		},
	}
	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Use new cli.ActionFunc signature<commit_after>package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strconv"

	"github.com/codegangsta/cli"
	"github.com/twpayne/go-doarama"
)

func baseDoaramaOptions(c *cli.Context) []doarama.Option {
	return []doarama.Option{
		doarama.APIURL(c.GlobalString("apiurl")),
		doarama.APIName(c.GlobalString("apiname")),
		doarama.APIKey(c.GlobalString("apikey")),
	}
}

func newDoaramaClient(c *cli.Context) *doarama.Client {
	options := baseDoaramaOptions(c)
	return doarama.NewClient(options...)
}

func newAuthenticatedDoaramaOptions(c *cli.Context) ([]doarama.Option, error) {
	options := baseDoaramaOptions(c)
	userID := c.GlobalString("userid")
	userKey := c.GlobalString("userkey")
	switch {
	case userID != "" && userKey == "":
		options = append(options, doarama.Anonymous(userID))
	case userID == "" && userKey != "":
		options = append(options, doarama.Delegate(userKey))
	default:
		return nil, errors.New("exactly one of -userid and -userkey must be specified")
	}
	return options, nil
}

func newAuthenticatedDoaramaClient(c *cli.Context) (*doarama.Client, error) {
	options, err := newAuthenticatedDoaramaOptions(c)
	if err != nil {
		return nil, err
	}
	return doarama.NewClient(options...), nil
}

func newVisualisationURLOptions(c *cli.Context) *doarama.VisualisationURLOptions {
	var vuo doarama.VisualisationURLOptions
	if c.StringSlice("name") != nil {
		vuo.Names = c.StringSlice("name")
	}
	if c.StringSlice("avatar") != nil {
		vuo.Avatars = c.StringSlice("avatar")
	}
	if c.String("avatarbaseurl") != "" {
		vuo.AvatarBaseURL = c.String("avatarbaseurl")
	}
	if c.Bool("fixedaspect") {
		vuo.FixedAspect = c.Bool("fixedaspect")
	}
	if c.Bool("minimalview") {
		vuo.MinimalView = c.Bool("minimalview")
	}
	if c.String("dzml") != "" {
		vuo.DZML = c.String("dzml")
	}
	return &vuo
}

func activityCreateOne(client *doarama.Client, filename string) (*doarama.Activity, error) {
	gpsTrack, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer gpsTrack.Close()
	return client.CreateActivity(filepath.Base(filename), gpsTrack)
}

func activityCreate(c *cli.Context) error {
	client, err := newAuthenticatedDoaramaClient(c)
	if err != nil {
		return err
	}
	activityType, err := doarama.DefaultActivityTypes.Find(c.String("activitytype"))
	if err != nil {
		return err
	}
	for _, arg := range c.Args() {
		a, err := activityCreateOne(client, arg)
		if err != nil {
			log.Print(err)
			continue
		}
		fmt.Printf("ActivityId: %d\n", a.ID)
		if err := a.SetInfo(&doarama.ActivityInfo{
			TypeID: activityType.ID,
		}); err != nil {
			log.Print(err)
			continue
		}
	}
	return nil
}

func activityDelete(c *cli.Context) error {
	client, err := newAuthenticatedDoaramaClient(c)
	if err != nil {
		return err
	}
	var ids []int
	for _, arg := range c.Args() {
		id64, err := strconv.ParseInt(arg, 10, 0)
		if err != nil {
			return err
		}
		ids = append(ids, int(id64))
	}
	for _, id := range ids {
		a := client.Activity(id)
		if err := a.Delete(); err != nil {
			log.Print(err)
			continue
		}
	}
	return nil
}

func create(c *cli.Context) error {
	client, err := newAuthenticatedDoaramaClient(c)
	if err != nil {
		return err
	}
	activityType, err := doarama.DefaultActivityTypes.Find(c.String("activitytype"))
	if err != nil {
		return err
	}
	var as []*doarama.Activity
	for _, arg := range c.Args() {
		var a *doarama.Activity
		a, err = activityCreateOne(client, arg)
		if err != nil {
			break
		}
		err = a.SetInfo(&doarama.ActivityInfo{
			TypeID: activityType.ID,
		})
		if err != nil {
			break
		}
		fmt.Printf("ActivityId: %d\n", a.ID)
		as = append(as, a)
	}
	if err != nil {
		for _, a := range as {
			a.Delete()
		}
		return err
	}
	if len(as) == 0 {
		return errors.New("no activitiess specified")
	}
	v, err := client.CreateVisualisation(as)
	if err != nil {
		return err
	}
	fmt.Printf("VisualisationKey: %s\n", v.Key)
	vuo := newVisualisationURLOptions(c)
	fmt.Printf("VisualisationURL: %s\n", v.URL(vuo))
	return nil
}

type byName doarama.ActivityTypes

func (ats byName) Len() int           { return len(ats) }
func (ats byName) Less(i, j int) bool { return ats[i].Name < ats[j].Name }
func (ats byName) Swap(i, j int)      { ats[i], ats[j] = ats[j], ats[i] }

func queryActivityTypes(c *cli.Context) error {
	client := newDoaramaClient(c)
	ats, err := client.ActivityTypes()
	if err != nil {
		return err
	}
	sort.Sort(byName(ats))
	for _, at := range ats {
		fmt.Printf("%s: %d\n", at.Name, at.ID)
	}
	return nil
}

func visualisationCreate(c *cli.Context) error {
	client, err := newAuthenticatedDoaramaClient(c)
	if err != nil {
		return err
	}
	var as []*doarama.Activity
	for _, arg := range c.Args() {
		var id64 int64
		id64, err = strconv.ParseInt(arg, 10, 0)
		if err != nil {
			return err
		}
		a := client.Activity(int(id64))
		as = append(as, a)
	}
	v, err := client.CreateVisualisation(as)
	if err != nil {
		return err
	}
	fmt.Printf("VisualisationKey: %s\n", v.Key)
	return nil
}

func visualisationURL(c *cli.Context) error {
	client := newDoaramaClient(c)
	vuo := newVisualisationURLOptions(c)
	for _, arg := range c.Args() {
		v := client.Visualisation(arg)
		fmt.Printf("VisualisationURL: %s\n", v.URL(vuo))
	}
	return nil
}

func main() {
	app := cli.NewApp()
	app.Name = "doarama"
	app.Usage = "A command line interface to doarama.com"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "apiurl",
			Value:  doarama.DefaultAPIURL,
			Usage:  "Doarama API URL",
			EnvVar: "DOARAMA_API_URL",
		},
		cli.StringFlag{
			Name:   "apikey",
			Usage:  "Doarama API key",
			EnvVar: "DOARAMA_API_KEY",
		},
		cli.StringFlag{
			Name:   "apiname",
			Usage:  "Doarama API name",
			EnvVar: "DOARAMA_API_NAME",
		},
		cli.StringFlag{
			Name:   "userid",
			Usage:  "Doarama user ID",
			EnvVar: "DOARAMA_USER_ID",
		},
		cli.StringFlag{
			Name:   "userkey",
			Usage:  "Doarama user key",
			EnvVar: "DOARAMA_USER_KEY",
		},
	}
	activityTypeFlag := cli.StringFlag{
		Name:  "activitytype",
		Usage: "activity type",
	}
	nameFlag := cli.StringSliceFlag{
		Name:  "name",
		Usage: "name",
	}
	avatarFlag := cli.StringSliceFlag{
		Name:  "avatar",
		Usage: "avatar",
	}
	avatarBaseURLFlag := cli.StringFlag{
		Name:  "avatarbaseurl",
		Usage: "avatar base URL",
	}
	fixedAspectFlag := cli.BoolFlag{
		Name:  "fixedaspect",
		Usage: "fixed aspect",
	}
	minimalViewFlag := cli.BoolFlag{
		Name:  "minimalview",
		Usage: "minimal view",
	}
	dzmlFlag := cli.StringFlag{
		Name:  "dzml",
		Usage: "DZML",
	}
	app.Commands = []cli.Command{
		{
			Name:    "activity",
			Aliases: []string{"a"},
			Usage:   "Manages activities",
			Subcommands: []cli.Command{
				{
					Name:    "create",
					Aliases: []string{"c"},
					Usage:   "Creates an activity from one or more tracklogs",
					Action:  activityCreate,
					Flags: []cli.Flag{
						activityTypeFlag,
					},
				},
				{
					Name:    "delete",
					Aliases: []string{"d"},
					Usage:   "Deletes one or more activities by id",
					Action:  activityDelete,
				},
			},
		},
		{
			Name:    "create",
			Aliases: []string{"c"},
			Usage:   "Creates a visualisation URL from one or more tracklogs",
			Action:  create,
			Flags: []cli.Flag{
				activityTypeFlag,
				nameFlag,
				avatarFlag,
				avatarBaseURLFlag,
				fixedAspectFlag,
				minimalViewFlag,
				dzmlFlag,
			},
		},
		{
			Name:    "query-activity-types",
			Aliases: []string{"qat"},
			Usage:   "Queries activity types",
			Action:  queryActivityTypes,
		},
		{
			Name:    "visualisation",
			Aliases: []string{"v"},
			Usage:   "Manages visualisations",
			Subcommands: []cli.Command{
				{
					Name:    "create",
					Aliases: []string{"c"},
					Usage:   "Creates a visualisation from a list of activities",
					Action:  visualisationCreate,
				},
				{
					Name:    "url",
					Aliases: []string{"u"},
					Usage:   "Creates a visualisation URL from a visualisation key",
					Action:  visualisationURL,
					Flags: []cli.Flag{
						nameFlag,
						avatarFlag,
						avatarBaseURLFlag,
						fixedAspectFlag,
						minimalViewFlag,
						dzmlFlag,
					},
				},
			},
		},
	}
	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/google-api-go-client/storage/v1"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/consulted/gcssync"
	"github.com/dustin/go-humanize"
	"os"
	"path/filepath"
)

const (
	_                = iota
	errorAuthInfo    = iota
	errorProjectInfo = iota
	errorClientInit  = iota
	errorListFiles   = iota
	errorUploadFiles = iota
	errorSyncFiles   = iota
)

const (
	scope       = storage.DevstorageFull_controlScope
	authURL     = "https://accounts.google.com/o/oauth2/auth"
	tokenURL    = "https://accounts.google.com/o/oauth2/token"
	entityName  = "allUsers"
	redirectURL = "urn:ietf:wg:oauth:2.0:oob"
)

func main() {
	app := cli.NewApp()
	app.Name = "gcssync"
	app.Usage = "Sync files with Google Cloud Storage"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "cachefile",
			Value:  "cache.json",
			Usage:  "Cache file for caching auth tokens",
			EnvVar: "AUTH_CACHE_FILE",
		},
		cli.StringFlag{
			Name:   "bucketname, b",
			Value:  "",
			Usage:  "Name of bucket",
			EnvVar: "BUCKET_NAME",
		},
		cli.StringFlag{
			Name:   "projectid, p",
			Value:  "",
			Usage:  "Google project",
			EnvVar: "PROJECT_ID",
		},
		cli.StringFlag{
			Name:   "clientid, c",
			Value:  "",
			Usage:  "Auth client id",
			EnvVar: "AUTH_CLIENT_ID",
		},
		cli.StringFlag{
			Name:   "clientsecret, s",
			Value:  "",
			Usage:  "Client secrect",
			EnvVar: "AUTH_CLIENT_SECRET",
		},
		cli.StringFlag{
			Name:   "code",
			Value:  "",
			Usage:  "Authorization Code",
			EnvVar: "AUTH_CODE",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "list",
			ShortName: "l",
			Usage:     "List remote files",
			Action:    listFiles,
		},
		{
			Name:      "upload",
			ShortName: "u",
			Usage:     "Upload a single file",
			Action:    uploadFile,
		},
		{
			Name:      "sync",
			ShortName: "s",
			Usage:     "Syncs a folder to a Google Cloudstorage bucket",
			Action:    syncFolder,
			// Flags: []cli.Flag{
			// 	cli.StringFlag{
			// 		Name:   "exclude,e",
			// 		Value:  "",
			// 		Usage:  "Exclude files matching this pattern",
			// 		EnvVar: "EXCLUDE_FILES",
			// 	},
			// },
		},
	}
	app.Run(os.Args)
}

func generateOAuthConfig(c *cli.Context) (*oauth.Config, error) {
	clientId := c.GlobalString("clientid")
	if clientId == "" {
		return &oauth.Config{}, fmt.Errorf("Could not find Client ID")
	}
	clientSecret := c.GlobalString("clientsecret")
	if clientSecret == "" {
		return &oauth.Config{}, fmt.Errorf("Could not find Client Secret")
	}

	return &oauth.Config{
		ClientId:     clientId,
		ClientSecret: clientSecret,
		Scope:        scope,
		AuthURL:      authURL,
		TokenURL:     tokenURL,
		TokenCache:   oauth.CacheFile(c.GlobalString("cachefile")),
		RedirectURL:  redirectURL,
	}, nil
}

func generateServiceConfig(c *cli.Context) (*gcssync.ServiceConfig, error) {
	projectID := c.GlobalString("projectid")
	if projectID == "" {
		return &gcssync.ServiceConfig{}, fmt.Errorf("Could not find project id")
	}
	bucketName := c.GlobalString("bucketname")
	if bucketName == "" {
		return &gcssync.ServiceConfig{}, fmt.Errorf("Cloud not find bucket name")
	}
	return &gcssync.ServiceConfig{
		ProjectID:  projectID,
		BucketName: bucketName,
	}, nil
}

func getClient(c *cli.Context) *gcssync.Client {
	oauthConfig, err := generateOAuthConfig(c)
	if err != nil {
		fmt.Println("Missing auth informations", err.Error())
		os.Exit(errorAuthInfo)
	}
	serviceConfig, err := generateServiceConfig(c)
	if err != nil {
		fmt.Println("Missing project config", err.Error())
		os.Exit(errorProjectInfo)
	}

	client, err := gcssync.NewClient(oauthConfig, c.GlobalString("code"), serviceConfig)
	if err != nil {
		fmt.Println("Error initilizing client: ", err.Error())
		os.Exit(errorClientInit)
	}

	return client
}

func listFiles(c *cli.Context) {
	client := getClient(c)
	files, err := client.ListFiles()
	if err != nil {
		fmt.Println(err)
		os.Exit(errorListFiles)
		return
	}
	fmt.Printf("Objects in %s\n", client.GetBucketname())
	for _, object := range files.Items {
		fmt.Printf("%s %s\n", object.Name, humanize.Bytes(object.Size))
	}
}

func uploadFile(c *cli.Context) {
	client := getClient(c)
	if len(c.Args()) != 2 {
		fmt.Println("Need local and remote name!")
		os.Exit(errorUploadFiles)
	}

	client.UploadFile(c.Args().Get(0), c.Args().Get(1))
}

func syncFolder(c *cli.Context) {
	client := getClient(c)
	var local, remote string
	switch len(c.Args()) {
	case 0:
		local = ""
		remote = ""
	case 1:
		local = c.Args().Get(0)
		remote = ""
	case 2:
		local = c.Args().Get(0)
		remote = c.Args().Get(1)
	default:
		fmt.Println("To many arguments")
		os.Exit(errorSyncFiles)
	}
	local, err := filepath.Abs(local)
	if err != nil {
		fmt.Println("Could not get absolute path")
		os.Exit(errorSyncFiles)
	}
	client.SyncFolder(local, remote /*c.String("exclude") */)
}
<commit_msg>feat(list): display file count<commit_after>package main

import (
	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/google-api-go-client/storage/v1"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/consulted/gcssync"
	"github.com/dustin/go-humanize"
	"os"
	"path/filepath"
)

const (
	_                = iota
	errorAuthInfo    = iota
	errorProjectInfo = iota
	errorClientInit  = iota
	errorListFiles   = iota
	errorUploadFiles = iota
	errorSyncFiles   = iota
)

const (
	scope       = storage.DevstorageFull_controlScope
	authURL     = "https://accounts.google.com/o/oauth2/auth"
	tokenURL    = "https://accounts.google.com/o/oauth2/token"
	entityName  = "allUsers"
	redirectURL = "urn:ietf:wg:oauth:2.0:oob"
)

func main() {
	app := cli.NewApp()
	app.Name = "gcssync"
	app.Usage = "Sync files with Google Cloud Storage"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "cachefile",
			Value:  "cache.json",
			Usage:  "Cache file for caching auth tokens",
			EnvVar: "AUTH_CACHE_FILE",
		},
		cli.StringFlag{
			Name:   "bucketname, b",
			Value:  "",
			Usage:  "Name of bucket",
			EnvVar: "BUCKET_NAME",
		},
		cli.StringFlag{
			Name:   "projectid, p",
			Value:  "",
			Usage:  "Google project",
			EnvVar: "PROJECT_ID",
		},
		cli.StringFlag{
			Name:   "clientid, c",
			Value:  "",
			Usage:  "Auth client id",
			EnvVar: "AUTH_CLIENT_ID",
		},
		cli.StringFlag{
			Name:   "clientsecret, s",
			Value:  "",
			Usage:  "Client secrect",
			EnvVar: "AUTH_CLIENT_SECRET",
		},
		cli.StringFlag{
			Name:   "code",
			Value:  "",
			Usage:  "Authorization Code",
			EnvVar: "AUTH_CODE",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "list",
			ShortName: "l",
			Usage:     "List remote files",
			Action:    listFiles,
		},
		{
			Name:      "upload",
			ShortName: "u",
			Usage:     "Upload a single file",
			Action:    uploadFile,
		},
		{
			Name:      "sync",
			ShortName: "s",
			Usage:     "Syncs a folder to a Google Cloudstorage bucket",
			Action:    syncFolder,
			// Flags: []cli.Flag{
			// 	cli.StringFlag{
			// 		Name:   "exclude,e",
			// 		Value:  "",
			// 		Usage:  "Exclude files matching this pattern",
			// 		EnvVar: "EXCLUDE_FILES",
			// 	},
			// },
		},
	}
	app.Run(os.Args)
}

func generateOAuthConfig(c *cli.Context) (*oauth.Config, error) {
	clientId := c.GlobalString("clientid")
	if clientId == "" {
		return &oauth.Config{}, fmt.Errorf("Could not find Client ID")
	}
	clientSecret := c.GlobalString("clientsecret")
	if clientSecret == "" {
		return &oauth.Config{}, fmt.Errorf("Could not find Client Secret")
	}

	return &oauth.Config{
		ClientId:     clientId,
		ClientSecret: clientSecret,
		Scope:        scope,
		AuthURL:      authURL,
		TokenURL:     tokenURL,
		TokenCache:   oauth.CacheFile(c.GlobalString("cachefile")),
		RedirectURL:  redirectURL,
	}, nil
}

func generateServiceConfig(c *cli.Context) (*gcssync.ServiceConfig, error) {
	projectID := c.GlobalString("projectid")
	if projectID == "" {
		return &gcssync.ServiceConfig{}, fmt.Errorf("Could not find project id")
	}
	bucketName := c.GlobalString("bucketname")
	if bucketName == "" {
		return &gcssync.ServiceConfig{}, fmt.Errorf("Cloud not find bucket name")
	}
	return &gcssync.ServiceConfig{
		ProjectID:  projectID,
		BucketName: bucketName,
	}, nil
}

func getClient(c *cli.Context) *gcssync.Client {
	oauthConfig, err := generateOAuthConfig(c)
	if err != nil {
		fmt.Println("Missing auth informations", err.Error())
		os.Exit(errorAuthInfo)
	}
	serviceConfig, err := generateServiceConfig(c)
	if err != nil {
		fmt.Println("Missing project config", err.Error())
		os.Exit(errorProjectInfo)
	}

	client, err := gcssync.NewClient(oauthConfig, c.GlobalString("code"), serviceConfig)
	if err != nil {
		fmt.Println("Error initilizing client: ", err.Error())
		os.Exit(errorClientInit)
	}

	return client
}

func listFiles(c *cli.Context) {
	client := getClient(c)
	files, err := client.ListFiles()
	if err != nil {
		fmt.Println(err)
		os.Exit(errorListFiles)
		return
	}
	fmt.Printf("Objects in %s - %d\n", client.GetBucketname(), len(files.Items))
	for _, object := range files.Items {
		fmt.Printf("%s %s\n", object.Name, humanize.Bytes(object.Size))
	}
}

func uploadFile(c *cli.Context) {
	client := getClient(c)
	if len(c.Args()) != 2 {
		fmt.Println("Need local and remote name!")
		os.Exit(errorUploadFiles)
	}

	client.UploadFile(c.Args().Get(0), c.Args().Get(1))
}

func syncFolder(c *cli.Context) {
	client := getClient(c)
	var local, remote string
	switch len(c.Args()) {
	case 0:
		local = ""
		remote = ""
	case 1:
		local = c.Args().Get(0)
		remote = ""
	case 2:
		local = c.Args().Get(0)
		remote = c.Args().Get(1)
	default:
		fmt.Println("To many arguments")
		os.Exit(errorSyncFiles)
	}
	local, err := filepath.Abs(local)
	if err != nil {
		fmt.Println("Could not get absolute path")
		os.Exit(errorSyncFiles)
	}
	client.SyncFolder(local, remote /*c.String("exclude") */)
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net"
	"path/filepath"
	"sort"

	"launchpad.net/gnuflag"
	"launchpad.net/goyaml"

	"launchpad.net/juju-core/agent"
	"launchpad.net/juju-core/agent/mongo"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api/params"
)

type BootstrapCommand struct {
	cmd.CommandBase
	AgentConf
	EnvConfig   map[string]interface{}
	Constraints constraints.Value
	Hardware    instance.HardwareCharacteristics
	InstanceId  string
}

// Info returns a decription of the command.
func (c *BootstrapCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "bootstrap-state",
		Purpose: "initialize juju state",
	}
}

func (c *BootstrapCommand) SetFlags(f *gnuflag.FlagSet) {
	c.AgentConf.AddFlags(f)
	yamlBase64Var(f, &c.EnvConfig, "env-config", "", "initial environment configuration (yaml, base64 encoded)")
	f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "initial environment constraints (space-separated strings)")
	f.Var(&c.Hardware, "hardware", "hardware characteristics (space-separated strings)")
	f.StringVar(&c.InstanceId, "instance-id", "", "unique instance-id for bootstrap machine")
}

// Init initializes the command for running.
func (c *BootstrapCommand) Init(args []string) error {
	if len(c.EnvConfig) == 0 {
		return requiredError("env-config")
	}
	if c.InstanceId == "" {
		return requiredError("instance-id")
	}
	return c.AgentConf.CheckArgs(args)
}

// Run initializes state for an environment.
func (c *BootstrapCommand) Run(_ *cmd.Context) error {
	envCfg, err := config.New(config.NoDefaults, c.EnvConfig)
	if err != nil {
		return err
	}
	err = c.ReadConfig("machine-0")
	if err != nil {
		return err
	}
	agentConfig := c.CurrentConfig()

	// agent.Jobs is an optional field in the agent config, and was
	// introduced after 1.17.2. We default to allowing units on
	// machine-0 if missing.
	jobs := agentConfig.Jobs()
	if len(jobs) == 0 {
		jobs = []params.MachineJob{
			params.JobManageEnviron,
			params.JobHostUnits,
		}
	}

	// Get the bootstrap machine's addresses from the provider.
	env, err := environs.New(envCfg)
	if err != nil {
		return err
	}
	instanceId := instance.Id(c.InstanceId)
	instances, err := env.Instances([]instance.Id{instanceId})
	if err != nil {
		return err
	}
	addrs, err := instances[0].Addresses()
	if err != nil {
		return err
	}

	// Generate a shared secret for the Mongo replica set, and write it out.
	sharedSecret, err := mongo.GenerateSharedSecret()
	if err != nil {
		return err
	}
	sharedSecretFile := filepath.Join(c.dataDir, mongo.SharedSecretFile)
	if err := ioutil.WriteFile(sharedSecretFile, []byte(sharedSecret), 0600); err != nil {
		return err
	}

	namespace := agentConfig.Value(agent.Namespace)

	if err := c.startMongo(addrs, envCfg.StatePort(), namespace); err != nil {
		return err
	}

	// Initialise state, and store any agent config (e.g. password) changes.
	var st *state.State
	err = nil
	writeErr := c.ChangeConfig(func(agentConfig agent.ConfigSetter) {
		st, _, err = agent.InitializeState(
			agentConfig,
			envCfg,
			agent.BootstrapMachineConfig{
				Addresses:       addrs,
				Constraints:     c.Constraints,
				Jobs:            jobs,
				InstanceId:      instanceId,
				Characteristics: c.Hardware,
				SharedSecret:    sharedSecret,
			},
			state.DefaultDialOpts(),
			environs.NewStatePolicy(),
		)
	})
	if writeErr != nil {
		return fmt.Errorf("cannot write initial configuration: %v", err)
	}
	if err != nil {
		return err
	}
	st.Close()
	return nil
}

func (c *BootstrapCommand) startMongo(addrs []instance.Address, port int, namespace string) error {
	logger.Debugf("starting mongo")
	preferredAddr, err := selectPreferredStateServerAddress(addrs)
	if err != nil {
		return err
	}
	agentConfig := c.CurrentConfig()
	dialInfo, err := state.DialInfo(agentConfig.StateInfo(), state.DefaultDialOpts())
	if err != nil {
		return err
	}
	// Use localhost to dial the mongo server, because it's running in
	// auth mode and will refuse to perform any operations unless
	// we dial that address.
	dialInfo.Addrs = []string{
		net.JoinHostPort("127.0.0.1", fmt.Sprint(port)),
	}

	if err := ensureMongoServer(agentConfig.DataDir(), port, namespace); err != nil {
		return err
	}

	return maybeInitiateMongoServer(mongo.InitiateMongoParams{
		DialInfo:       dialInfo,
		MemberHostPort: instance.HostPort{preferredAddr, port}.NetAddr(),
	})
}

func selectPreferredStateServerAddress(addrs []instance.Address) (instance.Address, error) {
	if len(addrs) == 0 {
		return instance.Address{}, fmt.Errorf("no state server addresses")
	}
	newAddrs := append(byAddressPreference{}, addrs...)
	sort.Sort(newAddrs)
	return newAddrs[0], nil
}

// byAddressPreference sorts addresses, preferring numeric cloud local addresses.
type byAddressPreference []instance.Address

func (a byAddressPreference) Len() int {
	return len(a)
}

func (a byAddressPreference) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byAddressPreference) Less(i, j int) bool {
	a0, a1 := &a[i], &a[j]
	if pref0, pref1 := netScopePref(a0.NetworkScope), netScopePref(a1.NetworkScope); pref0 != pref1 {
		return pref0 < pref1
	}
	if pref0, pref1 := netTypePref(a0.Type), netTypePref(a1.Type); pref0 != pref1 {
		return pref0 < pref1
	}
	return false
}

func netScopePref(scope instance.NetworkScope) int {
	switch scope {
	case instance.NetworkCloudLocal:
		return 0
	case instance.NetworkUnknown:
		return 1
	}
	return 2
}

func netTypePref(atype instance.AddressType) int {
	switch atype {
	case instance.HostName:
		return 0
	}
	return 1
}

// yamlBase64Value implements gnuflag.Value on a map[string]interface{}.
type yamlBase64Value map[string]interface{}

// Set decodes the base64 value into yaml then expands that into a map.
func (v *yamlBase64Value) Set(value string) error {
	decoded, err := base64.StdEncoding.DecodeString(value)
	if err != nil {
		return err
	}
	return goyaml.Unmarshal(decoded, v)
}

func (v *yamlBase64Value) String() string {
	return fmt.Sprintf("%v", *v)
}

// yamlBase64Var sets up a gnuflag flag analogous to the FlagSet.*Var methods.
func yamlBase64Var(fs *gnuflag.FlagSet, target *map[string]interface{}, name string, value string, usage string) {
	fs.Var((*yamlBase64Value)(target), name, usage)
}
<commit_msg>just use selectpeeraddress<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net"
	"path/filepath"

	"launchpad.net/gnuflag"
	"launchpad.net/goyaml"

	"launchpad.net/juju-core/agent"
	"launchpad.net/juju-core/agent/mongo"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api/params"
)

type BootstrapCommand struct {
	cmd.CommandBase
	AgentConf
	EnvConfig   map[string]interface{}
	Constraints constraints.Value
	Hardware    instance.HardwareCharacteristics
	InstanceId  string
}

// Info returns a decription of the command.
func (c *BootstrapCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "bootstrap-state",
		Purpose: "initialize juju state",
	}
}

func (c *BootstrapCommand) SetFlags(f *gnuflag.FlagSet) {
	c.AgentConf.AddFlags(f)
	yamlBase64Var(f, &c.EnvConfig, "env-config", "", "initial environment configuration (yaml, base64 encoded)")
	f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "initial environment constraints (space-separated strings)")
	f.Var(&c.Hardware, "hardware", "hardware characteristics (space-separated strings)")
	f.StringVar(&c.InstanceId, "instance-id", "", "unique instance-id for bootstrap machine")
}

// Init initializes the command for running.
func (c *BootstrapCommand) Init(args []string) error {
	if len(c.EnvConfig) == 0 {
		return requiredError("env-config")
	}
	if c.InstanceId == "" {
		return requiredError("instance-id")
	}
	return c.AgentConf.CheckArgs(args)
}

// Run initializes state for an environment.
func (c *BootstrapCommand) Run(_ *cmd.Context) error {
	envCfg, err := config.New(config.NoDefaults, c.EnvConfig)
	if err != nil {
		return err
	}
	err = c.ReadConfig("machine-0")
	if err != nil {
		return err
	}
	agentConfig := c.CurrentConfig()

	// agent.Jobs is an optional field in the agent config, and was
	// introduced after 1.17.2. We default to allowing units on
	// machine-0 if missing.
	jobs := agentConfig.Jobs()
	if len(jobs) == 0 {
		jobs = []params.MachineJob{
			params.JobManageEnviron,
			params.JobHostUnits,
		}
	}

	// Get the bootstrap machine's addresses from the provider.
	env, err := environs.New(envCfg)
	if err != nil {
		return err
	}
	instanceId := instance.Id(c.InstanceId)
	instances, err := env.Instances([]instance.Id{instanceId})
	if err != nil {
		return err
	}
	addrs, err := instances[0].Addresses()
	if err != nil {
		return err
	}

	// Generate a shared secret for the Mongo replica set, and write it out.
	sharedSecret, err := mongo.GenerateSharedSecret()
	if err != nil {
		return err
	}
	sharedSecretFile := filepath.Join(c.dataDir, mongo.SharedSecretFile)
	if err := ioutil.WriteFile(sharedSecretFile, []byte(sharedSecret), 0600); err != nil {
		return err
	}

	namespace := agentConfig.Value(agent.Namespace)

	if err := c.startMongo(addrs, envCfg.StatePort(), namespace); err != nil {
		return err
	}

	// Initialise state, and store any agent config (e.g. password) changes.
	var st *state.State
	err = nil
	writeErr := c.ChangeConfig(func(agentConfig agent.ConfigSetter) {
		st, _, err = agent.InitializeState(
			agentConfig,
			envCfg,
			agent.BootstrapMachineConfig{
				Addresses:       addrs,
				Constraints:     c.Constraints,
				Jobs:            jobs,
				InstanceId:      instanceId,
				Characteristics: c.Hardware,
				SharedSecret:    sharedSecret,
			},
			state.DefaultDialOpts(),
			environs.NewStatePolicy(),
		)
	})
	if writeErr != nil {
		return fmt.Errorf("cannot write initial configuration: %v", err)
	}
	if err != nil {
		return err
	}
	st.Close()
	return nil
}

func (c *BootstrapCommand) startMongo(addrs []instance.Address, port int, namespace string) error {
	logger.Debugf("starting mongo")

	agentConfig := c.CurrentConfig()
	dialInfo, err := state.DialInfo(agentConfig.StateInfo(), state.DefaultDialOpts())
	if err != nil {
		return err
	}
	// Use localhost to dial the mongo server, because it's running in
	// auth mode and will refuse to perform any operations unless
	// we dial that address.
	dialInfo.Addrs = []string{
		net.JoinHostPort("127.0.0.1", fmt.Sprint(port)),
	}

	if err := ensureMongoServer(agentConfig.DataDir(), port, namespace); err != nil {
		return err
	}

	return maybeInitiateMongoServer(mongo.InitiateMongoParams{
		DialInfo:       dialInfo,
		MemberHostPort: mongo.SelectPeerAddress(addrs),
	})
}

// yamlBase64Value implements gnuflag.Value on a map[string]interface{}.
type yamlBase64Value map[string]interface{}

// Set decodes the base64 value into yaml then expands that into a map.
func (v *yamlBase64Value) Set(value string) error {
	decoded, err := base64.StdEncoding.DecodeString(value)
	if err != nil {
		return err
	}
	return goyaml.Unmarshal(decoded, v)
}

func (v *yamlBase64Value) String() string {
	return fmt.Sprintf("%v", *v)
}

// yamlBase64Var sets up a gnuflag flag analogous to the FlagSet.*Var methods.
func yamlBase64Var(fs *gnuflag.FlagSet, target *map[string]interface{}, name string, value string, usage string) {
	fs.Var((*yamlBase64Value)(target), name, usage)
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package cmd

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

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"k8s.io/release/pkg/command"
	"k8s.io/release/pkg/gcp/auth"
	"k8s.io/release/pkg/gcp/build"
	"k8s.io/release/pkg/release"
	"k8s.io/release/pkg/util"
)

type gcbmgrOptions struct {
	stage        bool
	release      bool
	stream       bool
	branch       string
	releaseType  string
	buildVersion string
}

var (
	gcbmgrOpts = &gcbmgrOptions{}
	buildOpts  = &build.Options{}

	requiredPackages = []string{
		"jq",
		"git",
		"bsdmainutils",
	}

	// TODO: Do we really need this if we use the Google Cloud SDK instead?
	requiredCommands = []string{
		"gsutil",
		"gcloud",
	}
)

const (
	// TODO: This should maybe be in pkg/release
	defaultReleaseToolRepo   = "https://github.com/kubernetes/release"
	defaultReleaseToolBranch = "master"
	defaultProject           = "kubernetes-release-test"
	defaultDiskSize          = "300"

	bucketPrefix = "kubernetes-release-"
)

// gcbmgrCmd is the command when calling `krel version`
var gcbmgrCmd = &cobra.Command{
	Use:           "gcbmgr",
	Short:         "Run gcbmgr",
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runGcbmgr()
	},
}

func init() {
	// Submit types
	gcbmgrCmd.PersistentFlags().BoolVar(
		&gcbmgrOpts.stage,
		"stage",
		false,
		"Submit a stage run to GCB",
	)
	gcbmgrCmd.PersistentFlags().BoolVar(
		&gcbmgrOpts.release,
		"release",
		false,
		"Submit a release run to GCB",
	)

	gcbmgrCmd.PersistentFlags().StringVar(
		&gcbmgrOpts.branch,
		"branch",
		"",
		"Branch to run the specified GCB run against",
	)

	// Release types
	gcbmgrCmd.PersistentFlags().StringVar(
		&gcbmgrOpts.releaseType,
		"type",
		"prerelease",
		"Release type (must be one of: 'prerelease', 'rc', 'official')",
	)

	// TODO: Remove default once find_green_build logic exists
	gcbmgrCmd.PersistentFlags().StringVar(
		&gcbmgrOpts.buildVersion,
		"build-version",
		"FAKE+BUILD+POINT",
		"Build version",
	)

	// gcloud options
	gcbmgrCmd.PersistentFlags().StringVar(
		&buildOpts.Project,
		"project",
		defaultProject,
		"Branch to run the specified GCB run against",
	)
	gcbmgrCmd.PersistentFlags().BoolVar(
		&gcbmgrOpts.stream,
		"stream",
		false,
		"If specified, GCB will run synchronously, tailing its' logs to stdout",
	)

	rootCmd.AddCommand(gcbmgrCmd)
}

func runGcbmgr() error {
	logrus.Info("Checking for required packages...")
	pkgAvailable, pkgAvailableErr := util.PackagesAvailable(requiredPackages...)
	if pkgAvailableErr != nil {
		return pkgAvailableErr
	}
	if !pkgAvailable {
		return errors.New("packages required to run gcbmgr are not present; cannot continue")
	}

	logrus.Info("Checking for required commands...")
	if cmdAvailable := command.Available(requiredCommands...); !cmdAvailable {
		return errors.New("binaries required to run gcbmgr are not present; cannot continue")
	}

	// TODO: Add gitlib::repo_state check

	logrus.Infof("Running gcbmgr with the following options: %v", *gcbmgrOpts)
	logrus.Infof("Build options: %v", *buildOpts)

	buildOpts.NoSource = true
	buildOpts.DiskSize = defaultDiskSize

	if gcbmgrOpts.stream {
		buildOpts.Async = false
	} else {
		buildOpts.Async = true
	}

	if gcbmgrOpts.stage && gcbmgrOpts.release {
		logrus.Fatal("Cannot specify both the 'stage' and 'release' flag. Please resubmit with only one of those flags selected.")
	}

	gcbSubs, gcbSubsErr := setGCBSubstitutions()
	if gcbSubs == nil || gcbSubsErr != nil {
		return gcbSubsErr
	}

	if rootOpts.nomock {
		_, nomockSubmit, askErr := util.Ask(
			"Really submit a --nomock release job against the $RELEASE_BRANCH branch?",
			"yes",
			3,
		)
		if askErr != nil {
			return askErr
		}

		if nomockSubmit {
			gcbSubs["NOMOCK_TAG"] = "nomock"
			gcbSubs["NOMOCK"] = fmt.Sprintf("--%s", gcbSubs["NOMOCK_TAG"])
		}
	} else {
		// TODO: Remove once cloudbuild.yaml doesn't strictly require vars to be set.
		gcbSubs["NOMOCK_TAG"] = ""
		gcbSubs["NOMOCK"] = ""

		userBucket := fmt.Sprintf("%s%s", bucketPrefix, gcbSubs["GCP_USER_TAG"])
		userBucketSetErr := os.Setenv("USER_BUCKET", userBucket)
		if userBucketSetErr != nil {
			return userBucketSetErr
		}

		testBucket := fmt.Sprintf("%s%s", bucketPrefix, "gcb")
		testBucketSetErr := os.Setenv("BUCKET", testBucket)
		if testBucketSetErr != nil {
			return testBucketSetErr
		}
	}

	toolRoot, err := os.Getwd()
	if err != nil {
		return err
	}

	logrus.Info("Listing GCB substitutions prior to build submission...")
	for k, v := range gcbSubs {
		logrus.Infof("%s: %s", k, v)
	}

	switch {
	case gcbmgrOpts.stage:
		return submitStage(toolRoot, gcbSubs)
	case gcbmgrOpts.release:
		return submitRelease(toolRoot, gcbSubs)
	default:
		return listJobs()
	}
}

// TODO: We may consider pushing stage and release into the runGcbmgr() function
//       Seems to be a bit of duplication without a lot of benefit.
func submitStage(toolRoot string, substitutions map[string]string) error {
	logrus.Infof("Submitting a stage to GCB")

	// TODO: Fail if the file doesn't exist
	buildOpts.CloudbuildFile = filepath.Join(toolRoot, "gcb/stage/cloudbuild.yaml")

	// TODO: Need actual values
	var jobName, uploaded string
	version := "FAKEVERSION"

	return build.RunSingleJob(buildOpts, jobName, uploaded, version, substitutions)
}

func submitRelease(toolRoot string, substitutions map[string]string) error {
	logrus.Infof("Submitting a release to GCB")

	// TODO: Fail if the file doesn't exist
	buildOpts.CloudbuildFile = filepath.Join(toolRoot, "gcb/stage/cloudbuild.yaml")
	buildOpts.DiskSize = "100"

	// TODO: Need actual values
	var jobName, uploaded string
	version := "FAKEVERSION"

	return build.RunSingleJob(buildOpts, jobName, uploaded, version, substitutions)
}

func setGCBSubstitutions() (map[string]string, error) {
	gcbSubs := map[string]string{}

	releaseToolRepo := os.Getenv("RELEASE_TOOL_REPO")
	if releaseToolRepo == "" {
		releaseToolRepo = defaultReleaseToolRepo
	}

	releaseToolBranch := os.Getenv("RELEASE_TOOL_BRANCH")
	if releaseToolBranch == "" {
		releaseToolBranch = defaultReleaseToolBranch
	}

	gcbSubs["RELEASE_TOOL_REPO"] = releaseToolRepo
	gcbSubs["RELEASE_TOOL_BRANCH"] = releaseToolBranch

	gcpUser, gcpUserErr := auth.GetCurrentGCPUser()
	if gcpUserErr != nil {
		return gcbSubs, gcpUserErr
	}

	gcbSubs["GCP_USER_TAG"] = gcpUser

	// TODO: The naming for these env vars is clumsy/confusing, but we're bound by anago right now.
	releaseType := gcbmgrOpts.releaseType
	switch releaseType {
	case "official":
		gcbSubs["OFFICIAL_TAG"] = releaseType
		gcbSubs["OFFICIAL"] = fmt.Sprintf("--%s", releaseType)

		// TODO: Remove once cloudbuild.yaml doesn't strictly require vars to be set.
		gcbSubs["RC_TAG"] = ""
		gcbSubs["RC"] = ""
	case "rc":
		gcbSubs["RC_TAG"] = releaseType
		gcbSubs["RC"] = fmt.Sprintf("--%s", releaseType)

		// TODO: Remove once cloudbuild.yaml doesn't strictly require vars to be set.
		gcbSubs["OFFICIAL_TAG"] = ""
		gcbSubs["OFFICIAL"] = ""
	case "prerelease":
		// TODO: Remove once cloudbuild.yaml doesn't strictly require vars to be set.
		gcbSubs["OFFICIAL_TAG"] = ""
		gcbSubs["OFFICIAL"] = ""
		gcbSubs["RC_TAG"] = ""
		gcbSubs["RC"] = ""
	}

	// TODO: Remove once we remove support for --built-at-head.
	gcbSubs["BUILD_AT_HEAD"] = ""

	buildpoint := gcbmgrOpts.buildVersion
	buildpoint = strings.ReplaceAll(buildpoint, "+", "-")
	gcbSubs["BUILD_POINT"] = buildpoint

	// TODO: Add conditionals for find_green_build
	buildVersion := gcbmgrOpts.buildVersion
	buildVersion = fmt.Sprintf("--buildversion=%s", buildVersion)
	gcbSubs["BUILDVERSION"] = buildVersion

	branch := gcbmgrOpts.branch

	if branch != "" {
		gcbSubs["RELEASE_BRANCH"] = branch
	} else {
		return gcbSubs, errors.New("Release branch must be set to continue")
	}

	kubecrossBranches := []string{
		branch,
		"master",
	}

	kubecrossVersion, kubecrossVersionErr := release.GetKubecrossVersion(kubecrossBranches...)
	if kubecrossVersionErr != nil {
		return gcbSubs, kubecrossVersionErr
	}
	gcbSubs["KUBE_CROSS_VERSION"] = kubecrossVersion

	return gcbSubs, nil
}

func listJobs() error {
	logrus.Info("Listing GCB jobs is not currently supported.")

	// TODO: Add job listing logic
	// logrus.Info("Listing recent GCB jobs...")
	return nil
}
<commit_msg>[krel] gcbmgr: De-dupe stage/release logic using build.PrepareBuilds<commit_after>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package cmd

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

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"k8s.io/release/pkg/command"
	"k8s.io/release/pkg/gcp/auth"
	"k8s.io/release/pkg/gcp/build"
	"k8s.io/release/pkg/release"
	"k8s.io/release/pkg/util"
)

type gcbmgrOptions struct {
	stage        bool
	release      bool
	stream       bool
	branch       string
	releaseType  string
	buildVersion string
}

var (
	gcbmgrOpts = &gcbmgrOptions{}
	buildOpts  = &build.Options{}

	requiredPackages = []string{
		"jq",
		"git",
		"bsdmainutils",
	}

	// TODO: Do we really need this if we use the Google Cloud SDK instead?
	requiredCommands = []string{
		"gsutil",
		"gcloud",
	}
)

const (
	// TODO: This should maybe be in pkg/release
	defaultReleaseToolRepo   = "https://github.com/kubernetes/release"
	defaultReleaseToolBranch = "master"
	defaultProject           = "kubernetes-release-test"
	defaultDiskSize          = "300"

	bucketPrefix = "kubernetes-release-"
)

// gcbmgrCmd is the command when calling `krel version`
var gcbmgrCmd = &cobra.Command{
	Use:           "gcbmgr",
	Short:         "Run gcbmgr",
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runGcbmgr()
	},
}

func init() {
	// Submit types
	gcbmgrCmd.PersistentFlags().BoolVar(
		&gcbmgrOpts.stage,
		"stage",
		false,
		"Submit a stage run to GCB",
	)
	gcbmgrCmd.PersistentFlags().BoolVar(
		&gcbmgrOpts.release,
		"release",
		false,
		"Submit a release run to GCB",
	)

	gcbmgrCmd.PersistentFlags().StringVar(
		&gcbmgrOpts.branch,
		"branch",
		"",
		"Branch to run the specified GCB run against",
	)

	// Release types
	gcbmgrCmd.PersistentFlags().StringVar(
		&gcbmgrOpts.releaseType,
		"type",
		"prerelease",
		"Release type (must be one of: 'prerelease', 'rc', 'official')",
	)

	// TODO: Remove default once find_green_build logic exists
	gcbmgrCmd.PersistentFlags().StringVar(
		&gcbmgrOpts.buildVersion,
		"build-version",
		"FAKE+BUILD+POINT",
		"Build version",
	)

	// gcloud options
	gcbmgrCmd.PersistentFlags().StringVar(
		&buildOpts.Project,
		"project",
		defaultProject,
		"Branch to run the specified GCB run against",
	)
	gcbmgrCmd.PersistentFlags().BoolVar(
		&gcbmgrOpts.stream,
		"stream",
		false,
		"If specified, GCB will run synchronously, tailing its' logs to stdout",
	)
	gcbmgrCmd.PersistentFlags().StringVar(
		&buildOpts.CloudbuildFile,
		"gcb-config",
		build.DefaultCloudbuildFile,
		"If provided, this will be used as the name of the Google Cloud Build config file.",
	)

	rootCmd.AddCommand(gcbmgrCmd)
}

func runGcbmgr() error {
	logrus.Info("Checking for required packages...")
	pkgAvailable, pkgAvailableErr := util.PackagesAvailable(requiredPackages...)
	if pkgAvailableErr != nil {
		return pkgAvailableErr
	}
	if !pkgAvailable {
		return errors.New("packages required to run gcbmgr are not present; cannot continue")
	}

	logrus.Info("Checking for required commands...")
	if cmdAvailable := command.Available(requiredCommands...); !cmdAvailable {
		return errors.New("binaries required to run gcbmgr are not present; cannot continue")
	}

	// TODO: Add gitlib::repo_state check

	logrus.Infof("Running gcbmgr with the following options: %v", *gcbmgrOpts)
	logrus.Infof("Build options: %v", *buildOpts)

	buildOpts.NoSource = true
	buildOpts.DiskSize = defaultDiskSize

	if gcbmgrOpts.stream {
		buildOpts.Async = false
	} else {
		buildOpts.Async = true
	}

	if gcbmgrOpts.stage && gcbmgrOpts.release {
		logrus.Fatal("Cannot specify both the 'stage' and 'release' flag. Please resubmit with only one of those flags selected.")
	}

	gcbSubs, gcbSubsErr := setGCBSubstitutions()
	if gcbSubs == nil || gcbSubsErr != nil {
		return gcbSubsErr
	}

	if rootOpts.nomock {
		_, nomockSubmit, askErr := util.Ask(
			"Really submit a --nomock release job against the $RELEASE_BRANCH branch?",
			"yes",
			3,
		)
		if askErr != nil {
			return askErr
		}

		if nomockSubmit {
			gcbSubs["NOMOCK_TAG"] = "nomock"
			gcbSubs["NOMOCK"] = fmt.Sprintf("--%s", gcbSubs["NOMOCK_TAG"])
		}
	} else {
		// TODO: Remove once cloudbuild.yaml doesn't strictly require vars to be set.
		gcbSubs["NOMOCK_TAG"] = ""
		gcbSubs["NOMOCK"] = ""

		userBucket := fmt.Sprintf("%s%s", bucketPrefix, gcbSubs["GCP_USER_TAG"])
		userBucketSetErr := os.Setenv("USER_BUCKET", userBucket)
		if userBucketSetErr != nil {
			return userBucketSetErr
		}

		testBucket := fmt.Sprintf("%s%s", bucketPrefix, "gcb")
		testBucketSetErr := os.Setenv("BUCKET", testBucket)
		if testBucketSetErr != nil {
			return testBucketSetErr
		}
	}

	toolRoot, err := os.Getwd()
	if err != nil {
		return err
	}

	logrus.Info("Listing GCB substitutions prior to build submission...")
	for k, v := range gcbSubs {
		logrus.Infof("%s: %s", k, v)
	}

	var jobType string
	switch {
	case gcbmgrOpts.stage && gcbmgrOpts.release:
		return errors.New("The '--stage' and '--release' flags cannot be used together")
	case gcbmgrOpts.stage:
		jobType = "stage"
	case gcbmgrOpts.release:
		jobType = "release"
		buildOpts.DiskSize = "100"
	default:
		return listJobs()
	}

	buildOpts.ConfigDir = filepath.Join(toolRoot, "gcb", jobType)
	prepareBuildErr := build.PrepareBuilds(buildOpts)
	if prepareBuildErr != nil {
		return prepareBuildErr
	}

	// TODO: Need actual values
	var jobName, uploaded string
	version := "FAKEVERSION"

	return build.RunSingleJob(buildOpts, jobName, uploaded, version, gcbSubs)
}

func setGCBSubstitutions() (map[string]string, error) {
	gcbSubs := map[string]string{}

	releaseToolRepo := os.Getenv("RELEASE_TOOL_REPO")
	if releaseToolRepo == "" {
		releaseToolRepo = defaultReleaseToolRepo
	}

	releaseToolBranch := os.Getenv("RELEASE_TOOL_BRANCH")
	if releaseToolBranch == "" {
		releaseToolBranch = defaultReleaseToolBranch
	}

	gcbSubs["RELEASE_TOOL_REPO"] = releaseToolRepo
	gcbSubs["RELEASE_TOOL_BRANCH"] = releaseToolBranch

	gcpUser, gcpUserErr := auth.GetCurrentGCPUser()
	if gcpUserErr != nil {
		return gcbSubs, gcpUserErr
	}

	gcbSubs["GCP_USER_TAG"] = gcpUser

	// TODO: The naming for these env vars is clumsy/confusing, but we're bound by anago right now.
	releaseType := gcbmgrOpts.releaseType
	switch releaseType {
	case "official":
		gcbSubs["OFFICIAL_TAG"] = releaseType
		gcbSubs["OFFICIAL"] = fmt.Sprintf("--%s", releaseType)

		// TODO: Remove once cloudbuild.yaml doesn't strictly require vars to be set.
		gcbSubs["RC_TAG"] = ""
		gcbSubs["RC"] = ""
	case "rc":
		gcbSubs["RC_TAG"] = releaseType
		gcbSubs["RC"] = fmt.Sprintf("--%s", releaseType)

		// TODO: Remove once cloudbuild.yaml doesn't strictly require vars to be set.
		gcbSubs["OFFICIAL_TAG"] = ""
		gcbSubs["OFFICIAL"] = ""
	case "prerelease":
		// TODO: Remove once cloudbuild.yaml doesn't strictly require vars to be set.
		gcbSubs["OFFICIAL_TAG"] = ""
		gcbSubs["OFFICIAL"] = ""
		gcbSubs["RC_TAG"] = ""
		gcbSubs["RC"] = ""
	}

	// TODO: Remove once we remove support for --built-at-head.
	gcbSubs["BUILD_AT_HEAD"] = ""

	buildpoint := gcbmgrOpts.buildVersion
	buildpoint = strings.ReplaceAll(buildpoint, "+", "-")
	gcbSubs["BUILD_POINT"] = buildpoint

	// TODO: Add conditionals for find_green_build
	buildVersion := gcbmgrOpts.buildVersion
	buildVersion = fmt.Sprintf("--buildversion=%s", buildVersion)
	gcbSubs["BUILDVERSION"] = buildVersion

	branch := gcbmgrOpts.branch

	if branch != "" {
		gcbSubs["RELEASE_BRANCH"] = branch
	} else {
		return gcbSubs, errors.New("Release branch must be set to continue")
	}

	kubecrossBranches := []string{
		branch,
		"master",
	}

	kubecrossVersion, kubecrossVersionErr := release.GetKubecrossVersion(kubecrossBranches...)
	if kubecrossVersionErr != nil {
		return gcbSubs, kubecrossVersionErr
	}
	gcbSubs["KUBE_CROSS_VERSION"] = kubecrossVersion

	return gcbSubs, nil
}

func listJobs() error {
	logrus.Info("Listing GCB jobs is not currently supported.")

	// TODO: Add job listing logic
	// logrus.Info("Listing recent GCB jobs...")
	return nil
}
<|endoftext|>


<commit_before>package web

import (
	"net/http"

	"github.com/gophergala/goffee/data"
	"github.com/gophergala/goffee/web/controllers"
	"github.com/gorilla/sessions"
	"github.com/hypebeast/gojistatic"
	"github.com/justinas/nosurf"
	"github.com/unrolled/secure"
	"github.com/zenazn/goji/graceful"
	"github.com/zenazn/goji/web"
)

var store = sessions.NewCookieStore([]byte("something-very-secret"))

// SessionMiddleware adds session support to Goffee
func SessionMiddleware(c *web.C, h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		// Get a session. We're ignoring the error resulted from decoding an
		// existing session: Get() always returns a session, even if empty.
		session, _ := store.Get(r, "goffee-session")

		// Save it.
		session.Save(r, w)

		c.Env = make(map[string]interface{})
		c.Env["Session"] = session

		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

// StartServer starts the web server
func StartServer(bind string) {
	data.InitDatabase()

	secureMiddleware := secure.New(secure.Options{
		AllowedHosts:       []string{"example.com", "ssl.example.com"},
		FrameDeny:          true,
		ContentTypeNosniff: true,
		BrowserXssFilter:   true,
		IsDevelopment:      true,
	})

	m := web.New()

	m.Use(gojistatic.Static("web/public", gojistatic.StaticOptions{SkipLogging: true}))
	m.Use(secureMiddleware.Handler)
	m.Use(SessionMiddleware)
	m.Use(nosurf.NewPure)

	m.Get("/", controllers.Home)

	m.Get("/ip", controllers.IP)

	m.Get("/oauth/authorize", controllers.OAuthAuthorize)
	m.Get("/oauth/callback", controllers.OAuthCallback)
	m.Get("/sign_out", controllers.SignOut)

	m.Get("/checks", controllers.ChecksIndex)
	m.Get("/checks/new", controllers.NewCheck)
	m.Get("/checks/:id", controllers.ShowCheck)
	m.Post("/checks", controllers.CreateCheck)

	m.Get("/checks/:check_id/results", controllers.ResultsIndex)

	m.NotFound(controllers.NotFound)

	go graceful.ListenAndServe(bind, m)
}

// Wait ...
func Wait() {
	graceful.Wait()
}
<commit_msg>Use EnvInit middleware instead of creating the Env manually<commit_after>package web

import (
	"net/http"

	"github.com/gophergala/goffee/data"
	"github.com/gophergala/goffee/web/controllers"
	"github.com/gorilla/sessions"
	"github.com/hypebeast/gojistatic"
	"github.com/justinas/nosurf"
	"github.com/unrolled/secure"
	"github.com/zenazn/goji/graceful"
	"github.com/zenazn/goji/web"
	"github.com/zenazn/goji/web/middleware"
)

var store = sessions.NewCookieStore([]byte("something-very-secret"))

// SessionMiddleware adds session support to Goffee
func SessionMiddleware(c *web.C, h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		// Get a session. We're ignoring the error resulted from decoding an
		// existing session: Get() always returns a session, even if empty.
		session, _ := store.Get(r, "goffee-session")

		// Save it.
		session.Save(r, w)

		c.Env["Session"] = session

		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

// StartServer starts the web server
func StartServer(bind string) {
	data.InitDatabase()

	secureMiddleware := secure.New(secure.Options{
		AllowedHosts:       []string{"example.com", "ssl.example.com"},
		FrameDeny:          true,
		ContentTypeNosniff: true,
		BrowserXssFilter:   true,
		IsDevelopment:      true,
	})

	m := web.New()

	m.Use(gojistatic.Static("web/public", gojistatic.StaticOptions{SkipLogging: true}))
	m.Use(middleware.EnvInit)
	m.Use(secureMiddleware.Handler)
	m.Use(SessionMiddleware)
	m.Use(nosurf.NewPure)

	m.Get("/", controllers.Home)

	m.Get("/ip", controllers.IP)

	m.Get("/oauth/authorize", controllers.OAuthAuthorize)
	m.Get("/oauth/callback", controllers.OAuthCallback)
	m.Get("/sign_out", controllers.SignOut)

	m.Get("/checks", controllers.ChecksIndex)
	m.Get("/checks/new", controllers.NewCheck)
	m.Get("/checks/:id", controllers.ShowCheck)
	m.Post("/checks", controllers.CreateCheck)

	m.Get("/checks/:check_id/results", controllers.ResultsIndex)

	m.NotFound(controllers.NotFound)

	go graceful.ListenAndServe(bind, m)
}

// Wait ...
func Wait() {
	graceful.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/gorilla/schema"
	"net/http"
)

type Hasher struct {
	Query  string `schema:"q"`      //query
	Format string `schema:"format"` //format of hash
}

var hasher = new(Hasher)
var decoder = schema.NewDecoder()

func MyHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		panic(err)
	}
	err = decoder.Decode(hasher, r.Form)
	if err != nil {
		panic(err)
	}
	fmt.Fprintf(w, ""+"Query: %s"+"
"+"Format: %s"+"", hasher.Query, hasher.Format)
}

func InputHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, InputForm)
}

const InputForm = `

<form method="GET" action="/hash">

Type the text you want to convert:
<input type="text" name="q" />

<select name="format">
<option value="md5">MD5</option>
<option value="sha1">SHA1</option>
<option value="sha256">SHA256</option>
</select>
<button type="submit">Go</button>
</form>

`

func main() {
	http.HandleFunc("/", InputHandler)
	http.HandleFunc("/hash", MyHandler)
	http.ListenAndServe(":8080", nil)
}
<commit_msg>Implements MD5<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"github.com/gorilla/schema"
	"io"
	"net/http"
)

type Hasher struct {
	Query  string `schema:"q"`      //query
	Format string `schema:"format"` //format of hash
}

var hasher = new(Hasher)
var decoder = schema.NewDecoder()

func MyHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	err = decoder.Decode(hasher, r.Form)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	if hasher.Query == "" || hasher.Format == "" {
		fmt.Fprint(w, InputForm)
		return
	}
	switch hasher.Format {
	case "md5":
		h := md5.New()
		io.WriteString(h, hasher.Query)
		fmt.Fprintf(w, "%x", h.Sum(nil))
		return
	case "sha1":
		fmt.Fprintf(w, "sha1")
		return
	case "sha256":
		fmt.Fprintf(w, "sha256")
		return
	default:
		fmt.Fprintf(w, "Shit, not supported")
		return
	}
}

func InputHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, InputForm)
}

const InputForm = `

<form method="GET" action="/hash">

Type the text you want to convert:
<input type="text" name="q" />

<select name="format">
<option value="md5">MD5</option>
<option value="sha1">SHA1</option>
<option value="sha256">SHA256</option>
</select>
<button type="submit">Go</button>
</form>

`

func main() {
	http.HandleFunc("/", InputHandler)
	http.HandleFunc("/hash", MyHandler)
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package node

import (
	e "github.com/MG-RAST/Shock/shock-server/errors"
	"github.com/MG-RAST/Shock/shock-server/logger"
	"github.com/MG-RAST/Shock/shock-server/node"
	"github.com/MG-RAST/Shock/shock-server/request"
	"github.com/MG-RAST/Shock/shock-server/responder"
	"github.com/MG-RAST/golib/stretchr/goweb/context"
	"net/http"
)

// DELETE: /node/{id}
func (cr *NodeController) Delete(id string, ctx context.Context) error {
	u, err := request.Authenticate(ctx.HttpRequest())
	if err != nil && err.Error() != e.NoAuth {
		return request.AuthError(err, ctx)
	}

	if u == nil {
		return responder.RespondWithError(ctx, http.StatusUnauthorized, e.NoAuth)
	}

	// Load node and handle user unauthorized
	n, err := node.Load(id, u.Uuid)
	if err != nil {
		if err.Error() == e.UnAuth {
			return responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth)
		} else if err.Error() == e.MongoDocNotFound {
			return responder.RespondWithError(ctx, http.StatusNotFound, "Node not found")
		} else {
			// In theory the db connection could be lost between
			// checking user and load but seems unlikely.
			err_msg := "Err@node_Read:Delete: " + err.Error()
			logger.Error(err_msg)
			return responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg)
		}
	}

	rights := n.Acl.Check(u.Uuid)
	if !rights["delete"] {
		return responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth)
	}

	if err := n.Delete(); err == nil {
		return responder.RespondOK(ctx)
	} else {
		err_msg := "Err@node_Delet:Delete: " + err.Error()
		logger.Error(err_msg)
		return responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg)
	}
}
<commit_msg>Spelling error.<commit_after>package node

import (
	e "github.com/MG-RAST/Shock/shock-server/errors"
	"github.com/MG-RAST/Shock/shock-server/logger"
	"github.com/MG-RAST/Shock/shock-server/node"
	"github.com/MG-RAST/Shock/shock-server/request"
	"github.com/MG-RAST/Shock/shock-server/responder"
	"github.com/MG-RAST/golib/stretchr/goweb/context"
	"net/http"
)

// DELETE: /node/{id}
func (cr *NodeController) Delete(id string, ctx context.Context) error {
	u, err := request.Authenticate(ctx.HttpRequest())
	if err != nil && err.Error() != e.NoAuth {
		return request.AuthError(err, ctx)
	}

	if u == nil {
		return responder.RespondWithError(ctx, http.StatusUnauthorized, e.NoAuth)
	}

	// Load node and handle user unauthorized
	n, err := node.Load(id, u.Uuid)
	if err != nil {
		if err.Error() == e.UnAuth {
			return responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth)
		} else if err.Error() == e.MongoDocNotFound {
			return responder.RespondWithError(ctx, http.StatusNotFound, "Node not found")
		} else {
			// In theory the db connection could be lost between
			// checking user and load but seems unlikely.
			err_msg := "Err@node_Read:Delete: " + err.Error()
			logger.Error(err_msg)
			return responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg)
		}
	}

	rights := n.Acl.Check(u.Uuid)
	if !rights["delete"] {
		return responder.RespondWithError(ctx, http.StatusUnauthorized, e.UnAuth)
	}

	if err := n.Delete(); err == nil {
		return responder.RespondOK(ctx)
	} else {
		err_msg := "Err@node_Delete:Delete: " + err.Error()
		logger.Error(err_msg)
		return responder.RespondWithError(ctx, http.StatusInternalServerError, err_msg)
	}
}
<|endoftext|>