<commit_before>package cmds

import (
	"errors"
	"fmt"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"runtime"
	"strconv"
	"syscall"

	"github.com/derekparker/delve/config"
	"github.com/derekparker/delve/service"
	"github.com/derekparker/delve/service/api"
	"github.com/derekparker/delve/service/rpc"
	"github.com/derekparker/delve/terminal"
	"github.com/derekparker/delve/version"
	"github.com/spf13/cobra"
)

var (
	// Log is whether to log debug statements.
	Log bool
	// Headless is whether to run without terminal.
	Headless bool
	// AcceptMulti allows multiple clients to connect to the same server
	AcceptMulti bool
	// Addr is the debugging server listen address.
	Addr string
	// InitFile is the path to initialization file.
	InitFile string
	// BuildFlags is the flags passed during compiler invocation.
	BuildFlags string

	// RootCommand is the root of the command tree.
	RootCommand *cobra.Command

	traceAttachPid  int
	traceStackDepth int

	conf *config.Config
)

const (
	debugname     = "debug"
	testdebugname = "debug.test"
)

const dlvCommandLongDesc = `Delve is a source level debugger for Go programs.

Delve enables you to interact with your program by controlling the execution of the process,
evaluating variables, and providing information of thread / goroutine state, CPU register state and more.

The goal of this tool is to provide a simple yet powerful interface for debugging Go programs.
`

// New returns an initialized command tree.
func New() *cobra.Command {
	// Config setup and load.
	conf = config.LoadConfig()
	buildFlagsDefault := ""
	if runtime.GOOS == "windows" {
		// Work-around for https://github.com/golang/go/issues/13154
		buildFlagsDefault = "-ldflags=-linkmode internal"
	}

	// Main dlv root command.
	RootCommand = &cobra.Command{
		Use:   "dlv",
		Short: "Delve is a debugger for the Go programming language.",
		Long:  dlvCommandLongDesc,
	}

	RootCommand.PersistentFlags().StringVarP(&Addr, "listen", "l", "localhost:0", "Debugging server listen address.")
	RootCommand.PersistentFlags().BoolVarP(&Log, "log", "", false, "Enable debugging server logging.")
	RootCommand.PersistentFlags().BoolVarP(&Headless, "headless", "", false, "Run debug server only, in headless mode.")
	RootCommand.PersistentFlags().BoolVarP(&AcceptMulti, "accept-multiclient", "", false, "Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate")
	RootCommand.PersistentFlags().StringVar(&InitFile, "init", "", "Init file, executed by the terminal client.")
	RootCommand.PersistentFlags().StringVar(&BuildFlags, "build-flags", buildFlagsDefault, "Build flags, to be passed to the compiler.")

	// 'version' subcommand.
	versionCommand := &cobra.Command{
		Use:   "version",
		Short: "Prints version.",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("Delve Debugger\n%s\n", version.DelveVersion)
		},
	}
	RootCommand.AddCommand(versionCommand)

	// Deprecated 'run' subcommand.
	runCommand := &cobra.Command{
		Use:   "run",
		Short: "Deprecated command. Use 'debug' instead.",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("This command is deprecated, please use 'debug' instead.")
			os.Exit(0)
		},
	}
	RootCommand.AddCommand(runCommand)

	// 'debug' subcommand.
	debugCommand := &cobra.Command{
		Use:   "debug [package]",
		Short: "Compile and begin debugging program.",
		Long: `Compiles your program with optimizations disabled,
starts and attaches to it, and enables you to immediately begin debugging your program.`,
		Run: debugCmd,
	}
	RootCommand.AddCommand(debugCommand)

	// 'exec' subcommand.
	execCommand := &cobra.Command{
		Use:   "exec [./path/to/binary]",
		Short: "Runs precompiled binary, attaches and begins debug session.",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return errors.New("you must provide a path to a binary")
			}
			return nil
		},
		Run: func(cmd *cobra.Command, args []string) {
			os.Exit(execute(0, args, conf))
		},
	}
	RootCommand.AddCommand(execCommand)

	// 'trace' subcommand.
	traceCommand := &cobra.Command{
		Use:   "trace [package] regexp",
		Short: "Compile and begin tracing program.",
		Long:  "Trace program execution. Will set a tracepoint on every function matching the provided regular expression and output information when tracepoint is hit.",
		Run:   traceCmd,
	}
	traceCommand.Flags().IntVarP(&traceAttachPid, "pid", "p", 0, "Pid to attach to.")
	traceCommand.Flags().IntVarP(&traceStackDepth, "stack", "s", 0, "Show stack trace with given depth.")
	RootCommand.AddCommand(traceCommand)

	// 'test' subcommand.
	testCommand := &cobra.Command{
		Use:   "test [package]",
		Short: "Compile test binary and begin debugging program.",
		Long:  `Compiles a test binary with optimizations disabled, starts and attaches to it, and enable you to immediately begin debugging your program.`,
		Run:   testCmd,
	}
	RootCommand.AddCommand(testCommand)

	// 'attach' subcommand.
	attachCommand := &cobra.Command{
		Use:   "attach pid",
		Short: "Attach to running process and begin debugging.",
		Long:  "Attach to running process and begin debugging.",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return errors.New("you must provide a PID")
			}
			return nil
		},
		Run: attachCmd,
	}
	RootCommand.AddCommand(attachCommand)

	// 'connect' subcommand.
	connectCommand := &cobra.Command{
		Use:   "connect addr",
		Short: "Connect to a headless debug server.",
		Long:  "Connect to a headless debug server.",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return errors.New("you must provide an address as the first argument")
			}
			return nil
		},
		Run: connectCmd,
	}
	RootCommand.AddCommand(connectCommand)

	return RootCommand
}

func debugCmd(cmd *cobra.Command, args []string) {
	status := func() int {
		var pkg string
		dlvArgs, targetArgs := splitArgs(cmd, args)

		if len(dlvArgs) > 0 {
			pkg = args[0]
		}
		err := gobuild(debugname, pkg)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return 1
		}
		fp, err := filepath.Abs("./" + debugname)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return 1
		}
		defer os.Remove(fp)

		processArgs := append([]string{"./" + debugname}, targetArgs...)
		return execute(0, processArgs, conf)
	}()
	os.Exit(status)
}

func traceCmd(cmd *cobra.Command, args []string) {
	status := func() int {
		var regexp string
		var processArgs []string

		dlvArgs, targetArgs := splitArgs(cmd, args)

		if traceAttachPid == 0 {
			var pkg string
			switch len(dlvArgs) {
			case 1:
				regexp = args[0]
			case 2:
				pkg = args[0]
				regexp = args[1]
			}
			if err := gobuild(debugname, pkg); err != nil {
				return 1
			}
			defer os.Remove("./" + debugname)

			processArgs = append([]string{"./" + debugname}, targetArgs...)
		}
		// Make a TCP listener
		listener, err := net.Listen("tcp", Addr)
		if err != nil {
			fmt.Printf("couldn't start listener: %s\n", err)
			return 1
		}
		defer listener.Close()

		// Create and start a debug server
		server := rpc.NewServer(&service.Config{
			Listener:    listener,
			ProcessArgs: processArgs,
			AttachPid:   traceAttachPid,
		}, Log)
		if err := server.Run(); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
		client := rpc.NewClient(listener.Addr().String())
		funcs, err := client.ListFunctions(regexp)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
		for i := range funcs {
			_, err = client.CreateBreakpoint(&api.Breakpoint{FunctionName: funcs[i], Tracepoint: true, Line: -1, Stacktrace: traceStackDepth})
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				return 1
			}
		}
		cmds := terminal.DebugCommands(client)
		t := terminal.New(client, nil)
		defer t.Close()
		err = cmds.Call("continue", "", t)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
		return 0
	}()
	os.Exit(status)
}

func testCmd(cmd *cobra.Command, args []string) {
	status := func() int {
		var pkg string
		dlvArgs, targetArgs := splitArgs(cmd, args)

		if len(dlvArgs) > 0 {
			pkg = args[0]
		}
		err := gotestbuild(pkg)
		if err != nil {
			return 1
		}
		defer os.Remove("./" + testdebugname)
		processArgs := append([]string{"./" + testdebugname}, targetArgs...)

		return execute(0, processArgs, conf)
	}()
	os.Exit(status)
}

func attachCmd(cmd *cobra.Command, args []string) {
	pid, err := strconv.Atoi(args[0])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Invalid pid: %s\n", args[0])
		os.Exit(1)
	}
	os.Exit(execute(pid, nil, conf))
}

func connectCmd(cmd *cobra.Command, args []string) {
	addr := args[0]
	if addr == "" {
		fmt.Fprintf(os.Stderr, "An empty address was provided. You must provide an address as the first argument.\n")
		os.Exit(1)
	}
	os.Exit(connect(addr, conf))
}

func splitArgs(cmd *cobra.Command, args []string) ([]string, []string) {
	if cmd.ArgsLenAtDash() >= 0 {
		return args[:cmd.ArgsLenAtDash()], args[cmd.ArgsLenAtDash():]
	}
	return args, []string{}
}

func connect(addr string, conf *config.Config) int {
	// Create and start a terminal - attach to running instance
	var client service.Client
	client = rpc.NewClient(addr)
	term := terminal.New(client, conf)
	status, err := term.Run()
	if err != nil {
		fmt.Println(err)
	}
	return status
}

func execute(attachPid int, processArgs []string, conf *config.Config) int {
	// Make a TCP listener
	listener, err := net.Listen("tcp", Addr)
	if err != nil {
		fmt.Printf("couldn't start listener: %s\n", err)
		return 1
	}
	defer listener.Close()

	if Headless && (InitFile != "") {
		fmt.Fprintf(os.Stderr, "Warning: init file ignored\n")
	}

	// Create and start a debugger server
	server := rpc.NewServer(&service.Config{
		Listener:    listener,
		ProcessArgs: processArgs,
		AttachPid:   attachPid,
		AcceptMulti: AcceptMulti,
	}, Log)
	if err := server.Run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}

	var status int
	if Headless {
		// Print listener address
		fmt.Printf("start listener addr %s\n", listener.Addr())
		ch := make(chan os.Signal)
		signal.Notify(ch, syscall.SIGINT)
		<-ch
		err = server.Stop(true)
	} else {
		// Create and start a terminal
		var client service.Client
		client = rpc.NewClient(listener.Addr().String())
		term := terminal.New(client, conf)
		term.InitFile = InitFile
		status, err = term.Run()
	}

	if err != nil {
		fmt.Println(err)
	}

	return status
}

func gobuild(debugname, pkg string) error {
	args := []string{"-gcflags", "-N -l", "-o", debugname}
	if BuildFlags != "" {
		args = append(args, BuildFlags)
	}
	args = append(args, pkg)
	return gocommand("build", args...)
}

func gotestbuild(pkg string) error {
	args := []string{"-gcflags", "-N -l", "-c", "-o", testdebugname}
	if BuildFlags != "" {
		args = append(args, BuildFlags)
	}
	args = append(args, pkg)
	return gocommand("test", args...)
}

func gocommand(command string, args ...string) error {
	allargs := []string{command}
	allargs = append(allargs, args...)
	goBuild := exec.Command("go", allargs...)
	goBuild.Stderr = os.Stderr
	return goBuild.Run()
}
<commit_msg>if Headless && Addr == localhost:0 print address<commit_after>package cmds

import (
	"errors"
	"fmt"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"runtime"
	"strconv"
	"syscall"

	"github.com/derekparker/delve/config"
	"github.com/derekparker/delve/service"
	"github.com/derekparker/delve/service/api"
	"github.com/derekparker/delve/service/rpc"
	"github.com/derekparker/delve/terminal"
	"github.com/derekparker/delve/version"
	"github.com/spf13/cobra"
)

var (
	// Log is whether to log debug statements.
	Log bool
	// Headless is whether to run without terminal.
	Headless bool
	// AcceptMulti allows multiple clients to connect to the same server
	AcceptMulti bool
	// Addr is the debugging server listen address.
	Addr string
	// InitFile is the path to initialization file.
	InitFile string
	// BuildFlags is the flags passed during compiler invocation.
	BuildFlags string

	// RootCommand is the root of the command tree.
	RootCommand *cobra.Command

	traceAttachPid  int
	traceStackDepth int

	conf *config.Config
)

