<commit_before>/*
Copyright 2016 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 main

import (
	goflag "flag"
	"fmt"
	"io"
	"os"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"k8s.io/apimachinery/pkg/util/validation/field"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/kops/cmd/kops/util"
	kopsapi "k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/client/simple"
	"k8s.io/kops/pkg/kubeconfig"
	"k8s.io/kops/upup/pkg/kutil"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)

const (
	validResources = `

	* cluster
	* instancegroup
	* secret

	`
)

var (
	rootLong = templates.LongDesc(i18n.T(`
	kops is Kubernetes ops.

	kops is the easiest way to get a production grade Kubernetes cluster up and running.
	We like to think of it as kubectl for clusters.

	kops helps you create, destroy, upgrade and maintain production-grade, highly available,
	Kubernetes clusters from the command line.  AWS (Amazon Web Services) is currently
	officially supported, with GCE and VMware vSphere in alpha support.
	`))

	rootShort = i18n.T(`kops is Kubernetes ops.`)
)

type Factory interface {
	Clientset() (simple.Clientset, error)
}

type RootCmd struct {
	util.FactoryOptions

	factory *util.Factory

	configFile string

	clusterName string

	cobraCommand *cobra.Command
}

var _ Factory = &RootCmd{}

var rootCommand = RootCmd{
	cobraCommand: &cobra.Command{
		Use:   "kops",
		Short: rootShort,
		Long:  rootLong,
	},
}

func Execute() {
	goflag.Set("logtostderr", "true")
	goflag.CommandLine.Parse([]string{})
	if err := rootCommand.cobraCommand.Execute(); err != nil {
		exitWithError(err)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	factory := util.NewFactory(&rootCommand.FactoryOptions)
	rootCommand.factory = factory

	NewCmdRoot(factory, os.Stdout)
}

func NewCmdRoot(f *util.Factory, out io.Writer) *cobra.Command {

	cmd := rootCommand.cobraCommand

	//cmd.PersistentFlags().AddGoFlagSet(goflag.CommandLine)
	goflag.CommandLine.VisitAll(func(goflag *goflag.Flag) {
		switch goflag.Name {
		case "cloud-provider-gce-lb-src-cidrs":
			// Skip; this is dragged in by the google cloudprovider dependency

		default:
			cmd.PersistentFlags().AddGoFlag(goflag)
		}
	})

	cmd.PersistentFlags().StringVar(&rootCommand.configFile, "config", "", "yaml config file (default is $HOME/.kops.yaml)")
	viper.BindPFlag("config", cmd.PersistentFlags().Lookup("config"))
	viper.SetDefault("config", "$HOME/.kops.yaml")

	cmd.PersistentFlags().StringVar(&rootCommand.RegistryPath, "state", "", "Location of state storage (kops 'config' file). Overrides KOPS_STATE_STORE environment variable")
	viper.BindPFlag("KOPS_STATE_STORE", cmd.PersistentFlags().Lookup("state"))

	defaultClusterName := os.Getenv("KOPS_CLUSTER_NAME")
	cmd.PersistentFlags().StringVarP(&rootCommand.clusterName, "name", "", defaultClusterName, "Name of cluster. Overrides KOPS_CLUSTER_NAME environment variable")

	// create subcommands
	cmd.AddCommand(NewCmdCompletion(f, out))
	cmd.AddCommand(NewCmdCreate(f, out))
	cmd.AddCommand(NewCmdDelete(f, out))
	cmd.AddCommand(NewCmdEdit(f, out))
	cmd.AddCommand(NewCmdExport(f, out))
	cmd.AddCommand(NewCmdGet(f, out))
	cmd.AddCommand(NewCmdUpdate(f, out))
	cmd.AddCommand(NewCmdReplace(f, out))
	cmd.AddCommand(NewCmdRollingUpdate(f, out))
	cmd.AddCommand(NewCmdSet(f, out))
	cmd.AddCommand(NewCmdToolbox(f, out))
	cmd.AddCommand(NewCmdValidate(f, out))

	return cmd
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {

	// Take in order Flag --state, ENV, .kops.yaml, .kops/config

	if os.Getenv("KOPS_STATE_STORE") != "" && rootCommand.RegistryPath == "" {
		rootCommand.RegistryPath = os.Getenv("KOPS_STATE_STORE")
		viper.BindEnv("KOPS_STATE_STORE")
		viper.AutomaticEnv()
	}

	if rootCommand.configFile != "" {
		// enable ability to specify config file via flag
		viper.SetConfigFile(rootCommand.configFile)
		if err := viper.ReadInConfig(); err == nil {
			viper.BindEnv("KOPS_STATE_STORE")
			rootCommand.RegistryPath = viper.GetString("KOPS_STATE_STORE")
			viper.AutomaticEnv()
		}
	}
	if rootCommand.configFile == "" && rootCommand.RegistryPath == "" {
		viper.SetConfigName(".kops") // name of config file (without extension)
		viper.AddConfigPath("$HOME") // adding home directory as first search path. Assume that exist. Need to be fix for windows
		viper.ReadInConfig()
		if err := viper.ReadInConfig(); err == nil {
			viper.BindEnv("KOPS_STATE_STORE")
			rootCommand.RegistryPath = viper.GetString("KOPS_STATE_STORE")
			viper.AutomaticEnv()
		}
		viper.SetConfigName("config")
		viper.AddConfigPath("$HOME/.kops") // adding home/.kops/config directory as second search path
		viper.ReadInConfig()
		if err := viper.ReadInConfig(); err == nil {
			viper.BindEnv("KOPS_STATE_STORE")
			rootCommand.RegistryPath = viper.GetString("KOPS_STATE_STORE")
			viper.AutomaticEnv()
		}
	}
}

func (c *RootCmd) AddCommand(cmd *cobra.Command) {
	c.cobraCommand.AddCommand(cmd)
}

// ProcessArgs will parse the positional args.  It assumes one of these formats:
//  * <no arguments at all>
//  * <clustername> (and --name not specified)
// Everything else is an error.
func (c *RootCmd) ProcessArgs(args []string) error {
	if len(args) == 0 {
		return nil
	}

	if len(args) == 1 {
		// Assume <clustername>
		if c.clusterName == "" {
			c.clusterName = args[0]
			return nil
		}
	}

	fmt.Printf("\nFound multiple arguments which look like a cluster name\n")
	if c.clusterName != "" {
		fmt.Printf("\t%q (via flag)\n", c.clusterName)
	}
	for _, arg := range args {
		fmt.Printf("\t%q (as argument)\n", arg)
	}
	fmt.Printf("\n")
	fmt.Printf("This often happens if you specify an argument to a boolean flag without using =\n")
	fmt.Printf("For example: use `--bastion=true` or `--bastion`, not `--bastion true`\n\n")

	if len(args) == 1 {
		return fmt.Errorf("Cannot specify cluster via --name and positional argument")
	} else {
		return fmt.Errorf("expected a single <clustername> to be passed as an argument")
	}
}

func (c *RootCmd) ClusterName() string {
	if c.clusterName != "" {
		return c.clusterName
	}

	c.clusterName = ClusterNameFromKubecfg()

	return c.clusterName
}

func ClusterNameFromKubecfg() string {
	// Read from kubeconfig
	pathOptions := clientcmd.NewDefaultPathOptions()

	clusterName := ""

	config, err := pathOptions.GetStartingConfig()
	if err != nil {
		glog.Warningf("error reading kubecfg: %v", err)
	} else if config.CurrentContext == "" {
		glog.Warningf("no context set in kubecfg")
	} else {
		context := config.Contexts[config.CurrentContext]
		if context == nil {
			glog.Warningf("context %q in kubecfg not found", config.CurrentContext)
		} else if context.Cluster == "" {
			glog.Warningf("context %q in kubecfg did not have a cluster", config.CurrentContext)
		} else {
			fmt.Fprintf(os.Stderr, "Using cluster from kubectl context: %s\n\n", context.Cluster)
			clusterName = context.Cluster
		}
	}

	//config, err := readKubectlClusterConfig()
	//if err != nil {
	//	glog.Warningf("error reading kubecfg: %v", err)
	//} else if config != nil && config.Name != "" {
	//	fmt.Fprintf(os.Stderr, "Using cluster from kubectl context: %s\n\n", config.Name)
	//	c.clusterName = config.Name
	//}

	return clusterName
}

func readKubectlClusterConfig() (*kubeconfig.KubectlClusterWithName, error) {
	kubectl := &kutil.Kubectl{}
	context, err := kubectl.GetCurrentContext()
	if err != nil {
		return nil, fmt.Errorf("error getting current context from kubectl: %v", err)
	}
	glog.V(4).Infof("context = %q", context)

	config, err := kubectl.GetConfig(true)
	if err != nil {
		return nil, fmt.Errorf("error getting current config from kubectl: %v", err)
	}

	// Minify should have done this
	if len(config.Clusters) != 1 {
		return nil, fmt.Errorf("expected exactly one cluster in kubectl config, found %d", len(config.Clusters))
	}

	return config.Clusters[0], nil
}

func (c *RootCmd) Clientset() (simple.Clientset, error) {
	return c.factory.Clientset()
}

func (c *RootCmd) Cluster() (*kopsapi.Cluster, error) {
	clusterName := c.ClusterName()
	if clusterName == "" {
		return nil, fmt.Errorf("--name is required")
	}

	return GetCluster(c.factory, clusterName)
}

func GetCluster(factory Factory, clusterName string) (*kopsapi.Cluster, error) {
	if clusterName == "" {
		return nil, field.Required(field.NewPath("ClusterName"), "Cluster name is required")
	}

	clientset, err := factory.Clientset()
	if err != nil {
		return nil, err
	}

	cluster, err := clientset.GetCluster(clusterName)
	if err != nil {
		return nil, fmt.Errorf("error reading cluster configuration: %v", err)
	}
	if cluster == nil {
		return nil, fmt.Errorf("cluster %q not found", clusterName)
	}

	if clusterName != cluster.ObjectMeta.Name {
		return nil, fmt.Errorf("cluster name did not match expected name: %v vs %v", clusterName, cluster.ObjectMeta.Name)
	}
	return cluster, nil
}
<commit_msg>Use less viper discovery<commit_after>/*
Copyright 2016 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 main

import (
	goflag "flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"k8s.io/apimachinery/pkg/util/validation/field"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
	"k8s.io/kops/cmd/kops/util"
	kopsapi "k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/client/simple"
	"k8s.io/kops/pkg/kubeconfig"
	"k8s.io/kops/upup/pkg/kutil"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)

const (
	validResources = `

	* cluster
	* instancegroup
	* secret

	`
)

var (
	rootLong = templates.LongDesc(i18n.T(`
	kops is Kubernetes ops.

	kops is the easiest way to get a production grade Kubernetes cluster up and running.
	We like to think of it as kubectl for clusters.

	kops helps you create, destroy, upgrade and maintain production-grade, highly available,
	Kubernetes clusters from the command line.  AWS (Amazon Web Services) is currently
	officially supported, with GCE and VMware vSphere in alpha support.
	`))

	rootShort = i18n.T(`kops is Kubernetes ops.`)
)

type Factory interface {
	Clientset() (simple.Clientset, error)
}

type RootCmd struct {
	util.FactoryOptions

	factory *util.Factory

	configFile string

	clusterName string

	cobraCommand *cobra.Command
}

var _ Factory = &RootCmd{}

var rootCommand = RootCmd{
	cobraCommand: &cobra.Command{
		Use:   "kops",
		Short: rootShort,
		Long:  rootLong,
	},
}

func Execute() {
	goflag.Set("logtostderr", "true")
	goflag.CommandLine.Parse([]string{})
	if err := rootCommand.cobraCommand.Execute(); err != nil {
		exitWithError(err)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	factory := util.NewFactory(&rootCommand.FactoryOptions)
	rootCommand.factory = factory

	NewCmdRoot(factory, os.Stdout)
}

func NewCmdRoot(f *util.Factory, out io.Writer) *cobra.Command {

	cmd := rootCommand.cobraCommand

	//cmd.PersistentFlags().AddGoFlagSet(goflag.CommandLine)
	goflag.CommandLine.VisitAll(func(goflag *goflag.Flag) {
		switch goflag.Name {
		case "cloud-provider-gce-lb-src-cidrs":
			// Skip; this is dragged in by the google cloudprovider dependency

		default:
			cmd.PersistentFlags().AddGoFlag(goflag)
		}
	})

	cmd.PersistentFlags().StringVar(&rootCommand.configFile, "config", "", "yaml config file (default is $HOME/.kops.yaml)")
	viper.BindPFlag("config", cmd.PersistentFlags().Lookup("config"))
	viper.SetDefault("config", "$HOME/.kops.yaml")

	cmd.PersistentFlags().StringVar(&rootCommand.RegistryPath, "state", "", "Location of state storage (kops 'config' file). Overrides KOPS_STATE_STORE environment variable")
	viper.BindPFlag("KOPS_STATE_STORE", cmd.PersistentFlags().Lookup("state"))
	viper.BindEnv("KOPS_STATE_STORE")

	defaultClusterName := os.Getenv("KOPS_CLUSTER_NAME")
	cmd.PersistentFlags().StringVarP(&rootCommand.clusterName, "name", "", defaultClusterName, "Name of cluster. Overrides KOPS_CLUSTER_NAME environment variable")

	// create subcommands
	cmd.AddCommand(NewCmdCompletion(f, out))
	cmd.AddCommand(NewCmdCreate(f, out))
	cmd.AddCommand(NewCmdDelete(f, out))
	cmd.AddCommand(NewCmdEdit(f, out))
	cmd.AddCommand(NewCmdExport(f, out))
	cmd.AddCommand(NewCmdGet(f, out))
	cmd.AddCommand(NewCmdUpdate(f, out))
	cmd.AddCommand(NewCmdReplace(f, out))
	cmd.AddCommand(NewCmdRollingUpdate(f, out))
	cmd.AddCommand(NewCmdSet(f, out))
	cmd.AddCommand(NewCmdToolbox(f, out))
	cmd.AddCommand(NewCmdValidate(f, out))

	return cmd
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	// Config file precedence: --config flag, ${HOME}/.kops.yaml ${HOME}/.kops/config
	configFile := rootCommand.configFile
	if configFile == "" {
		home := homedir.HomeDir()
		configPaths := []string{
			filepath.Join(home, ".kops.yaml"),
			filepath.Join(home, ".kops", "config"),
		}
		for _, p := range configPaths {
			_, err := os.Stat(p)
			if err == nil {
				configFile = p
				break
			} else if !os.IsNotExist(err) {
				glog.V(2).Infof("error checking for file %s: %v", p, err)
			}
		}
	}

	if configFile != "" {
		viper.SetConfigFile(configFile)
		viper.SetConfigType("yaml")

		if err := viper.ReadInConfig(); err != nil {
			glog.Warningf("error reading config: %v", err)
		}
	}

	rootCommand.RegistryPath = viper.GetString("KOPS_STATE_STORE")

	// Tolerate multiple slashes at end
	rootCommand.RegistryPath = strings.TrimSuffix(rootCommand.RegistryPath, "/")
}

func (c *RootCmd) AddCommand(cmd *cobra.Command) {
	c.cobraCommand.AddCommand(cmd)
}

// ProcessArgs will parse the positional args.  It assumes one of these formats:
//  * <no arguments at all>
//  * <clustername> (and --name not specified)
// Everything else is an error.
func (c *RootCmd) ProcessArgs(args []string) error {
	if len(args) == 0 {
		return nil
	}

	if len(args) == 1 {
		// Assume <clustername>
		if c.clusterName == "" {
			c.clusterName = args[0]
			return nil
		}
	}

	fmt.Printf("\nFound multiple arguments which look like a cluster name\n")
	if c.clusterName != "" {
		fmt.Printf("\t%q (via flag)\n", c.clusterName)
	}
	for _, arg := range args {
		fmt.Printf("\t%q (as argument)\n", arg)
	}
	fmt.Printf("\n")
	fmt.Printf("This often happens if you specify an argument to a boolean flag without using =\n")
	fmt.Printf("For example: use `--bastion=true` or `--bastion`, not `--bastion true`\n\n")

	if len(args) == 1 {
		return fmt.Errorf("Cannot specify cluster via --name and positional argument")
	} else {
		return fmt.Errorf("expected a single <clustername> to be passed as an argument")
	}
}

func (c *RootCmd) ClusterName() string {
	if c.clusterName != "" {
		return c.clusterName
	}

	c.clusterName = ClusterNameFromKubecfg()

	return c.clusterName
}

func ClusterNameFromKubecfg() string {
	// Read from kubeconfig
	pathOptions := clientcmd.NewDefaultPathOptions()

	clusterName := ""

	config, err := pathOptions.GetStartingConfig()
	if err != nil {
		glog.Warningf("error reading kubecfg: %v", err)
	} else if config.CurrentContext == "" {
		glog.Warningf("no context set in kubecfg")
	} else {
		context := config.Contexts[config.CurrentContext]
		if context == nil {
			glog.Warningf("context %q in kubecfg not found", config.CurrentContext)
		} else if context.Cluster == "" {
			glog.Warningf("context %q in kubecfg did not have a cluster", config.CurrentContext)
		} else {
			fmt.Fprintf(os.Stderr, "Using cluster from kubectl context: %s\n\n", context.Cluster)
			clusterName = context.Cluster
		}
	}

	//config, err := readKubectlClusterConfig()
	//if err != nil {
	//	glog.Warningf("error reading kubecfg: %v", err)
	//} else if config != nil && config.Name != "" {
	//	fmt.Fprintf(os.Stderr, "Using cluster from kubectl context: %s\n\n", config.Name)
	//	c.clusterName = config.Name
	//}

	return clusterName
}

func readKubectlClusterConfig() (*kubeconfig.KubectlClusterWithName, error) {
	kubectl := &kutil.Kubectl{}
	context, err := kubectl.GetCurrentContext()
	if err != nil {
		return nil, fmt.Errorf("error getting current context from kubectl: %v", err)
	}
	glog.V(4).Infof("context = %q", context)

	config, err := kubectl.GetConfig(true)
	if err != nil {
		return nil, fmt.Errorf("error getting current config from kubectl: %v", err)
	}

	// Minify should have done this
	if len(config.Clusters) != 1 {
		return nil, fmt.Errorf("expected exactly one cluster in kubectl config, found %d", len(config.Clusters))
	}

	return config.Clusters[0], nil
}

func (c *RootCmd) Clientset() (simple.Clientset, error) {
	return c.factory.Clientset()
}

func (c *RootCmd) Cluster() (*kopsapi.Cluster, error) {
	clusterName := c.ClusterName()
	if clusterName == "" {
		return nil, fmt.Errorf("--name is required")
	}

	return GetCluster(c.factory, clusterName)
}

func GetCluster(factory Factory, clusterName string) (*kopsapi.Cluster, error) {
	if clusterName == "" {
		return nil, field.Required(field.NewPath("ClusterName"), "Cluster name is required")
	}

	clientset, err := factory.Clientset()
	if err != nil {
		return nil, err
	}

	cluster, err := clientset.GetCluster(clusterName)
	if err != nil {
		return nil, fmt.Errorf("error reading cluster configuration: %v", err)
	}
	if cluster == nil {
		return nil, fmt.Errorf("cluster %q not found", clusterName)
	}

	if clusterName != cluster.ObjectMeta.Name {
		return nil, fmt.Errorf("cluster name did not match expected name: %v vs %v", clusterName, cluster.ObjectMeta.Name)
	}
	return cluster, nil
}
<|endoftext|>


<commit_before>package main

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

	"github.com/jessevdk/go-flags"
	"github.com/lestrrat/peco"
	"github.com/nsf/termbox-go"
)

func showHelp() {
	const v = `
Usage: percol [options] [FILE]

Options:
  -h, --help            show this help message and exit
  --rcfile=RCFILE       path to the settings file
  --query=QUERY         pre-input query
  --no-ignore-case      start in case-sensitive mode
`
	os.Stderr.Write([]byte(v))
}

type CmdOptions struct {
	Help   bool   `short:"h" long:"help" description:"show this help message and exit"`
	TTY    string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	Query  string `long:"query"`
	Rcfile string `long:"rcfile" descriotion:"path to the settings file"`
	NoIgnoreCase bool `long:"no-ignore-case" description:"start in case-sensitive-mode" default:"false"`
}

func main() {
	var err error

	opts := &CmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse() // &opts, os.Args)
	if err != nil {
		showHelp()
		os.Exit(1)
	}

	if opts.Help {
		showHelp()
		os.Exit(1)
	}

	var in *os.File

	// receive in from either a file or Stdin
	if len(args) > 0 {
		in, err = os.Open(args[0])
		if err != nil {
			os.Exit(1)
		}
	} else if !peco.IsTty() {
		in = os.Stdin
	}

	ctx := peco.NewCtx()
	defer func() {
		if result := ctx.Result(); result != "" {
			if result[len(result)-1] != '\n' {
				result = result + "\n"
			}
			os.Stdout.WriteString(result)
		}
		os.Exit(ctx.ExitStatus)
	}()

	if opts.Rcfile == "" {
		user, err := user.Current()
		if err == nil { // silently ignore failure for user.Current()
			file := filepath.Join(user.HomeDir, ".peco", "config.json")
			_, err := os.Stat(file)
			if err == nil {
				opts.Rcfile = file
			}
		}
	}

	if opts.Rcfile != "" {
		err = ctx.ReadConfig(opts.Rcfile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			ctx.ExitStatus = 1
			return
		}
	}

	ctx.IgnoreCase = !opts.NoIgnoreCase

	if err = ctx.ReadBuffer(in); err != nil {
		// Nothing to process, bail out
		fmt.Fprintln(os.Stderr, "You must supply something to work with via filename or stdin")
		ctx.ExitStatus = 1
		return
	}

	err = peco.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		ctx.ExitStatus = 1
		return
	}
	defer peco.TtyTerm()

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		ctx.ExitStatus = 1
		return
	}
	defer termbox.Close()

	view := ctx.NewView()
	filter := ctx.NewFilter()
	input := ctx.NewInput()

	go view.Loop()
	go filter.Loop()
	go input.Loop()

	if len(opts.Query) > 0 {
		ctx.SetQuery([]rune(opts.Query))
		ctx.ExecQuery(opts.Query)
	} else {
		view.Refresh()
	}

	ctx.WaitDone()
}
<commit_msg>whoa, one more reference to percol, gone<commit_after>package main

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

	"github.com/jessevdk/go-flags"
	"github.com/lestrrat/peco"
	"github.com/nsf/termbox-go"
)

func showHelp() {
	const v = `
Usage: peco [options] [FILE]

Options:
  -h, --help            show this help message and exit
  --rcfile=RCFILE       path to the settings file
  --query=QUERY         pre-input query
  --no-ignore-case      start in case-sensitive mode
`
	os.Stderr.Write([]byte(v))
}

type CmdOptions struct {
	Help   bool   `short:"h" long:"help" description:"show this help message and exit"`
	TTY    string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	Query  string `long:"query"`
	Rcfile string `long:"rcfile" descriotion:"path to the settings file"`
	NoIgnoreCase bool `long:"no-ignore-case" description:"start in case-sensitive-mode" default:"false"`
}

func main() {
	var err error

	opts := &CmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse() // &opts, os.Args)
	if err != nil {
		showHelp()
		os.Exit(1)
	}

	if opts.Help {
		showHelp()
		os.Exit(1)
	}

	var in *os.File

	// receive in from either a file or Stdin
	if len(args) > 0 {
		in, err = os.Open(args[0])
		if err != nil {
			os.Exit(1)
		}
	} else if !peco.IsTty() {
		in = os.Stdin
	}

	ctx := peco.NewCtx()
	defer func() {
		if result := ctx.Result(); result != "" {
			if result[len(result)-1] != '\n' {
				result = result + "\n"
			}
			os.Stdout.WriteString(result)
		}
		os.Exit(ctx.ExitStatus)
	}()

	if opts.Rcfile == "" {
		user, err := user.Current()
		if err == nil { // silently ignore failure for user.Current()
			file := filepath.Join(user.HomeDir, ".peco", "config.json")
			_, err := os.Stat(file)
			if err == nil {
				opts.Rcfile = file
			}
		}
	}

	if opts.Rcfile != "" {
		err = ctx.ReadConfig(opts.Rcfile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			ctx.ExitStatus = 1
			return
		}
	}

	ctx.IgnoreCase = !opts.NoIgnoreCase

	if err = ctx.ReadBuffer(in); err != nil {
		// Nothing to process, bail out
		fmt.Fprintln(os.Stderr, "You must supply something to work with via filename or stdin")
		ctx.ExitStatus = 1
		return
	}

	err = peco.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		ctx.ExitStatus = 1
		return
	}
	defer peco.TtyTerm()

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		ctx.ExitStatus = 1
		return
	}
	defer termbox.Close()

	view := ctx.NewView()
	filter := ctx.NewFilter()
	input := ctx.NewInput()

	go view.Loop()
	go filter.Loop()
	go input.Loop()

	if len(opts.Query) > 0 {
		ctx.SetQuery([]rune(opts.Query))
		ctx.ExecQuery(opts.Query)
	} else {
		view.Refresh()
	}

	ctx.WaitDone()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/prometheus/client_golang/prometheus/promhttp"

	etcdraft "github.com/coreos/etcd/raft"
	hashic "github.com/hashicorp/raft"
	"github.com/hashicorp/raft-boltdb"
	"github.com/relab/raft"
	etcd "github.com/relab/raft/etcd"
	hraft "github.com/relab/raft/hashicorp"
	"github.com/relab/raft/raftgorums"
	"github.com/relab/rkv/rkvpb"

	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
)

const (
	bgorums    = "gorums"
	betcd      = "etcd"
	bhashicorp = "hashicorp"
)

var (
	bench             = flag.Bool("quiet", false, "Silence log output")
	recover           = flag.Bool("recover", false, "Recover from stable storage")
	batch             = flag.Bool("batch", true, "enable batching")
	serverMetrics     = flag.Bool("servermetrics", false, "enable server-side metrics")
	electionTimeout   = flag.Duration("election", time.Second, "How long servers wait before starting an election")
	heartbeatTimeout  = flag.Duration("heartbeat", 20*time.Millisecond, "How often a heartbeat should be sent")
	entriesPerMsg     = flag.Uint64("entriespermsg", 64, "Entries per Appendentries message")
	catchupMultiplier = flag.Uint64("catchupmultiplier", 160, "How many more times entries per message allowed during catch up")
)

func main() {
	var (
		id      = flag.Uint64("id", 0, "server ID")
		servers = flag.String("servers", ":9201,:9202,:9203,:9204,:9205,:9206,:9207", "comma separated list of server addresses")
		cluster = flag.String("cluster", "1,2,3", "comma separated list of server ids to form cluster with, [1 >= id <= len(servers)]")
		backend = flag.String("backend", "gorums", "Raft backend to use [gorums|etcd|hashicorp]")
	)

	flag.Parse()
	rand.Seed(time.Now().UnixNano())

	if *id == 0 {
		fmt.Print("-id argument is required\n\n")
		flag.Usage()
		os.Exit(1)
	}

	nodes := strings.Split(*servers, ",")

	if len(nodes) == 0 {
		fmt.Print("-server argument is required\n\n")
		flag.Usage()
		os.Exit(1)
	}

	selected := strings.Split(*cluster, ",")

	var ids []uint64

	for _, sid := range selected {
		id, err := strconv.ParseUint(sid, 10, 64)

		if err != nil {
			fmt.Print("could not parse -cluster argument\n\n")
			flag.Usage()
			os.Exit(1)
		}

		if id <= 0 || id > uint64(len(nodes)) {
			fmt.Print("invalid -cluster argument\n\n")
			flag.Usage()
			os.Exit(1)
		}

		ids = append(ids, id)
	}

	if len(ids) == 0 {
		fmt.Print("-cluster argument is required\n\n")
		flag.Usage()
		os.Exit(1)
	}

	if len(ids) > len(nodes) {
		fmt.Print("-cluster specifies too many servers\n\n")
		flag.Usage()
		os.Exit(1)
	}

	if *entriesPerMsg < 1 {
		fmt.Print("-entriespermsg must be atleast 1\n\n")
		flag.Usage()
		os.Exit(1)
	}

	if *catchupMultiplier < 1 {
		fmt.Print("-catchupmultiplier must be atleast 1\n\n")
		flag.Usage()
		os.Exit(1)
	}

	logFile, err := os.OpenFile(
		fmt.Sprintf("%s%sraft%.2d.log", os.TempDir(), string(filepath.Separator), *id),
		os.O_CREATE|os.O_TRUNC|os.O_APPEND|os.O_WRONLY, 0666,
	)

	if err != nil {
		logrus.Fatal(err)
	}

	logger := logrus.New()
	logger.Hooks.Add(NewLogToFileHook(logFile))

	if *bench {
		logger.Out = ioutil.Discard
		grpc.EnableTracing = false
	}

	grpclog.SetLogger(logger)

	lis, err := net.Listen("tcp", nodes[*id-1])

	if err != nil {
		logger.Fatal(err)
	}

	grpcServer := grpc.NewServer()

	switch *backend {
	case bgorums:
		rungorums(logger, lis, grpcServer, *id, ids, nodes)
	case betcd:
		runetcd(logger, lis, grpcServer, *id, ids, nodes)
	case bhashicorp:
		runhashicorp(logger, lis, grpcServer, *id, ids, nodes)
	}

}

func runhashicorp(
	logger logrus.FieldLogger,
	lis net.Listener, grpcServer *grpc.Server,
	id uint64, ids []uint64, nodes []string,
) {
	var selflis string
	selflis, ids, nodes = selfIDsNodes(logger, id, ids, nodes)

	servers := make([]hashic.Server, len(nodes)+1)
	servers[0] = hashic.Server{
		Suffrage: hashic.Voter,
		ID:       hashic.ServerID(selflis),
		Address:  hashic.ServerAddress(selflis),
	}
	for i, addr := range nodes {
		host, port, err := net.SplitHostPort(addr)
		if err != nil {
			logger.Fatal(err)
		}
		p, _ := strconv.Atoi(port)
		addr = host + ":" + strconv.Itoa(p-100)

		servers[i+1] = hashic.Server{
			Suffrage: hashic.Voter,
			ID:       hashic.ServerID(addr),
			Address:  hashic.ServerAddress(addr),
		}
	}

	fmt.Println(servers)

	addr, err := net.ResolveTCPAddr("tcp", selflis)
	if err != nil {
		logger.Fatal(err)
	}
	trans, err := hashic.NewTCPTransport(selflis, addr, len(ids), 10*time.Second, os.Stderr)
	if err != nil {
		logger.Fatal(err)
	}

	path := fmt.Sprintf("hashicorp%.2d.bolt", id)
	overwrite := !*recover
	// Check if file already exists.
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			// We don't need to overwrite a file that doesn't exist.
			overwrite = false
		} else {
			// If we are unable to verify the existence of the file,
			// there is probably a permission problem.
			logger.Fatal(err)
		}
	}
	if overwrite {
		if err := os.Remove(path); err != nil {
			logger.Fatal(err)
		}
	}
	logs, err := raftboltdb.NewBoltStore(path)
	if err != nil {
		logger.Fatal(err)
	}

	snaps := hashic.NewDiscardSnapshotStore()

	cfg := &hashic.Config{
		LocalID:            hashic.ServerID(selflis),
		ProtocolVersion:    hashic.ProtocolVersionMax,
		HeartbeatTimeout:   *electionTimeout,
		ElectionTimeout:    *electionTimeout,
		CommitTimeout:      *heartbeatTimeout,
		MaxAppendEntries:   int(*entriesPerMsg),
		ShutdownOnRemove:   true,
		TrailingLogs:       10240,
		SnapshotInterval:   120 * time.Hour,
		SnapshotThreshold:  math.MaxUint64,
		LeaderLeaseTimeout: *electionTimeout / 2,
	}

	node := hraft.NewRaft(logger, NewStore(), cfg, servers, trans, logs, logs, snaps)

	service := NewService(node)
	rkvpb.RegisterRKVServer(grpcServer, service)

	logrus.Fatal(grpcServer.Serve(lis))
}

func runetcd(
	logger logrus.FieldLogger,
	lis net.Listener, grpcServer *grpc.Server,
	id uint64, ids []uint64, nodes []string,
) {
	var selflis string
	selflis, ids, nodes = selfIDsNodes(logger, id, ids, nodes)

	peers := make([]etcdraft.Peer, len(nodes))

	for i, addr := range nodes {
		nid := ids[i]

		host, port, err := net.SplitHostPort(addr)
		if err != nil {
			logger.Fatal(err)
		}
		p, _ := strconv.Atoi(port)

		ur, err := url.Parse("http://" + addr)
		ur.Host = host + ":" + strconv.Itoa(p-100)
		if err != nil {
			logger.Fatal(err)
		}

		peers[i] = etcdraft.Peer{
			ID:      nid,
			Context: []byte(ur.String()),
		}
	}

	storage := etcdraft.NewMemoryStorage()
	node := etcd.NewRaft(
		logger,
		NewStore(),
		storage,
		&etcdraft.Config{
			ID:            id,
			ElectionTick:  int(*electionTimeout / *heartbeatTimeout),
			HeartbeatTick: 1,
			Storage:       storage,
			MaxSizePerMsg: *entriesPerMsg,
			// etcdserver says: Never overflow the rafthttp buffer,
			// which is 4096. We keep the same constant.
			MaxInflightMsgs: 4096 / 8,
			Logger:          logger,
		},
		peers,
		*heartbeatTimeout,
	)

	service := NewService(node)
	rkvpb.RegisterRKVServer(grpcServer, service)

	go func() {
		logrus.Fatal(grpcServer.Serve(lis))
	}()

	lishttp, err := net.Listen("tcp", selflis)

	if err != nil {
		logger.Fatal(err)
	}

	logger.Fatal(http.Serve(lishttp, node.Handler()))
}

func rungorums(
	logger logrus.FieldLogger,
	lis net.Listener, grpcServer *grpc.Server,
	id uint64, ids []uint64, nodes []string,
) {
	storage, err := raft.NewFileStorage(fmt.Sprintf("db%.2d.bolt", id), !*recover)

	if err != nil {
		logger.Fatal(err)
	}

	storageWithCache := raft.NewCacheStorage(storage, 20000)

	node := raftgorums.NewRaft(NewStore(), &raftgorums.Config{
		ID:                id,
		Servers:           nodes,
		InitialCluster:    ids,
		Batch:             *batch,
		Storage:           storageWithCache,
		ElectionTimeout:   *electionTimeout,
		HeartbeatTimeout:  *heartbeatTimeout,
		EntriesPerMsg:     *entriesPerMsg,
		CatchupMultiplier: *catchupMultiplier,
		Logger:            logger,
		MetricsEnabled:    true,
	})

	service := NewService(node)
	rkvpb.RegisterRKVServer(grpcServer, service)

	go func() {
		logrus.Fatal(grpcServer.Serve(lis))
	}()

	if *serverMetrics {
		go func() {
			http.Handle("/metrics", promhttp.Handler())
			logrus.Fatal(http.ListenAndServe(":5"+nodes[id-1][1:], nil))
		}()
	}

	logger.Fatal(node.Run(grpcServer))
}

func selfIDsNodes(logger logrus.FieldLogger, id uint64, ids []uint64, nodes []string) (string, []uint64, []string) {
	host, port, err := net.SplitHostPort(nodes[id-1])
	if err != nil {
		logger.Fatal(err)
	}
	p, _ := strconv.Atoi(port)
	selflis := host + ":" + strconv.Itoa(p-100)

	ids = append(ids[:id-1], ids[id:]...)
	nodes = append(nodes[:id-1], nodes[id:]...)

	return selflis, ids, nodes
}
<commit_msg>rkvd/main.go: Remove left over comment<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/prometheus/client_golang/prometheus/promhttp"

	etcdraft "github.com/coreos/etcd/raft"
	hashic "github.com/hashicorp/raft"
	"github.com/hashicorp/raft-boltdb"
	"github.com/relab/raft"
	etcd "github.com/relab/raft/etcd"
	hraft "github.com/relab/raft/hashicorp"
	"github.com/relab/raft/raftgorums"
	"github.com/relab/rkv/rkvpb"

	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
)

const (
	bgorums    = "gorums"
	betcd      = "etcd"
	bhashicorp = "hashicorp"
)

var (
	bench             = flag.Bool("quiet", false, "Silence log output")
	recover           = flag.Bool("recover", false, "Recover from stable storage")
	batch             = flag.Bool("batch", true, "enable batching")
	serverMetrics     = flag.Bool("servermetrics", false, "enable server-side metrics")
	electionTimeout   = flag.Duration("election", time.Second, "How long servers wait before starting an election")
	heartbeatTimeout  = flag.Duration("heartbeat", 20*time.Millisecond, "How often a heartbeat should be sent")
	entriesPerMsg     = flag.Uint64("entriespermsg", 64, "Entries per Appendentries message")
	catchupMultiplier = flag.Uint64("catchupmultiplier", 160, "How many more times entries per message allowed during catch up")
)

func main() {
	var (
		id      = flag.Uint64("id", 0, "server ID")
		servers = flag.String("servers", ":9201,:9202,:9203,:9204,:9205,:9206,:9207", "comma separated list of server addresses")
		cluster = flag.String("cluster", "1,2,3", "comma separated list of server ids to form cluster with, [1 >= id <= len(servers)]")
		backend = flag.String("backend", "gorums", "Raft backend to use [gorums|etcd|hashicorp]")
	)

	flag.Parse()
	rand.Seed(time.Now().UnixNano())

	if *id == 0 {
		fmt.Print("-id argument is required\n\n")
		flag.Usage()
		os.Exit(1)
	}

	nodes := strings.Split(*servers, ",")

	if len(nodes) == 0 {
		fmt.Print("-server argument is required\n\n")
		flag.Usage()
		os.Exit(1)
	}

	selected := strings.Split(*cluster, ",")

	var ids []uint64

	for _, sid := range selected {
		id, err := strconv.ParseUint(sid, 10, 64)

		if err != nil {
			fmt.Print("could not parse -cluster argument\n\n")
			flag.Usage()
			os.Exit(1)
		}

		if id <= 0 || id > uint64(len(nodes)) {
			fmt.Print("invalid -cluster argument\n\n")
			flag.Usage()
			os.Exit(1)
		}

		ids = append(ids, id)
	}

	if len(ids) == 0 {
		fmt.Print("-cluster argument is required\n\n")
		flag.Usage()
		os.Exit(1)
	}

	if len(ids) > len(nodes) {
		fmt.Print("-cluster specifies too many servers\n\n")
		flag.Usage()
		os.Exit(1)
	}

	if *entriesPerMsg < 1 {
		fmt.Print("-entriespermsg must be atleast 1\n\n")
		flag.Usage()
		os.Exit(1)
	}

	if *catchupMultiplier < 1 {
		fmt.Print("-catchupmultiplier must be atleast 1\n\n")
		flag.Usage()
		os.Exit(1)
	}

	logFile, err := os.OpenFile(
		fmt.Sprintf("%s%sraft%.2d.log", os.TempDir(), string(filepath.Separator), *id),
		os.O_CREATE|os.O_TRUNC|os.O_APPEND|os.O_WRONLY, 0666,
	)

	if err != nil {
		logrus.Fatal(err)
	}

	logger := logrus.New()
	logger.Hooks.Add(NewLogToFileHook(logFile))

	if *bench {
		logger.Out = ioutil.Discard
		grpc.EnableTracing = false
	}

	grpclog.SetLogger(logger)

	lis, err := net.Listen("tcp", nodes[*id-1])

	if err != nil {
		logger.Fatal(err)
	}

	grpcServer := grpc.NewServer()

	switch *backend {
	case bgorums:
		rungorums(logger, lis, grpcServer, *id, ids, nodes)
	case betcd:
		runetcd(logger, lis, grpcServer, *id, ids, nodes)
	case bhashicorp:
		runhashicorp(logger, lis, grpcServer, *id, ids, nodes)
	}

}

func runhashicorp(
	logger logrus.FieldLogger,
	lis net.Listener, grpcServer *grpc.Server,
	id uint64, ids []uint64, nodes []string,
) {
	var selflis string
	selflis, ids, nodes = selfIDsNodes(logger, id, ids, nodes)

	servers := make([]hashic.Server, len(nodes)+1)
	servers[0] = hashic.Server{
		Suffrage: hashic.Voter,
		ID:       hashic.ServerID(selflis),
		Address:  hashic.ServerAddress(selflis),
	}
	for i, addr := range nodes {
		host, port, err := net.SplitHostPort(addr)
		if err != nil {
			logger.Fatal(err)
		}
		p, _ := strconv.Atoi(port)
		addr = host + ":" + strconv.Itoa(p-100)

		servers[i+1] = hashic.Server{
			Suffrage: hashic.Voter,
			ID:       hashic.ServerID(addr),
			Address:  hashic.ServerAddress(addr),
		}
	}

	addr, err := net.ResolveTCPAddr("tcp", selflis)
	if err != nil {
		logger.Fatal(err)
	}
	trans, err := hashic.NewTCPTransport(selflis, addr, len(ids), 10*time.Second, os.Stderr)
	if err != nil {
		logger.Fatal(err)
	}

	path := fmt.Sprintf("hashicorp%.2d.bolt", id)
	overwrite := !*recover
	// Check if file already exists.
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			// We don't need to overwrite a file that doesn't exist.
			overwrite = false
		} else {
			// If we are unable to verify the existence of the file,
			// there is probably a permission problem.
			logger.Fatal(err)
		}
	}
	if overwrite {
		if err := os.Remove(path); err != nil {
			logger.Fatal(err)
		}
	}
	logs, err := raftboltdb.NewBoltStore(path)
	if err != nil {
		logger.Fatal(err)
	}

	snaps := hashic.NewDiscardSnapshotStore()

	cfg := &hashic.Config{
		LocalID:            hashic.ServerID(selflis),
		ProtocolVersion:    hashic.ProtocolVersionMax,
		HeartbeatTimeout:   *electionTimeout,
		ElectionTimeout:    *electionTimeout,
		CommitTimeout:      *heartbeatTimeout,
		MaxAppendEntries:   int(*entriesPerMsg),
		ShutdownOnRemove:   true,
		TrailingLogs:       10240,
		SnapshotInterval:   120 * time.Hour,
		SnapshotThreshold:  math.MaxUint64,
		LeaderLeaseTimeout: *electionTimeout / 2,
	}

	node := hraft.NewRaft(logger, NewStore(), cfg, servers, trans, logs, logs, snaps)

	service := NewService(node)
	rkvpb.RegisterRKVServer(grpcServer, service)

	logrus.Fatal(grpcServer.Serve(lis))
}

func runetcd(
	logger logrus.FieldLogger,
	lis net.Listener, grpcServer *grpc.Server,
	id uint64, ids []uint64, nodes []string,
) {
	var selflis string
	selflis, ids, nodes = selfIDsNodes(logger, id, ids, nodes)

	peers := make([]etcdraft.Peer, len(nodes))

	for i, addr := range nodes {
		nid := ids[i]

		host, port, err := net.SplitHostPort(addr)
		if err != nil {
			logger.Fatal(err)
		}
		p, _ := strconv.Atoi(port)

		ur, err := url.Parse("http://" + addr)
		ur.Host = host + ":" + strconv.Itoa(p-100)
		if err != nil {
			logger.Fatal(err)
		}

		peers[i] = etcdraft.Peer{
			ID:      nid,
			Context: []byte(ur.String()),
		}
	}

	storage := etcdraft.NewMemoryStorage()
	node := etcd.NewRaft(
		logger,
		NewStore(),
		storage,
		&etcdraft.Config{
			ID:            id,
			ElectionTick:  int(*electionTimeout / *heartbeatTimeout),
			HeartbeatTick: 1,
			Storage:       storage,
			MaxSizePerMsg: *entriesPerMsg,
			// etcdserver says: Never overflow the rafthttp buffer,
			// which is 4096. We keep the same constant.
			MaxInflightMsgs: 4096 / 8,
			Logger:          logger,
		},
		peers,
		*heartbeatTimeout,
	)

	service := NewService(node)
	rkvpb.RegisterRKVServer(grpcServer, service)

	go func() {
		logrus.Fatal(grpcServer.Serve(lis))
	}()

	lishttp, err := net.Listen("tcp", selflis)

	if err != nil {
		logger.Fatal(err)
	}

	logger.Fatal(http.Serve(lishttp, node.Handler()))
}

func rungorums(
	logger logrus.FieldLogger,
	lis net.Listener, grpcServer *grpc.Server,
	id uint64, ids []uint64, nodes []string,
) {
	storage, err := raft.NewFileStorage(fmt.Sprintf("db%.2d.bolt", id), !*recover)

	if err != nil {
		logger.Fatal(err)
	}

	storageWithCache := raft.NewCacheStorage(storage, 20000)

	node := raftgorums.NewRaft(NewStore(), &raftgorums.Config{
		ID:                id,
		Servers:           nodes,
		InitialCluster:    ids,
		Batch:             *batch,
		Storage:           storageWithCache,
		ElectionTimeout:   *electionTimeout,
		HeartbeatTimeout:  *heartbeatTimeout,
		EntriesPerMsg:     *entriesPerMsg,
		CatchupMultiplier: *catchupMultiplier,
		Logger:            logger,
		MetricsEnabled:    true,
	})

	service := NewService(node)
	rkvpb.RegisterRKVServer(grpcServer, service)

	go func() {
		logrus.Fatal(grpcServer.Serve(lis))
	}()

	if *serverMetrics {
		go func() {
			http.Handle("/metrics", promhttp.Handler())
			logrus.Fatal(http.ListenAndServe(":5"+nodes[id-1][1:], nil))
		}()
	}

	logger.Fatal(node.Run(grpcServer))
}

func selfIDsNodes(logger logrus.FieldLogger, id uint64, ids []uint64, nodes []string) (string, []uint64, []string) {
	host, port, err := net.SplitHostPort(nodes[id-1])
	if err != nil {
		logger.Fatal(err)
	}
	p, _ := strconv.Atoi(port)
	selflis := host + ":" + strconv.Itoa(p-100)

	ids = append(ids[:id-1], ids[id:]...)
	nodes = append(nodes[:id-1], nodes[id:]...)

	return selflis, ids, nodes
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Google Inc.

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

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

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

import (
	"bufio"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/gorilla/websocket"
	"golang.org/x/exp/inotify"
)

var (
	wsupgrade = websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
)

const (
	maxLineLength = 1000
)

func tailHandler(w http.ResponseWriter, r *http.Request) {
	f, err := os.Open(*squidLog)
	if err != nil {
		log.Printf("File open failed: %v", err)
		http.Error(w, "File open failed", http.StatusInternalServerError)
		return
	}
	pos, err := f.Seek(-maxLineLength, 2)
	if err != nil {
		log.Printf("File seek failed: %v", err)
		http.Error(w, "File seek failed", http.StatusInternalServerError)
		return
	}

	//log.Printf("Creating websocket...")
	conn, err := wsupgrade.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("Upgrade failed: %v", err)
		http.Error(w, "Upgrade failed", http.StatusBadRequest)
		return
	}
	defer func() {
		//log.Printf("Closing websocket")
		conn.Close()
	}()
	changeTick := make(chan struct{}, 1)
	changeTick <- struct{}{}

	// Tick every time the file changes, or 10s if it doesn't.
	{
		w, err := inotify.NewWatcher()
		if err != nil {
			log.Fatalf("Couldn't create watcher: %v", err)
		}
		defer w.Close()
		if err := w.AddWatch(*squidLog, inotify.IN_MODIFY); err != nil {
			log.Fatalf("Couldn't add watcher on %s: %v", *squidLog, err)
		}
		go func() {
			defer close(changeTick)
			for {
				select {
				case _, ok := <-w.Event:
					//log.Printf("Event %v!", ok)
					if !ok {
						return
					}
				case <-time.After(10 * time.Second):
				}
				select {
				case changeTick <- struct{}{}:
				default:
				}
			}
		}()
	}

	sleep := false
	first := true
	done := websocketDone(conn)
	for {
		if sleep {
			<-changeTick
		}
		sleep = false
		if done() {
			return
		}

		if _, err := f.Seek(pos, 0); err != nil {
			log.Printf("File seek failed: %v", err)
			return
		}
		rd := bufio.NewReader(f)
		line, err := rd.ReadString('\n')
		if err == io.EOF {
			sleep = true
			if err := conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(10*time.Second)); err != nil {
				log.Printf("Ping failed: %v", err)
				return
			}
			continue
		}
		if err != nil {
			log.Printf("File read error: %v", err)
			return
		}
		if !strings.HasSuffix(line, "\n") {
			// Not a complete line yet.
			if _, err := f.Seek(pos, 0); err != nil {
				log.Printf("File seek failed: %v", err)
				return
			}
			sleep = true
			continue
		}
		pos += int64(len(line))

		e, err := parseLogEntry(line)
		if err == errSkip {
		} else if err != nil {
			if !first {
				log.Printf("Error parsing log line: %v", err)
			}
			first = false
			continue
		}
		data, err := json.Marshal(e)
		if err != nil {
			log.Printf("Failed to mashal tail: %v", err)
			return
		}
		if err := conn.WriteMessage(websocket.TextMessage, data); err != nil {
			//log.Printf("Message write failed: %v", err)
			return
		}
	}
}

func websocketDone(c *websocket.Conn) func() bool {
	done := make(chan struct{})
	go func() {
		defer close(done)
		typ, _, err := c.ReadMessage()
		if websocket.IsCloseError(err, websocket.CloseGoingAway) {
			return
		}
		if err != nil {
			log.Printf("Error reading message: %v %v", typ, err)
			return
		}
		if typ == websocket.CloseMessage {
			log.Printf("Socket closed using close message")
			return
		}
	}()
	return func() bool {
		select {
		case <-done:
			return true
		default:
			return false
		}
	}
}
<commit_msg>Switch from deleted inotify->fsnotify<commit_after>/*
Copyright 2016 Google Inc.

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

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

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

import (
	"bufio"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/gorilla/websocket"
)

var (
	wsupgrade = websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
)

const (
	maxLineLength = 1000
)

func tailHandler(w http.ResponseWriter, r *http.Request) {
	f, err := os.Open(*squidLog)
	if err != nil {
		log.Printf("File open failed: %v", err)
		http.Error(w, "File open failed", http.StatusInternalServerError)
		return
	}
	pos, err := f.Seek(-maxLineLength, 2)
	if err != nil {
		log.Printf("File seek failed: %v", err)
		http.Error(w, "File seek failed", http.StatusInternalServerError)
		return
	}

	//log.Printf("Creating websocket...")
	conn, err := wsupgrade.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("Upgrade failed: %v", err)
		http.Error(w, "Upgrade failed", http.StatusBadRequest)
		return
	}
	defer func() {
		//log.Printf("Closing websocket")
		conn.Close()
	}()
	changeTick := make(chan struct{}, 1)
	changeTick <- struct{}{}

	// Tick every time the file changes, or 10s if it doesn't.
	{
		w, err := fsnotify.NewWatcher()
		if err != nil {
			log.Fatalf("Couldn't create watcher: %v", err)
		}
		defer w.Close()
		if err := w.Add(*squidLog); err != nil {
			log.Fatalf("Couldn't add watcher on %s: %v", *squidLog, err)
		}
		go func() {
			defer close(changeTick)
			for {
				select {
				case _, ok := <-w.Events:
					//log.Printf("Event %v!", ok)
					if !ok {
						return
					}
				case <-time.After(10 * time.Second):
				}
				select {
				case changeTick <- struct{}{}:
				default:
				}
			}
		}()
	}

	sleep := false
	first := true
	done := websocketDone(conn)
	for {
		if sleep {
			<-changeTick
		}
		sleep = false
		if done() {
			return
		}

		if _, err := f.Seek(pos, 0); err != nil {
			log.Printf("File seek failed: %v", err)
			return
		}
		rd := bufio.NewReader(f)
		line, err := rd.ReadString('\n')
		if err == io.EOF {
			sleep = true
			if err := conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(10*time.Second)); err != nil {
				log.Printf("Ping failed: %v", err)
				return
			}
			continue
		}
		if err != nil {
			log.Printf("File read error: %v", err)
			return
		}
		if !strings.HasSuffix(line, "\n") {
			// Not a complete line yet.
			if _, err := f.Seek(pos, 0); err != nil {
				log.Printf("File seek failed: %v", err)
				return
			}
			sleep = true
			continue
		}
		pos += int64(len(line))

		e, err := parseLogEntry(line)
		if err == errSkip {
		} else if err != nil {
			if !first {
				log.Printf("Error parsing log line: %v", err)
			}
			first = false
			continue
		}
		data, err := json.Marshal(e)
		if err != nil {
			log.Printf("Failed to mashal tail: %v", err)
			return
		}
		if err := conn.WriteMessage(websocket.TextMessage, data); err != nil {
			//log.Printf("Message write failed: %v", err)
			return
		}
	}
}

func websocketDone(c *websocket.Conn) func() bool {
	done := make(chan struct{})
	go func() {
		defer close(done)
		typ, _, err := c.ReadMessage()
		if websocket.IsCloseError(err, websocket.CloseGoingAway) {
			return
		}
		if err != nil {
			log.Printf("Error reading message: %v %v", typ, err)
			return
		}
		if typ == websocket.CloseMessage {
			log.Printf("Socket closed using close message")
			return
		}
	}()
	return func() bool {
		select {
		case <-done:
			return true
		default:
			return false
		}
	}
}
<|endoftext|>


<commit_before>package user

import "github.com/spf13/cobra"

var EditUserCmd = &cobra.Command{}<commit_msg>Add ability to edit users<commit_after>package user

import (
	"fmt"
	"github.com/AlecAivazis/survey/v2"
	"github.com/jinzhu/gorm"
	"github.com/pufferpanel/pufferpanel/v2"
	"github.com/pufferpanel/pufferpanel/v2/database"
	"github.com/pufferpanel/pufferpanel/v2/services"
	"github.com/spf13/cobra"
)

var EditUserCmd = &cobra.Command{
	Use:   "edit",
	Short: "Edit a user",
	Run:   editUser,
	Args:  cobra.NoArgs,
}

func editUser(cmd *cobra.Command, args []string) {
	err := pufferpanel.LoadConfig()
	if err != nil {
		fmt.Printf("Error loading config: %s", err.Error())
		return
	}

	db, err := database.GetConnection()
	if err != nil {
		fmt.Printf("Error connecting to database: %s", err.Error())
		return
	}
	defer database.Close()

	var username string
	_ = survey.AskOne(&survey.Input{
		Message: "Username:",
	}, &username, survey.WithValidator(survey.Required))

	us := &services.User{DB: db}

	user, err := us.Get(username)
	if err != nil && gorm.IsRecordNotFoundError(err) {
		fmt.Printf("No user with username '%s'", username)
		return
	} else if err != nil {
		fmt.Printf("Error getting user: %s", err.Error())
		return
	}

	action := ""
	_ = survey.AskOne(&survey.Select{
		Message: "Select option to edit",
		Options: []string{"Username", "Email", "Password", "Change Admin Status"},
	}, &action)

	switch action {
	case "Username":
		{
			prompt := ""
			_ = survey.AskOne(&survey.Input{
				Message: "New Username:",
			}, &prompt, survey.WithValidator(survey.Required))
			user.Username = prompt

			err = us.Update(user)
			if err != nil {
				fmt.Printf("Error updating username: %s", err.Error())
			}
		}
	case "Email":
		{
			prompt := ""
			_ = survey.AskOne(&survey.Input{
				Message: "New Email:",
			}, &prompt, survey.WithValidator(survey.Required))
			user.Email = prompt

			err = us.Update(user)
			if err != nil {
				fmt.Printf("Error updating email: %s", err.Error())
			}
		}
	case "Password":
		{
			prompt := ""
			_ = survey.AskOne(&survey.Password{
				Message: "New Password:",
			}, &prompt, survey.WithValidator(validatePassword))

			err = user.SetPassword(prompt)
			if err != nil {
				fmt.Printf("Error updating password: %s", err.Error())
			}

			err = us.Update(user)
			if err != nil {
				fmt.Printf("Error updating password: %s", err.Error())
			}
		}
	case "Change Admin Status":
		{
			prompt := false
			_ = survey.AskOne(&survey.Confirm{
				Message: "Set Admin Status: ",
			}, &prompt)

			ps := &services.Permission{DB: db}
			perms, err := ps.GetForUserAndServer(user.ID, nil)
			if err != nil {
				fmt.Printf("Error updating permissions: %s", err.Error())
				return
			}

			perms.Admin = prompt

			err = ps.UpdatePermissions(perms)
			if err != nil {
				fmt.Printf("Error updating password: %s", err.Error())
			}
		}
	}
}
<|endoftext|>


<commit_before>// zeno is the executable for the ZenoDB database, and can run as a standalone
// server, as a cluster leader or as a cluster follower.
package main

import (
	"github.com/getlantern/golog"
	"github.com/getlantern/zenodb/cmd"
	"github.com/getlantern/zenodb/server"
	"github.com/vharitonsky/iniflags"
)

var (
	log = golog.LoggerFor("zeno")
)

func main() {
	srv := &server.Server{}
	srv.ConfigureFlags()

	iniflags.Parse()

	srv.Schema = *cmd.Schema
	srv.AliasesFile = *cmd.AliasesFile
	srv.EnableGeo = *cmd.EnableGeo
	srv.RedisCacheSize = *cmd.RedisCacheSize

	cmd.StartPprof()

	srv.HandleShutdownSignal()
	_, run, err := srv.Prepare()
	if err != nil {
		log.Fatal(err)
	}
	if err := run(); err != nil {
		log.Fatal(err)
	}
	log.Debug("Done")
}
<commit_msg>Allowing unknown flags<commit_after>// zeno is the executable for the ZenoDB database, and can run as a standalone
// server, as a cluster leader or as a cluster follower.
package main

import (
	"github.com/getlantern/golog"
	"github.com/getlantern/zenodb/cmd"
	"github.com/getlantern/zenodb/server"
	"github.com/vharitonsky/iniflags"
)

var (
	log = golog.LoggerFor("zeno")
)

func main() {
	srv := &server.Server{}
	srv.ConfigureFlags()
	iniflags.SetAllowUnknownFlags(true)
	iniflags.Parse()

	srv.Schema = *cmd.Schema
	srv.AliasesFile = *cmd.AliasesFile
	srv.EnableGeo = *cmd.EnableGeo
	srv.RedisCacheSize = *cmd.RedisCacheSize

	cmd.StartPprof()

	srv.HandleShutdownSignal()
	_, run, err := srv.Prepare()
	if err != nil {
		log.Fatal(err)
	}
	if err := run(); err != nil {
		log.Fatal(err)
	}
	log.Debug("Done")
}
<|endoftext|>


<commit_before>package jobsupervisor

import (
	"encoding/json"

	boshalert "github.com/cloudfoundry/bosh-agent/agent/alert"
	boshhandler "github.com/cloudfoundry/bosh-agent/handler"
	bosherror "github.com/cloudfoundry/bosh-agent/internal/github.com/cloudfoundry/bosh-utils/errors"
)

type dummyNatsJobSupervisor struct {
	mbusHandler       boshhandler.Handler
	status            string
	jobFailureHandler JobFailureHandler
}

func NewDummyNatsJobSupervisor(mbusHandler boshhandler.Handler) JobSupervisor {
	return &dummyNatsJobSupervisor{
		mbusHandler: mbusHandler,
		status:      "running",
	}
}

func (d *dummyNatsJobSupervisor) Reload() error {
	return nil
}

func (d *dummyNatsJobSupervisor) AddJob(jobName string, jobIndex int, configPath string) error {
	return nil
}

func (d *dummyNatsJobSupervisor) Start() error {
	if d.status == "fail_task" {
		return bosherror.Error("fake-task-fail-error")
	}
	return nil
}

func (d *dummyNatsJobSupervisor) Stop() error {
	return nil
}

func (d *dummyNatsJobSupervisor) Unmonitor() error {
	return nil
}

func (d *dummyNatsJobSupervisor) RemoveAllJobs() error {
	return nil
}

func (d *dummyNatsJobSupervisor) Status() string {
	return d.status
}

func (d *dummyNatsJobSupervisor) MonitorJobFailures(handler JobFailureHandler) error {
	d.jobFailureHandler = handler

	d.mbusHandler.RegisterAdditionalFunc(d.statusHandler)

	return nil
}

func (d *dummyNatsJobSupervisor) statusHandler(req boshhandler.Request) boshhandler.Response {
	switch req.Method {
	case "set_dummy_status":
		// Do not unmarshal message until determining its method
		var body map[string]string

		err := json.Unmarshal(req.GetPayload(), &body)
		if err != nil {
			return boshhandler.NewExceptionResponse(err)
		}

		d.status = body["status"]

		if d.status == "failing" && d.jobFailureHandler != nil {
			_ = d.jobFailureHandler(boshalert.MonitAlert{
				ID:          "fake-monit-alert",
				Service:     "fake-monit-service",
				Event:       "failing",
				Action:      "start",
				Date:        "Sun, 22 May 2011 20:07:41 +0500",
				Description: "fake-monit-description",
			})
		}

		return boshhandler.NewValueResponse("ok")

	case "set_task_fail":
		// Do not unmarshal message until determining its method
		var body map[string]string

		err := json.Unmarshal(req.GetPayload(), &body)
		if err != nil {
			return boshhandler.NewExceptionResponse(err)
		}

		d.status = body["status"]

		return nil
	default:
		return nil
	}
}
<commit_msg>Dummy job supervisor handles stop request<commit_after>package jobsupervisor

import (
	"encoding/json"

	boshalert "github.com/cloudfoundry/bosh-agent/agent/alert"
	boshhandler "github.com/cloudfoundry/bosh-agent/handler"
	bosherror "github.com/cloudfoundry/bosh-agent/internal/github.com/cloudfoundry/bosh-utils/errors"
)

type dummyNatsJobSupervisor struct {
	mbusHandler       boshhandler.Handler
	status            string
	jobFailureHandler JobFailureHandler
}

func NewDummyNatsJobSupervisor(mbusHandler boshhandler.Handler) JobSupervisor {
	return &dummyNatsJobSupervisor{
		mbusHandler: mbusHandler,
		status:      "running",
	}
}

func (d *dummyNatsJobSupervisor) Reload() error {
	return nil
}

func (d *dummyNatsJobSupervisor) AddJob(jobName string, jobIndex int, configPath string) error {
	return nil
}

func (d *dummyNatsJobSupervisor) Start() error {
	if d.status == "fail_task" {
		return bosherror.Error("fake-task-fail-error")
	}
	if d.status != "failing" {
		d.status = "running"
	}
	return nil
}

func (d *dummyNatsJobSupervisor) Stop() error {
	d.status = "stopped"
	return nil
}

func (d *dummyNatsJobSupervisor) Unmonitor() error {
	return nil
}

func (d *dummyNatsJobSupervisor) RemoveAllJobs() error {
	return nil
}

func (d *dummyNatsJobSupervisor) Status() string {
	return d.status
}

func (d *dummyNatsJobSupervisor) MonitorJobFailures(handler JobFailureHandler) error {
	d.jobFailureHandler = handler

	d.mbusHandler.RegisterAdditionalFunc(d.statusHandler)

	return nil
}

func (d *dummyNatsJobSupervisor) statusHandler(req boshhandler.Request) boshhandler.Response {
	switch req.Method {
	case "set_dummy_status":
		// Do not unmarshal message until determining its method
		var body map[string]string

		err := json.Unmarshal(req.GetPayload(), &body)
		if err != nil {
			return boshhandler.NewExceptionResponse(err)
		}

		d.status = body["status"]

		if d.status == "failing" && d.jobFailureHandler != nil {
			_ = d.jobFailureHandler(boshalert.MonitAlert{
				ID:          "fake-monit-alert",
				Service:     "fake-monit-service",
				Event:       "failing",
				Action:      "start",
				Date:        "Sun, 22 May 2011 20:07:41 +0500",
				Description: "fake-monit-description",
			})
		}

		return boshhandler.NewValueResponse("ok")

	case "set_task_fail":
		// Do not unmarshal message until determining its method
		var body map[string]string

		err := json.Unmarshal(req.GetPayload(), &body)
		if err != nil {
			return boshhandler.NewExceptionResponse(err)
		}

		d.status = body["status"]

		return nil
	default:
		return nil
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Davis Webb
// Copyright 2015 Luke Shumaker

package store

import (
	"github.com/jinzhu/gorm"
)

type Medium struct {
	Id string
}

func (o Medium) dbSchema(db *gorm.DB) error {
	return db.CreateTable(&o).Error
}

func (o Medium) dbSeed(db *gorm.DB) error {
	return db.Create(&Medium{"email"}).Error
}
<commit_msg>create the twilio medium; needed by group seed if foreign keys are enforced<commit_after>// Copyright 2015 Davis Webb
// Copyright 2015 Luke Shumaker

package store

import (
	"github.com/jinzhu/gorm"
)

type Medium struct {
	Id string
}

func (o Medium) dbSchema(db *gorm.DB) error {
	return db.CreateTable(&o).Error
}

func (o Medium) dbSeed(db *gorm.DB) error {
	errs := []error{}
	errHelper(&errs, db.Create(&Medium{"email"}).Error)
	errHelper(&errs, db.Create(&Medium{"twilio"}).Error)
	return errorList(errs)
}
<|endoftext|>


<commit_before>package main

import (
    "github.com/go-martini/martini"
    "github.com/martini-contrib/render"
    "os"
    "os/exec"
    "log"
    "net/http"
)

func main() {
    motionPidFile := "/var/run/motion/motion.pid"
    logFile := "/tmp/motion-control.log"
    f, err := os.OpenFile(logFile, os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
    if err != nil {
        log.Fatalf("error opening file: %v", err)
    }
    defer f.Close()
    m := martini.Classic()
    m.Map(log.New(f, "[martini]", log.LstdFlags))
    m.Use(render.Renderer())
    m.Get("/", func(r render.Render) {
        if _, err := os.Stat(motionPidFile); err == nil {
            r.HTML(200, "status", map[string]interface{}{"motionStatus": "running", "availableAction": "stop"})
        } else {
            r.HTML(200, "status", map[string]interface{}{"motionStatus": "not running", "availableAction": "start"})
        }
    })
    m.Get("/stop", func(res http.ResponseWriter, req *http.Request) {
        cmd := exec.Command("service", "motion", "stop")
        err := cmd.Run()
        if err != nil {
            log.Println(err)
        } else {
            log.Println("Stop motion from motion-control")
        }
        http.Redirect(res, req, "/", http.StatusSeeOther)
    })
    m.Get("/start", func(res http.ResponseWriter, req *http.Request) {
        cmd := exec.Command("service", "motion", "start")
        err := cmd.Run()
        if err != nil {
            log.Println(err)
        } else {
            log.Println("Stop motion from motion-control")
        }
        http.Redirect(res, req, "/", http.StatusSeeOther)
    })
    m.Run()
}

<commit_msg>Fix no log issue, fix typo.<commit_after>package main

import (
    "github.com/go-martini/martini"
    "github.com/martini-contrib/render"
    "os"
    "os/exec"
    "log"
    "net/http"
)

func main() {
    motionPidFile := "/var/run/motion/motion.pid"
    logFile := "/tmp/motion-control.log"
    f, err := os.OpenFile(logFile, os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
    if err != nil {
        log.Fatalf("error opening file: %v", err)
    }
    defer f.Close()
    logger := log.New(f, "[martini]", log.LstdFlags)
    m := martini.Classic()
    m.Map(logger)
    m.Use(render.Renderer())
    m.Get("/", func(r render.Render) {
        if _, err := os.Stat(motionPidFile); err == nil {
            r.HTML(200, "status", map[string]interface{}{"motionStatus": "running", "availableAction": "stop"})
        } else {
            r.HTML(200, "status", map[string]interface{}{"motionStatus": "not running", "availableAction": "start"})
        }
    })
    m.Get("/stop", func(res http.ResponseWriter, req *http.Request) {
        cmd := exec.Command("service", "motion", "stop")
        err := cmd.Run()
        if err != nil {
            logger.Println(err)
        } else {
            logger.Println("Motion has been stopped")
        }
        http.Redirect(res, req, "/", http.StatusSeeOther)
    })
    m.Get("/start", func(res http.ResponseWriter, req *http.Request) {
        cmd := exec.Command("service", "motion", "start")
        err := cmd.Run()
        if err != nil {
            logger.Println(err)
        } else {
            logger.Println("Motion has been started")
        }
        http.Redirect(res, req, "/", http.StatusSeeOther)
    })
    m.Run()
}

<|endoftext|>


<commit_before>package utils

// transfer2go/utils - Go utilities for transfer2go
//
// Copyright (c) 2017 - Valentin Kuznetsov <vkuznet@gmail.com>

import (
	"bytes"
	"crypto/sha256"
	"crypto/tls"
	"encoding/hex"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"os/user"
	"regexp"
	"runtime"
	"time"

	"github.com/vkuznet/x509proxy"
)

// VERBOSE variable control verbosity level of client's utilities
var VERBOSE int

// client X509 certificates
func tlsCerts() ([]tls.Certificate, error) {
	uproxy := os.Getenv("X509_USER_PROXY")
	uckey := os.Getenv("X509_USER_KEY")
	ucert := os.Getenv("X509_USER_CERT")

	// check if /tmp/x509up_u$UID exists, if so setup X509_USER_PROXY env
	u, err := user.Current()
	if err == nil {
		fname := fmt.Sprintf("/tmp/x509up_u%s", u.Uid)
		if _, err := os.Stat(fname); err == nil {
			uproxy = fname
		}
	}
	if VERBOSE > 0 {
		log.Println("uproxy", uproxy)
		log.Println("uckey", uckey)
		log.Println("ucert", ucert)
	}

	if uproxy == "" && uckey == "" { // user doesn't have neither proxy or user certs
		return nil, nil
	}
	if uproxy != "" {
		// use local implementation of LoadX409KeyPair instead of tls one
		x509cert, err := x509proxy.LoadX509Proxy(uproxy)
		if err != nil {
			return nil, fmt.Errorf("failed to parse proxy X509 proxy set by X509_USER_PROXY: %v", err)
		}
		return []tls.Certificate{x509cert}, nil
	}
	x509cert, err := tls.LoadX509KeyPair(ucert, uckey)
	if err != nil {
		return nil, fmt.Errorf("failed to parse user X509 certificate: %v", err)
	}
	return []tls.Certificate{x509cert}, nil
}

// HttpClient is HTTP client for urlfetch server
func HttpClient() (client *http.Client) {
	// get X509 certs
	certs, err := tlsCerts()
	if err != nil {
		panic(err.Error())
	}
	if len(certs) == 0 {
		client = &http.Client{}
		return
	}
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{Certificates: certs,
			InsecureSkipVerify: true},
	}
	client = &http.Client{Transport: tr}
	return
}

// create global HTTP client and re-use it through the code
var client = HttpClient()

// ResponseType structure is what we expect to get for our URL call.
// It contains a request URL, the data chunk and possible error from remote
type ResponseType struct {
	Url        string // response url
	Data       []byte // response data, i.e. what we got with Body of the response
	Error      error  // http error, a non-2xx return code is not an error
	Status     string // http status string
	StatusCode int    // http status code
}

func (r *ResponseType) String() string {
	return fmt.Sprintf("<Response: url=%s, data=%s, error=%v>", r.Url, string(r.Data), r.Error)
}

// UrlRequest structure holds details about url request's attributes
type UrlRequest struct {
	rurl string
	args string
	out  chan<- ResponseType
	ts   int64
}

// FetchResponse fetches data for provided URL, args is a json dump of arguments
func FetchResponse(rurl string, args []byte) ResponseType {
	startTime := time.Now()
	var response ResponseType
	response.Url = rurl
	if validateUrl(rurl) == false {
		response.Error = errors.New("Invalid URL")
		return response
	}
	var req *http.Request
	var e error
	if len(args) > 0 {
		req, e = http.NewRequest("POST", rurl, bytes.NewBuffer(args))
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, e = http.NewRequest("GET", rurl, nil)
		if e != nil {
			log.Println("Unable to make GET request", e)
		}
		req.Header.Add("Accept", "*/*")
	}
	if VERBOSE > 1 {
		dump1, err1 := httputil.DumpRequestOut(req, true)
		log.Println("HTTP request", req, string(dump1), err1)
	}
	resp, err := client.Do(req)
	response.Status = resp.Status
	response.StatusCode = resp.StatusCode
	if VERBOSE > 0 {
		if len(args) > 0 {
			log.Println("HTTP POST", rurl, string(args), err, time.Now().Sub(startTime))
		} else {
			log.Println("HTTP GET", rurl, string(args), err, time.Now().Sub(startTime))
		}
	}
	if VERBOSE > 1 {
		dump2, err2 := httputil.DumpResponse(resp, true)
		log.Println("HTTP response", string(dump2), err2)
	}
	if err != nil {
		response.Error = err
		return response
	}
	defer resp.Body.Close()
	response.Data, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		response.Error = err
	}
	return response
}

