<commit_before>package cmd

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"text/tabwriter"
	"time"

	"google.golang.org/grpc"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/version"
	pfscmds "github.com/pachyderm/pachyderm/src/server/pfs/cmds"
	ppscmds "github.com/pachyderm/pachyderm/src/server/pps/cmds"
	"github.com/spf13/cobra"
	"go.pedge.io/pb/go/google/protobuf"
	"go.pedge.io/pkg/cobra"
	"go.pedge.io/proto/version"
	"golang.org/x/net/context"
)

func PachctlCmd(address string) (*cobra.Command, error) {
	rootCmd := &cobra.Command{
		Use: os.Args[0],
		Long: `Access the Pachyderm API.

Envronment variables:
  ADDRESS=0.0.0.0:30650, the server to connect to.
`,
	}
	pfsCmds := pfscmds.Cmds(address)
	for _, cmd := range pfsCmds {
		rootCmd.AddCommand(cmd)
	}
	ppsCmds, err := ppscmds.Cmds(address)
	if err != nil {
		return nil, err
	}
	for _, cmd := range ppsCmds {
		rootCmd.AddCommand(cmd)
	}

	version := &cobra.Command{
		Use:   "version",
		Short: "Return version information.",
		Long:  "Return version information.",
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
			printVersionHeader(writer)
			printVersion(writer, "pachctl", version.Version)
			writer.Flush()

			versionClient, err := getVersionAPIClient(address)
			if err != nil {
				return err
			}
			ctx, _ := context.WithTimeout(context.Background(), time.Second)
			version, err := versionClient.GetVersion(ctx, &google_protobuf.Empty{})

			if err != nil {
				fmt.Fprintf(writer, "pachd\tUNKNOWN: Error %v\n", err)
				return writer.Flush()
			}

			printVersion(writer, "pachd", version)
			return writer.Flush()
		}),
	}
	deleteAll := &cobra.Command{
		Use:   "delete-all",
		Short: "Delete everything.",
		Long: `Delete all repos, commits, files, pipelines and jobs.
This resets the cluster to its initial state.`,
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			client, err := client.NewFromAddress(address)
			if err != nil {
				return err
			}
			fmt.Printf("Are you sure you want to delete all repos, commits, files, pipelines and jobs? yN\n")
			r := bufio.NewReader(os.Stdin)
			bytes, err := r.ReadBytes('\n')
			if err != nil {
				return err
			}
			if bytes[0] == 'y' || bytes[0] == 'Y' {
				return client.DeleteAll()
			}
			return nil
		}),
	}
	rootCmd.AddCommand(version)
	rootCmd.AddCommand(deleteAll)
	return rootCmd, nil
}

func getVersionAPIClient(address string) (protoversion.APIClient, error) {
	clientConn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}
	return protoversion.NewAPIClient(clientConn), nil
}

func printVersionHeader(w io.Writer) {
	fmt.Fprintf(w, "COMPONENT\tVERSION\t\n")
}

func printVersion(w io.Writer, component string, v *protoversion.Version) {
	fmt.Fprintf(w, "%s\t%s\t\n", component, version.PrettyPrintVersion(v))
}
<commit_msg>Fix linting errors in pachctl<commit_after>package cmd

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"text/tabwriter"
	"time"

	"google.golang.org/grpc"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/version"
	pfscmds "github.com/pachyderm/pachyderm/src/server/pfs/cmds"
	ppscmds "github.com/pachyderm/pachyderm/src/server/pps/cmds"
	"github.com/spf13/cobra"
	"go.pedge.io/pb/go/google/protobuf"
	"go.pedge.io/pkg/cobra"
	"go.pedge.io/proto/version"
	"golang.org/x/net/context"
)

// PachctlCmd takes a pachd host-address and creates a cobra.Command
// which may interact with the host.
func PachctlCmd(address string) (*cobra.Command, error) {
	rootCmd := &cobra.Command{
		Use: os.Args[0],
		Long: `Access the Pachyderm API.

Envronment variables:
  ADDRESS=0.0.0.0:30650, the server to connect to.
`,
	}
	pfsCmds := pfscmds.Cmds(address)
	for _, cmd := range pfsCmds {
		rootCmd.AddCommand(cmd)
	}
	ppsCmds, err := ppscmds.Cmds(address)
	if err != nil {
		return nil, err
	}
	for _, cmd := range ppsCmds {
		rootCmd.AddCommand(cmd)
	}

	version := &cobra.Command{
		Use:   "version",
		Short: "Return version information.",
		Long:  "Return version information.",
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
			printVersionHeader(writer)
			printVersion(writer, "pachctl", version.Version)
			writer.Flush()

			versionClient, err := getVersionAPIClient(address)
			if err != nil {
				return err
			}
			ctx, _ := context.WithTimeout(context.Background(), time.Second)
			version, err := versionClient.GetVersion(ctx, &google_protobuf.Empty{})

			if err != nil {
				fmt.Fprintf(writer, "pachd\tUNKNOWN: Error %v\n", err)
				return writer.Flush()
			}

			printVersion(writer, "pachd", version)
			return writer.Flush()
		}),
	}
	deleteAll := &cobra.Command{
		Use:   "delete-all",
		Short: "Delete everything.",
		Long: `Delete all repos, commits, files, pipelines and jobs.
This resets the cluster to its initial state.`,
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			client, err := client.NewFromAddress(address)
			if err != nil {
				return err
			}
			fmt.Printf("Are you sure you want to delete all repos, commits, files, pipelines and jobs? yN\n")
			r := bufio.NewReader(os.Stdin)
			bytes, err := r.ReadBytes('\n')
			if err != nil {
				return err
			}
			if bytes[0] == 'y' || bytes[0] == 'Y' {
				return client.DeleteAll()
			}
			return nil
		}),
	}
	rootCmd.AddCommand(version)
	rootCmd.AddCommand(deleteAll)
	return rootCmd, nil
}

func getVersionAPIClient(address string) (protoversion.APIClient, error) {
	clientConn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}
	return protoversion.NewAPIClient(clientConn), nil
}

func printVersionHeader(w io.Writer) {
	fmt.Fprintf(w, "COMPONENT\tVERSION\t\n")
}

func printVersion(w io.Writer, component string, v *protoversion.Version) {
	fmt.Fprintf(w, "%s\t%s\t\n", component, version.PrettyPrintVersion(v))
}
<|endoftext|>


<commit_before>package initCmd

import (
	// Stdlib
	"bytes"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	// Internal
	"github.com/salsita/salsaflow/app"
	"github.com/salsita/salsaflow/asciiart"
	"github.com/salsita/salsaflow/config"
	"github.com/salsita/salsaflow/errs"
	"github.com/salsita/salsaflow/git"
	"github.com/salsita/salsaflow/log"
	"github.com/salsita/salsaflow/prompt"
	"github.com/salsita/salsaflow/shell"

	// Other
	"bitbucket.org/kardianos/osext"
	"gopkg.in/tchap/gocli.v1"
)

var CommitMsgHookFileName = "salsaflow-commit-msg"

func init() {
	if runtime.GOOS == "windows" {
		CommitMsgHookFileName += ".exe"
	}
}

var Command = &gocli.Command{
	UsageLine: "init",
	Short:     "initialize the repository",
	Long: `
  Initialize the repository so that it works with SalsaFlow.
	`,
	Action: run,
}

func run(cmd *gocli.Command, args []string) {
	if len(args) != 0 {
		cmd.Usage()
		os.Exit(2)
	}

	// Ignore errors here (we'll sort them out in runMain).
	app.Init()

	if err := runMain(); err != nil {
		log.Fatalln("\nError: " + err.Error())
	}
}

func handleError(task string, err error, stderr *bytes.Buffer) error {
	errs.NewError(task, stderr, err).Log(log.V(log.Info))
	return err
}

func runMain() (err error) {
	// Handles expected init errors or success (i.e., no expected errors).
	defer func() {
		if err != nil {
			return
		}

		// Success! Mark the repository as initialized in git config.
		msg := "Mark the repository as initialized"
		_, stderr, ex := git.Git("config", "salsaflow.initialized", "true")
		if ex != nil {
			err = handleError(msg, ex, stderr)
			return
		}
		asciiart.PrintThumbsUp()
		log.Println("\nSwell, your repo is initialized!\n")
	}()

	// Check whether the repository has been initialized yet.
	msg := "Check whether the repository has been initialized yet"
	initialized, stderr, err := git.GetConfigBool("salsaflow.initialized")
	if err != nil {
		return handleError(msg, err, stderr)
	}
	if initialized {
		return errors.New("repository already initialized")
	}

	// Make sure the user is using the right version of Git.
	//
	// The check is here and not in app.Init because it is highly improbable
	// that the check would pass onece and then fail later. It is expected
	// that once the user starts using git version 2.x, he keeps doing so.
	msg = "Check the git version being used"
	log.Run(msg)
	stdout, stderr, err := shell.Run("git", "--version")
	if err != nil {
		return handleError(msg, err, stderr)
	}
	pattern := regexp.MustCompile("^git version ([0-9]+)[.]([0-9]+)[.]([0-9]+)")
	parts := pattern.FindStringSubmatch(stdout.String())
	if len(parts) != 4 {
		return handleError(msg, errors.New("unexpected git --version output"), nil)
	}
	major, _ := strconv.Atoi(parts[1])
	minor, _ := strconv.Atoi(parts[2])
	patch, _ := strconv.Atoi(parts[3])
	gitVersion := fmt.Sprintf("%v.%v.%v", major, minor, patch)
	if runtime.GOOS == "windows" {
		// We require git 1.9.4+ on Windows.
		switch {
		case major >= 2:
			// OK
		case minor == 1 && minor > 9:
			// OK
		case minor == 1 && minor == 9 && patch >= 4:
			// OK
		default:
			return handleError(
				msg,
				errors.New("unsupported git version detected: "+gitVersion),
				nil)
		}
	} else {
		// Don't bother, just require git 2.0.0+ on other systems.
		if major < 2 {
			return handleError(
				msg,
				errors.New("unsupported git version detected: "+gitVersion),
				nil)
		}
	}

	// Make sure that the master branch exists.
	msg = "Make sure the master branch exists"
	log.Run(msg)
	exists, stderr, err := git.RefExists(config.MasterBranch)
	if err != nil {
		return handleError(msg, err, stderr)
	}
	if !exists {
		log.Fail(msg)
		log.NewLine(fmt.Sprintf(
			"Make sure that branch '%v' exists and run init again.", config.MasterBranch))
		return fmt.Errorf("branch '%v' not found", config.MasterBranch)
	}

	// Make sure that the trunk branch exists.
	msg = "Make sure the trunk branch exists"
	log.Run(msg)
	exists, stderr, err = git.RefExists(config.TrunkBranch)
	if err != nil {
		return handleError(msg, err, stderr)
	}
	if !exists {
		msg := "Create the trunk branch"
		log.Log(fmt.Sprintf(
			"No branch '%s' found. Will create one for you for free!", config.TrunkBranch))
		log.NewLine(fmt.Sprintf(
			"The newly created branch is pointing to '%v'.", config.MasterBranch))
		stderr, err := git.Branch(config.TrunkBranch, config.MasterBranch)
		if err != nil {
			return handleError(msg, err, stderr)
		}

		msg = "Push the newly created trunk branch"
		log.Run(msg)
		_, stderr, err = git.Git("push", "-u", config.OriginName,
			config.TrunkBranch+":"+config.TrunkBranch)
		if err != nil {
			return handleError(msg, err, stderr)
		}
	}

	// Check the project-specific configuration file.
	msg = "Check the local SalsaFlow configuration"
	log.Run(msg)
	if _, stderr, err = config.ReadLocalConfig(); err != nil {
		return handleError(msg, fmt.Errorf("could not read config file '%v' on branch '%v': %v",
			config.LocalConfigFileName, config.ConfigBranch, err), stderr)
	}

	// Check the global configuration file.
	msg = "Check the global SalsaFlow configuration"
	log.Run(msg)
	if _, err := config.ReadGlobalConfig(); err != nil {
		return handleError(msg, fmt.Errorf("could not read config file '%v': %v",
			"$HOME/"+config.GlobalConfigFileName, err), nil)
	}

	// Verify our git hook is installed and used.
	msg = "Check the git commit-msg hook"
	log.Run(msg)
	if err := checkGitHook(); err != nil {
		return handleError(msg, err, nil)
	}

	return nil
}