const (
	debugname     = "debug"
	testdebugname = "debug.test"
)

const dlvCommandLongDesc = `Delve is a source level debugger for Go programs.

Delve enables you to interact with your program by controlling the execution of the process,
evaluating variables, and providing information of thread / goroutine state, CPU register state and more.

The goal of this tool is to provide a simple yet powerful interface for debugging Go programs.
`

// New returns an initialized command tree.
func New() *cobra.Command {
	// Config setup and load.
	conf = config.LoadConfig()
	buildFlagsDefault := ""
	if runtime.GOOS == "windows" {
		// Work-around for https://github.com/golang/go/issues/13154
		buildFlagsDefault = "-ldflags=-linkmode internal"
	}

	// Main dlv root command.
	RootCommand = &cobra.Command{
		Use:   "dlv",
		Short: "Delve is a debugger for the Go programming language.",
		Long:  dlvCommandLongDesc,
	}

	RootCommand.PersistentFlags().StringVarP(&Addr, "listen", "l", "localhost:0", "Debugging server listen address.")
	RootCommand.PersistentFlags().BoolVarP(&Log, "log", "", false, "Enable debugging server logging.")
	RootCommand.PersistentFlags().BoolVarP(&Headless, "headless", "", false, "Run debug server only, in headless mode.")
	RootCommand.PersistentFlags().BoolVarP(&AcceptMulti, "accept-multiclient", "", false, "Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate")
	RootCommand.PersistentFlags().StringVar(&InitFile, "init", "", "Init file, executed by the terminal client.")
	RootCommand.PersistentFlags().StringVar(&BuildFlags, "build-flags", buildFlagsDefault, "Build flags, to be passed to the compiler.")

	// 'version' subcommand.
	versionCommand := &cobra.Command{
		Use:   "version",
		Short: "Prints version.",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("Delve Debugger\n%s\n", version.DelveVersion)
		},
	}
	RootCommand.AddCommand(versionCommand)

	// Deprecated 'run' subcommand.
	runCommand := &cobra.Command{
		Use:   "run",
		Short: "Deprecated command. Use 'debug' instead.",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("This command is deprecated, please use 'debug' instead.")
			os.Exit(0)
		},
	}
	RootCommand.AddCommand(runCommand)

	// 'debug' subcommand.
	debugCommand := &cobra.Command{
		Use:   "debug [package]",
		Short: "Compile and begin debugging program.",
		Long: `Compiles your program with optimizations disabled,
starts and attaches to it, and enables you to immediately begin debugging your program.`,
		Run: debugCmd,
	}
	RootCommand.AddCommand(debugCommand)

	// 'exec' subcommand.
	execCommand := &cobra.Command{
		Use:   "exec [./path/to/binary]",
		Short: "Runs precompiled binary, attaches and begins debug session.",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return errors.New("you must provide a path to a binary")
			}
			return nil
		},
		Run: func(cmd *cobra.Command, args []string) {
			os.Exit(execute(0, args, conf))
		},
	}
	RootCommand.AddCommand(execCommand)

	// 'trace' subcommand.
	traceCommand := &cobra.Command{
		Use:   "trace [package] regexp",
		Short: "Compile and begin tracing program.",
		Long:  "Trace program execution. Will set a tracepoint on every function matching the provided regular expression and output information when tracepoint is hit.",
		Run:   traceCmd,
	}
	traceCommand.Flags().IntVarP(&traceAttachPid, "pid", "p", 0, "Pid to attach to.")
	traceCommand.Flags().IntVarP(&traceStackDepth, "stack", "s", 0, "Show stack trace with given depth.")
	RootCommand.AddCommand(traceCommand)

	// 'test' subcommand.
	testCommand := &cobra.Command{
		Use:   "test [package]",
		Short: "Compile test binary and begin debugging program.",
		Long:  `Compiles a test binary with optimizations disabled, starts and attaches to it, and enable you to immediately begin debugging your program.`,
		Run:   testCmd,
	}
	RootCommand.AddCommand(testCommand)

	// 'attach' subcommand.
	attachCommand := &cobra.Command{
		Use:   "attach pid",
		Short: "Attach to running process and begin debugging.",
		Long:  "Attach to running process and begin debugging.",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return errors.New("you must provide a PID")
			}
			return nil
		},
		Run: attachCmd,
	}
	RootCommand.AddCommand(attachCommand)

	// 'connect' subcommand.
	connectCommand := &cobra.Command{
		Use:   "connect addr",
		Short: "Connect to a headless debug server.",
		Long:  "Connect to a headless debug server.",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return errors.New("you must provide an address as the first argument")
			}
			return nil
		},
		Run: connectCmd,
	}
	RootCommand.AddCommand(connectCommand)

	return RootCommand
}

func debugCmd(cmd *cobra.Command, args []string) {
	status := func() int {
		var pkg string
		dlvArgs, targetArgs := splitArgs(cmd, args)

		if len(dlvArgs) > 0 {
			pkg = args[0]
		}
		err := gobuild(debugname, pkg)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return 1
		}
		fp, err := filepath.Abs("./" + debugname)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return 1
		}
		defer os.Remove(fp)

		processArgs := append([]string{"./" + debugname}, targetArgs...)
		return execute(0, processArgs, conf)
	}()
	os.Exit(status)
}

func traceCmd(cmd *cobra.Command, args []string) {
	status := func() int {
		var regexp string
		var processArgs []string

		dlvArgs, targetArgs := splitArgs(cmd, args)

		if traceAttachPid == 0 {
			var pkg string
			switch len(dlvArgs) {
			case 1:
				regexp = args[0]
			case 2:
				pkg = args[0]
				regexp = args[1]
			}
			if err := gobuild(debugname, pkg); err != nil {
				return 1
			}
			defer os.Remove("./" + debugname)

			processArgs = append([]string{"./" + debugname}, targetArgs...)
		}
		// Make a TCP listener
		listener, err := net.Listen("tcp", Addr)
		if err != nil {
			fmt.Printf("couldn't start listener: %s\n", err)
			return 1
		}
		defer listener.Close()

		// Create and start a debug server
		server := rpc.NewServer(&service.Config{
			Listener:    listener,
			ProcessArgs: processArgs,
			AttachPid:   traceAttachPid,
		}, Log)
		if err := server.Run(); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
		client := rpc.NewClient(listener.Addr().String())
		funcs, err := client.ListFunctions(regexp)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
		for i := range funcs {
			_, err = client.CreateBreakpoint(&api.Breakpoint{FunctionName: funcs[i], Tracepoint: true, Line: -1, Stacktrace: traceStackDepth})
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				return 1
			}
		}
		cmds := terminal.DebugCommands(client)
		t := terminal.New(client, nil)
		defer t.Close()
		err = cmds.Call("continue", "", t)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return 1
		}
		return 0
	}()
	os.Exit(status)
}

func testCmd(cmd *cobra.Command, args []string) {
	status := func() int {
		var pkg string
		dlvArgs, targetArgs := splitArgs(cmd, args)

		if len(dlvArgs) > 0 {
			pkg = args[0]
		}
		err := gotestbuild(pkg)
		if err != nil {
			return 1
		}
		defer os.Remove("./" + testdebugname)
		processArgs := append([]string{"./" + testdebugname}, targetArgs...)

		return execute(0, processArgs, conf)
	}()
	os.Exit(status)
}

func attachCmd(cmd *cobra.Command, args []string) {
	pid, err := strconv.Atoi(args[0])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Invalid pid: %s\n", args[0])
		os.Exit(1)
	}
	os.Exit(execute(pid, nil, conf))
}

func connectCmd(cmd *cobra.Command, args []string) {
	addr := args[0]
	if addr == "" {
		fmt.Fprintf(os.Stderr, "An empty address was provided. You must provide an address as the first argument.\n")
		os.Exit(1)
	}
	os.Exit(connect(addr, conf))
}

func splitArgs(cmd *cobra.Command, args []string) ([]string, []string) {
	if cmd.ArgsLenAtDash() >= 0 {
		return args[:cmd.ArgsLenAtDash()], args[cmd.ArgsLenAtDash():]
	}
	return args, []string{}
}

func connect(addr string, conf *config.Config) int {
	// Create and start a terminal - attach to running instance
	var client service.Client
	client = rpc.NewClient(addr)
	term := terminal.New(client, conf)
	status, err := term.Run()
	if err != nil {
		fmt.Println(err)
	}
	return status
}

func execute(attachPid int, processArgs []string, conf *config.Config) int {
	// Make a TCP listener
	listener, err := net.Listen("tcp", Addr)
	if err != nil {
		fmt.Printf("couldn't start listener: %s\n", err)
		return 1
	}
	defer listener.Close()

	if Headless && (InitFile != "") {
		fmt.Fprintf(os.Stderr, "Warning: init file ignored\n")
	}

	// Create and start a debugger server
	server := rpc.NewServer(&service.Config{
		Listener:    listener,
		ProcessArgs: processArgs,
		AttachPid:   attachPid,
		AcceptMulti: AcceptMulti,
	}, Log)
	if err := server.Run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}

	var status int
	if Headless {
		if Addr == "localhost:0" {
			// Print listener address
			fmt.Printf("API server listening at: %s\n", listener.Addr())
		}
		ch := make(chan os.Signal)
		signal.Notify(ch, syscall.SIGINT)
		<-ch
		err = server.Stop(true)
	} else {
		// Create and start a terminal
		var client service.Client
		client = rpc.NewClient(listener.Addr().String())
		term := terminal.New(client, conf)
		term.InitFile = InitFile
		status, err = term.Run()
	}

	if err != nil {
		fmt.Println(err)
	}

	return status
}

func gobuild(debugname, pkg string) error {
	args := []string{"-gcflags", "-N -l", "-o", debugname}
	if BuildFlags != "" {
		args = append(args, BuildFlags)
	}
	args = append(args, pkg)
	return gocommand("build", args...)
}

func gotestbuild(pkg string) error {
	args := []string{"-gcflags", "-N -l", "-c", "-o", testdebugname}
	if BuildFlags != "" {
		args = append(args, BuildFlags)
	}
	args = append(args, pkg)
	return gocommand("test", args...)
}

func gocommand(command string, args ...string) error {
	allargs := []string{command}
	allargs = append(allargs, args...)
	goBuild := exec.Command("go", allargs...)
	goBuild.Stderr = os.Stderr
	return goBuild.Run()
}
<|endoftext|>


<commit_before>package main

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

	"github.com/pkg/errors"

	"github.com/datawire/ambassador/pkg/supervisor"
)

func (d *Daemon) interceptMessage() string {
	switch {
	case d.cluster == nil:
		return "Not connected (use 'edgectl connect' to connect to your cluster)"
	case d.trafficMgr == nil:
		return "Intercept unavailable: no traffic manager"
	case !d.trafficMgr.IsOkay():
		return "Connecting to traffic manager..."
	default:
		return ""
	}
}

// InterceptInfo tracks one intercept operation
type InterceptInfo struct {
	Name       string // Name of the intercept (user/logging)
	Namespace  string // Name in which to create the Intercept mapping
	Deployment string // Name of the deployment being intercepted
	Prefix     string // Prefix to intercept (default /)
	Patterns   map[string]string
	TargetHost string
	TargetPort int
}