// Fetch data for provided URL and redirect results to given channel
func Fetch(rurl string, args []byte, ch chan<- ResponseType) {
	urlRetry := 3
	var resp, r ResponseType
	resp = FetchResponse(rurl, args)
	if resp.Error != nil {
		log.Println("TRANSFER2GO WARNING, fail to fetch data", rurl, "error", resp.Error)
		for i := 1; i <= urlRetry; i++ {
			sleep := time.Duration(i) * time.Second
			time.Sleep(sleep)
			r = FetchResponse(rurl, args)
			if r.Error == nil {
				break
			}
			log.Println("TRANSFER2GO WARNING", rurl, "retry", i, "error", r.Error)
		}
		resp = r
	}
	if resp.Error != nil {
		log.Println("TRANSFER2GO ERROR, fail to fetch data", rurl, "retries", urlRetry, "error", resp.Error)
	}
	ch <- resp
}

// Helper function which validates given URL
func validateUrl(rurl string) bool {
	if len(rurl) > 0 {
		pat := "(https|http)://[-A-Za-z0-9_+&@#/%?=~_|!:,.;]*[-A-Za-z0-9+&@#/%=~_|]"
		matched, err := regexp.MatchString(pat, rurl)
		if err == nil {
			if matched == true {
				return true
			}
		}
		log.Println("ERROR invalid URL:", rurl)
	}
	return false
}