// Check whether SalsaFlow git hook is used. Prompts user to install our hook if it isn't.
func checkGitHook() error {
	// Ping the git hook with our secret argument.
	repoRoot, _, err := git.RepositoryRootAbsolutePath()
	if err != nil {
		return err
	}

	hookPath := filepath.Join(repoRoot, ".git", "hooks", "commit-msg")
	stdout, _, _ := shell.Run(hookPath, config.SecretGitHookFilename)
	secret := strings.TrimSpace(stdout.String())

	if secret == config.SecretGitHookResponse {
		return nil
	}

	// Prompt the user to confirm the SalsaFlow git commit-msg hook.
	log.Warn("SalsaFlow git commit-msg hook not detected")
	msg := "Prompt the user to confirm the commit-msg hook"

	// Get the hook executable absolute path. It's supposed to be installed
	// in the same directory as the salsaflow executable itself.
	binDir, err := osext.ExecutableFolder()
	if err != nil {
		return handleError(msg, err, nil)
	}
	hookBin := filepath.Join(binDir, CommitMsgHookFileName)

	confirmed, err := prompt.Confirm(`
I need my own git commit-msg hook to be placed in the repository.
Shall I create or replace your current commit-msg hook?`)
	fmt.Println()
	if err != nil {
		return handleError(msg, err, nil)
	}
	if !confirmed {
		// User stubbornly refuses to let us overwrite their webhook.
		// Inform the init has failed and let them do their thing.
		fmt.Printf(`I need the hook in order to do my job!

Please make sure the executable located at

  %v

runs as your commit-msg hook and run me again!

`, hookBin)
		return errors.New("SalsaFlow git commit-msg hook not detected")
	}

	// Install the SalsaFlow commit-msg git hook by copying the hook executable
	// from the expected absolute path to the git config hook directory.
	msg = "Install the SalsaFlow git commit-msg hook"
	if err := CopyFile(hookBin, hookPath); err != nil {
		return handleError(msg, err, nil)
	}
	log.Log("SalsaFlow commit-msg git hook installed. Sweet.")

	return nil
}
<commit_msg>repo init: Fix git version check on Win<commit_after>package initCmd

import (
	// Stdlib
	"bytes"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	// Internal
	"github.com/salsita/salsaflow/app"
	"github.com/salsita/salsaflow/asciiart"
	"github.com/salsita/salsaflow/config"
	"github.com/salsita/salsaflow/errs"
	"github.com/salsita/salsaflow/git"
	"github.com/salsita/salsaflow/log"
	"github.com/salsita/salsaflow/prompt"
	"github.com/salsita/salsaflow/shell"

	// Other
	"bitbucket.org/kardianos/osext"
	"gopkg.in/tchap/gocli.v1"
)

var CommitMsgHookFileName = "salsaflow-commit-msg"

func init() {
	if runtime.GOOS == "windows" {
		CommitMsgHookFileName += ".exe"
	}
}

var Command = &gocli.Command{
	UsageLine: "init",
	Short:     "initialize the repository",
	Long: `
  Initialize the repository so that it works with SalsaFlow.
	`,
	Action: run,
}

func run(cmd *gocli.Command, args []string) {
	if len(args) != 0 {
		cmd.Usage()
		os.Exit(2)
	}

	// Ignore errors here (we'll sort them out in runMain).
	app.Init()

	if err := runMain(); err != nil {
		log.Fatalln("\nError: " + err.Error())
	}
}

func handleError(task string, err error, stderr *bytes.Buffer) error {
	errs.NewError(task, stderr, err).Log(log.V(log.Info))
	return err
}

func runMain() (err error) {
	// Handles expected init errors or success (i.e., no expected errors).
	defer func() {
		if err != nil {
			return
		}

		// Success! Mark the repository as initialized in git config.
		msg := "Mark the repository as initialized"
		_, stderr, ex := git.Git("config", "salsaflow.initialized", "true")
		if ex != nil {
			err = handleError(msg, ex, stderr)
			return
		}
		asciiart.PrintThumbsUp()
		log.Println("\nSwell, your repo is initialized!\n")
	}()

	// Check whether the repository has been initialized yet.
	msg := "Check whether the repository has been initialized yet"
	initialized, stderr, err := git.GetConfigBool("salsaflow.initialized")
	if err != nil {
		return handleError(msg, err, stderr)
	}
	if initialized {
		return errors.New("repository already initialized")
	}

	// Make sure the user is using the right version of Git.
	//
	// The check is here and not in app.Init because it is highly improbable
	// that the check would pass onece and then fail later. It is expected
	// that once the user starts using git version 2.x, he keeps doing so.
	msg = "Check the git version being used"
	log.Run(msg)
	stdout, stderr, err := shell.Run("git", "--version")
	if err != nil {
		return handleError(msg, err, stderr)
	}
	pattern := regexp.MustCompile("^git version ([0-9]+)[.]([0-9]+)[.]([0-9]+)")
	parts := pattern.FindStringSubmatch(stdout.String())
	if len(parts) != 4 {
		return handleError(msg, errors.New("unexpected git --version output"), nil)
	}
	major, _ := strconv.Atoi(parts[1])
	minor, _ := strconv.Atoi(parts[2])
	patch, _ := strconv.Atoi(parts[3])
	gitVersion := fmt.Sprintf("%v.%v.%v", major, minor, patch)
	if runtime.GOOS == "windows" {
		// We require git 1.9.4+ on Windows.
		switch {
		case major >= 2:
			// OK
		case major == 1 && minor > 9:
			// OK
		case major == 1 && minor == 9 && patch >= 4:
			// OK
		default:
			return handleError(
				msg,
				errors.New("unsupported git version detected: "+gitVersion),
				nil)
		}
	} else {
		// Don't bother, just require git 2.0.0+ on other systems.
		if major < 2 {
			return handleError(
				msg,
				errors.New("unsupported git version detected: "+gitVersion),
				nil)
		}
	}

	// Make sure that the master branch exists.
	msg = "Make sure the master branch exists"
	log.Run(msg)
	exists, stderr, err := git.RefExists(config.MasterBranch)
	if err != nil {
		return handleError(msg, err, stderr)
	}
	if !exists {
		log.Fail(msg)
		log.NewLine(fmt.Sprintf(
			"Make sure that branch '%v' exists and run init again.", config.MasterBranch))
		return fmt.Errorf("branch '%v' not found", config.MasterBranch)
	}

	// Make sure that the trunk branch exists.
	msg = "Make sure the trunk branch exists"
	log.Run(msg)
	exists, stderr, err = git.RefExists(config.TrunkBranch)
	if err != nil {
		return handleError(msg, err, stderr)
	}
	if !exists {
		msg := "Create the trunk branch"
		log.Log(fmt.Sprintf(
			"No branch '%s' found. Will create one for you for free!", config.TrunkBranch))
		log.NewLine(fmt.Sprintf(
			"The newly created branch is pointing to '%v'.", config.MasterBranch))
		stderr, err := git.Branch(config.TrunkBranch, config.MasterBranch)
		if err != nil {
			return handleError(msg, err, stderr)
		}

		msg = "Push the newly created trunk branch"
		log.Run(msg)
		_, stderr, err = git.Git("push", "-u", config.OriginName,
			config.TrunkBranch+":"+config.TrunkBranch)
		if err != nil {
			return handleError(msg, err, stderr)
		}
	}

	// Check the project-specific configuration file.
	msg = "Check the local SalsaFlow configuration"
	log.Run(msg)
	if _, stderr, err = config.ReadLocalConfig(); err != nil {
		return handleError(msg, fmt.Errorf("could not read config file '%v' on branch '%v': %v",
			config.LocalConfigFileName, config.ConfigBranch, err), stderr)
	}

	// Check the global configuration file.
	msg = "Check the global SalsaFlow configuration"
	log.Run(msg)
	if _, err := config.ReadGlobalConfig(); err != nil {
		return handleError(msg, fmt.Errorf("could not read config file '%v': %v",
			"$HOME/"+config.GlobalConfigFileName, err), nil)
	}

	// Verify our git hook is installed and used.
	msg = "Check the git commit-msg hook"
	log.Run(msg)
	if err := checkGitHook(); err != nil {
		return handleError(msg, err, nil)
	}

	return nil
}

// Check whether SalsaFlow git hook is used. Prompts user to install our hook if it isn't.
func checkGitHook() error {
	// Ping the git hook with our secret argument.
	repoRoot, _, err := git.RepositoryRootAbsolutePath()
	if err != nil {
		return err
	}

	hookPath := filepath.Join(repoRoot, ".git", "hooks", "commit-msg")
	stdout, _, _ := shell.Run(hookPath, config.SecretGitHookFilename)
	secret := strings.TrimSpace(stdout.String())

	if secret == config.SecretGitHookResponse {
		return nil
	}

	// Prompt the user to confirm the SalsaFlow git commit-msg hook.
	log.Warn("SalsaFlow git commit-msg hook not detected")
	msg := "Prompt the user to confirm the commit-msg hook"

	// Get the hook executable absolute path. It's supposed to be installed
	// in the same directory as the salsaflow executable itself.
	binDir, err := osext.ExecutableFolder()
	if err != nil {
		return handleError(msg, err, nil)
	}
	hookBin := filepath.Join(binDir, CommitMsgHookFileName)

	confirmed, err := prompt.Confirm(`
I need my own git commit-msg hook to be placed in the repository.
Shall I create or replace your current commit-msg hook?`)
	fmt.Println()
	if err != nil {
		return handleError(msg, err, nil)
	}
	if !confirmed {
		// User stubbornly refuses to let us overwrite their webhook.
		// Inform the init has failed and let them do their thing.
		fmt.Printf(`I need the hook in order to do my job!

Please make sure the executable located at

  %v

runs as your commit-msg hook and run me again!

`, hookBin)
		return errors.New("SalsaFlow git commit-msg hook not detected")
	}

	// Install the SalsaFlow commit-msg git hook by copying the hook executable
	// from the expected absolute path to the git config hook directory.
	msg = "Install the SalsaFlow git commit-msg hook"
	if err := CopyFile(hookBin, hookPath); err != nil {
		return handleError(msg, err, nil)
	}
	log.Log("SalsaFlow commit-msg git hook installed. Sweet.")

	return nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/dutchcoders/transfer.sh/server"
	"github.com/fatih/color"
	"github.com/urfave/cli"
	"google.golang.org/api/googleapi"
)