// Acquire an intercept from the traffic manager
func (ii *InterceptInfo) Acquire(_ *supervisor.Process, tm *TrafficManager) (int, error) {
	reqPatterns := make([]map[string]string, 0, len(ii.Patterns))
	for header, regex := range ii.Patterns {
		pattern := map[string]string{"name": header, "regex_match": regex}
		reqPatterns = append(reqPatterns, pattern)
	}
	request := map[string]interface{}{
		"name":     ii.Name,
		"patterns": reqPatterns,
	}
	reqData, err := json.Marshal(request)
	if err != nil {
		return 0, err
	}

	requestUrl := fmt.Sprintf("intercept/%s/%s", ii.Namespace, ii.Deployment)
	result, code, err := tm.request("POST", requestUrl, reqData)
	if err != nil {
		return 0, errors.Wrap(err, "acquire intercept")
	}
	if code == 404 {
		return 0, fmt.Errorf("deployment %q is not known to the traffic manager", ii.Deployment)
	}
	if !(200 <= code && code <= 299) {
		return 0, fmt.Errorf("acquire intercept: %s: %s", http.StatusText(code), result)
	}
	port, err := strconv.Atoi(result)
	if err != nil {
		return 0, errors.Wrapf(err, "bad port number from traffic manager: %q", result)
	}
	return port, nil
}

// Retain the given intercept. This likely needs to be called every
// five seconds or so.
func (ii *InterceptInfo) Retain(_ *supervisor.Process, tm *TrafficManager, port int) error {
	data := []byte(fmt.Sprintf("{\"port\": %d}", port))
	result, code, err := tm.request("POST", "intercept/"+ii.Deployment, data)
	if err != nil {
		return errors.Wrap(err, "retain intercept")
	}
	if !(200 <= code && code <= 299) {
		return fmt.Errorf("retain intercept: %s: %s", http.StatusText(code), result)
	}
	return nil
}

// Release the given intercept.
func (ii *InterceptInfo) Release(_ *supervisor.Process, tm *TrafficManager, port int) error {
	data := []byte(fmt.Sprintf("%d", port))
	result, code, err := tm.request("DELETE", "intercept/"+ii.Deployment, data)
	if err != nil {
		return errors.Wrap(err, "release intercept")
	}
	if !(200 <= code && code <= 299) {
		return fmt.Errorf("release intercept: %s: %s", http.StatusText(code), result)
	}
	return nil
}

// ListIntercepts lists active intercepts
func (d *Daemon) ListIntercepts(_ *supervisor.Process, out *Emitter) error {
	msg := d.interceptMessage()
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}
	for idx, cept := range d.intercepts {
		ii := cept.ii
		out.Printf("%4d. %s\n", idx+1, ii.Name)
		out.Send(fmt.Sprintf("local_intercept.%d", idx+1), ii.Name)
		key := "local_intercept." + ii.Name
		out.Printf("      Intercepting requests to %s when\n", ii.Deployment)
		out.Send(key, ii.Deployment)
		for k, v := range ii.Patterns {
			out.Printf("      - %s: %s\n", k, v)
			out.Send(key+"."+k, v)
		}
		out.Printf("      and redirecting them to %s:%d\n", ii.TargetHost, ii.TargetPort)
		out.Send(key+".host", ii.TargetHost)
		out.Send(key+".port", ii.TargetPort)
	}
	if len(d.intercepts) == 0 {
		out.Println("No intercepts")
	}
	return nil
}

// AddIntercept adds one intercept
func (d *Daemon) AddIntercept(p *supervisor.Process, out *Emitter, ii *InterceptInfo) error {
	msg := d.interceptMessage()
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}

	for _, cept := range d.intercepts {
		if cept.ii.Name == ii.Name {
			out.Printf("Intercept with name %q already exists\n", ii.Name)
			out.Send("failed", "intercept name exists")
			out.SendExit(1)
			return nil
		}
	}

	// Do we already have a namespace?
	if ii.Namespace == "" {
		// Nope. See if we have an interceptable that matches the name.

		matches := make([]InterceptInfo, 0)
		for _, deployment := range d.trafficMgr.interceptables {
			fields := strings.SplitN(deployment, "/", 2)

			appName := fields[0]
			appNamespace := d.cluster.namespace

			if len(fields) > 1 {
				appNamespace = fields[0]
				appName = fields[1]
			}

			if ii.Deployment == appName {
				// Abuse InterceptInfo rather than defining a new tuple type.
				matches = append(matches, InterceptInfo{"", appNamespace, appName, "", nil, "", 0})
			}
		}

		switch len(matches) {
		case 0:
			out.Printf("No interceptable deployment matching %s found", ii.Name)
			out.Send("failed", "no interceptable deployment matches")
			out.SendExit(1)
			return nil

		case 1:
			// Good to go.
			ii.Namespace = matches[0].Namespace
			out.Printf("Using deployment %s in namespace %s\n", ii.Name, ii.Namespace)

		default:
			out.Printf("Found more than one possible match:\n")

			for idx, match := range matches {
				out.Printf("%4d: %s in namespace %s\n", idx+1, match.Deployment, match.Namespace)
			}
			out.Send("failed", "multiple interceptable deployment matched")
			out.SendExit(1)
			return nil
		}
	}

	cept, err := MakeIntercept(p, out, d.trafficMgr, d.cluster, ii)
	if err != nil {
		out.Printf("Failed to establish intercept: %s\n", err)
		out.Send("failed", err.Error())
		out.SendExit(1)
		return nil
	}
	d.intercepts = append(d.intercepts, cept)
	out.Printf("Added intercept %q\n", ii.Name)
	return nil
}

// RemoveIntercept removes one intercept by name
func (d *Daemon) RemoveIntercept(p *supervisor.Process, out *Emitter, name string) error {
	msg := d.interceptMessage()
	for idx, cept := range d.intercepts {
		if cept.ii.Name == name {
			d.intercepts = append(d.intercepts[:idx], d.intercepts[idx+1:]...)

			out.Printf("Removed intercept %q\n", name)

			if err := cept.Close(); err != nil {
				out.Printf("Error while removing intercept: %v\n", err)
				out.Send("failed", err.Error())
				out.SendExit(1)
			}
			return nil
		}
	}
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}
	out.Printf("Intercept named %q not found\n", name)
	out.Send("failed", "not found")
	out.SendExit(1)
	return nil
}

// ClearIntercepts removes all intercepts
func (d *Daemon) ClearIntercepts(p *supervisor.Process) error {
	for _, cept := range d.intercepts {
		if err := cept.Close(); err != nil {
			p.Logf("Closing intercept %q: %v", cept.ii.Name, err)
		}
	}
	d.intercepts = d.intercepts[:0]
	return nil
}

// Intercept is a Resource handle that represents a live intercept
type Intercept struct {
	ii            *InterceptInfo
	tm            *TrafficManager
	cluster       *KCluster
	port          int
	crc           Resource
	mappingExists bool
	ResourceBase
}

// removeMapping drops an Intercept's mapping if needed (and possible).
func (cept *Intercept) removeMapping(p *supervisor.Process) error {
	var err error
	err = nil

	if cept.mappingExists {
		p.Logf("%v: Deleting mapping in namespace %v", cept.ii.Name, cept.ii.Namespace)
		delete := cept.cluster.GetKubectlCmd(p, "delete", "-n", cept.ii.Namespace, "mapping", fmt.Sprintf("%s-mapping", cept.ii.Name))
		err = delete.Run()
		p.Logf("%v: Deleted mapping in namespace %v", cept.ii.Name, cept.ii.Namespace)
	}

	if err != nil {
		return errors.Wrap(err, "Intercept: mapping could not be deleted")
	}

	return nil
}

type mappingMetadata struct {
	Name      string `json:"name"`
	Namespace string `json:"namespace"`
}

type mappingSpec struct {
	AmbassadorID []string          `json:"ambassador_id"`
	Prefix       string            `json:"prefix"`
	Service      string            `json:"service"`
	Headers      map[string]string `json:"headers"`
}

type interceptMapping struct {
	APIVersion string          `json:"apiVersion"`
	Kind       string          `json:"kind"`
	Metadata   mappingMetadata `json:"metadata"`
	Spec       mappingSpec     `json:"spec"`
}

// MakeIntercept acquires an intercept and returns a Resource handle
// for it
func MakeIntercept(p *supervisor.Process, out *Emitter, tm *TrafficManager, cluster *KCluster, ii *InterceptInfo) (*Intercept, error) {
	port, err := ii.Acquire(p, tm)
	if err != nil {
		return nil, err
	}

	cept := &Intercept{ii: ii, tm: tm, cluster: cluster, port: port}
	cept.mappingExists = false
	cept.doCheck = cept.check
	cept.doQuit = cept.quit
	cept.setup(p.Supervisor(), ii.Name)

	p.Logf("%s: Intercepting via port %v, using namespace %v", ii.Name, port, ii.Namespace)

	mapping := interceptMapping{
		APIVersion: "getambassador.io/v2",
		Kind:       "Mapping",
		Metadata: mappingMetadata{
			Name:      fmt.Sprintf("%s-mapping", ii.Name),
			Namespace: ii.Namespace,
		},
		Spec: mappingSpec{
			AmbassadorID: []string{fmt.Sprintf("intercept-%s", ii.Deployment)},
			Prefix:       ii.Prefix,
			Service:      fmt.Sprintf("telepresence-proxy.%s:%d", cluster.namespace, port),
			Headers:      ii.Patterns,
		},
	}

	manifest, err := json.MarshalIndent(&mapping, "", "  ")
	if err != nil {
		_ = cept.Close()
		return nil, errors.Wrap(err, "Intercept: mapping could not be constructed")
	}

	p.Logf("%s: Intercept using mapping %v", ii.Name, string(manifest))
	out.Printf("%s: applying intercept mapping in namespace %s\n", ii.Name, ii.Namespace)

	apply := cluster.GetKubectlCmdNoNamespace(p, "apply", "-f", "-")
	apply.Stdin = strings.NewReader(string(manifest))
	apply.Stdout = os.Stdout
	apply.Stderr = os.Stderr
	err = apply.Run()

	if err != nil {
		p.Logf("%v: Intercept could not apply mapping: %v", ii.Name, err)
		_ = cept.Close()
		return nil, errors.Wrap(err, "Intercept: kubectl apply")
	}

	cept.mappingExists = true

	sshCmd := []string{
		"ssh", "-C", "-N", "telepresence@localhost",
		"-oConnectTimeout=10", "-oExitOnForwardFailure=yes",
		"-oStrictHostKeyChecking=no", "-oUserKnownHostsFile=/dev/null",
		"-p", strconv.Itoa(tm.sshPort),
		"-R", fmt.Sprintf("%d:%s:%d", cept.port, ii.TargetHost, ii.TargetPort),
	}

	p.Logf("%s: starting SSH tunnel", ii.Name)
	out.Printf("%s: starting SSH tunnel\n", ii.Name)

	ssh, err := CheckedRetryingCommand(p, ii.Name+"-ssh", sshCmd, nil, nil, 5*time.Second)
	if err != nil {
		_ = cept.Close()
		return nil, err
	}

	cept.crc = ssh

	return cept, nil
}

func (cept *Intercept) check(p *supervisor.Process) error {
	return cept.ii.Retain(p, cept.tm, cept.port)
}

func (cept *Intercept) quit(p *supervisor.Process) error {
	cept.done = true

	p.Logf("cept.Quit removing %v", cept.ii.Name)

	cept.removeMapping(p)

	p.Logf("cept.Quit removed %v", cept.ii.Name)

	if cept.crc != nil {
		_ = cept.crc.Close()
	}

	p.Logf("cept.Quit releasing %v", cept.ii.Name)

	if err := cept.ii.Release(p, cept.tm, cept.port); err != nil {
		p.Log(err)
	}

	p.Logf("cept.Quit released %v", cept.ii.Name)

	return nil
}
<commit_msg>Use consistent paths for intercepts so manager stays in sync<commit_after>package main

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

	"github.com/pkg/errors"

	"github.com/datawire/ambassador/pkg/supervisor"
)

func (d *Daemon) interceptMessage() string {
	switch {
	case d.cluster == nil:
		return "Not connected (use 'edgectl connect' to connect to your cluster)"
	case d.trafficMgr == nil:
		return "Intercept unavailable: no traffic manager"
	case !d.trafficMgr.IsOkay():
		return "Connecting to traffic manager..."
	default:
		return ""
	}
}

// InterceptInfo tracks one intercept operation
type InterceptInfo struct {
	Name       string // Name of the intercept (user/logging)
	Namespace  string // Name in which to create the Intercept mapping
	Deployment string // Name of the deployment being intercepted
	Prefix     string // Prefix to intercept (default /)
	Patterns   map[string]string
	TargetHost string
	TargetPort int
}