// Response represents final response in a form of JSON structure
// we use custorm representation
func Response(rurl string, data []byte) []byte {
	b := []byte(`{"url":`)
	u := []byte(rurl)
	c := []byte(",")
	d := []byte(`"data":`)
	e := []byte(`}`)
	a := [][]byte{b, u, c, d, data, e}
	s := []byte(" ")
	r := bytes.Join(a, s)
	return r

}

// Hash implements hash function for data, it returns a hash and number of bytes
func Hash(data []byte) (string, int64) {
	hasher := sha256.New()
	b, e := hasher.Write(data)
	if e != nil {
		log.Println("ERROR, Unable to write chunk of data via hasher.Write", e)
	}
	return hex.EncodeToString(hasher.Sum(nil)), int64(b)
}

// Stack helper function to return Stack
func Stack() string {
	trace := make([]byte, 2048)
	count := runtime.Stack(trace, false)
	return fmt.Sprintf("\nStack of %d bytes: %s\n", count, trace)
}

// ErrPropagate error helper function which can be used in defer ErrPropagate()
func ErrPropagate(api string) {
	if err := recover(); err != nil {
		log.Println("DAS ERROR", api, "error", err, Stack())
		panic(fmt.Sprintf("%s:%s", api, err))
	}
}

// ErrPropagate2Channel error helper function which can be used in goroutines as
// ch := make(chan interface{})
// go func() {
//    defer ErrPropagate2Channel(api, ch)
//    someFunction()
// }()
func ErrPropagate2Channel(api string, ch chan interface{}) {
	if err := recover(); err != nil {
		log.Println("DAS ERROR", api, "error", err, Stack())
		ch <- fmt.Sprintf("%s:%s", api, err)
	}
}