var Version = "1.1.2"
var helpTemplate = `NAME:
{{.Name}} - {{.Usage}}

DESCRIPTION:
{{.Description}}

USAGE:
{{.Name}} {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]

COMMANDS:
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{if .Flags}}
FLAGS:
{{range .Flags}}{{.}}
{{end}}{{end}}
VERSION:
` + Version +
	`{{ "\n"}}`

var globalFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "listener",
		Usage: "127.0.0.1:8080",
		Value: "127.0.0.1:8080",
	},
	// redirect to https?
	// hostnames
	cli.StringFlag{
		Name:  "profile-listener",
		Usage: "127.0.0.1:6060",
		Value: "",
	},
	cli.BoolFlag{
		Name:  "force-https",
		Usage: "",
	},
	cli.StringFlag{
		Name:  "tls-listener",
		Usage: "127.0.0.1:8443",
		Value: "",
	},
	cli.BoolFlag{
		Name:  "tls-listener-only",
		Usage: "",
	},
	cli.StringFlag{
		Name:  "tls-cert-file",
		Value: "",
	},
	cli.StringFlag{
		Name:  "tls-private-key",
		Value: "",
	},
	cli.StringFlag{
		Name:  "temp-path",
		Usage: "path to temp files",
		Value: os.TempDir(),
	},
	cli.StringFlag{
		Name:  "web-path",
		Usage: "path to static web files",
		Value: "",
	},
	cli.StringFlag{
		Name:  "proxy-path",
		Usage: "path prefix when service is run behind a proxy",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ga-key",
		Usage: "key for google analytics (front end)",
		Value: "",
	},
	cli.StringFlag{
		Name:  "uservoice-key",
		Usage: "key for user voice (front end)",
		Value: "",
	},
	cli.StringFlag{
		Name:  "provider",
		Usage: "s3|gdrive|local",
		Value: "",
	},
	cli.StringFlag{
		Name:   "s3-endpoint",
		Usage:  "",
		Value:  "",
		EnvVar: "S3_ENDPOINT",
	},
	cli.StringFlag{
		Name:   "s3-region",
		Usage:  "",
		Value:  "eu-west-1",
		EnvVar: "S3_REGION",
	},
	cli.StringFlag{
		Name:   "aws-access-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_ACCESS_KEY",
	},
	cli.StringFlag{
		Name:   "aws-secret-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_SECRET_KEY",
	},
	cli.StringFlag{
		Name:   "bucket",
		Usage:  "",
		Value:  "",
		EnvVar: "BUCKET",
	},
	cli.BoolFlag{
		Name:  "s3-no-multipart",
		Usage: "Disables S3 Multipart Puts",
	},
	cli.BoolFlag{
		Name:  "s3-path-style",
		Usage: "Forces path style URLs, required for Minio.",
	},
	cli.StringFlag{
		Name:  "gdrive-client-json-filepath",
		Usage: "",
		Value: "",
	},
	cli.StringFlag{
		Name:  "gdrive-local-config-path",
		Usage: "",
		Value: "",
	},
	cli.IntFlag{
		Name:  "gdrive-chunk-size",
		Usage: "",
		Value: googleapi.DefaultUploadChunkSize / 1024 / 1024,
	},
	cli.StringFlag{
		Name:  "storj-endpoint",
		Usage: "Satellite Address including Port.",
		Value: "",
	},
	cli.StringFlag{
		Name:  "storj-apikey",
		Usage: "",
		Value: "",
	},
	cli.StringFlag{
		Name:  "storj-bucket",
		Usage: "",
		Value: "",
	},
	cli.StringFlag{
		Name:  "storj-enckey",
		Usage: "Encryption Key for local file encryption",
		Value: "",
	},
	cli.IntFlag{
		Name:   "rate-limit",
		Usage:  "requests per minute",
		Value:  0,
		EnvVar: "",
	},
	cli.StringFlag{
		Name:   "lets-encrypt-hosts",
		Usage:  "host1, host2",
		Value:  "",
		EnvVar: "HOSTS",
	},
	cli.StringFlag{
		Name:  "log",
		Usage: "/var/log/transfersh.log",
		Value: "",
	},
	cli.StringFlag{
		Name:  "basedir",
		Usage: "path to storage",
		Value: "",
	},
	cli.StringFlag{
		Name:   "clamav-host",
		Usage:  "clamav-host",
		Value:  "",
		EnvVar: "CLAMAV_HOST",
	},
	cli.StringFlag{
		Name:   "virustotal-key",
		Usage:  "virustotal-key",
		Value:  "",
		EnvVar: "VIRUSTOTAL_KEY",
	},
	cli.BoolFlag{
		Name:  "profiler",
		Usage: "enable profiling",
	},
	cli.StringFlag{
		Name:  "http-auth-user",
		Usage: "user for http basic auth",
		Value: "",
	},
	cli.StringFlag{
		Name:  "http-auth-pass",
		Usage: "pass for http basic auth",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ip-whitelist",
		Usage: "comma separated list of ips allowed to connect to the service",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ip-blacklist",
		Usage: "comma separated list of ips not allowed to connect to the service",
		Value: "",
	},
}

type Cmd struct {
	*cli.App
}

func VersionAction(c *cli.Context) {
	fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line")))
}

func New() *Cmd {
	logger := log.New(os.Stdout, "[transfer.sh]", log.LstdFlags)

	app := cli.NewApp()
	app.Name = "transfer.sh"
	app.Author = ""
	app.Usage = "transfer.sh"
	app.Description = `Easy file sharing from the command line`
	app.Version = Version
	app.Flags = globalFlags
	app.CustomAppHelpTemplate = helpTemplate
	app.Commands = []cli.Command{
		{
			Name:   "version",
			Action: VersionAction,
		},
	}

	app.Before = func(c *cli.Context) error {
		return nil
	}

	app.Action = func(c *cli.Context) {
		options := []server.OptionFn{}
		if v := c.String("listener"); v != "" {
			options = append(options, server.Listener(v))
		}

		if v := c.String("tls-listener"); v == "" {
		} else if c.Bool("tls-listener-only") {
			options = append(options, server.TLSListener(v, true))
		} else {
			options = append(options, server.TLSListener(v, false))
		}

		if v := c.String("profile-listener"); v != "" {
			options = append(options, server.ProfileListener(v))
		}

		if v := c.String("web-path"); v != "" {
			options = append(options, server.WebPath(v))
		}

		if v := c.String("proxy-path"); v != "" {
			options = append(options, server.ProxyPath(v))
		}

		if v := c.String("ga-key"); v != "" {
			options = append(options, server.GoogleAnalytics(v))
		}

		if v := c.String("uservoice-key"); v != "" {
			options = append(options, server.UserVoice(v))
		}

		if v := c.String("temp-path"); v != "" {
			options = append(options, server.TempPath(v))
		}

		if v := c.String("log"); v != "" {
			options = append(options, server.LogFile(logger, v))
		} else {
			options = append(options, server.Logger(logger))
		}

		if v := c.String("lets-encrypt-hosts"); v != "" {
			options = append(options, server.UseLetsEncrypt(strings.Split(v, ",")))
		}

		if v := c.String("virustotal-key"); v != "" {
			options = append(options, server.VirustotalKey(v))
		}

		if v := c.String("clamav-host"); v != "" {
			options = append(options, server.ClamavHost(v))
		}

		if v := c.Int("rate-limit"); v > 0 {
			options = append(options, server.RateLimit(v))
		}

		if cert := c.String("tls-cert-file"); cert == "" {
		} else if pk := c.String("tls-private-key"); pk == "" {
		} else {
			options = append(options, server.TLSConfig(cert, pk))
		}

		if c.Bool("profiler") {
			options = append(options, server.EnableProfiler())
		}

		if c.Bool("force-https") {
			options = append(options, server.ForceHTTPs())
		}

		if httpAuthUser := c.String("http-auth-user"); httpAuthUser == "" {
		} else if httpAuthPass := c.String("http-auth-pass"); httpAuthPass == "" {
		} else {
			options = append(options, server.HttpAuthCredentials(httpAuthUser, httpAuthPass))
		}

		applyIPFilter := false
		ipFilterOptions := server.IPFilterOptions{}
		if ipWhitelist := c.String("ip-whitelist"); ipWhitelist != "" {
			applyIPFilter = true
			ipFilterOptions.AllowedIPs = strings.Split(ipWhitelist, ",")
			ipFilterOptions.BlockByDefault = true
		}

		if ipBlacklist := c.String("ip-blacklist"); ipBlacklist != "" {
			applyIPFilter = true
			ipFilterOptions.BlockedIPs = strings.Split(ipBlacklist, ",")
		}

		if applyIPFilter {
			options = append(options, server.FilterOptions(ipFilterOptions))
		}

		switch provider := c.String("provider"); provider {
		case "s3":
			if accessKey := c.String("aws-access-key"); accessKey == "" {
				panic("access-key not set.")
			} else if secretKey := c.String("aws-secret-key"); secretKey == "" {
				panic("secret-key not set.")
			} else if bucket := c.String("bucket"); bucket == "" {
				panic("bucket not set.")
			} else if storage, err := server.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-region"), c.String("s3-endpoint"), logger, c.Bool("s3-no-multipart"), c.Bool("s3-path-style")); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "gdrive":
			chunkSize := c.Int("gdrive-chunk-size")

			if clientJsonFilepath := c.String("gdrive-client-json-filepath"); clientJsonFilepath == "" {
				panic("client-json-filepath not set.")
			} else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
				panic("local-config-path not set.")
			} else if basedir := c.String("basedir"); basedir == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "storj":
			if endpoint := c.String("storj-endpoint"); endpoint == "" {
				panic("storj-endpoint not set.")
			} else if apiKey := c.String("storj-apikey"); apiKey == "" {
				panic("storj-apikey not set.")
			} else if bucket := c.String("storj-bucket"); bucket == "" {
				panic("storj-enckey not set.")
			} else if encKey := c.String("storj-enckey"); encKey == "" {
				panic("storj-bucket not set.")
			} else if storage, err := server.NewStorjStorage(endpoint, apiKey, bucket, encKey, logger); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "local":
			if v := c.String("basedir"); v == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewLocalStorage(v, logger); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		default:
			panic("Provider not set or invalid.")
		}

		srvr, err := server.New(
			options...,
		)

		if err != nil {
			logger.Println(color.RedString("Error starting server: %s", err.Error()))
			return
		}

		srvr.Run()
	}

	return &Cmd{
		App: app,
	}
}
<commit_msg>Add Env Vars<commit_after>package cmd

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/dutchcoders/transfer.sh/server"
	"github.com/fatih/color"
	"github.com/urfave/cli"
	"google.golang.org/api/googleapi"
)

var Version = "1.1.2"
var helpTemplate = `NAME:
{{.Name}} - {{.Usage}}

DESCRIPTION:
{{.Description}}

USAGE:
{{.Name}} {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]

COMMANDS:
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{if .Flags}}
FLAGS:
{{range .Flags}}{{.}}
{{end}}{{end}}
VERSION:
` + Version +
	`{{ "\n"}}`

var globalFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "listener",
		Usage: "127.0.0.1:8080",
		Value: "127.0.0.1:8080",
	},
	// redirect to https?
	// hostnames
	cli.StringFlag{
		Name:  "profile-listener",
		Usage: "127.0.0.1:6060",
		Value: "",
	},
	cli.BoolFlag{
		Name:  "force-https",
		Usage: "",
	},
	cli.StringFlag{
		Name:  "tls-listener",
		Usage: "127.0.0.1:8443",
		Value: "",
	},
	cli.BoolFlag{
		Name:  "tls-listener-only",
		Usage: "",
	},
	cli.StringFlag{
		Name:  "tls-cert-file",
		Value: "",
	},
	cli.StringFlag{
		Name:  "tls-private-key",
		Value: "",
	},
	cli.StringFlag{
		Name:  "temp-path",
		Usage: "path to temp files",
		Value: os.TempDir(),
	},
	cli.StringFlag{
		Name:  "web-path",
		Usage: "path to static web files",
		Value: "",
	},
	cli.StringFlag{
		Name:  "proxy-path",
		Usage: "path prefix when service is run behind a proxy",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ga-key",
		Usage: "key for google analytics (front end)",
		Value: "",
	},
	cli.StringFlag{
		Name:  "uservoice-key",
		Usage: "key for user voice (front end)",
		Value: "",
	},
	cli.StringFlag{
		Name:  "provider",
		Usage: "s3|gdrive|local",
		Value: "",
	},
	cli.StringFlag{
		Name:   "s3-endpoint",
		Usage:  "",
		Value:  "",
		EnvVar: "S3_ENDPOINT",
	},
	cli.StringFlag{
		Name:   "s3-region",
		Usage:  "",
		Value:  "eu-west-1",
		EnvVar: "S3_REGION",
	},
	cli.StringFlag{
		Name:   "aws-access-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_ACCESS_KEY",
	},
	cli.StringFlag{
		Name:   "aws-secret-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_SECRET_KEY",
	},
	cli.StringFlag{
		Name:   "bucket",
		Usage:  "",
		Value:  "",
		EnvVar: "BUCKET",
	},
	cli.BoolFlag{
		Name:  "s3-no-multipart",
		Usage: "Disables S3 Multipart Puts",
	},
	cli.BoolFlag{
		Name:  "s3-path-style",
		Usage: "Forces path style URLs, required for Minio.",
	},
	cli.StringFlag{
		Name:  "gdrive-client-json-filepath",
		Usage: "",
		Value: "",
	},
	cli.StringFlag{
		Name:  "gdrive-local-config-path",
		Usage: "",
		Value: "",
	},
	cli.IntFlag{
		Name:  "gdrive-chunk-size",
		Usage: "",
		Value: googleapi.DefaultUploadChunkSize / 1024 / 1024,
	},
	cli.StringFlag{
		Name:   "storj-endpoint",
		Usage:  "Satellite Address including Port.",
		Value:  "",
		EnvVar: "STORJ_ENDPOINT",
	},
	cli.StringFlag{
		Name:   "storj-apikey",
		Usage:  "",
		Value:  "",
		EnvVar: "STORJ_API_KEY",
	},
	cli.StringFlag{
		Name:   "storj-bucket",
		Usage:  "",
		Value:  "",
		EnvVar: "STORJ_BUCKET",
	},
	cli.StringFlag{
		Name:   "storj-enckey",
		Usage:  "Encryption Key for local file encryption",
		Value:  "",
		EnvVar: "STORJ_ENC_KEY",
	},
	cli.IntFlag{
		Name:   "rate-limit",
		Usage:  "requests per minute",
		Value:  0,
		EnvVar: "",
	},
	cli.StringFlag{
		Name:   "lets-encrypt-hosts",
		Usage:  "host1, host2",
		Value:  "",
		EnvVar: "HOSTS",
	},
	cli.StringFlag{
		Name:  "log",
		Usage: "/var/log/transfersh.log",
		Value: "",
	},
	cli.StringFlag{
		Name:  "basedir",
		Usage: "path to storage",
		Value: "",
	},
	cli.StringFlag{
		Name:   "clamav-host",
		Usage:  "clamav-host",
		Value:  "",
		EnvVar: "CLAMAV_HOST",
	},
	cli.StringFlag{
		Name:   "virustotal-key",
		Usage:  "virustotal-key",
		Value:  "",
		EnvVar: "VIRUSTOTAL_KEY",
	},
	cli.BoolFlag{
		Name:  "profiler",
		Usage: "enable profiling",
	},
	cli.StringFlag{
		Name:  "http-auth-user",
		Usage: "user for http basic auth",
		Value: "",
	},
	cli.StringFlag{
		Name:  "http-auth-pass",
		Usage: "pass for http basic auth",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ip-whitelist",
		Usage: "comma separated list of ips allowed to connect to the service",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ip-blacklist",
		Usage: "comma separated list of ips not allowed to connect to the service",
		Value: "",
	},
}

type Cmd struct {
	*cli.App
}

func VersionAction(c *cli.Context) {
	fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line")))
}

func New() *Cmd {
	logger := log.New(os.Stdout, "[transfer.sh]", log.LstdFlags)

	app := cli.NewApp()
	app.Name = "transfer.sh"
	app.Author = ""
	app.Usage = "transfer.sh"
	app.Description = `Easy file sharing from the command line`
	app.Version = Version
	app.Flags = globalFlags
	app.CustomAppHelpTemplate = helpTemplate
	app.Commands = []cli.Command{
		{
			Name:   "version",
			Action: VersionAction,
		},
	}

	app.Before = func(c *cli.Context) error {
		return nil
	}

	app.Action = func(c *cli.Context) {
		options := []server.OptionFn{}
		if v := c.String("listener"); v != "" {
			options = append(options, server.Listener(v))
		}

		if v := c.String("tls-listener"); v == "" {
		} else if c.Bool("tls-listener-only") {
			options = append(options, server.TLSListener(v, true))
		} else {
			options = append(options, server.TLSListener(v, false))
		}

		if v := c.String("profile-listener"); v != "" {
			options = append(options, server.ProfileListener(v))
		}

		if v := c.String("web-path"); v != "" {
			options = append(options, server.WebPath(v))
		}

		if v := c.String("proxy-path"); v != "" {
			options = append(options, server.ProxyPath(v))
		}

		if v := c.String("ga-key"); v != "" {
			options = append(options, server.GoogleAnalytics(v))
		}

		if v := c.String("uservoice-key"); v != "" {
			options = append(options, server.UserVoice(v))
		}

		if v := c.String("temp-path"); v != "" {
			options = append(options, server.TempPath(v))
		}

		if v := c.String("log"); v != "" {
			options = append(options, server.LogFile(logger, v))
		} else {
			options = append(options, server.Logger(logger))
		}

		if v := c.String("lets-encrypt-hosts"); v != "" {
			options = append(options, server.UseLetsEncrypt(strings.Split(v, ",")))
		}

		if v := c.String("virustotal-key"); v != "" {
			options = append(options, server.VirustotalKey(v))
		}

		if v := c.String("clamav-host"); v != "" {
			options = append(options, server.ClamavHost(v))
		}

		if v := c.Int("rate-limit"); v > 0 {
			options = append(options, server.RateLimit(v))
		}

		if cert := c.String("tls-cert-file"); cert == "" {
		} else if pk := c.String("tls-private-key"); pk == "" {
		} else {
			options = append(options, server.TLSConfig(cert, pk))
		}

		if c.Bool("profiler") {
			options = append(options, server.EnableProfiler())
		}

		if c.Bool("force-https") {
			options = append(options, server.ForceHTTPs())
		}

		if httpAuthUser := c.String("http-auth-user"); httpAuthUser == "" {
		} else if httpAuthPass := c.String("http-auth-pass"); httpAuthPass == "" {
		} else {
			options = append(options, server.HttpAuthCredentials(httpAuthUser, httpAuthPass))
		}

		applyIPFilter := false
		ipFilterOptions := server.IPFilterOptions{}
		if ipWhitelist := c.String("ip-whitelist"); ipWhitelist != "" {
			applyIPFilter = true
			ipFilterOptions.AllowedIPs = strings.Split(ipWhitelist, ",")
			ipFilterOptions.BlockByDefault = true
		}

		if ipBlacklist := c.String("ip-blacklist"); ipBlacklist != "" {
			applyIPFilter = true
			ipFilterOptions.BlockedIPs = strings.Split(ipBlacklist, ",")
		}

		if applyIPFilter {
			options = append(options, server.FilterOptions(ipFilterOptions))
		}

		switch provider := c.String("provider"); provider {
		case "s3":
			if accessKey := c.String("aws-access-key"); accessKey == "" {
				panic("access-key not set.")
			} else if secretKey := c.String("aws-secret-key"); secretKey == "" {
				panic("secret-key not set.")
			} else if bucket := c.String("bucket"); bucket == "" {
				panic("bucket not set.")
			} else if storage, err := server.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-region"), c.String("s3-endpoint"), logger, c.Bool("s3-no-multipart"), c.Bool("s3-path-style")); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "gdrive":
			chunkSize := c.Int("gdrive-chunk-size")

			if clientJsonFilepath := c.String("gdrive-client-json-filepath"); clientJsonFilepath == "" {
				panic("client-json-filepath not set.")
			} else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
				panic("local-config-path not set.")
			} else if basedir := c.String("basedir"); basedir == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "storj":
			if endpoint := c.String("storj-endpoint"); endpoint == "" {
				panic("storj-endpoint not set.")
			} else if apiKey := c.String("storj-apikey"); apiKey == "" {
				panic("storj-apikey not set.")
			} else if bucket := c.String("storj-bucket"); bucket == "" {
				panic("storj-enckey not set.")
			} else if encKey := c.String("storj-enckey"); encKey == "" {
				panic("storj-bucket not set.")
			} else if storage, err := server.NewStorjStorage(endpoint, apiKey, bucket, encKey, logger); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "local":
			if v := c.String("basedir"); v == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewLocalStorage(v, logger); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		default:
			panic("Provider not set or invalid.")
		}

		srvr, err := server.New(
			options...,
		)

		if err != nil {
			logger.Println(color.RedString("Error starting server: %s", err.Error()))
			return
		}

		srvr.Run()
	}

	return &Cmd{
		App: app,
	}
}
<|endoftext|>


<commit_before>package cmd

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

	flag "github.com/spf13/pflag"
)

var commands = map[string]*Command{}

// inspired by https://github.com/constabulary/gb/blob/master/cmd/cmd.go

// Command represents a subcommand, or plugin that is executed
type Command struct {
	// Name of the command
	name string

	// UsageLine demonstrates how to use this command
	usageLine string

	// Single line description of the purpose of the command
	short string

	// Description of this command
	long string

	// Run is invoked with arguments left over after flag parsing.
	run func(args []string) error

	// FlagSet for adding flags for that command
	fs *flag.FlagSet

	// function for adding flags for that command and any sub-Command FlagSet
	gfs func(*flag.FlagSet)

	// Parent Command
	parent parent

	// Subcommands
	subcmds map[string]*Command

	// Args passed when running command
	args []string
}

type parent interface {
	fullCommand() string
	parseFlags()
}

func NewCommand(name, usageLine, short, long string, run func([]string) error, parent *Command) *Command {
	cmd := &Command{
		name:      name,
		usageLine: usageLine,
		short:     short,
		long:      long,
		run:       run,
		fs:        flag.NewFlagSet(name, flag.ExitOnError),
		subcmds:   make(map[string]*Command),
		args:      []string{},
	}
	// fmt.Printf("New Command '%s', nil parent: %v\n", cmd.name, cmd.parent == nil)
	if parent == nil {
		// This is a root command
		commands[name] = cmd
	} else {
		cmd.parent = *parent
	}
	// fmt.Printf("registered parent for name '%s': %v\n", name, commands)
	return cmd
}