// path returns the URL path for this intercept
func (ii *InterceptInfo) path() string {
	return fmt.Sprintf("intercept/%s/%s", ii.Namespace, ii.Deployment)
}

// Acquire an intercept from the traffic manager
func (ii *InterceptInfo) Acquire(_ *supervisor.Process, tm *TrafficManager) (int, error) {
	reqPatterns := make([]map[string]string, 0, len(ii.Patterns))
	for header, regex := range ii.Patterns {
		pattern := map[string]string{"name": header, "regex_match": regex}
		reqPatterns = append(reqPatterns, pattern)
	}
	request := map[string]interface{}{
		"name":     ii.Name,
		"patterns": reqPatterns,
	}
	reqData, err := json.Marshal(request)
	if err != nil {
		return 0, err
	}

	result, code, err := tm.request("POST", ii.path(), reqData)
	if err != nil {
		return 0, errors.Wrap(err, "acquire intercept")
	}
	if code == 404 {
		return 0, fmt.Errorf("deployment %q is not known to the traffic manager", ii.Deployment)
	}
	if !(200 <= code && code <= 299) {
		return 0, fmt.Errorf("acquire intercept: %s: %s", http.StatusText(code), result)
	}
	port, err := strconv.Atoi(result)
	if err != nil {
		return 0, errors.Wrapf(err, "bad port number from traffic manager: %q", result)
	}
	return port, nil
}

// Retain the given intercept. This likely needs to be called every
// five seconds or so.
func (ii *InterceptInfo) Retain(_ *supervisor.Process, tm *TrafficManager, port int) error {
	data := []byte(fmt.Sprintf("{\"port\": %d}", port))
	result, code, err := tm.request("POST", ii.path(), data)
	if err != nil {
		return errors.Wrap(err, "retain intercept")
	}
	if !(200 <= code && code <= 299) {
		return fmt.Errorf("retain intercept: %s: %s", http.StatusText(code), result)
	}
	return nil
}

// Release the given intercept.
func (ii *InterceptInfo) Release(_ *supervisor.Process, tm *TrafficManager, port int) error {
	data := []byte(fmt.Sprintf("%d", port))
	result, code, err := tm.request("DELETE", ii.path(), data)
	if err != nil {
		return errors.Wrap(err, "release intercept")
	}
	if !(200 <= code && code <= 299) {
		return fmt.Errorf("release intercept: %s: %s", http.StatusText(code), result)
	}
	return nil
}

// ListIntercepts lists active intercepts
func (d *Daemon) ListIntercepts(_ *supervisor.Process, out *Emitter) error {
	msg := d.interceptMessage()
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}
	for idx, cept := range d.intercepts {
		ii := cept.ii
		out.Printf("%4d. %s\n", idx+1, ii.Name)
		out.Send(fmt.Sprintf("local_intercept.%d", idx+1), ii.Name)
		key := "local_intercept." + ii.Name
		out.Printf("      Intercepting requests to %s when\n", ii.Deployment)
		out.Send(key, ii.Deployment)
		for k, v := range ii.Patterns {
			out.Printf("      - %s: %s\n", k, v)
			out.Send(key+"."+k, v)
		}
		out.Printf("      and redirecting them to %s:%d\n", ii.TargetHost, ii.TargetPort)
		out.Send(key+".host", ii.TargetHost)
		out.Send(key+".port", ii.TargetPort)
	}
	if len(d.intercepts) == 0 {
		out.Println("No intercepts")
	}
	return nil
}

// AddIntercept adds one intercept
func (d *Daemon) AddIntercept(p *supervisor.Process, out *Emitter, ii *InterceptInfo) error {
	msg := d.interceptMessage()
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}

	for _, cept := range d.intercepts {
		if cept.ii.Name == ii.Name {
			out.Printf("Intercept with name %q already exists\n", ii.Name)
			out.Send("failed", "intercept name exists")
			out.SendExit(1)
			return nil
		}
	}

	// Do we already have a namespace?
	if ii.Namespace == "" {
		// Nope. See if we have an interceptable that matches the name.

		matches := make([]InterceptInfo, 0)
		for _, deployment := range d.trafficMgr.interceptables {
			fields := strings.SplitN(deployment, "/", 2)

			appName := fields[0]
			appNamespace := d.cluster.namespace

			if len(fields) > 1 {
				appNamespace = fields[0]
				appName = fields[1]
			}

			if ii.Deployment == appName {
				// Abuse InterceptInfo rather than defining a new tuple type.
				matches = append(matches, InterceptInfo{"", appNamespace, appName, "", nil, "", 0})
			}
		}

		switch len(matches) {
		case 0:
			out.Printf("No interceptable deployment matching %s found", ii.Name)
			out.Send("failed", "no interceptable deployment matches")
			out.SendExit(1)
			return nil

		case 1:
			// Good to go.
			ii.Namespace = matches[0].Namespace
			out.Printf("Using deployment %s in namespace %s\n", ii.Name, ii.Namespace)

		default:
			out.Printf("Found more than one possible match:\n")

			for idx, match := range matches {
				out.Printf("%4d: %s in namespace %s\n", idx+1, match.Deployment, match.Namespace)
			}
			out.Send("failed", "multiple interceptable deployment matched")
			out.SendExit(1)
			return nil
		}
	}

	cept, err := MakeIntercept(p, out, d.trafficMgr, d.cluster, ii)
	if err != nil {
		out.Printf("Failed to establish intercept: %s\n", err)
		out.Send("failed", err.Error())
		out.SendExit(1)
		return nil
	}
	d.intercepts = append(d.intercepts, cept)
	out.Printf("Added intercept %q\n", ii.Name)
	return nil
}

// RemoveIntercept removes one intercept by name
func (d *Daemon) RemoveIntercept(p *supervisor.Process, out *Emitter, name string) error {
	msg := d.interceptMessage()
	for idx, cept := range d.intercepts {
		if cept.ii.Name == name {
			d.intercepts = append(d.intercepts[:idx], d.intercepts[idx+1:]...)

			out.Printf("Removed intercept %q\n", name)

			if err := cept.Close(); err != nil {
				out.Printf("Error while removing intercept: %v\n", err)
				out.Send("failed", err.Error())
				out.SendExit(1)
			}
			return nil
		}
	}
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}
	out.Printf("Intercept named %q not found\n", name)
	out.Send("failed", "not found")
	out.SendExit(1)
	return nil
}

// ClearIntercepts removes all intercepts
func (d *Daemon) ClearIntercepts(p *supervisor.Process) error {
	for _, cept := range d.intercepts {
		if err := cept.Close(); err != nil {
			p.Logf("Closing intercept %q: %v", cept.ii.Name, err)
		}
	}
	d.intercepts = d.intercepts[:0]
	return nil
}

// Intercept is a Resource handle that represents a live intercept
type Intercept struct {
	ii            *InterceptInfo
	tm            *TrafficManager
	cluster       *KCluster
	port          int
	crc           Resource
	mappingExists bool
	ResourceBase
}

// removeMapping drops an Intercept's mapping if needed (and possible).
func (cept *Intercept) removeMapping(p *supervisor.Process) error {
	var err error
	err = nil

	if cept.mappingExists {
		p.Logf("%v: Deleting mapping in namespace %v", cept.ii.Name, cept.ii.Namespace)
		delete := cept.cluster.GetKubectlCmd(p, "delete", "-n", cept.ii.Namespace, "mapping", fmt.Sprintf("%s-mapping", cept.ii.Name))
		err = delete.Run()
		p.Logf("%v: Deleted mapping in namespace %v", cept.ii.Name, cept.ii.Namespace)
	}

	if err != nil {
		return errors.Wrap(err, "Intercept: mapping could not be deleted")
	}

	return nil
}

type mappingMetadata struct {
	Name      string `json:"name"`
	Namespace string `json:"namespace"`
}

type mappingSpec struct {
	AmbassadorID []string          `json:"ambassador_id"`
	Prefix       string            `json:"prefix"`
	Service      string            `json:"service"`
	Headers      map[string]string `json:"headers"`
}

type interceptMapping struct {
	APIVersion string          `json:"apiVersion"`
	Kind       string          `json:"kind"`
	Metadata   mappingMetadata `json:"metadata"`
	Spec       mappingSpec     `json:"spec"`
}

// MakeIntercept acquires an intercept and returns a Resource handle
// for it
func MakeIntercept(p *supervisor.Process, out *Emitter, tm *TrafficManager, cluster *KCluster, ii *InterceptInfo) (*Intercept, error) {
	port, err := ii.Acquire(p, tm)
	if err != nil {
		return nil, err
	}

	cept := &Intercept{ii: ii, tm: tm, cluster: cluster, port: port}
	cept.mappingExists = false
	cept.doCheck = cept.check
	cept.doQuit = cept.quit
	cept.setup(p.Supervisor(), ii.Name)

	p.Logf("%s: Intercepting via port %v, using namespace %v", ii.Name, port, ii.Namespace)

	mapping := interceptMapping{
		APIVersion: "getambassador.io/v2",
		Kind:       "Mapping",
		Metadata: mappingMetadata{
			Name:      fmt.Sprintf("%s-mapping", ii.Name),
			Namespace: ii.Namespace,
		},
		Spec: mappingSpec{
			AmbassadorID: []string{fmt.Sprintf("intercept-%s", ii.Deployment)},
			Prefix:       ii.Prefix,
			Service:      fmt.Sprintf("telepresence-proxy.%s:%d", cluster.namespace, port),
			Headers:      ii.Patterns,
		},
	}

	manifest, err := json.MarshalIndent(&mapping, "", "  ")
	if err != nil {
		_ = cept.Close()
		return nil, errors.Wrap(err, "Intercept: mapping could not be constructed")
	}

	p.Logf("%s: Intercept using mapping %v", ii.Name, string(manifest))
	out.Printf("%s: applying intercept mapping in namespace %s\n", ii.Name, ii.Namespace)

	apply := cluster.GetKubectlCmdNoNamespace(p, "apply", "-f", "-")
	apply.Stdin = strings.NewReader(string(manifest))
	apply.Stdout = os.Stdout
	apply.Stderr = os.Stderr
	err = apply.Run()

	if err != nil {
		p.Logf("%v: Intercept could not apply mapping: %v", ii.Name, err)
		_ = cept.Close()
		return nil, errors.Wrap(err, "Intercept: kubectl apply")
	}

	cept.mappingExists = true

	sshCmd := []string{
		"ssh", "-C", "-N", "telepresence@localhost",
		"-oConnectTimeout=10", "-oExitOnForwardFailure=yes",
		"-oStrictHostKeyChecking=no", "-oUserKnownHostsFile=/dev/null",
		"-p", strconv.Itoa(tm.sshPort),
		"-R", fmt.Sprintf("%d:%s:%d", cept.port, ii.TargetHost, ii.TargetPort),
	}

	p.Logf("%s: starting SSH tunnel", ii.Name)
	out.Printf("%s: starting SSH tunnel\n", ii.Name)

	ssh, err := CheckedRetryingCommand(p, ii.Name+"-ssh", sshCmd, nil, nil, 5*time.Second)
	if err != nil {
		_ = cept.Close()
		return nil, err
	}

	cept.crc = ssh

	return cept, nil
}

func (cept *Intercept) check(p *supervisor.Process) error {
	return cept.ii.Retain(p, cept.tm, cept.port)
}

func (cept *Intercept) quit(p *supervisor.Process) error {
	cept.done = true

	p.Logf("cept.Quit removing %v", cept.ii.Name)

	cept.removeMapping(p)

	p.Logf("cept.Quit removed %v", cept.ii.Name)

	if cept.crc != nil {
		_ = cept.crc.Close()
	}

	p.Logf("cept.Quit releasing %v", cept.ii.Name)

	if err := cept.ii.Release(p, cept.tm, cept.port); err != nil {
		p.Log(err)
	}

	p.Logf("cept.Quit released %v", cept.ii.Name)

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"reflect"
	"testing"
)