// GoDeferFunc helper function to run any given function in defered go routine
func GoDeferFunc(api string, f func()) {
	ch := make(chan interface{})
	go func() {
		defer ErrPropagate2Channel(api, ch)
		f()
		ch <- "ok" // send to channel that we can read it later in case of success of f()
	}()
	err := <-ch
	if err != nil && err != "ok" {
		panic(err)
	}
}

// FindInList helper function to find item in a list
func FindInList(a string, arr []string) bool {
	for _, e := range arr {
		if e == a {
			return true
		}
	}
	return false
}

// InList helper function to check item in a list
func InList(a string, list []string) bool {
	check := 0
	for _, b := range list {
		if b == a {
			check += 1
		}
	}
	if check != 0 {
		return true
	}
	return false
}

// MapKeys helper function to return keys from a map
func MapKeys(rec map[string]interface{}) []string {
	keys := make([]string, 0, len(rec))
	for k := range rec {
		keys = append(keys, k)
	}
	return keys
}

// List2Set helper function to convert input list into set
func List2Set(arr []string) []string {
	var out []string
	for _, key := range arr {
		if !InList(key, out) {
			out = append(out, key)
		}
	}
	return out
}

// HostIP provides a list of host IPs
func HostIP() []string {
	var out []string
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		log.Println("ERROR unable to resolve net.InterfaceAddrs", err)
	}
	for _, addr := range addrs {
		// check the address type and if it is not a loopback the display it
		if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				out = append(out, ipnet.IP.String())
			}
			if ipnet.IP.To16() != nil {
				out = append(out, ipnet.IP.String())
			}
		}
	}
	return List2Set(out)
}
<commit_msg>Re-arrange utils functions<commit_after>package utils