// FlagSet for adding flags for that command
func (cmd *Command) FS() *flag.FlagSet {
	return cmd.fs
}

// Set function for adding flags for that command and any sub-Command FlagSet
func (cmd *Command) SetGFS(gfs func(*flag.FlagSet)) {
	cmd.gfs = gfs
	gfs(cmd.fs)
}

// Runnable indicates this is a command that can be involved.
// Non runnable commands are only informational.
func (c *Command) Runnable() bool { return c.run != nil }

// RunCommand parses flags and runs the Command.
func RunCommand(args []string) error {
	// fmt.Printf("args %+v, c %v '%v'\n", os.Args, commands == nil, commands)
	cmd, err := commandFromArgs(os.Args)
	// fmt.Printf("cmd nil?? %v, args %+v, err='%v'\n", cmd == nil, os.Args, err)
	if err != nil {
		return err
	}
	cmd.parseFlags()
	if cmd.run == nil {
		return nil
	}
	return cmd.run(cmd.fs.Args())
}

func (cmd Command) parseFlags() {
	if cmd.parent != nil {
		cmd.parent.parseFlags()
	}
	if err := cmd.fs.Parse(cmd.args); err != nil {
		fmt.Printf("Incorrect usage of %s:", cmd.fullCommand())
		cmd.fs.Usage()
		os.Exit(1)
	}
}

func (cmd Command) fullCommand() string {
	res := cmd.name
	if cmd.parent != nil {
		res = cmd.parent.fullCommand() + " " + res
	}
	return res
}

func commandFromArgs(args []string) (*Command, error) {
	var cmd *Command
	// fmt.Printf("len %v\n", len(args))
	for i, arg := range args {
		if i == 0 {
			arg = filepath.Base(arg)
			ext := filepath.Ext(arg)
			if ext != "" {
				arg = arg[:len(arg)-len(ext)]
			}
		}
		// fmt.Printf("arg='%s'\n", arg)
		if arg == "--" {
			cmd.args = append(cmd.args, args[i:]...)
			return cmd, nil
		}
		if strings.HasPrefix(arg, "-") {
			cmd.args = append(cmd.args, arg)
			continue
		}
		var subcmd *Command
		// fmt.Printf("cmd nil %v, reg %v\n", cmd == nil, commands)
		if cmd == nil {
			cmd = commands[arg]
			if cmd == nil {
				return nil, fmt.Errorf("Unknown command '%s'", arg)
			}
		} else {
			subcmd = cmd.subcmds[arg]
			if subcmd == nil {
				cmd.args = append(cmd.args, arg)
			} else {
				cmd = subcmd
			}
		}
	}
	if cmd == nil {
		return nil, fmt.Errorf("Unknown command from args '%v'", args)
	}
	return cmd, nil
}
<commit_msg>cmd.go: differentiate local and global flags, add Usage()<commit_after>package cmd

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

	flag "github.com/spf13/pflag"
)

var commands = map[string]*Command{}

// inspired by https://github.com/constabulary/gb/blob/master/cmd/cmd.go

// Command represents a subcommand, or plugin that is executed
type Command struct {
	// Name of the command
	name string

	// UsageLine demonstrates how to use this command
	usageLine string

	// Single line description of the purpose of the command
	short string

	// Description of this command
	long string

	// Run is invoked with arguments left over after flag parsing.
	run func(args []string) error

	// FlagSet for that command only
	fs *flag.FlagSet
	// FlagSet for that command and all sub-commands
	gfs *flag.FlagSet
	// FlagSet combined (command only and all sub-commands)
	afs *flag.FlagSet
	// output buffer for afs
	abuf *bytes.Buffer

	// function for adding FlagSet for that command
	ffs func(*flag.FlagSet)

	// function for adding flags for that command and any sub-Command FlagSet
	fgfs func(*flag.FlagSet)

	// Parent Command
	parent parent

	// Subcommands
	subcmds map[string]*Command

	// Args passed when running command
	args []string
}

type parent interface {
	fullCommand() string
	parseFlags()
}

func NewCommand(name, usageLine, short, long string, run func([]string) error, parent *Command) *Command {
	cmd := &Command{
		name:      name,
		usageLine: usageLine,
		short:     short,
		long:      long,
		run:       run,
		fs:        flag.NewFlagSet(name, flag.ExitOnError),
		gfs:       flag.NewFlagSet(name, flag.ExitOnError),
		afs:       flag.NewFlagSet(name, flag.ContinueOnError),
		subcmds:   make(map[string]*Command),
		args:      []string{},
	}
	cmd.abuf = new(bytes.Buffer)
	cmd.afs.SetOutput(cmd.abuf)
	cmd.afs.Usage = cmd.FUsage
	// fmt.Printf("New Command '%s', nil parent: %v\n", cmd.name, cmd.parent == nil)
	if parent == nil {
		// This is a root command
		commands[name] = cmd
	} else {
		cmd.parent = *parent
	}
	// fmt.Printf("registered parent for name '%s': %v\n", name, commands)
	return cmd
}

// Set function for adding flags for that command and any sub-Command FlagSet
func (cmd *Command) SetGFS(fgfs func(*flag.FlagSet)) {
	cmd.fgfs = fgfs
	fgfs(cmd.afs)
	fgfs(cmd.gfs)
}

// Set function for adding flags for that command FlagSet only
func (cmd *Command) SetFS(ffs func(*flag.FlagSet)) {
	cmd.ffs = ffs
	ffs(cmd.afs)
	ffs(cmd.gfs)
}

// Runnable indicates this is a command that can be involved.
// Non runnable commands are only informational.
func (c *Command) Runnable() bool { return c.run != nil }

// RunCommand parses flags and runs the Command.
func RunCommand(args []string) error {
	// fmt.Printf("args %+v, c %v '%v'\n", os.Args, commands == nil, commands)
	cmd, err := commandFromArgs(os.Args)
	// fmt.Printf("cmd nil?? %v, args %+v, err='%v'\n", cmd == nil, os.Args, err)
	if err != nil {
		return err
	}
	cmd.parseFlags()
	if cmd.run == nil {
		return nil
	}
	return cmd.run(cmd.afs.Args())
}

func (cmd Command) parseFlags() {
	if cmd.parent != nil {
		cmd.parent.parseFlags()
	}
	if err := cmd.afs.Parse(cmd.args); err != nil {
		fmt.Printf("Incorrect usage of %s:\n", cmd.fullCommand())
		fmt.Printf("%s", cmd.abuf.String())
		os.Exit(1)
	}
}

func (cmd Command) fullCommand() string {
	res := cmd.name
	if cmd.parent != nil {
		res = cmd.parent.fullCommand() + " " + res
	}
	return res
}

func commandFromArgs(args []string) (*Command, error) {
	var cmd *Command
	// fmt.Printf("len %v\n", len(args))
	for i, arg := range args {
		if i == 0 {
			arg = filepath.Base(arg)
			ext := filepath.Ext(arg)
			if ext != "" {
				arg = arg[:len(arg)-len(ext)]
			}
		}
		// fmt.Printf("arg='%s'\n", arg)
		if arg == "--" {
			cmd.args = append(cmd.args, args[i:]...)
			return cmd, nil
		}
		if strings.HasPrefix(arg, "-") {
			cmd.args = append(cmd.args, arg)
			continue
		}
		var subcmd *Command
		// fmt.Printf("cmd nil %v, reg %v\n", cmd == nil, commands)
		if cmd == nil {
			cmd = commands[arg]
			if cmd == nil {
				return nil, fmt.Errorf("Unknown command '%s'", arg)
			}
		} else {
			subcmd = cmd.subcmds[arg]
			if subcmd == nil {
				cmd.args = append(cmd.args, arg)
			} else {
				cmd = subcmd
			}
		}
	}
	if cmd == nil {
		return nil, fmt.Errorf("Unknown command from args '%v'", args)
	}
	return cmd, nil
}

func (c *Command) FUsage() {
	s := strings.Split(c.abuf.String(), "\n")[0]
	c.abuf.Truncate(len(s))
	fmt.Fprintf(c.abuf, "\n%s", c.Usage())
}

func (c *Command) Usage() string {
	u := ""
	u = u + "local flags:\n"
	u = u + c.fs.FlagUsages()
	u = u + "global flags:\n"
	u = u + c.gfs.FlagUsages()
	return u
}
<|endoftext|>


<commit_before>package lmdbsync

import (
	"errors"
	"time"

	"github.com/bmatsuo/lmdb-go/lmdb"
)

type Handler interface {
	HandleTxnErr(c Bag, err error) (Bag, error)
}

// HandlerChain is a Handler implementation that iteratively calls each handler
// in the underlying slice when handling an error.
type HandlerChain []Handler

// HandleTxnError implements the Handler interface.  Each handler in c
// processes the Bag and error returned by the previous handler.  If RetryTxn
// is returned by a handler in c then processing stops and the current bag is
// returned with the error.
func (c HandlerChain) HandleTxnErr(b Bag, err error) (Bag, error) {
	for _, h := range c {
		b, err = h.HandleTxnErr(b, err)
		if err == RetryTxn {
			return b, err
		}
		if err == nil {
			return b, nil
		}
	}
	return b, err
}

func (c HandlerChain) Append(h ...Handler) HandlerChain {
	_c := make(HandlerChain, len(c)+len(h))
	copy(_c, c)
	copy(_c[len(c):], h)
	return _c
}

// MapResizedHandler returns a Handler than transparently retrie Txns that
// failed to start due to MapResized errors.
//
// When MapResizeHandler is in use transactions must not be nested inside other
// transactions.  Adopting the new map size requires all transactions to
// terminate first.  If any transactions wait for other transactions to
// complete they may deadlock in the presence of a MapResized error.
func MapResizedHandler(maxRetry int, repeatDelay func(retry int) time.Duration) Handler {
	return &resizedHandler{
		RetryResize:       maxRetry,
		DelayRepeatResize: repeatDelay,
	}
}

// MapFullFunc is a function for resizing a memory map after it has become
// full.  The function receives the current map size as its argument and
// returns a new map size.  The new size will only be applied if the second
// return value is true.
type MapFullFunc func(size int64) (int64, bool)

// MapFullHandler returns a Handler that retries Txns that failed due to
// MapFull errors by increasing the environment map size according to fn.
//
// A lmdb.TxnOp which is handled by the returned Handler will execute multiple
// times in the occurrance of a MapFull error.
//
// When MapFullHandler is in use update transactions must not be nested inside
// view transactions.  Resizing the database requires all transactions to
// terminate first.  If any transactions wait for update transactions to
// complete they may deadlock in the presence of a MapFull error.
func MapFullHandler(fn MapFullFunc) Handler {
	return &mapFullHandler{fn}
}

// The default number of times to retry a transaction that is returning
// repeatedly MapResized. This signifies rapid database growth from another
// process or some bug/corruption in memory.
//
// If DefaultRetryResize is less than zero the transaction will be retried
// indefinitely.
var DefaultRetryResize = 2

// If a transaction returns MapResize DefaultRetryResize times consequtively an
// Env will stop attempting to run it and return MapResize to the caller.
var DefaultDelayRepeatResize = time.Millisecond

// RetryTxn is returned by a Handler to have the Env retry the transaction.
var RetryTxn = errors.New("lmdbsync: retry failed txn")