func TestUserGitconfigMap_EmptyString(t *testing.T) {
	d := ""
	userGitconfigInfo := userGitconfigMap(d)
	if len(userGitconfigInfo) != 0 {
		t.Fatal("expected map with no keys")
	}
}

func TestUserGitconfigMap(t *testing.T) {
	d := `push.default=simple
	merge.conflictstyle=diff3
	pull.ff=only
	core.repositoryformatversion=0
	core.filemode=true
	core.bare=false`
	expected := gitConfigMap(nil)

	userGitconfigInfo := userGitconfigMap(d)
	if len(userGitconfigInfo) != 6 {
		t.Fatal("got map with unexpected number of keys")
	}
	if !reflect.DeepEqual(userGitconfigInfo, expected) {
		t.Fatal("result does not match expected structure")
	}
}

func TestUserGitconfigMap_WithEmptyLines(t *testing.T) {
	d := `
	user.name=Jane Doe
	push.default=simple
	merge.conflictstyle=diff3
	pull.ff=only

	core.repositoryformatversion=0
	core.filemode=true
	core.bare=false

	`
	expected := gitConfigMap(map[string]string{
		"user.name": "Jane Doe",
	})
	userGitconfigInfo := userGitconfigMap(d)

	if len(userGitconfigInfo) != 7 {
		t.Fatal("got map with unexpected number of keys")
	}
	if !reflect.DeepEqual(userGitconfigInfo, expected) {
		t.Fatal("result does not match expected structure")
	}
}

func TestUserGitconfigMap_WithNoKeys(t *testing.T) {
	d := `
	`
	expected := make(map[string]string)

	userGitconfigInfo := userGitconfigMap(d)
	if len(userGitconfigInfo) != 0 {
		t.Fatal("expected map with no keys")
	}
	if !reflect.DeepEqual(userGitconfigInfo, expected) {
		t.Fatal("result does not match expected structure")
	}
}

func TestGetCommitAuthor_BothNameAndEmail(t *testing.T) {
	input := gitConfigMap(map[string]string{
		"user.name":  "Jane Doe",
		"user.email": "jd@j.d",
	})
	checkAuthor(t, input, "Jane Doe <jd@j.d>")
}

func TestGetCommitAuthor_OnlyName(t *testing.T) {
	input := gitConfigMap(map[string]string{
		"user.name": "Jane Doe",
	})
	checkAuthor(t, input, "Jane Doe")
}

func TestGetCommitAuthor_OnlyEmail(t *testing.T) {
	input := gitConfigMap(map[string]string{
		"user.email": "jd@j.d",
	})
	checkAuthor(t, input, "jd@j.d")
}

func TestGetCommitAuthor_NoNameNoEmail(t *testing.T) {
	input := gitConfigMap(nil)
	checkAuthor(t, input, "")
}

func TestGetCommitAuthor_NameAndEmptyEmail(t *testing.T) {
	input := gitConfigMap(map[string]string{
		"user.name":  "Jane Doe",
		"user.email": "",
	})
	checkAuthor(t, input, "Jane Doe")
}

func TestGetCommitAuthor_EmailAndEmptyName(t *testing.T) {
	input := gitConfigMap(map[string]string{
		"user.name":  "",
		"user.email": "jd@j.d",
	})
	checkAuthor(t, input, "jd@j.d")
}

func checkAuthor(t *testing.T, input map[string]string, expected string) {
	author := getCommitAuthor(input)
	if author != expected {
		t.Fatalf("author %q does not match expected value %q", author, expected)
	}
}

func gitConfigMap(input map[string]string) map[string]string {
	res := map[string]string{
		"push.default":                 "simple",
		"merge.conflictstyle":          "diff3",
		"pull.ff":                      "only",
		"core.repositoryformatversion": "0",
		"core.filemode":                "true",
		"core.bare":                    "false",
	}
	for k, v := range input {
		res[k] = v
	}
	return res
}
<commit_msg>refactor: extract base git config in tests<commit_after>package main

import (
	"reflect"
	"testing"
)

func TestUserGitconfigMap_EmptyString(t *testing.T) {
	d := ""
	userGitconfigInfo := userGitconfigMap(d)
	if len(userGitconfigInfo) != 0 {
		t.Fatal("expected map with no keys")
	}
}

func TestUserGitconfigMap(t *testing.T) {
	d := gitConfig
	expected := gitConfigMap(nil)

	userGitconfigInfo := userGitconfigMap(d)
	if len(userGitconfigInfo) != 6 {
		t.Fatal("got map with unexpected number of keys")
	}
	if !reflect.DeepEqual(userGitconfigInfo, expected) {
		t.Fatal("result does not match expected structure")
	}
}

func TestUserGitconfigMap_WithEmptyLines(t *testing.T) {
	d := `
    user.name=Jane Doe

    ` + gitConfig + `
    `
	expected := gitConfigMap(map[string]string{
		"user.name": "Jane Doe",
	})
	userGitconfigInfo := userGitconfigMap(d)

	if len(userGitconfigInfo) != 7 {
		t.Fatal("got map with unexpected number of keys")
	}
	if !reflect.DeepEqual(userGitconfigInfo, expected) {
		t.Fatal("result does not match expected structure")
	}
}

func TestUserGitconfigMap_WithNoKeys(t *testing.T) {
	d := `
	`
	expected := make(map[string]string)

	userGitconfigInfo := userGitconfigMap(d)
	if len(userGitconfigInfo) != 0 {
		t.Fatal("expected map with no keys")
	}
	if !reflect.DeepEqual(userGitconfigInfo, expected) {
		t.Fatal("result does not match expected structure")
	}
}

func TestGetCommitAuthor_BothNameAndEmail(t *testing.T) {
	input := gitConfigMap(map[string]string{
		"user.name":  "Jane Doe",
		"user.email": "jd@j.d",
	})
	checkAuthor(t, input, "Jane Doe <jd@j.d>")
}

func TestGetCommitAuthor_OnlyName(t *testing.T) {
	input := gitConfigMap(map[string]string{
		"user.name": "Jane Doe",
	})
	checkAuthor(t, input, "Jane Doe")
}

func TestGetCommitAuthor_OnlyEmail(t *testing.T) {
	input := gitConfigMap(map[string]string{
		"user.email": "jd@j.d",
	})
	checkAuthor(t, input, "jd@j.d")
}

func TestGetCommitAuthor_NoNameNoEmail(t *testing.T) {
	input := gitConfigMap(nil)
	checkAuthor(t, input, "")
}

func TestGetCommitAuthor_NameAndEmptyEmail(t *testing.T) {
	input := gitConfigMap(map[string]string{
		"user.name":  "Jane Doe",
		"user.email": "",
	})
	checkAuthor(t, input, "Jane Doe")
}

func TestGetCommitAuthor_EmailAndEmptyName(t *testing.T) {
	input := gitConfigMap(map[string]string{
		"user.name":  "",
		"user.email": "jd@j.d",
	})
	checkAuthor(t, input, "jd@j.d")
}

func checkAuthor(t *testing.T, input map[string]string, expected string) {
	author := getCommitAuthor(input)
	if author != expected {
		t.Fatalf("author %q does not match expected value %q", author, expected)
	}
}

var gitConfig = `push.default=simple
    merge.conflictstyle=diff3
    pull.ff=only
    core.repositoryformatversion=0
    core.filemode=true
    core.bare=false
    `

func gitConfigMap(input map[string]string) map[string]string {
	res := map[string]string{
		"push.default":                 "simple",
		"merge.conflictstyle":          "diff3",
		"pull.ff":                      "only",
		"core.repositoryformatversion": "0",
		"core.filemode":                "true",
		"core.bare":                    "false",
	}
	for k, v := range input {
		res[k] = v
	}
	return res
}
<|endoftext|>


<commit_before>//
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package main

import (
	"archive/tar"
	"compress/gzip"
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/spf13/cobra"
)

var (
	// version of application at compile time (-X 'main.version=$(VERSION)').
	version = "(Unknown Version)"
	// buildHash GIT hash of application at compile time (-X 'main.buildHash=$(GITCOMMIT)').
	buildHash = "No Git-hash Provided."
	// buildDate of application at compile time (-X 'main.buildDate=$(BUILDDATE)').
	buildDate = "No Build Date Provided."
	// show debug output
	debug = false
	// output filepath to save snapshot to
	outPath string
)

type FileInfoFilter func(os.FileInfo) (bool, error)

var (
	pseudoFiles = []string{
		"/proc/cpuinfo",
		"/proc/meminfo",
	}
)

func regularFileFilter(fi os.FileInfo) (bool, error) {
	return fi.Mode().IsRegular(), nil
}

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "ghw-snapshot",
	Short: "ghw-snapshot - Snapshot filesystem containing system information.",
	RunE:  execute,
}

func trace(msg string, args ...interface{}) {
	if !debug {
		return
	}
	fmt.Printf(msg, args...)
}

func systemFingerprint() string {
	hn, err := os.Hostname()
	if err != nil {
		return "unknown"
	}
	m := md5.New()
	io.WriteString(m, hn)
	return fmt.Sprintf("%x", m.Sum(nil))
}

func defaultOutPath() string {
	fp := systemFingerprint()
	return fmt.Sprintf("%s-%s-%s.tar.gz", runtime.GOOS, runtime.GOARCH, fp)
}

func execute(cmd *cobra.Command, args []string) error {
	scratchDir, err := ioutil.TempDir("", "ghw-snapshot")
	if err != nil {
		return err
	}
	defer os.RemoveAll(scratchDir)

	var createPaths = []string{
		"proc",
		"sys/block",
	}

	for _, path := range createPaths {
		if err = os.MkdirAll(filepath.Join(scratchDir, path), os.ModePerm); err != nil {
			return err
		}
	}

	if err = createProcFiles(scratchDir); err != nil {
		return err
	}
	if err = createBlockDevices(scratchDir); err != nil {
		return err
	}
	return doSnapshot(scratchDir)
}

func createProcFiles(buildDir string) error {
	// Attempting to tar up pseudofiles like /proc/cpuinfo is an exercise in
	// futility. Instead, it is necessary to build a directory structure in a
	// tmpdir and create actual files with copies of the pseudofile contents
	var createPaths = []string{
		"/proc/cpuinfo",
		"/proc/meminfo",
	}

	for _, path := range createPaths {
		buf, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}
		targetPath := filepath.Join(buildDir, path)
		f, err := os.Create(targetPath)
		if err != nil {
			return err
		}
		if _, err = f.Write(buf); err != nil {
			return err
		}
		f.Close()
	}
	return nil
}

func createBlockDevices(buildDir string) error {
	// Grab all the block device pseudo-directories from /sys/block symlinks
	// (excluding loopback devices) and inject them into our build filesystem
	// with all but the circular symlink'd subsystem directories
	devLinks, err := ioutil.ReadDir("/sys/block")
	if err != nil {
		return err
	}
	for _, devLink := range devLinks {
		dname := devLink.Name()
		if strings.HasPrefix(dname, "loop") {
			continue
		}
		devPath := filepath.Join("/sys/block", dname)
		fi, err := os.Lstat(devPath)
		if err != nil {
			return err
		}
		var link string
		if fi.Mode()&os.ModeSymlink != 0 {
			link, err = os.Readlink(devPath)
			if err != nil {
				return err
			}
		}
		// Create a symlink in our build filesystem that is a directory
		// pointing to the actual device bus path where the block device's
		// information directory resides
		linkPath := filepath.Join(buildDir, "sys/block", dname)
		linkTargetPath := filepath.Join(buildDir, "sys/block", strings.TrimPrefix(link, string(os.PathSeparator)))
		trace("creating device directory %s\n", linkTargetPath)
		if err = os.MkdirAll(linkTargetPath, os.ModePerm); err != nil {
			return err
		}
		trace("linking device directory %s to %s\n", linkPath, linkTargetPath)
		if err = os.Symlink(linkTargetPath, linkPath); err != nil {
			return err
		}
		if err = createBlockDeviceDir(linkTargetPath); err != nil {
			return err
		}
	}
	return nil
}