// transfer2go/utils - Go utilities for transfer2go
//
// Copyright (c) 2017 - Valentin Kuznetsov <vkuznet@gmail.com>

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"os"
	"os/user"
	"regexp"
	"time"

	"github.com/vkuznet/x509proxy"
)

// VERBOSE variable control verbosity level of client's utilities
var VERBOSE int

// client X509 certificates
func tlsCerts() ([]tls.Certificate, error) {
	uproxy := os.Getenv("X509_USER_PROXY")
	uckey := os.Getenv("X509_USER_KEY")
	ucert := os.Getenv("X509_USER_CERT")

	// check if /tmp/x509up_u$UID exists, if so setup X509_USER_PROXY env
	u, err := user.Current()
	if err == nil {
		fname := fmt.Sprintf("/tmp/x509up_u%s", u.Uid)
		if _, err := os.Stat(fname); err == nil {
			uproxy = fname
		}
	}
	if VERBOSE > 0 {
		log.Println("uproxy", uproxy)
		log.Println("uckey", uckey)
		log.Println("ucert", ucert)
	}

	if uproxy == "" && uckey == "" { // user doesn't have neither proxy or user certs
		return nil, nil
	}
	if uproxy != "" {
		// use local implementation of LoadX409KeyPair instead of tls one
		x509cert, err := x509proxy.LoadX509Proxy(uproxy)
		if err != nil {
			return nil, fmt.Errorf("failed to parse proxy X509 proxy set by X509_USER_PROXY: %v", err)
		}
		return []tls.Certificate{x509cert}, nil
	}
	x509cert, err := tls.LoadX509KeyPair(ucert, uckey)
	if err != nil {
		return nil, fmt.Errorf("failed to parse user X509 certificate: %v", err)
	}
	return []tls.Certificate{x509cert}, nil
}