// TxnRunner is an interface for types that can run lmdb transactions.
// TxnRunner is satisfied by Env.
type TxnRunner interface {
	RunTxn(flags uint, op lmdb.TxnOp) error
	View(op lmdb.TxnOp) error
	Update(op lmdb.TxnOp) error
	UpdateLocked(op lmdb.TxnOp) error
	WithHandler(h Handler) TxnRunner
}

type handlerRunner struct {
	env *Env
	h   Handler
}

func (r *handlerRunner) WithHandler(h Handler) TxnRunner {
	return &handlerRunner{
		env: r.env,
		h:   HandlerChain{r.h, h},
	}
}

func (r *handlerRunner) RunTxn(flags uint, op lmdb.TxnOp) error {
	readonly := flags&lmdb.Readonly != 0
	return r.env.runHandler(readonly, func() error { return r.env.RunTxn(flags, op) }, r.h)
}

func (r *handlerRunner) View(op lmdb.TxnOp) error {
	return r.env.runHandler(true, func() error { return r.env.View(op) }, r.h)
}

func (r *handlerRunner) Update(op lmdb.TxnOp) error {
	return r.env.runHandler(false, func() error { return r.env.Update(op) }, r.h)
}

func (r *handlerRunner) UpdateLocked(op lmdb.TxnOp) error {
	return r.env.runHandler(false, func() error { return r.env.UpdateLocked(op) }, r.h)
}

type mapFullHandler struct {
	fn MapFullFunc
}

func (h *mapFullHandler) HandleTxnErr(b Bag, err error) (Bag, error) {
	if !lmdb.IsMapFull(err) {
		return b, err
	}

	env := BagEnv(b)

	newsize, ok := h.getNewSize(env)
	if !ok {
		return b, err
	}
	if env.setMapSize(newsize, 0) != nil {
		return b, err
	}

	return b, RetryTxn
}

func (h *mapFullHandler) getNewSize(env *Env) (int64, bool) {
	info, err := env.Info()
	if err != nil {
		return 0, false
	}
	newsize, ok := h.fn(info.MapSize)
	if !ok || newsize <= info.MapSize {
		return 0, false
	}
	return newsize, true
}

type resizedHandlerBagKey int

type resizeRetryCount struct {
	n int
}

func (r *resizeRetryCount) Get() int {
	if r == nil {
		return 0
	}
	return r.n
}

func (r *resizeRetryCount) Add(n int) *resizeRetryCount {
	if r == nil {
		return &resizeRetryCount{1}
	}
	return &resizeRetryCount{r.n + 1}
}

func bagResizedRetryCount(b Bag) *resizeRetryCount {
	v, _ := b.Value(resizedHandlerBagKey(0)).(*resizeRetryCount)
	return v
}

func bagWithResizedRetryCount(b Bag, count *resizeRetryCount) Bag {
	return BagWith(b, resizedHandlerBagKey(0), count)
}

type resizedHandler struct {
	// RetryResize overrides DefaultRetryResize for the Env.
	RetryResize int
	// DelayRepeateResize overrides DefaultDelayRetryResize for the Env.
	DelayRepeatResize func(retry int) time.Duration
}

func (h *resizedHandler) getRetryResize() int {
	if h.RetryResize != 0 {
		return h.RetryResize
	}
	return DefaultRetryResize
}

func (h *resizedHandler) getDelayRepeatResize(i int) time.Duration {
	if h.DelayRepeatResize != nil {
		return h.DelayRepeatResize(i)
	}
	return DefaultDelayRepeatResize
}

func (h *resizedHandler) HandleTxnErr(b Bag, err error) (Bag, error) {
	if !lmdb.IsMapResized(err) {
		b := BagWith(b, resizedHandlerBagKey(0), nil)
		return b, err
	}

	env := BagEnv(b)
	count := bagResizedRetryCount(b)
	numRetry := count.Get()

	// fail the transaction with MapResized error when too many attempts have
	// been made.
	maxRetry := h.getRetryResize()
	if maxRetry == 0 {
		b := bagWithResizedRetryCount(b, nil)
		return b, err
	}
	if maxRetry > 0 && numRetry >= maxRetry {
		b := bagWithResizedRetryCount(b, nil)
		return b, err
	}

	b = bagWithResizedRetryCount(b, count.Add(1))

	var delay time.Duration
	if numRetry > 0 {
		delay = h.getDelayRepeatResize(numRetry)
	}

	err = env.setMapSize(0, delay)
	if err != nil {
		return b, err
	}
	return b, RetryTxn
}

type HandlerFunc func(c Bag, err error) (Bag, error)

func (fn HandlerFunc) HandleTxnErr(c Bag, err error) (Bag, error) {
	return fn(c, err)
}
<commit_msg>remove the HandlerFunc implementation which is so far unused<commit_after>package lmdbsync

import (
	"errors"
	"time"

	"github.com/bmatsuo/lmdb-go/lmdb"
)

type Handler interface {
	HandleTxnErr(c Bag, err error) (Bag, error)
}

// HandlerChain is a Handler implementation that iteratively calls each handler
// in the underlying slice when handling an error.
type HandlerChain []Handler

// HandleTxnError implements the Handler interface.  Each handler in c
// processes the Bag and error returned by the previous handler.  If RetryTxn
// is returned by a handler in c then processing stops and the current bag is
// returned with the error.
func (c HandlerChain) HandleTxnErr(b Bag, err error) (Bag, error) {
	for _, h := range c {
		b, err = h.HandleTxnErr(b, err)
		if err == RetryTxn {
			return b, err
		}
		if err == nil {
			return b, nil
		}
	}
	return b, err
}

func (c HandlerChain) Append(h ...Handler) HandlerChain {
	_c := make(HandlerChain, len(c)+len(h))
	copy(_c, c)
	copy(_c[len(c):], h)
	return _c
}

// MapResizedHandler returns a Handler than transparently retrie Txns that
// failed to start due to MapResized errors.
//
// When MapResizeHandler is in use transactions must not be nested inside other
// transactions.  Adopting the new map size requires all transactions to
// terminate first.  If any transactions wait for other transactions to
// complete they may deadlock in the presence of a MapResized error.
func MapResizedHandler(maxRetry int, repeatDelay func(retry int) time.Duration) Handler {
	return &resizedHandler{
		RetryResize:       maxRetry,
		DelayRepeatResize: repeatDelay,
	}
}

// MapFullFunc is a function for resizing a memory map after it has become
// full.  The function receives the current map size as its argument and
// returns a new map size.  The new size will only be applied if the second
// return value is true.
type MapFullFunc func(size int64) (int64, bool)

// MapFullHandler returns a Handler that retries Txns that failed due to
// MapFull errors by increasing the environment map size according to fn.
//
// A lmdb.TxnOp which is handled by the returned Handler will execute multiple
// times in the occurrance of a MapFull error.
//
// When MapFullHandler is in use update transactions must not be nested inside
// view transactions.  Resizing the database requires all transactions to
// terminate first.  If any transactions wait for update transactions to
// complete they may deadlock in the presence of a MapFull error.
func MapFullHandler(fn MapFullFunc) Handler {
	return &mapFullHandler{fn}
}

// The default number of times to retry a transaction that is returning
// repeatedly MapResized. This signifies rapid database growth from another
// process or some bug/corruption in memory.
//
// If DefaultRetryResize is less than zero the transaction will be retried
// indefinitely.
var DefaultRetryResize = 2

// If a transaction returns MapResize DefaultRetryResize times consequtively an
// Env will stop attempting to run it and return MapResize to the caller.
var DefaultDelayRepeatResize = time.Millisecond

// RetryTxn is returned by a Handler to have the Env retry the transaction.
var RetryTxn = errors.New("lmdbsync: retry failed txn")

// TxnRunner is an interface for types that can run lmdb transactions.
// TxnRunner is satisfied by Env.
type TxnRunner interface {
	RunTxn(flags uint, op lmdb.TxnOp) error
	View(op lmdb.TxnOp) error
	Update(op lmdb.TxnOp) error
	UpdateLocked(op lmdb.TxnOp) error
	WithHandler(h Handler) TxnRunner
}

type handlerRunner struct {
	env *Env
	h   Handler
}

func (r *handlerRunner) WithHandler(h Handler) TxnRunner {
	return &handlerRunner{
		env: r.env,
		h:   HandlerChain{r.h, h},
	}
}

func (r *handlerRunner) RunTxn(flags uint, op lmdb.TxnOp) error {
	readonly := flags&lmdb.Readonly != 0
	return r.env.runHandler(readonly, func() error { return r.env.RunTxn(flags, op) }, r.h)
}

func (r *handlerRunner) View(op lmdb.TxnOp) error {
	return r.env.runHandler(true, func() error { return r.env.View(op) }, r.h)
}

func (r *handlerRunner) Update(op lmdb.TxnOp) error {
	return r.env.runHandler(false, func() error { return r.env.Update(op) }, r.h)
}

func (r *handlerRunner) UpdateLocked(op lmdb.TxnOp) error {
	return r.env.runHandler(false, func() error { return r.env.UpdateLocked(op) }, r.h)
}

type mapFullHandler struct {
	fn MapFullFunc
}

func (h *mapFullHandler) HandleTxnErr(b Bag, err error) (Bag, error) {
	if !lmdb.IsMapFull(err) {
		return b, err
	}

	env := BagEnv(b)

	newsize, ok := h.getNewSize(env)
	if !ok {
		return b, err
	}
	if env.setMapSize(newsize, 0) != nil {
		return b, err
	}

	return b, RetryTxn
}

func (h *mapFullHandler) getNewSize(env *Env) (int64, bool) {
	info, err := env.Info()
	if err != nil {
		return 0, false
	}
	newsize, ok := h.fn(info.MapSize)
	if !ok || newsize <= info.MapSize {
		return 0, false
	}
	return newsize, true
}

type resizedHandlerBagKey int

type resizeRetryCount struct {
	n int
}

func (r *resizeRetryCount) Get() int {
	if r == nil {
		return 0
	}
	return r.n
}

func (r *resizeRetryCount) Add(n int) *resizeRetryCount {
	if r == nil {
		return &resizeRetryCount{1}
	}
	return &resizeRetryCount{r.n + 1}
}

func bagResizedRetryCount(b Bag) *resizeRetryCount {
	v, _ := b.Value(resizedHandlerBagKey(0)).(*resizeRetryCount)
	return v
}

func bagWithResizedRetryCount(b Bag, count *resizeRetryCount) Bag {
	return BagWith(b, resizedHandlerBagKey(0), count)
}

type resizedHandler struct {
	// RetryResize overrides DefaultRetryResize for the Env.
	RetryResize int
	// DelayRepeateResize overrides DefaultDelayRetryResize for the Env.
	DelayRepeatResize func(retry int) time.Duration
}

func (h *resizedHandler) getRetryResize() int {
	if h.RetryResize != 0 {
		return h.RetryResize
	}
	return DefaultRetryResize
}

func (h *resizedHandler) getDelayRepeatResize(i int) time.Duration {
	if h.DelayRepeatResize != nil {
		return h.DelayRepeatResize(i)
	}
	return DefaultDelayRepeatResize
}