func createBlockDeviceDir(deviceDir string) error {
	// Populate the supplied directory (in our build filesystem) with all the
	// appropriate information pseudofile contents for the block device
	return nil
}

func doSnapshot(buildDir string) error {
	if outPath == "" {
		outPath = defaultOutPath()
		trace("using default output filepath %s\n", outPath)
	}

	var f *os.File
	var err error

	if _, err = os.Stat(outPath); os.IsNotExist(err) {
		if f, err = os.Create(outPath); err != nil {
			return err
		}
	} else if err != nil {
		return err
	} else {
		f, err := os.OpenFile(outPath, os.O_WRONLY, 0600)
		if err != nil {
			return err
		}
		fs, err := f.Stat()
		if err != nil {
			return err
		}
		if fs.Size() > 0 {
			return fmt.Errorf("File %s already exists and is of size >0", outPath)
		}
	}
	defer f.Close()

	gzw := gzip.NewWriter(f)
	defer gzw.Close()

	tw := tar.NewWriter(gzw)
	defer tw.Close()

	return createSnapshot(tw, buildDir)
}

func main() {
	rootCmd.Execute()
}

func init() {
	rootCmd.PersistentFlags().StringVarP(
		&outPath,
		"out", "o",
		outPath,
		"Path to place snapshot. Defaults to file in current directory with name $OS-$ARCH-$HASHSYSTEMNAME.tar.gz",
	)
	rootCmd.PersistentFlags().BoolVarP(
		&debug, "debug", "d", false, "Enable or disable debug mode",
	)
}

func createSnapshot(tw *tar.Writer, buildDir string) error {
	return filepath.Walk(buildDir, func(path string, fi os.FileInfo, err error) error {
		if path == buildDir {
			return nil
		}
		var link string

		if fi.Mode()&os.ModeSymlink != 0 {
			trace("processing symlink %s\n", path)
			link, err = os.Readlink(path)
			if err != nil {
				return err
			}
		}

		hdr, err := tar.FileInfoHeader(fi, link)
		if err != nil {
			return err
		}
		hdr.Name = strings.TrimPrefix(strings.TrimPrefix(path, buildDir), string(os.PathSeparator))

		if err = tw.WriteHeader(hdr); err != nil {
			return err
		}

		switch hdr.Typeflag {
		case tar.TypeReg, tar.TypeRegA:
			f, err := os.Open(path)
			if err != nil {
				return err
			}
			if _, err = io.Copy(tw, f); err != nil {
				return err
			}
			f.Close()
		}
		return nil
	})
}
<commit_msg>place block device info into snapshot<commit_after>//
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package main

import (
	"archive/tar"
	"compress/gzip"
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/spf13/cobra"
)

var (
	// version of application at compile time (-X 'main.version=$(VERSION)').
	version = "(Unknown Version)"
	// buildHash GIT hash of application at compile time (-X 'main.buildHash=$(GITCOMMIT)').
	buildHash = "No Git-hash Provided."
	// buildDate of application at compile time (-X 'main.buildDate=$(BUILDDATE)').
	buildDate = "No Build Date Provided."
	// show debug output
	debug = false
	// output filepath to save snapshot to
	outPath string
)

type FileInfoFilter func(os.FileInfo) (bool, error)

var (
	pseudoFiles = []string{
		"/proc/cpuinfo",
		"/proc/meminfo",
	}
)

func regularFileFilter(fi os.FileInfo) (bool, error) {
	return fi.Mode().IsRegular(), nil
}

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "ghw-snapshot",
	Short: "ghw-snapshot - Snapshot filesystem containing system information.",
	RunE:  execute,
}

func trace(msg string, args ...interface{}) {
	if !debug {
		return
	}
	fmt.Printf(msg, args...)
}

func systemFingerprint() string {
	hn, err := os.Hostname()
	if err != nil {
		return "unknown"
	}
	m := md5.New()
	io.WriteString(m, hn)
	return fmt.Sprintf("%x", m.Sum(nil))
}

func defaultOutPath() string {
	fp := systemFingerprint()
	return fmt.Sprintf("%s-%s-%s.tar.gz", runtime.GOOS, runtime.GOARCH, fp)
}

func execute(cmd *cobra.Command, args []string) error {
	scratchDir, err := ioutil.TempDir("", "ghw-snapshot")
	if err != nil {
		return err
	}
	defer os.RemoveAll(scratchDir)

	var createPaths = []string{
		"proc",
		"sys/block",
	}

	for _, path := range createPaths {
		if err = os.MkdirAll(filepath.Join(scratchDir, path), os.ModePerm); err != nil {
			return err
		}
	}

	if err = createPseudofiles(scratchDir); err != nil {
		return err
	}
	if err = createBlockDevices(scratchDir); err != nil {
		return err
	}
	return doSnapshot(scratchDir)
}

// Attempting to tar up pseudofiles like /proc/cpuinfo is an exercise in
// futility. Notably, the pseudofiles, when read by syscalls, do not return the
// number of bytes read. This causes the tar writer to write zero-length files.
//
// Instead, it is necessary to build a directory structure in a tmpdir and
// create actual files with copies of the pseudofile contents
func createPseudofiles(buildDir string) error {
	var createPaths = []string{
		"/proc/cpuinfo",
		"/proc/meminfo",
	}

	for _, path := range createPaths {
		buf, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}
		targetPath := filepath.Join(buildDir, path)
		f, err := os.Create(targetPath)
		if err != nil {
			return err
		}
		if _, err = f.Write(buf); err != nil {
			return err
		}
		f.Close()
	}
	return nil
}

func createBlockDevices(buildDir string) error {
	// Grab all the block device pseudo-directories from /sys/block symlinks
	// (excluding loopback devices) and inject them into our build filesystem
	// with all but the circular symlink'd subsystem directories
	devLinks, err := ioutil.ReadDir("/sys/block")
	if err != nil {
		return err
	}
	for _, devLink := range devLinks {
		dname := devLink.Name()
		if strings.HasPrefix(dname, "loop") {
			continue
		}
		devPath := filepath.Join("/sys/block", dname)
		fi, err := os.Lstat(devPath)
		if err != nil {
			return err
		}
		var link string
		if fi.Mode()&os.ModeSymlink != 0 {
			link, err = os.Readlink(devPath)
			if err != nil {
				return err
			}
		}
		// Create a symlink in our build filesystem that is a directory
		// pointing to the actual device bus path where the block device's
		// information directory resides
		linkPath := filepath.Join(buildDir, "sys/block", dname)
		linkTargetPath := filepath.Join(
			buildDir,
			"sys/block",
			strings.TrimPrefix(link, string(os.PathSeparator)),
		)
		trace("creating device directory %s\n", linkTargetPath)
		if err = os.MkdirAll(linkTargetPath, os.ModePerm); err != nil {
			return err
		}
		trace("linking device directory %s to %s\n", linkPath, linkTargetPath)
		if err = os.Symlink(linkTargetPath, linkPath); err != nil {
			return err
		}
		// Now read the source block device directory and populate the
		// newly-created target link in the build directory with the
		// appropriate block device pseudofiles
		srcDeviceDir := filepath.Join(
			"/sys/block",
			strings.TrimPrefix(link, string(os.PathSeparator)),
		)
		if err = createBlockDeviceDir(linkTargetPath, srcDeviceDir); err != nil {
			return err
		}
	}
	return nil
}

func createBlockDeviceDir(buildDeviceDir string, srcDeviceDir string) error {
	// Populate the supplied directory (in our build filesystem) with all the
	// appropriate information pseudofile contents for the block device.
	devName := filepath.Base(srcDeviceDir)
	devFiles, err := ioutil.ReadDir(srcDeviceDir)
	if err != nil {
		return err
	}
	for _, f := range devFiles {
		fname := f.Name()
		fp := filepath.Join(srcDeviceDir, fname)
		fi, err := os.Lstat(fp)
		if err != nil {
			return err
		}
		// Ignore any symlinks in the deviceDir since they simply point to either
		// self-referential links or information we aren't interested in like
		// "subsystem"
		if fi.Mode()&os.ModeSymlink != 0 {
			continue
		}
		if fi.IsDir() {
			// The only directories we're interested in are the directories
			// that begin with the block device name. These are directories
			// with information about the partitions on the device
			if !strings.HasPrefix(fname, devName) {
				buildPartitionDir := filepath.Join(
					buildDeviceDir, fname,
				)
				srcPartitionDir := filepath.Join(
					srcDeviceDir, fname,
				)
				err = createPartitionDir(buildPartitionDir, srcPartitionDir)
				if err != nil {
					return err
				}
			}
		} else if fi.Mode().IsRegular() {
			// Regular files in the block device directory are both regular and
			// pseudofiles containing information such as the size (in sectors)
			// and whether the device is read-only
			buf, err := ioutil.ReadFile(fp)
			if err != nil {
				return err
			}
			targetPath := filepath.Join(buildDeviceDir, fname)
			f, err := os.Create(targetPath)
			if err != nil {
				return err
			}
			if _, err = f.Write(buf); err != nil {
				return err
			}
			f.Close()
		}
	}
	return nil
}

func createPartitionDir(buildPartitionDir string, srcPartitionDir string) error {
	return nil
}

func doSnapshot(buildDir string) error {
	if outPath == "" {
		outPath = defaultOutPath()
		trace("using default output filepath %s\n", outPath)
	}

	var f *os.File
	var err error

	if _, err = os.Stat(outPath); os.IsNotExist(err) {
		if f, err = os.Create(outPath); err != nil {
			return err
		}
	} else if err != nil {
		return err
	} else {
		f, err := os.OpenFile(outPath, os.O_WRONLY, 0600)
		if err != nil {
			return err
		}
		fs, err := f.Stat()
		if err != nil {
			return err
		}
		if fs.Size() > 0 {
			return fmt.Errorf("File %s already exists and is of size >0", outPath)
		}
	}
	defer f.Close()

	gzw := gzip.NewWriter(f)
	defer gzw.Close()

	tw := tar.NewWriter(gzw)
	defer tw.Close()

	return createSnapshot(tw, buildDir)
}

func main() {
	rootCmd.Execute()
}

func init() {
	rootCmd.PersistentFlags().StringVarP(
		&outPath,
		"out", "o",
		outPath,
		"Path to place snapshot. Defaults to file in current directory with name $OS-$ARCH-$HASHSYSTEMNAME.tar.gz",
	)
	rootCmd.PersistentFlags().BoolVarP(
		&debug, "debug", "d", false, "Enable or disable debug mode",
	)
}

func createSnapshot(tw *tar.Writer, buildDir string) error {
	return filepath.Walk(buildDir, func(path string, fi os.FileInfo, err error) error {
		if path == buildDir {
			return nil
		}
		var link string

		if fi.Mode()&os.ModeSymlink != 0 {
			trace("processing symlink %s\n", path)
			link, err = os.Readlink(path)
			if err != nil {
				return err
			}
		}

		hdr, err := tar.FileInfoHeader(fi, link)
		if err != nil {
			return err
		}
		hdr.Name = strings.TrimPrefix(strings.TrimPrefix(path, buildDir), string(os.PathSeparator))

		if err = tw.WriteHeader(hdr); err != nil {
			return err
		}

		switch hdr.Typeflag {
		case tar.TypeReg, tar.TypeRegA:
			f, err := os.Open(path)
			if err != nil {
				return err
			}
			if _, err = io.Copy(tw, f); err != nil {
				return err
			}
			f.Close()
		}
		return nil
	})
}
<|endoftext|>


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

package commands

import (
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/gnuflag"
	"gopkg.in/juju/names.v2"

	actionapi "github.com/juju/juju/api/action"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/juju/action"
	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
)

func newRunCommand() cmd.Command {
	return modelcmd.Wrap(&runCommand{})
}

// runCommand is responsible for running arbitrary commands on remote machines.
type runCommand struct {
	modelcmd.ModelCommandBase
	out      cmd.Output
	all      bool
	timeout  time.Duration
	machines []string
	services []string
	units    []string
	commands string
}