// HttpClient is HTTP client for urlfetch server
func HttpClient() (client *http.Client) {
	// get X509 certs
	certs, err := tlsCerts()
	if err != nil {
		panic(err.Error())
	}
	if len(certs) == 0 {
		client = &http.Client{}
		return
	}
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{Certificates: certs,
			InsecureSkipVerify: true},
	}
	client = &http.Client{Transport: tr}
	return
}

// create global HTTP client and re-use it through the code
var client = HttpClient()

// ResponseType structure is what we expect to get for our URL call.
// It contains a request URL, the data chunk and possible error from remote
type ResponseType struct {
	Url        string // response url
	Data       []byte // response data, i.e. what we got with Body of the response
	Error      error  // http error, a non-2xx return code is not an error
	Status     string // http status string
	StatusCode int    // http status code
}

func (r *ResponseType) String() string {
	return fmt.Sprintf("<Response: url=%s, data=%s, error=%v>", r.Url, string(r.Data), r.Error)
}

// UrlRequest structure holds details about url request's attributes
type UrlRequest struct {
	rurl string
	args string
	out  chan<- ResponseType
	ts   int64
}

// FetchResponse fetches data for provided URL, args is a json dump of arguments
func FetchResponse(rurl string, args []byte) ResponseType {
	startTime := time.Now()
	var response ResponseType
	response.Url = rurl
	if validateUrl(rurl) == false {
		response.Error = errors.New("Invalid URL")
		return response
	}
	var req *http.Request
	var e error
	if len(args) > 0 {
		req, e = http.NewRequest("POST", rurl, bytes.NewBuffer(args))
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, e = http.NewRequest("GET", rurl, nil)
		if e != nil {
			log.Println("Unable to make GET request", e)
		}
		req.Header.Add("Accept", "*/*")
	}
	if VERBOSE > 1 {
		dump1, err1 := httputil.DumpRequestOut(req, true)
		log.Println("HTTP request", req, string(dump1), err1)
	}
	resp, err := client.Do(req)
	response.Status = resp.Status
	response.StatusCode = resp.StatusCode
	if VERBOSE > 0 {
		if len(args) > 0 {
			log.Println("HTTP POST", rurl, string(args), err, time.Now().Sub(startTime))
		} else {
			log.Println("HTTP GET", rurl, string(args), err, time.Now().Sub(startTime))
		}
	}
	if VERBOSE > 1 {
		dump2, err2 := httputil.DumpResponse(resp, true)
		log.Println("HTTP response", string(dump2), err2)
	}
	if err != nil {
		response.Error = err
		return response
	}
	defer resp.Body.Close()
	response.Data, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		response.Error = err
	}
	return response
}