func (h *resizedHandler) HandleTxnErr(b Bag, err error) (Bag, error) {
	if !lmdb.IsMapResized(err) {
		b := BagWith(b, resizedHandlerBagKey(0), nil)
		return b, err
	}

	env := BagEnv(b)
	count := bagResizedRetryCount(b)
	numRetry := count.Get()

	// fail the transaction with MapResized error when too many attempts have
	// been made.
	maxRetry := h.getRetryResize()
	if maxRetry == 0 {
		b := bagWithResizedRetryCount(b, nil)
		return b, err
	}
	if maxRetry > 0 && numRetry >= maxRetry {
		b := bagWithResizedRetryCount(b, nil)
		return b, err
	}

	b = bagWithResizedRetryCount(b, count.Add(1))

	var delay time.Duration
	if numRetry > 0 {
		delay = h.getDelayRepeatResize(numRetry)
	}

	err = env.setMapSize(0, delay)
	if err != nil {
		return b, err
	}
	return b, RetryTxn
}
<|endoftext|>


<commit_before>package main

import (
	"io"
)

type ObjectType int

func (t ObjectType) String() string {
	switch t {
	case COMMIT:
		return "commit"
	case TREE:
		return "tree"
	case BLOB:
		return "blob"
	case TAG:
		return "tag"
	case DELTA1:
		return "delta1"
	case DELTA2:
		return "delta2"
	}
	return "Unknown type"
}

const (
	_ = iota
	COMMIT
	TREE
	BLOB
	TAG
	_
	DELTA1
	DELTA2
)

func ReadPackedDataAtOffset(offset int64, in io.ReadSeeker) (ObjectType, int, []byte, error) {
	_, err := in.Seek(offset, 0)
	if err != nil {
		return 0, 0, nil, err
	}
	headByte := make([]byte, 1, 1)
	_, err = in.Read(headByte)
	if err != nil {
		return 0, 0, nil, err
	}

	objectType := (int(headByte[0]) & 0x70) >> 4
	size := (int(headByte[0])) & int(0x0f)
	var shiftBit uint = 4
	for {
		sizeByte := make([]byte, 1, 1)
		_, err = in.Read(sizeByte)
		if err != nil {
			return 0, 0, nil, err
		}
		sizeByteInInt := int(sizeByte[0])
		size = size + ((sizeByteInInt & 0x7f) << shiftBit)
		shiftBit += 7
		cont := (sizeByteInInt & 0x80) >> 7
		if cont == 0 {
			break
		}
	}

	buff := make([]byte, size, size)
	_, err = in.Read(buff)
	if err != nil {
		return 0, 0, nil, err
	}

	return ObjectType(objectType), size, buff, err
}

<commit_msg>unpack update<commit_after>package main

import (
	"io"
	"compress/zlib"
)

type ObjectType int

func (t ObjectType) String() string {
	switch t {
	case COMMIT:
		return "commit"
	case TREE:
		return "tree"
	case BLOB:
		return "blob"
	case TAG:
		return "tag"
	case DELTA1:
		return "delta1"
	case DELTA2:
		return "delta2"
	}
	return "Unknown type"
}

const (
	_ = iota
	COMMIT
	TREE
	BLOB
	TAG
	_
	DELTA1
	DELTA2
)

func ReadPackedDataAtOffset(offset int64, in io.ReadSeeker) (ObjectType, int, []byte, error) {
	_, err := in.Seek(offset, 0)
	if err != nil {
		return 0, 0, nil, err
	}
	headByte := make([]byte, 1, 1)
	_, err = in.Read(headByte)
	if err != nil {
		return 0, 0, nil, err
	}

	objectType := ObjectType((int(headByte[0]) & 0x70) >> 4)
	objectSize := (int(headByte[0])) & int(0x0f)
	var shiftBit uint = 4
	for {
		sizeByte := make([]byte, 1, 1)
		_, err = in.Read(sizeByte)
		if err != nil {
			return 0, 0, nil, err
		}
		sizeByteInInt := int(sizeByte[0])
		objectSize = objectSize + ((sizeByteInInt & 0x7f) << shiftBit)
		shiftBit += 7
		cont := (sizeByteInInt & 0x80) >> 7
		if cont == 0 {
			break
		}
	}

	var buff []byte
	switch (objectType) {
		case TREE, BLOB, COMMIT:
			buff, err = readPackedBasicObject(in, objectSize)
		case DELTA1:
		case DELTA2:
	}

	return objectType, objectSize, buff, err
}

func readPackedBasicObject(in io.Reader, objectSize int) ([]byte, error) {
	buff := make([]byte, objectSize)
	zr, err := zlib.NewReader(in)
	if err != nil {
		return nil, err
	}
	defer zr.Close()
	n, err := zr.Read(buff)
	if err != nil {
		if err == io.EOF {
			err = nil
		} else {
			return nil, err
		}
	}
	buff = buff[:n]
	return buff, nil
}

<|endoftext|>


<commit_before>package main

import (
	//import libraries
	"fmt"
	"net/http"
	"io/ioutil"
	"strings"
	"time"
	"bufio"
	"os"
	"errors"
)

type Server struct {
	health bool
	service string
}

type Servers map[string]*Server

//adds server to servers hash table
func addserver(servers map[string]*Server, url string, service string) {
	servers[url] = &Server{false, service}
}

//removes server from servers hash table
func rmserver(servers map[string]*Server, url string){
	delete(servers, url)
}

//runs health checks on all servers
func loopservers(mm *Maglev, servers map[string]*Server, num float64, timeout int){
	for k:= range servers{
		go loop(mm, servers, k, num, timeout)
	}
}

//runs health check on a single server
func loop(mm *Maglev, servers map[string]*Server, url string, num float64, timeout int) {
	count := 0
	boo := true

	for boo{
		num := time.Duration(num)

		time.Sleep(num * time.Millisecond)
		//fmt.Println(url, health(url), "\n", count, servers)

		if health(url) != true{
			count += 1
			fmt.Println(count)
		}

		if health(url) == true {
			count = 0
			servers[url].health = true
			mm.Add(url)
		}

		if count >= timeout{ //change this later
			servers[url].health = false
			mm.Remove(url)
		}

	}
}

//checks health of server
func health(url string) bool{
	resp, _ := http.Get(url)
	bytes, _ := ioutil.ReadAll(resp.Body)

	resp.Body.Close()

	if resp == nil {
		return false
	}

	if strings.Contains(string(bytes),"healthy") {
		return true
	}

	return false
}

const BlockSize = 64

// Hash returns the 64-bit SipHash-2-4 of the given byte slice with two 64-bit
// parts of 128-bit key: k0 and k1.
func Hash(k0, k1 uint64, p []byte) uint64 {
	// Initialization.
	v0 := k0 ^ 0x736f6d6570736575
	v1 := k1 ^ 0x646f72616e646f6d
	v2 := k0 ^ 0x6c7967656e657261
	v3 := k1 ^ 0x7465646279746573
	t := uint64(len(p)) << 56

	// Compression.
	for len(p) >= BlockSize {
		m := uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 |
			uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56
		v3 ^= m

		// Round 1.
		v0 += v1
		v1 = v1<<13 | v1>>(64-13)
		v1 ^= v0
		v0 = v0<<32 | v0>>(64-32)

		v2 += v3
		v3 = v3<<16 | v3>>(64-16)
		v3 ^= v2

		v0 += v3
		v3 = v3<<21 | v3>>(64-21)
		v3 ^= v0

		v2 += v1
		v1 = v1<<17 | v1>>(64-17)
		v1 ^= v2
		v2 = v2<<32 | v2>>(64-32)

		// Round 2.
		v0 += v1
		v1 = v1<<13 | v1>>(64-13)
		v1 ^= v0
		v0 = v0<<32 | v0>>(64-32)

		v2 += v3
		v3 = v3<<16 | v3>>(64-16)
		v3 ^= v2

		v0 += v3
		v3 = v3<<21 | v3>>(64-21)
		v3 ^= v0

		v2 += v1
		v1 = v1<<17 | v1>>(64-17)
		v1 ^= v2
		v2 = v2<<32 | v2>>(64-32)

		v0 ^= m
		p = p[BlockSize:]
	}

	// Compress last block.
	switch len(p) {
	case 7:
		t |= uint64(p[6]) << 48
		fallthrough
	case 6:
		t |= uint64(p[5]) << 40
		fallthrough
	case 5:
		t |= uint64(p[4]) << 32
		fallthrough
	case 4:
		t |= uint64(p[3]) << 24
		fallthrough
	case 3:
		t |= uint64(p[2]) << 16
		fallthrough
	case 2:
		t |= uint64(p[1]) << 8
		fallthrough
	case 1:
		t |= uint64(p[0])
	}

	v3 ^= t

	// Round 1.
	v0 += v1
	v1 = v1<<13 | v1>>(64-13)
	v1 ^= v0
	v0 = v0<<32 | v0>>(64-32)

	v2 += v3
	v3 = v3<<16 | v3>>(64-16)
	v3 ^= v2

	v0 += v3
	v3 = v3<<21 | v3>>(64-21)
	v3 ^= v0

	v2 += v1
	v1 = v1<<17 | v1>>(64-17)
	v1 ^= v2
	v2 = v2<<32 | v2>>(64-32)

	// Round 2.
	v0 += v1
	v1 = v1<<13 | v1>>(64-13)
	v1 ^= v0
	v0 = v0<<32 | v0>>(64-32)

	v2 += v3
	v3 = v3<<16 | v3>>(64-16)
	v3 ^= v2

	v0 += v3
	v3 = v3<<21 | v3>>(64-21)
	v3 ^= v0

	v2 += v1
	v1 = v1<<17 | v1>>(64-17)
	v1 ^= v2
	v2 = v2<<32 | v2>>(64-32)

	v0 ^= t

	// Finalization.
	v2 ^= 0xff

	// Round 1.
	v0 += v1
	v1 = v1<<13 | v1>>(64-13)
	v1 ^= v0
	v0 = v0<<32 | v0>>(64-32)

	v2 += v3
	v3 = v3<<16 | v3>>(64-16)
	v3 ^= v2

	v0 += v3
	v3 = v3<<21 | v3>>(64-21)
	v3 ^= v0

	v2 += v1
	v1 = v1<<17 | v1>>(64-17)
	v1 ^= v2
	v2 = v2<<32 | v2>>(64-32)

	// Round 2.
	v0 += v1
	v1 = v1<<13 | v1>>(64-13)
	v1 ^= v0
	v0 = v0<<32 | v0>>(64-32)

	v2 += v3
	v3 = v3<<16 | v3>>(64-16)
	v3 ^= v2

	v0 += v3
	v3 = v3<<21 | v3>>(64-21)
	v3 ^= v0

	v2 += v1
	v1 = v1<<17 | v1>>(64-17)
	v1 ^= v2
	v2 = v2<<32 | v2>>(64-32)

	// Round 3.
	v0 += v1
	v1 = v1<<13 | v1>>(64-13)
	v1 ^= v0
	v0 = v0<<32 | v0>>(64-32)

	v2 += v3
	v3 = v3<<16 | v3>>(64-16)
	v3 ^= v2

	v0 += v3
	v3 = v3<<21 | v3>>(64-21)
	v3 ^= v0

	v2 += v1
	v1 = v1<<17 | v1>>(64-17)
	v1 ^= v2
	v2 = v2<<32 | v2>>(64-32)

	// Round 4.
	v0 += v1
	v1 = v1<<13 | v1>>(64-13)
	v1 ^= v0
	v0 = v0<<32 | v0>>(64-32)

	v2 += v3
	v3 = v3<<16 | v3>>(64-16)
	v3 ^= v2

	v0 += v3
	v3 = v3<<21 | v3>>(64-21)
	v3 ^= v0

	v2 += v1
	v1 = v1<<17 | v1>>(64-17)
	v1 ^= v2
	v2 = v2<<32 | v2>>(64-32)

	return v0 ^ v1 ^ v2 ^ v3
}