const runDoc = `
Run the commands on the specified targets.

Targets are specified using either machine ids, application names or unit
names.  At least one target specifier is needed.

Multiple values can be set for --machine, --application, and --unit by using
comma separated values.

If the target is a machine, the command is run as the "ubuntu" user on
the remote machine.

If the target is an application, the command is run on all units for that
application. For example, if there was an application "mysql" and that application
had two units, "mysql/0" and "mysql/1", then
  --application mysql
is equivalent to
  --unit mysql/0,mysql/1

Commands run for applications or units are executed in a 'hook context' for
the unit.

--all is provided as a simple way to run the command on all the machines
in the model.  If you specify --all you cannot provide additional
targets.

Since juju run creates actions, you can query for the status of commands
started with juju run by calling "juju show-action-status --name juju-run".
`

func (c *runCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "run",
		Args:    "<commands>",
		Purpose: "Run the commands on the remote targets specified.",
		Doc:     runDoc,
	}
}

func (c *runCommand) SetFlags(f *gnuflag.FlagSet) {
	c.ModelCommandBase.SetFlags(f)
	c.out.AddFlags(f, "default", map[string]cmd.Formatter{
		"yaml": cmd.FormatYaml,
		"json": cmd.FormatJson,
		// default is used to format a single result specially.
		"default": cmd.FormatYaml,
	})
	f.BoolVar(&c.all, "all", false, "Run the commands on all the machines")
	f.DurationVar(&c.timeout, "timeout", 5*time.Minute, "How long to wait before the remote command is considered to have failed")
	f.Var(cmd.NewStringsValue(nil, &c.machines), "machine", "One or more machine ids")
	f.Var(cmd.NewStringsValue(nil, &c.services), "application", "One or more application names")
	f.Var(cmd.NewStringsValue(nil, &c.units), "unit", "One or more unit ids")
}

func (c *runCommand) Init(args []string) error {
	if len(args) == 0 {
		return errors.Errorf("no commands specified")
	}
	c.commands, args = args[0], args[1:]

	if c.all {
		if len(c.machines) != 0 {
			return errors.Errorf("You cannot specify --all and individual machines")
		}
		if len(c.services) != 0 {
			return errors.Errorf("You cannot specify --all and individual applications")
		}
		if len(c.units) != 0 {
			return errors.Errorf("You cannot specify --all and individual units")
		}
	} else {
		if len(c.machines) == 0 && len(c.services) == 0 && len(c.units) == 0 {
			return errors.Errorf("You must specify a target, either through --all, --machine, --application or --unit")
		}
	}

	var nameErrors []string
	for _, machineId := range c.machines {
		if !names.IsValidMachine(machineId) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid machine id", machineId))
		}
	}
	for _, service := range c.services {
		if !names.IsValidApplication(service) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid application name", service))
		}
	}
	for _, unit := range c.units {
		if !names.IsValidUnit(unit) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid unit name", unit))
		}
	}
	if len(nameErrors) > 0 {
		return errors.Errorf("The following run targets are not valid:\n%s",
			strings.Join(nameErrors, "\n"))
	}

	return cmd.CheckEmpty(args)
}

// ConvertActionResults takes the results from the api and creates a map
// suitable for format converstion to YAML or JSON.
func ConvertActionResults(result params.ActionResult, query actionQuery) map[string]interface{} {
	values := make(map[string]interface{})
	values[query.receiver.receiverType] = query.receiver.tag.Id()
	if result.Error != nil {
		values["Error"] = result.Error.Error()
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Action.Tag != query.actionTag.String() {
		values["Error"] = fmt.Sprintf("expected action tag %q, got %q", query.actionTag.String(), result.Action.Tag)
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Action.Receiver != query.receiver.tag.String() {
		values["Error"] = fmt.Sprintf("expected action receiver %q, got %q", query.receiver.tag.String(), result.Action.Receiver)
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Message != "" {
		values["Message"] = result.Message
	}
	// We always want to have a string for stdout, but only show stderr,
	// code and error if they are there.
	if res, ok := result.Output["Stdout"].(string); ok {
		values["Stdout"] = strings.Replace(res, "\r\n", "\n", -1)
		if res, ok := result.Output["StdoutEncoding"].(string); ok && res != "" {
			values["Stdout.encoding"] = res
		}
	} else {
		values["Stdout"] = ""
	}
	if res, ok := result.Output["Stderr"].(string); ok && res != "" {
		values["Stderr"] = strings.Replace(res, "\r\n", "\n", -1)
		if res, ok := result.Output["StderrEncoding"].(string); ok && res != "" {
			values["Stderr.encoding"] = res
		}
	}
	if res, ok := result.Output["Code"].(string); ok {
		code, err := strconv.Atoi(res)
		if err == nil && code != 0 {
			values["ReturnCode"] = code
		}
	}
	return values
}

func (c *runCommand) Run(ctx *cmd.Context) error {
	client, err := getRunAPIClient(c)
	if err != nil {
		return err
	}
	defer client.Close()

	var runResults []params.ActionResult
	if c.all {
		runResults, err = client.RunOnAllMachines(c.commands, c.timeout)
	} else {
		params := params.RunParams{
			Commands:     c.commands,
			Timeout:      c.timeout,
			Machines:     c.machines,
			Applications: c.services,
			Units:        c.units,
		}
		runResults, err = client.Run(params)
	}

	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	actionsToQuery := []actionQuery{}
	for _, result := range runResults {
		if result.Error != nil {
			fmt.Fprintf(ctx.GetStderr(), "couldn't queue one action: %v", result.Error)
			continue
		}
		actionTag, err := names.ParseActionTag(result.Action.Tag)
		if err != nil {
			fmt.Fprintf(ctx.GetStderr(), "got invalid action tag %v for receiver %v", result.Action.Tag, result.Action.Receiver)
			continue
		}

		receiverTag, err := names.ActionReceiverFromTag(result.Action.Receiver)
		if err != nil {
			fmt.Fprintf(ctx.GetStderr(), "got invalid action receiver tag %v for action %v", result.Action.Receiver, result.Action.Tag)
			continue
		}
		var receiverType string
		switch receiverTag.(type) {
		case names.UnitTag:
			receiverType = "UnitId"
		case names.MachineTag:
			receiverType = "MachineId"
		default:
			receiverType = "ReceiverId"
		}
		actionsToQuery = append(actionsToQuery, actionQuery{
			actionTag: actionTag,
			receiver: actionReceiver{
				receiverType: receiverType,
				tag:          receiverTag,
			}})
	}

	if len(actionsToQuery) == 0 {
		return errors.New("no actions were successfully enqueued, aborting")
	}

	values := []interface{}{}
	for len(actionsToQuery) > 0 {
		actionResults, err := client.Actions(entities(actionsToQuery))
		if err != nil {
			return errors.Trace(err)
		}

		newActionsToQuery := []actionQuery{}
		for i, result := range actionResults.Results {
			if result.Error == nil {
				switch result.Status {
				case params.ActionRunning, params.ActionPending:
					newActionsToQuery = append(newActionsToQuery, actionsToQuery[i])
					continue
				}
			}

			values = append(values, ConvertActionResults(result, actionsToQuery[i]))
		}

		actionsToQuery = newActionsToQuery

		// TODO: use a watcher instead of sleeping
		// this should be easier once we implement action grouping
		<-afterFunc(1 * time.Second)
	}

	// If we are just dealing with one result, AND we are using the default
	// format, then pretend we were running it locally.
	if len(values) == 1 && c.out.Name() == "default" {
		result, ok := values[0].(map[string]interface{})
		if !ok {
			return errors.New("couldn't read action output")
		}
		if res, ok := result["Error"].(string); ok {
			return errors.New(res)
		}
		ctx.Stdout.Write(formatOutput(result, "Stdout"))
		ctx.Stderr.Write(formatOutput(result, "Stderr"))
		if code, ok := result["ReturnCode"].(int); ok && code != 0 {
			return cmd.NewRcPassthroughError(code)
		}
		// Message should always contain only errors.
		if res, ok := result["Message"].(string); ok && res != "" {
			ctx.Stderr.Write([]byte(res))
		}

		return nil
	}

	return c.out.Write(ctx, values)
}

type actionReceiver struct {
	receiverType string
	tag          names.Tag
}

type actionQuery struct {
	receiver  actionReceiver
	actionTag names.ActionTag
}

// RunClient exposes the capabilities required by the CLI
type RunClient interface {
	action.APIClient
	RunOnAllMachines(commands string, timeout time.Duration) ([]params.ActionResult, error)
	Run(params.RunParams) ([]params.ActionResult, error)
}

// In order to be able to easily mock out the API side for testing,
// the API client is retrieved using a function.
var getRunAPIClient = func(c *runCommand) (RunClient, error) {
	root, err := c.NewAPIRoot()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return actionapi.NewClient(root), errors.Trace(err)
}

// getActionResult abstracts over the action CLI function that we use here to fetch results
var getActionResult = func(c RunClient, actionId string, wait *time.Timer) (params.ActionResult, error) {
	return action.GetActionResult(c, actionId, wait)
}

var afterFunc = func(d time.Duration) <-chan time.Time {
	return time.After(d)
}

// entities is a convenience constructor for params.Entities.
func entities(actions []actionQuery) params.Entities {
	entities := params.Entities{
		Entities: make([]params.Entity, len(actions)),
	}
	for i, action := range actions {
		entities.Entities[i].Tag = action.actionTag.String()
	}
	return entities
}

func formatOutput(results map[string]interface{}, key string) []byte {
	res, ok := results[key].(string)
	if !ok {
		return []byte("")
	}
	if enc, ok := results[key+".encoding"].(string); ok && enc != "" {
		switch enc {
		case "base64":
			decoded, err := base64.StdEncoding.DecodeString(res)
			if err != nil {
				return []byte("expected b64 encoded string, got " + res)
			}
			return decoded
		default:
			return []byte(res)
		}
	}
	return []byte(res)
}
<commit_msg>Document admin only.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package commands

import (
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/gnuflag"
	"gopkg.in/juju/names.v2"

	actionapi "github.com/juju/juju/api/action"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/juju/action"
	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
)

func newRunCommand() cmd.Command {
	return modelcmd.Wrap(&runCommand{})
}

// runCommand is responsible for running arbitrary commands on remote machines.
type runCommand struct {
	modelcmd.ModelCommandBase
	out      cmd.Output
	all      bool
	timeout  time.Duration
	machines []string
	services []string
	units    []string
	commands string
}

const runDoc = `
Run the commands on the specified targets. Only admin users of a model
are able to use this command.

Targets are specified using either machine ids, application names or unit
names.  At least one target specifier is needed.

Multiple values can be set for --machine, --application, and --unit by using
comma separated values.

If the target is a machine, the command is run as the "ubuntu" user on
the remote machine.

If the target is an application, the command is run on all units for that
application. For example, if there was an application "mysql" and that application
had two units, "mysql/0" and "mysql/1", then
  --application mysql
is equivalent to
  --unit mysql/0,mysql/1

Commands run for applications or units are executed in a 'hook context' for
the unit.

--all is provided as a simple way to run the command on all the machines
in the model.  If you specify --all you cannot provide additional
targets.

Since juju run creates actions, you can query for the status of commands
started with juju run by calling "juju show-action-status --name juju-run".
`

func (c *runCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "run",
		Args:    "<commands>",
		Purpose: "Run the commands on the remote targets specified.",
		Doc:     runDoc,
	}
}

func (c *runCommand) SetFlags(f *gnuflag.FlagSet) {
	c.ModelCommandBase.SetFlags(f)
	c.out.AddFlags(f, "default", map[string]cmd.Formatter{
		"yaml": cmd.FormatYaml,
		"json": cmd.FormatJson,
		// default is used to format a single result specially.
		"default": cmd.FormatYaml,
	})
	f.BoolVar(&c.all, "all", false, "Run the commands on all the machines")
	f.DurationVar(&c.timeout, "timeout", 5*time.Minute, "How long to wait before the remote command is considered to have failed")
	f.Var(cmd.NewStringsValue(nil, &c.machines), "machine", "One or more machine ids")
	f.Var(cmd.NewStringsValue(nil, &c.services), "application", "One or more application names")
	f.Var(cmd.NewStringsValue(nil, &c.units), "unit", "One or more unit ids")
}

func (c *runCommand) Init(args []string) error {
	if len(args) == 0 {
		return errors.Errorf("no commands specified")
	}
	c.commands, args = args[0], args[1:]

	if c.all {
		if len(c.machines) != 0 {
			return errors.Errorf("You cannot specify --all and individual machines")
		}
		if len(c.services) != 0 {
			return errors.Errorf("You cannot specify --all and individual applications")
		}
		if len(c.units) != 0 {
			return errors.Errorf("You cannot specify --all and individual units")
		}
	} else {
		if len(c.machines) == 0 && len(c.services) == 0 && len(c.units) == 0 {
			return errors.Errorf("You must specify a target, either through --all, --machine, --application or --unit")
		}
	}

	var nameErrors []string
	for _, machineId := range c.machines {
		if !names.IsValidMachine(machineId) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid machine id", machineId))
		}
	}
	for _, service := range c.services {
		if !names.IsValidApplication(service) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid application name", service))
		}
	}
	for _, unit := range c.units {
		if !names.IsValidUnit(unit) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid unit name", unit))
		}
	}
	if len(nameErrors) > 0 {
		return errors.Errorf("The following run targets are not valid:\n%s",
			strings.Join(nameErrors, "\n"))
	}

	return cmd.CheckEmpty(args)
}