// Fetch data for provided URL and redirect results to given channel
func Fetch(rurl string, args []byte, ch chan<- ResponseType) {
	urlRetry := 3
	var resp, r ResponseType
	resp = FetchResponse(rurl, args)
	if resp.Error != nil {
		log.Println("TRANSFER2GO WARNING, fail to fetch data", rurl, "error", resp.Error)
		for i := 1; i <= urlRetry; i++ {
			sleep := time.Duration(i) * time.Second
			time.Sleep(sleep)
			r = FetchResponse(rurl, args)
			if r.Error == nil {
				break
			}
			log.Println("TRANSFER2GO WARNING", rurl, "retry", i, "error", r.Error)
		}
		resp = r
	}
	if resp.Error != nil {
		log.Println("TRANSFER2GO ERROR, fail to fetch data", rurl, "retries", urlRetry, "error", resp.Error)
	}
	ch <- resp
}

// Helper function which validates given URL
func validateUrl(rurl string) bool {
	if len(rurl) > 0 {
		pat := "(https|http)://[-A-Za-z0-9_+&@#/%?=~_|!:,.;]*[-A-Za-z0-9+&@#/%=~_|]"
		matched, err := regexp.MatchString(pat, rurl)
		if err == nil {
			if matched == true {
				return true
			}
		}
		log.Println("ERROR invalid URL:", rurl)
	}
	return false
}

// Response represents final response in a form of JSON structure
// we use custorm representation
func Response(rurl string, data []byte) []byte {
	b := []byte(`{"url":`)
	u := []byte(rurl)
	c := []byte(",")
	d := []byte(`"data":`)
	e := []byte(`}`)
	a := [][]byte{b, u, c, d, data, e}
	s := []byte(" ")
	r := bytes.Join(a, s)
	return r

}
<|endoftext|>


<commit_before>package utils

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net"
	"strconv"

	"github.com/awnumar/memguard"
	"github.com/stephane-martin/skewer/utils/sbox"
	"golang.org/x/crypto/ed25519"
)

var NOW []byte = []byte("now")
var SP []byte = []byte(" ")

type SigWriter struct {
	dest io.Writer
	key  *memguard.LockedBuffer
}

func NewSignatureWriter(dest io.Writer, privsignkey *memguard.LockedBuffer) *SigWriter {
	return &SigWriter{dest: dest, key: privsignkey}
}

func (s *SigWriter) Write(p []byte) (n int, err error) {
	if len(p) == 0 {
		return 0, nil
	}
	signature := ed25519.Sign(s.key.Buffer(), p)
	err = ChainWrites(
		s.dest,
		[]byte(fmt.Sprintf("%010d %010d ", len(p), len(signature))),
		p,
		signature,
	)
	if err == nil {
		return len(p), nil
	}
	return 0, err
}

func (s *SigWriter) WriteWithHeader(header []byte, message []byte) (err error) {
	fullmessage := make([]byte, 0, len(header)+len(message)+1)
	fullmessage = append(fullmessage, header...)
	fullmessage = append(fullmessage, SP...)
	fullmessage = append(fullmessage, message...)
	_, err = s.Write(fullmessage)
	return err
}

// MakeSignSplit returns a split function that checks for message signatures.
func MakeSignSplit(signpubkey *memguard.LockedBuffer) (signSplit bufio.SplitFunc) {

	signSplit = func(data []byte, atEOF bool) (advance int, token []byte, eoferr error) {
		if atEOF {
			eoferr = io.EOF
		}
		if len(data) < 22 {
			return 0, nil, eoferr
		}
		if data[10] != byte(' ') || data[21] != byte(' ') {
			return 0, nil, fmt.Errorf("Wrong sign format, 11th or 22th char is not space: '%s'", string(data))
		}

		var i int
		for i = 0; i < 10; i++ {
			if data[i] < byte('0') || data[i] > byte('9') {
				return 0, nil, fmt.Errorf("Wrong sign format")
			}
		}
		for i = 11; i < 21; i++ {
			if data[i] < byte('0') || data[i] > byte('9') {
				return 0, nil, fmt.Errorf("Wrong sign format")
			}
		}
		fullmessagelen, err := strconv.Atoi(string(data[:10]))
		if err != nil {
			return 0, nil, err
		}
		signaturelen, err := strconv.Atoi(string(data[11:21]))
		if err != nil {
			return 0, nil, err
		}

		advance = 22 + fullmessagelen + signaturelen
		if len(data) < advance {
			return 0, nil, eoferr
		}
		fullmessage := data[22 : 22+fullmessagelen]
		signature := data[22+fullmessagelen : 22+fullmessagelen+signaturelen]
		if ed25519.Verify(signpubkey.Buffer(), fullmessage, signature) {
			return advance, fullmessage, nil
		}
		return 0, nil, fmt.Errorf("Wrong signature")
	}
	return signSplit
}

type EncryptWriter struct {
	dest io.Writer
	key  *memguard.LockedBuffer
}

func NewEncryptWriter(dest io.Writer, encryptkey *memguard.LockedBuffer) *EncryptWriter {
	return &EncryptWriter{dest: dest, key: encryptkey}
}

func (s *EncryptWriter) WriteMsgUnix(b []byte, oob []byte, addr *net.UnixAddr) (n int, oobn int, err error) {
	if len(b) == 0 {
		return 0, 0, nil
	}
	if conn, ok := s.dest.(*net.UnixConn); ok {
		var enc []byte
		if s.key == nil {
			enc = b
		} else {
			enc, err = sbox.Encrypt(b, s.key)
			if err != nil {
				return 0, 0, err
			}
		}
		buf := bytes.NewBuffer(nil)
		buf.Write([]byte(fmt.Sprintf("%010d ", len(enc))))
		buf.Write(enc)
		return conn.WriteMsgUnix(buf.Bytes(), oob, addr)
	}
	return 0, 0, nil
}

func (s *EncryptWriter) Write(p []byte) (n int, err error) {
	if len(p) == 0 {
		return 0, nil
	}
	var enc []byte
	if s.key == nil {
		enc = p
	} else {
		enc, err = sbox.Encrypt(p, s.key)
		if err != nil {
			return 0, err
		}
	}
	// TODO: make it atomic
	err = ChainWrites(
		s.dest,
		[]byte(fmt.Sprintf("%010d ", len(enc))),
		enc,
	)
	if err == nil {
		return len(p), nil
	}
	return 0, err
}

func (s *EncryptWriter) WriteWithHeader(header []byte, message []byte) (err error) {
	fullmessage := make([]byte, 0, len(header)+len(message)+1)
	fullmessage = append(fullmessage, header...)
	fullmessage = append(fullmessage, SP...)
	fullmessage = append(fullmessage, message...)
	_, err = s.Write(fullmessage)
	return err
}

// MakeDecryptSplit returns a split function that extracts and decrypts messages.
func MakeDecryptSplit(secret *memguard.LockedBuffer) bufio.SplitFunc {
	spl := func(data []byte, atEOF bool) (int, []byte, error) {
		adv, tok, err := PluginSplit(data, atEOF)
		if err != nil || tok == nil || secret == nil {
			return adv, tok, err
		}
		dec, err := sbox.Decrypt(tok, secret)
		if err != nil {
			return 0, nil, err
		}
		return adv, dec, nil
	}
	return spl
}

// PluginSplit is a split function used by plugins.
func PluginSplit(data []byte, atEOF bool) (advance int, token []byte, eoferr error) {
	if atEOF {
		eoferr = io.EOF
	}
	if len(data) < 11 {
		return 0, nil, eoferr
	}
	if data[10] != byte(' ') {
		return 0, nil, fmt.Errorf("Wrong plugin format, 11th char is not space: '%s'", string(data))
	}
	var i int
	for i = 0; i < 10; i++ {
		if data[i] < byte('0') || data[i] > byte('9') {
			return 0, nil, fmt.Errorf("Wrong plugin format")
		}
	}
	datalen, err := strconv.Atoi(string(data[:10]))
	if err != nil {
		return 0, nil, err
	}
	advance = 11 + datalen
	if len(data) < advance {
		return 0, nil, eoferr
	}
	return advance, data[11:advance], nil
}

// RelpSplit is used to extract RELP lines from the incoming TCP stream
func RelpSplit(data []byte, atEOF bool) (advance int, token []byte, eoferr error) {
	if atEOF {
		eoferr = io.EOF
	}
	trimmedData := bytes.TrimLeft(data, " \r\n")
	if len(trimmedData) == 0 {

		return 0, nil, eoferr
	}
	splits := bytes.FieldsFunc(trimmedData, splitSpaceOrLF)
	l := len(splits)
	if l < 3 {
		// Request more data
		return 0, nil, eoferr
	}

	txnrStr := string(splits[0])
	command := string(splits[1])
	datalenStr := string(splits[2])
	tokenStr := txnrStr + " " + command + " " + datalenStr
	advance = len(data) - len(trimmedData) + len(tokenStr) + 1

	if l == 3 && (len(data) < advance) {
		// datalen field is not complete, request more data
		return 0, nil, eoferr
	}

	_, err := strconv.Atoi(txnrStr)
	if err != nil {
		return 0, nil, err
	}
	datalen, err := strconv.Atoi(datalenStr)
	if err != nil {
		return 0, nil, err
	}
	if datalen == 0 {
		return advance, []byte(tokenStr), nil
	}
	advance += datalen + 1
	if len(data) >= advance {
		token = bytes.Trim(data[:advance], " \r\n")
		return advance, token, nil
	}
	// Request more data
	return 0, nil, eoferr
}

func splitSpaceOrLF(r rune) bool {
	return r == ' ' || r == '\n' || r == '\r'
}
<commit_msg>only one call to write<commit_after>package utils

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net"
	"strconv"

	"github.com/awnumar/memguard"
	"github.com/stephane-martin/skewer/utils/sbox"
	"golang.org/x/crypto/ed25519"
)

var NOW []byte = []byte("now")
var SP []byte = []byte(" ")

type SigWriter struct {
	dest io.Writer
	key  *memguard.LockedBuffer
}

func NewSignatureWriter(dest io.Writer, privsignkey *memguard.LockedBuffer) *SigWriter {
	return &SigWriter{dest: dest, key: privsignkey}
}

func (s *SigWriter) Write(p []byte) (n int, err error) {
	if len(p) == 0 {
		return 0, nil
	}
	signature := ed25519.Sign(s.key.Buffer(), p)
	err = ChainWrites(
		s.dest,
		[]byte(fmt.Sprintf("%010d %010d ", len(p), len(signature))),
		p,
		signature,
	)
	if err == nil {
		return len(p), nil
	}
	return 0, err
}

func (s *SigWriter) WriteWithHeader(header []byte, message []byte) (err error) {
	fullmessage := make([]byte, 0, len(header)+len(message)+1)
	fullmessage = append(fullmessage, header...)
	fullmessage = append(fullmessage, SP...)
	fullmessage = append(fullmessage, message...)
	_, err = s.Write(fullmessage)
	return err
}

// MakeSignSplit returns a split function that checks for message signatures.
func MakeSignSplit(signpubkey *memguard.LockedBuffer) (signSplit bufio.SplitFunc) {

	signSplit = func(data []byte, atEOF bool) (advance int, token []byte, eoferr error) {
		if atEOF {
			eoferr = io.EOF
		}
		if len(data) < 22 {
			return 0, nil, eoferr
		}
		if data[10] != byte(' ') || data[21] != byte(' ') {
			return 0, nil, fmt.Errorf("Wrong sign format, 11th or 22th char is not space: '%s'", string(data))
		}

		var i int
		for i = 0; i < 10; i++ {
			if data[i] < byte('0') || data[i] > byte('9') {
				return 0, nil, fmt.Errorf("Wrong sign format")
			}
		}
		for i = 11; i < 21; i++ {
			if data[i] < byte('0') || data[i] > byte('9') {
				return 0, nil, fmt.Errorf("Wrong sign format")
			}
		}
		fullmessagelen, err := strconv.Atoi(string(data[:10]))
		if err != nil {
			return 0, nil, err
		}
		signaturelen, err := strconv.Atoi(string(data[11:21]))
		if err != nil {
			return 0, nil, err
		}

		advance = 22 + fullmessagelen + signaturelen
		if len(data) < advance {
			return 0, nil, eoferr
		}
		fullmessage := data[22 : 22+fullmessagelen]
		signature := data[22+fullmessagelen : 22+fullmessagelen+signaturelen]
		if ed25519.Verify(signpubkey.Buffer(), fullmessage, signature) {
			return advance, fullmessage, nil
		}
		return 0, nil, fmt.Errorf("Wrong signature")
	}
	return signSplit
}

type EncryptWriter struct {
	dest io.Writer
	key  *memguard.LockedBuffer
}

func NewEncryptWriter(dest io.Writer, encryptkey *memguard.LockedBuffer) *EncryptWriter {
	return &EncryptWriter{dest: dest, key: encryptkey}
}