const (
	bigM uint64 = 65537
)

//Maglev :
type Maglev struct {
	n           uint64 //size of VIP backends
	m           uint64 //sie of the lookup table
	permutation [][]uint64
	lookup      []int64
	nodeList    []string
}

//NewMaglev :
func NewMaglev(backends []string, m uint64) *Maglev {
	mag := &Maglev{n: uint64(len(backends)), m: m}
	mag.nodeList = backends
	mag.generatePopulation()
	mag.populate()
	return mag
}

//Add : Return nil if add success, otherwise return error
func (m *Maglev) Add(backend string) error {
	for _, v := range m.nodeList {
		if v == backend {
			return errors.New("Exist already")
		}
	}

	m.nodeList = append(m.nodeList, backend)
	m.n = uint64(len(m.nodeList))
	m.generatePopulation()
	m.populate()
	return nil
}

//Remove :
func (m *Maglev) Remove(backend string) error {
	notFound := true
	for _, v := range m.nodeList {
		if v == backend {
			notFound = false
		}
	}
	if notFound {
		return errors.New("Not found")
	}

	for i, v := range m.nodeList {
		if v == backend {
			m.nodeList = append(m.nodeList[:i], m.nodeList[i+1:]...)
			break
		}
	}

	m.n = uint64(len(m.nodeList))
	m.generatePopulation()
	m.populate()
	return nil
}

//Get :Get node name by object string.
func (m *Maglev) Get(obj string) (string, error) {
	if len(m.nodeList) == 0 {
		return "", errors.New("Empty")
	}
	key := m.hashKey(obj)
	return m.nodeList[m.lookup[key%m.m]], nil
}

func (m *Maglev) hashKey(obj string) uint64 {
	return Hash(0xdeadbabe, 0, []byte(obj))
}

func (m *Maglev) generatePopulation() {
	if len(m.nodeList) == 0 {
		return
	}

	for i := 0; i < len(m.nodeList); i++ {
		bData := []byte(m.nodeList[i])

		offset := Hash(0xdeadbabe, 0, bData) % m.m
		skip := (Hash(0xdeadbeef, 0, bData) % (m.m - 1)) + 1

		iRow := make([]uint64, m.m)
		var j uint64
		for j = 0; j < m.m; j++ {
			iRow[j] = (offset + uint64(j)*skip) % m.m
		}

		m.permutation = append(m.permutation, iRow)
	}
}

func (m *Maglev) populate() {
	if len(m.nodeList) == 0 {
		return
	}

	var i, j uint64
	next := make([]uint64, m.n)
	entry := make([]int64, m.m)
	for j = 0; j < m.m; j++ {
		entry[j] = -1
	}

	var n uint64

	for { //true
		for i = 0; i < m.n; i++ {
			c := m.permutation[i][next[i]]
			for entry[c] >= 0 {
				next[i] = next[i] + 1
				c = m.permutation[i][next[i]]
			}

			entry[c] = int64(i)
			next[i] = next[i] + 1
			n++

			if n == m.m {
				m.lookup = entry
				return
			}
		}

	}

}

func serverstring(servers map[string]*Server) []string{
	var names []string
	for k:= range servers{
		names = append(names, k)
	}
	return names
}

const sizeN = 2
const lookupSizeM = 13

func main() {
	servers := make(Servers)

	addserver(servers, "http://cheesy-fries.mit.edu/health", "service")
	addserver(servers, "http://strawberry-habanero.mit.edu/health", "service")

	names := serverstring(servers)
  	mm := NewMaglev(names, lookupSizeM)

	loopservers(mm, servers, 100, 500)

    fmt.Printf("%v\n", mm.lookup)
    ret := make(map[string]string)
    packets := []string{"19.168.124.100/572/81.9.179.69/80/4", "192.16.124.100/50270/81.209.179.69/80/6", "12.168.12.100/50268/81.209.179.69/80/6", "192.168.1.0/50266/81.209.179.69/80/6", "92.168.124.100/50264/81.209.179.69/80/6"}
    for i := 0; i < len(packets); i++ {
      serv, _ := mm.Get(packets[i])
      ret[packets[i]] = serv
    }
    fmt.Printf("5-tuple to Server mapping:\n")
    for k, v := range ret {
      fmt.Printf("%v: %v\n", k, v)
    }

	//takes user input command to add or remove server
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		var input = scanner.Text()
		fmt.Println("Executing: ",input)
		words := strings.Fields(input)

		//add server works but rm server makes loopserver in line 27 crash
		//need to implement channel...?
		if strings.Contains(input, "rmserver"){
			rmserver(servers, words[1])
			fmt.Println(servers)
		}

		if strings.Contains(input, "addserver"){
			addserver(servers, words[1], words[2])
			fmt.Println(servers)
		}
	}
}<commit_msg>Delete combine.go<commit_after><|endoftext|>


<commit_before>package pearl

import "fmt"

// Command represents a cell packet command byte.
type Command byte

// Enumerate all possible cell commands.
//
// Reference: https://github.com/torproject/torspec/blob/master/tor-spec.txt#L418-L438
//
//	   The 'Command' field of a fixed-length cell holds one of the following
//	   values:
//	         0 -- PADDING     (Padding)                 (See Sec 7.2)
//	         1 -- CREATE      (Create a circuit)        (See Sec 5.1)
//	         2 -- CREATED     (Acknowledge create)      (See Sec 5.1)
//	         3 -- RELAY       (End-to-end data)         (See Sec 5.5 and 6)
//	         4 -- DESTROY     (Stop using a circuit)    (See Sec 5.4)
//	         5 -- CREATE_FAST (Create a circuit, no PK) (See Sec 5.1)
//	         6 -- CREATED_FAST (Circuit created, no PK) (See Sec 5.1)
//	         8 -- NETINFO     (Time and address info)   (See Sec 4.5)
//	         9 -- RELAY_EARLY (End-to-end data; limited)(See Sec 5.6)
//	         10 -- CREATE2    (Extended CREATE cell)    (See Sec 5.1)
//	         11 -- CREATED2   (Extended CREATED cell)    (See Sec 5.1)
//
//	    Variable-length command values are:
//	         7 -- VERSIONS    (Negotiate proto version) (See Sec 4)
//	         128 -- VPADDING  (Variable-length padding) (See Sec 7.2)
//	         129 -- CERTS     (Certificates)            (See Sec 4.2)
//	         130 -- AUTH_CHALLENGE (Challenge value)    (See Sec 4.3)
//	         131 -- AUTHENTICATE (Client authentication)(See Sec 4.5)
//	         132 -- AUTHORIZE (Client authorization)    (Not yet used)
//
const (
	Padding       Command = 0
	Create        Command = 1
	Created       Command = 2
	Relay         Command = 3
	Destroy       Command = 4
	CreateFast    Command = 5
	CreatedFast   Command = 6
	Netinfo       Command = 8
	RelayEarly    Command = 9
	Create2       Command = 10
	Created2      Command = 11
	Versions      Command = 7
	Vpadding      Command = 128
	Certs         Command = 129
	AuthChallenge Command = 130
	Authenticate  Command = 131
	Authorize     Command = 132
)

var commandStrings = map[Command]string{
	0:   "PADDING",
	1:   "CREATE",
	2:   "CREATED",
	3:   "RELAY",
	4:   "DESTROY",
	5:   "CREATE_FAST",
	6:   "CREATED_FAST",
	8:   "NETINFO",
	9:   "RELAY_EARLY",
	10:  "CREATE2",
	11:  "CREATED2",
	7:   "VERSIONS",
	128: "VPADDING",
	129: "CERTS",
	130: "AUTH_CHALLENGE",
	131: "AUTHENTICATE",
	132: "AUTHORIZE",
}

func (c Command) String() string {
	s, ok := commandStrings[c]
	if ok {
		return s
	}
	return fmt.Sprintf("Command(%d)", byte(c))
}

// IsCommand determines whether the given byte is a recognized cell command.
func IsCommand(c byte) bool {
	_, ok := commandStrings[c]
	return ok
}
<commit_msg>goimports<commit_after>package pearl

import "fmt"

// Command represents a cell packet command byte.
type Command byte

// Enumerate all possible cell commands.
//
// Reference: https://github.com/torproject/torspec/blob/master/tor-spec.txt#L418-L438
//
//	   The 'Command' field of a fixed-length cell holds one of the following
//	   values:
//	         0 -- PADDING     (Padding)                 (See Sec 7.2)
//	         1 -- CREATE      (Create a circuit)        (See Sec 5.1)
//	         2 -- CREATED     (Acknowledge create)      (See Sec 5.1)
//	         3 -- RELAY       (End-to-end data)         (See Sec 5.5 and 6)
//	         4 -- DESTROY     (Stop using a circuit)    (See Sec 5.4)
//	         5 -- CREATE_FAST (Create a circuit, no PK) (See Sec 5.1)
//	         6 -- CREATED_FAST (Circuit created, no PK) (See Sec 5.1)
//	         8 -- NETINFO     (Time and address info)   (See Sec 4.5)
//	         9 -- RELAY_EARLY (End-to-end data; limited)(See Sec 5.6)
//	         10 -- CREATE2    (Extended CREATE cell)    (See Sec 5.1)
//	         11 -- CREATED2   (Extended CREATED cell)    (See Sec 5.1)
//
//	    Variable-length command values are:
//	         7 -- VERSIONS    (Negotiate proto version) (See Sec 4)
//	         128 -- VPADDING  (Variable-length padding) (See Sec 7.2)
//	         129 -- CERTS     (Certificates)            (See Sec 4.2)
//	         130 -- AUTH_CHALLENGE (Challenge value)    (See Sec 4.3)
//	         131 -- AUTHENTICATE (Client authentication)(See Sec 4.5)
//	         132 -- AUTHORIZE (Client authorization)    (Not yet used)
//
const (
	Padding       Command = 0
	Create        Command = 1
	Created       Command = 2
	Relay         Command = 3
	Destroy       Command = 4
	CreateFast    Command = 5
	CreatedFast   Command = 6
	Netinfo       Command = 8
	RelayEarly    Command = 9
	Create2       Command = 10
	Created2      Command = 11
	Versions      Command = 7
	Vpadding      Command = 128
	Certs         Command = 129
	AuthChallenge Command = 130
	Authenticate  Command = 131
	Authorize     Command = 132
)

var commandStrings = map[Command]string{
	0:   "PADDING",
	1:   "CREATE",
	2:   "CREATED",
	3:   "RELAY",
	4:   "DESTROY",
	5:   "CREATE_FAST",
	6:   "CREATED_FAST",
	8:   "NETINFO",
	9:   "RELAY_EARLY",
	10:  "CREATE2",
	11:  "CREATED2",
	7:   "VERSIONS",
	128: "VPADDING",
	129: "CERTS",
	130: "AUTH_CHALLENGE",
	131: "AUTHENTICATE",
	132: "AUTHORIZE",
}

func (c Command) String() string {
	s, ok := commandStrings[c]
	if ok {
		return s
	}
	return fmt.Sprintf("Command(%d)", byte(c))
}

// IsCommand determines whether the given byte is a recognized cell command.
func IsCommand(c byte) bool {
	_, ok := commandStrings[c]
	return ok
}
<|endoftext|>


<commit_before>package translation

import "io"
<commit_msg>refactor: removed 'translation/rfs.go' as it is not used anywhere<commit_after><|endoftext|>