// ConvertActionResults takes the results from the api and creates a map
// suitable for format converstion to YAML or JSON.
func ConvertActionResults(result params.ActionResult, query actionQuery) map[string]interface{} {
	values := make(map[string]interface{})
	values[query.receiver.receiverType] = query.receiver.tag.Id()
	if result.Error != nil {
		values["Error"] = result.Error.Error()
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Action.Tag != query.actionTag.String() {
		values["Error"] = fmt.Sprintf("expected action tag %q, got %q", query.actionTag.String(), result.Action.Tag)
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Action.Receiver != query.receiver.tag.String() {
		values["Error"] = fmt.Sprintf("expected action receiver %q, got %q", query.receiver.tag.String(), result.Action.Receiver)
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Message != "" {
		values["Message"] = result.Message
	}
	// We always want to have a string for stdout, but only show stderr,
	// code and error if they are there.
	if res, ok := result.Output["Stdout"].(string); ok {
		values["Stdout"] = strings.Replace(res, "\r\n", "\n", -1)
		if res, ok := result.Output["StdoutEncoding"].(string); ok && res != "" {
			values["Stdout.encoding"] = res
		}
	} else {
		values["Stdout"] = ""
	}
	if res, ok := result.Output["Stderr"].(string); ok && res != "" {
		values["Stderr"] = strings.Replace(res, "\r\n", "\n", -1)
		if res, ok := result.Output["StderrEncoding"].(string); ok && res != "" {
			values["Stderr.encoding"] = res
		}
	}
	if res, ok := result.Output["Code"].(string); ok {
		code, err := strconv.Atoi(res)
		if err == nil && code != 0 {
			values["ReturnCode"] = code
		}
	}
	return values
}

func (c *runCommand) Run(ctx *cmd.Context) error {
	client, err := getRunAPIClient(c)
	if err != nil {
		return err
	}
	defer client.Close()

	var runResults []params.ActionResult
	if c.all {
		runResults, err = client.RunOnAllMachines(c.commands, c.timeout)
	} else {
		params := params.RunParams{
			Commands:     c.commands,
			Timeout:      c.timeout,
			Machines:     c.machines,
			Applications: c.services,
			Units:        c.units,
		}
		runResults, err = client.Run(params)
	}

	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	actionsToQuery := []actionQuery{}
	for _, result := range runResults {
		if result.Error != nil {
			fmt.Fprintf(ctx.GetStderr(), "couldn't queue one action: %v", result.Error)
			continue
		}
		actionTag, err := names.ParseActionTag(result.Action.Tag)
		if err != nil {
			fmt.Fprintf(ctx.GetStderr(), "got invalid action tag %v for receiver %v", result.Action.Tag, result.Action.Receiver)
			continue
		}

		receiverTag, err := names.ActionReceiverFromTag(result.Action.Receiver)
		if err != nil {
			fmt.Fprintf(ctx.GetStderr(), "got invalid action receiver tag %v for action %v", result.Action.Receiver, result.Action.Tag)
			continue
		}
		var receiverType string
		switch receiverTag.(type) {
		case names.UnitTag:
			receiverType = "UnitId"
		case names.MachineTag:
			receiverType = "MachineId"
		default:
			receiverType = "ReceiverId"
		}
		actionsToQuery = append(actionsToQuery, actionQuery{
			actionTag: actionTag,
			receiver: actionReceiver{
				receiverType: receiverType,
				tag:          receiverTag,
			}})
	}

	if len(actionsToQuery) == 0 {
		return errors.New("no actions were successfully enqueued, aborting")
	}

	values := []interface{}{}
	for len(actionsToQuery) > 0 {
		actionResults, err := client.Actions(entities(actionsToQuery))
		if err != nil {
			return errors.Trace(err)
		}

		newActionsToQuery := []actionQuery{}
		for i, result := range actionResults.Results {
			if result.Error == nil {
				switch result.Status {
				case params.ActionRunning, params.ActionPending:
					newActionsToQuery = append(newActionsToQuery, actionsToQuery[i])
					continue
				}
			}

			values = append(values, ConvertActionResults(result, actionsToQuery[i]))
		}

		actionsToQuery = newActionsToQuery

		// TODO: use a watcher instead of sleeping
		// this should be easier once we implement action grouping
		<-afterFunc(1 * time.Second)
	}

	// If we are just dealing with one result, AND we are using the default
	// format, then pretend we were running it locally.
	if len(values) == 1 && c.out.Name() == "default" {
		result, ok := values[0].(map[string]interface{})
		if !ok {
			return errors.New("couldn't read action output")
		}
		if res, ok := result["Error"].(string); ok {
			return errors.New(res)
		}
		ctx.Stdout.Write(formatOutput(result, "Stdout"))
		ctx.Stderr.Write(formatOutput(result, "Stderr"))
		if code, ok := result["ReturnCode"].(int); ok && code != 0 {
			return cmd.NewRcPassthroughError(code)
		}
		// Message should always contain only errors.
		if res, ok := result["Message"].(string); ok && res != "" {
			ctx.Stderr.Write([]byte(res))
		}

		return nil
	}

	return c.out.Write(ctx, values)
}

type actionReceiver struct {
	receiverType string
	tag          names.Tag
}

type actionQuery struct {
	receiver  actionReceiver
	actionTag names.ActionTag
}

// RunClient exposes the capabilities required by the CLI
type RunClient interface {
	action.APIClient
	RunOnAllMachines(commands string, timeout time.Duration) ([]params.ActionResult, error)
	Run(params.RunParams) ([]params.ActionResult, error)
}

// In order to be able to easily mock out the API side for testing,
// the API client is retrieved using a function.
var getRunAPIClient = func(c *runCommand) (RunClient, error) {
	root, err := c.NewAPIRoot()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return actionapi.NewClient(root), errors.Trace(err)
}

// getActionResult abstracts over the action CLI function that we use here to fetch results
var getActionResult = func(c RunClient, actionId string, wait *time.Timer) (params.ActionResult, error) {
	return action.GetActionResult(c, actionId, wait)
}

var afterFunc = func(d time.Duration) <-chan time.Time {
	return time.After(d)
}

// entities is a convenience constructor for params.Entities.
func entities(actions []actionQuery) params.Entities {
	entities := params.Entities{
		Entities: make([]params.Entity, len(actions)),
	}
	for i, action := range actions {
		entities.Entities[i].Tag = action.actionTag.String()
	}
	return entities
}

func formatOutput(results map[string]interface{}, key string) []byte {
	res, ok := results[key].(string)
	if !ok {
		return []byte("")
	}
	if enc, ok := results[key+".encoding"].(string); ok && enc != "" {
		switch enc {
		case "base64":
			decoded, err := base64.StdEncoding.DecodeString(res)
			if err != nil {
				return []byte("expected b64 encoded string, got " + res)
			}
			return decoded
		default:
			return []byte(res)
		}
	}
	return []byte(res)
}
<|endoftext|>


<commit_before>package jwtauthd

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

type Args struct {
    Port int
    Conf string
}

type JwtAuthCommand struct {
    Args  Args
    Cmd   *cobra.Command
    Viper *viper.Viper
}

var JwtAuth *JwtAuthCommand = &JwtAuthCommand{}

func UsageTemplate() string {
    return `Usage:{{if .Runnable}}
  {{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "[OPTIONS] COMMAND [arg...]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
  {{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}
Aliases:
  {{.NameAndAliases}}
{{end}}{{if .HasExample}}
Examples:
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}
Commands:{{range .Commands}}{{if .IsAvailableCommand}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}}
Options:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableSubCommands }}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
}

func init() {
    JwtAuth.Viper = viper.GetViper()
    JwtAuth.Cmd = &cobra.Command{
        Use: "jwt-authd",
        Short: "Jwt auth server",
        Long: "User login information verification service",
        SilenceErrors: true,
        RunE: func(cmd *cobra.Command, args []string) error {
            JwtAuth.Viper.SetConfigFile(JwtAuth.Args.Conf)
            if err := JwtAuth.Viper.ReadInConfig(); err != nil {
                return err
            }

            JwtAuth.Args = Args{
                Port: JwtAuth.Viper.GetInt("port"),
            }

            return nil
        },
        //PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
        //    // todo
        //
        //    return nil
        //},
    }
    JwtAuth.Cmd.SetUsageTemplate(UsageTemplate())

    var PFlags *pflag.FlagSet = JwtAuth.Cmd.Flags()

    PFlags.IntVarP(&JwtAuth.Args.Port, "port", "p", 6010, "set the server listening port")
    PFlags.StringVarP(&JwtAuth.Args.Conf, "config", "c", "/etc/jwt_authd.json", "configuration file specifying")

    JwtAuth.Viper.BindPFlag("port", PFlags.Lookup("port"));
}<commit_msg>debug<commit_after>package jwtauthd

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

type Args struct {
    Port int
    Conf string
}

type JwtAuthCommand struct {
    Args  Args
    Cmd   *cobra.Command
    Viper *viper.Viper
}

var JwtAuth *JwtAuthCommand = &JwtAuthCommand{}

func UsageTemplate() string {
    return `Usage:{{if .Runnable}}
  {{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "[OPTIONS] COMMAND [arg...]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
  {{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}
Aliases:
  {{.NameAndAliases}}
{{end}}{{if .HasExample}}
Examples:
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}
Commands:{{range .Commands}}{{if .IsAvailableCommand}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}}
Options:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableSubCommands }}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
}

func init() {
    JwtAuth.Viper = viper.GetViper()
    JwtAuth.Cmd = &cobra.Command{
        Use: "jwt-authd",
        Short: "Jwt auth server",
        Long: "User login information verification service",
        SilenceErrors: true,
        RunE: func(cmd *cobra.Command, args []string) error {
            JwtAuth.Viper.SetConfigFile(JwtAuth.Args.Conf)
            if err := JwtAuth.Viper.ReadInConfig(); err != nil {
                return err
            }

            JwtAuth.Args.Port = JwtAuth.Viper.GetInt("port")

            return nil
        },
        //PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
        //    // todo
        //
        //    return nil
        //},
    }
    JwtAuth.Cmd.SetUsageTemplate(UsageTemplate())

    var PFlags *pflag.FlagSet = JwtAuth.Cmd.Flags()

    PFlags.IntVarP(&JwtAuth.Args.Port, "port", "p", 6010, "set the server listening port")
    PFlags.StringVarP(&JwtAuth.Args.Conf, "config", "c", "/etc/jwt_authd.json", "configuration file specifying")

    JwtAuth.Viper.BindPFlag("port", PFlags.Lookup("port"));
}<|endoftext|>