func (s *EncryptWriter) WriteMsgUnix(b []byte, oob []byte, addr *net.UnixAddr) (n int, oobn int, err error) {
	if len(b) == 0 {
		return 0, 0, nil
	}
	if conn, ok := s.dest.(*net.UnixConn); ok {
		var enc []byte
		if s.key == nil {
			enc = b
		} else {
			enc, err = sbox.Encrypt(b, s.key)
			if err != nil {
				return 0, 0, err
			}
		}
		buf := bytes.NewBuffer(nil)
		buf.Write([]byte(fmt.Sprintf("%010d ", len(enc))))
		buf.Write(enc)
		return conn.WriteMsgUnix(buf.Bytes(), oob, addr)
	}
	return 0, 0, nil
}

func (s *EncryptWriter) Write(p []byte) (n int, err error) {
	if len(p) == 0 {
		return 0, nil
	}
	var enc []byte
	if s.key == nil {
		enc = p
	} else {
		enc, err = sbox.Encrypt(p, s.key)
		if err != nil {
			return 0, err
		}
	}
	buf := make([]byte, 0, 11+len(enc))
	buf = append(buf, fmt.Sprintf("%010d ", len(enc))...)
	buf = append(buf, enc...)
	_, err = s.dest.Write(buf)
	if err == nil {
		return len(p), nil
	}
	return 0, err
}

func (s *EncryptWriter) WriteWithHeader(header []byte, message []byte) (err error) {
	fullmessage := make([]byte, 0, len(header)+len(message)+1)
	fullmessage = append(fullmessage, header...)
	fullmessage = append(fullmessage, SP...)
	fullmessage = append(fullmessage, message...)
	_, err = s.Write(fullmessage)
	return err
}

// MakeDecryptSplit returns a split function that extracts and decrypts messages.
func MakeDecryptSplit(secret *memguard.LockedBuffer) bufio.SplitFunc {
	spl := func(data []byte, atEOF bool) (int, []byte, error) {
		adv, tok, err := PluginSplit(data, atEOF)
		if err != nil || tok == nil || secret == nil {
			return adv, tok, err
		}
		dec, err := sbox.Decrypt(tok, secret)
		if err != nil {
			return 0, nil, err
		}
		return adv, dec, nil
	}
	return spl
}

// PluginSplit is a split function used by plugins.
func PluginSplit(data []byte, atEOF bool) (advance int, token []byte, eoferr error) {
	if atEOF {
		eoferr = io.EOF
	}
	if len(data) < 11 {
		return 0, nil, eoferr
	}
	if data[10] != byte(' ') {
		return 0, nil, fmt.Errorf("Wrong plugin format, 11th char is not space: '%s'", string(data))
	}
	var i int
	for i = 0; i < 10; i++ {
		if data[i] < byte('0') || data[i] > byte('9') {
			return 0, nil, fmt.Errorf("Wrong plugin format")
		}
	}
	datalen, err := strconv.Atoi(string(data[:10]))
	if err != nil {
		return 0, nil, err
	}
	advance = 11 + datalen
	if len(data) < advance {
		return 0, nil, eoferr
	}
	return advance, data[11:advance], nil
}

// RelpSplit is used to extract RELP lines from the incoming TCP stream
func RelpSplit(data []byte, atEOF bool) (advance int, token []byte, eoferr error) {
	if atEOF {
		eoferr = io.EOF
	}
	trimmedData := bytes.TrimLeft(data, " \r\n")
	if len(trimmedData) == 0 {

		return 0, nil, eoferr
	}
	splits := bytes.FieldsFunc(trimmedData, splitSpaceOrLF)
	l := len(splits)
	if l < 3 {
		// Request more data
		return 0, nil, eoferr
	}

	txnrStr := string(splits[0])
	command := string(splits[1])
	datalenStr := string(splits[2])
	tokenStr := txnrStr + " " + command + " " + datalenStr
	advance = len(data) - len(trimmedData) + len(tokenStr) + 1

	if l == 3 && (len(data) < advance) {
		// datalen field is not complete, request more data
		return 0, nil, eoferr
	}

	_, err := strconv.Atoi(txnrStr)
	if err != nil {
		return 0, nil, err
	}
	datalen, err := strconv.Atoi(datalenStr)
	if err != nil {
		return 0, nil, err
	}
	if datalen == 0 {
		return advance, []byte(tokenStr), nil
	}
	advance += datalen + 1
	if len(data) >= advance {
		token = bytes.Trim(data[:advance], " \r\n")
		return advance, token, nil
	}
	// Request more data
	return 0, nil, eoferr
}

func splitSpaceOrLF(r rune) bool {
	return r == ' ' || r == '\n' || r == '\r'
}
<|endoftext|>


<commit_before>package utils

import (
	"bufio"
	"bytes"
	"compress/zlib"
	"errors"
	"fmt"
	"io"
	"kmud/telnet"
	"log"
	"regexp"
	"strings"
	"unicode"
)

func Write(conn io.Writer, text string) (int, error) {
	return WriteRaw(conn, []byte(text))
}

func WriteRaw(conn io.Writer, bytes []byte) (int, error) {
	return conn.Write(bytes)
}

func WriteLine(conn io.Writer, line string) (int, error) {
	return Write(conn, line+"\r\n")
}

// ClearLine sends the VT100 code for erasing the line followed by a carriage
// return to move the cursor back to the beginning of the line
func ClearLine(conn io.Writer) {
	clearline := "\x1B[2K"
	Write(conn, clearline+"\r")
}

func Simplify(str string) string {
	simpleStr := strings.TrimSpace(str)
	simpleStr = strings.ToLower(simpleStr)
	return simpleStr
}

func GetRawUserInput(conn io.ReadWriter, prompt string) string {
	reader := bufio.NewReader(conn)

	for {
		Write(conn, prompt)

		bytes, err := reader.ReadBytes('\n')
		input := string(bytes)

		if input == "\r\n" || input == "\n" {
			input = ""
		}

		PanicIfError(err)

		if input == "x" || input == "X" {
			return ""
		} else if input != "" {
			return input
		}
	}

	panic("Unexpected code path")
	return ""
}

func GetPassword(conn io.ReadWriter, prompt string) string {
	WriteRaw(conn, telnet.WillEcho())
	defer func() { WriteRaw(conn, telnet.WontEcho()) }()

	reader := bufio.NewReader(conn)
	Write(conn, prompt)
	bytes, err := reader.ReadBytes('\n')

	input := string(bytes)
	PanicIfError(err)

	Write(conn, "\r\n")

	return input
}

func GetUserInput(conn io.ReadWriter, prompt string) string {
	input := GetRawUserInput(conn, prompt)
	return Simplify(input)
}

func HandleError(err error) {
	if err != nil {
		log.Fatalf("Error: %s", err)
	}
}

func FormatName(name string) string {
	if name == "" {
		return name
	}

	runes := []rune(Simplify(name))
	runes[0] = unicode.ToUpper(runes[0])
	return string(runes)
}

func PanicIfError(err error) {
	if err != nil {
		panic(err)
	}
}

func Argify(data string) (string, []string) {
	fields := strings.Fields(data)

	if len(fields) == 0 {
		return "", []string{}
	}

	arg1 := Simplify(fields[0])
	args := fields[1:]

	return arg1, args
}

func rowEmpty(row string) bool {
	for _, char := range row {
		if char != ' ' {
			return false
		}
	}
	return true
}

func TrimUpperRows(rows []string) []string {
	for _, row := range rows {
		if !rowEmpty(row) {
			break
		}

		rows = rows[1:]
	}

	return rows
}

func TrimLowerRows(rows []string) []string {
	for i := len(rows) - 1; i >= 0; i -= 1 {
		row := rows[i]
		if !rowEmpty(row) {
			break
		}
		rows = rows[:len(rows)-1]
	}

	return rows
}

func TrimEmptyRows(str string) string {
	rows := strings.Split(str, "\n")
	return strings.Join(TrimLowerRows(TrimUpperRows(rows)), "\n")
}

func ValidateName(name string) error {
	const MinSize = 3
	const MaxSize = 12

	if len(name) < MinSize || len(name) > MaxSize {
		return errors.New(fmt.Sprintf("Names must be between %v and %v letters long", MinSize, MaxSize))
	}

	regex := regexp.MustCompile("^[a-zA-Z]*$")

	if !regex.MatchString(name) {
		return errors.New("Names may only contain letters (A-Z)")
	}

	return nil
}

func MonitorChannel() {
	// TODO: See if there's a way to take in a generic channel and see how close it is to being full
}

// BestMatch searches the given list for the given pattern, the index of the
// longest match that starts with the given pattern is returned. Returns -1 if
// no match was found, -2 if the result is ambiguous. The search is case
// insensitive
func BestMatch(pattern string, searchList []string) int {
	pattern = strings.ToLower(pattern)

	index := -1

	for i, searchItem := range searchList {
		searchItem = strings.ToLower(searchItem)

		if searchItem == pattern {
			return i
		}

		if strings.HasPrefix(searchItem, pattern) {
			if index != -1 {
				return -2
			}

			index = i
		}
	}

	return index
}

func compress(data []byte) []byte {
	var b bytes.Buffer
	w := zlib.NewWriter(&b)
	w.Write(data)
	w.Close()
	return b.Bytes()
}

// vim: nocindent
<commit_msg>Cleaned up handling of newline characters when reading input<commit_after>package utils

import (
	"bufio"
	"bytes"
	"compress/zlib"
	"errors"
	"fmt"
	"io"
	"kmud/telnet"
	"log"
	"regexp"
	"strings"
	"unicode"
)

func Write(conn io.Writer, text string) (int, error) {
	return WriteRaw(conn, []byte(text))
}

func WriteRaw(conn io.Writer, bytes []byte) (int, error) {
	return conn.Write(bytes)
}

func WriteLine(conn io.Writer, line string) (int, error) {
	return Write(conn, line+"\r\n")
}

// ClearLine sends the VT100 code for erasing the line followed by a carriage
// return to move the cursor back to the beginning of the line
func ClearLine(conn io.Writer) {
	clearline := "\x1B[2K"
	Write(conn, clearline+"\r")
}

func Simplify(str string) string {
	simpleStr := strings.TrimSpace(str)
	simpleStr = strings.ToLower(simpleStr)
	return simpleStr
}

func GetRawUserInput(conn io.ReadWriter, prompt string) string {
	reader := bufio.NewReader(conn)

	for {
		Write(conn, prompt)

		input, err := reader.ReadString('\n')
		input = strings.Trim(input, "\r\n")

		PanicIfError(err)

		if input == "x" || input == "X" {
			return ""
		} else if input != "" {
			return input
		}
	}

	panic("Unexpected code path")
	return ""
}

func GetPassword(conn io.ReadWriter, prompt string) string {
	WriteRaw(conn, telnet.WillEcho())
	defer func() { WriteRaw(conn, telnet.WontEcho()) }()

	reader := bufio.NewReader(conn)
	Write(conn, prompt)
	bytes, err := reader.ReadBytes('\n')

	input := string(bytes)
	PanicIfError(err)

	Write(conn, "\r\n")

	return input
}

func GetUserInput(conn io.ReadWriter, prompt string) string {
	input := GetRawUserInput(conn, prompt)
	return Simplify(input)
}

func HandleError(err error) {
	if err != nil {
		log.Fatalf("Error: %s", err)
	}
}

func FormatName(name string) string {
	if name == "" {
		return name
	}

	runes := []rune(Simplify(name))
	runes[0] = unicode.ToUpper(runes[0])
	return string(runes)
}

func PanicIfError(err error) {
	if err != nil {
		panic(err)
	}
}

func Argify(data string) (string, []string) {
	fields := strings.Fields(data)

	if len(fields) == 0 {
		return "", []string{}
	}

	arg1 := Simplify(fields[0])
	args := fields[1:]

	return arg1, args
}

func rowEmpty(row string) bool {
	for _, char := range row {
		if char != ' ' {
			return false
		}
	}
	return true
}

func TrimUpperRows(rows []string) []string {
	for _, row := range rows {
		if !rowEmpty(row) {
			break
		}

		rows = rows[1:]
	}

	return rows
}

func TrimLowerRows(rows []string) []string {
	for i := len(rows) - 1; i >= 0; i -= 1 {
		row := rows[i]
		if !rowEmpty(row) {
			break
		}
		rows = rows[:len(rows)-1]
	}

	return rows
}

func TrimEmptyRows(str string) string {
	rows := strings.Split(str, "\n")
	return strings.Join(TrimLowerRows(TrimUpperRows(rows)), "\n")
}

func ValidateName(name string) error {
	const MinSize = 3
	const MaxSize = 12

	if len(name) < MinSize || len(name) > MaxSize {
		return errors.New(fmt.Sprintf("Names must be between %v and %v letters long", MinSize, MaxSize))
	}

	regex := regexp.MustCompile("^[a-zA-Z]*$")

	if !regex.MatchString(name) {
		return errors.New("Names may only contain letters (A-Z)")
	}

	return nil
}

func MonitorChannel() {
	// TODO: See if there's a way to take in a generic channel and see how close it is to being full
}

// BestMatch searches the given list for the given pattern, the index of the
// longest match that starts with the given pattern is returned. Returns -1 if
// no match was found, -2 if the result is ambiguous. The search is case
// insensitive
func BestMatch(pattern string, searchList []string) int {
	pattern = strings.ToLower(pattern)

	index := -1

	for i, searchItem := range searchList {
		searchItem = strings.ToLower(searchItem)

		if searchItem == pattern {
			return i
		}

		if strings.HasPrefix(searchItem, pattern) {
			if index != -1 {
				return -2
			}

			index = i
		}
	}

	return index
}

func compress(data []byte) []byte {
	var b bytes.Buffer
	w := zlib.NewWriter(&b)
	w.Write(data)
	w.Close()
	return b.Bytes()
}

// vim: nocindent
<|endoftext|>