diff options
Diffstat (limited to 'vendor/github.com/hashicorp/go-sockaddr/cmd/sockaddr/command/tech_support.go')
-rw-r--r-- | vendor/github.com/hashicorp/go-sockaddr/cmd/sockaddr/command/tech_support.go | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/go-sockaddr/cmd/sockaddr/command/tech_support.go b/vendor/github.com/hashicorp/go-sockaddr/cmd/sockaddr/command/tech_support.go new file mode 100644 index 000000000..fd706009d --- /dev/null +++ b/vendor/github.com/hashicorp/go-sockaddr/cmd/sockaddr/command/tech_support.go @@ -0,0 +1,216 @@ +package command + +import ( + "flag" + "fmt" + "net" + "os/exec" + "runtime" + + "github.com/hashicorp/errwrap" + sockaddr "github.com/hashicorp/go-sockaddr" + "github.com/mitchellh/cli" +) + +type TechSupportCommand struct { + Ui cli.Ui + + // outputMode controls the type of output encoding. + outputMode string + + // flags is a list of options belonging to this command + flags *flag.FlagSet +} + +// Description is the long-form command help. +func (c *TechSupportCommand) Description() string { + return `Print out network diagnostic information that can be used by support. + +` + "The `sockaddr` library relies on OS-specific commands and output which can potentially be " + + "brittle. The `tech-support` subcommand emits all of the platform-specific " + + "network details required to debug why a given `sockaddr` API call is behaving " + + "differently than expected. The `-output` flag controls the output format. " + + "The default output mode is Markdown (`md`) however a raw mode (`raw`) is " + + "available to obtain the original output." +} + +// Help returns the full help output expected by `sockaddr -h cmd` +func (c *TechSupportCommand) Help() string { + return MakeHelp(c) +} + +// InitOpts is responsible for setup of this command's configuration via the +// command line. InitOpts() does not parse the arguments (see parseOpts()). +func (c *TechSupportCommand) InitOpts() { + c.flags = flag.NewFlagSet("tech-support", flag.ContinueOnError) + c.flags.Usage = func() { c.Ui.Output(c.Help()) } + c.flags.StringVar(&c.outputMode, "output", "md", `Encode the output using one of Markdown ("md") or Raw ("raw")`) +} + +// Run executes this command. +func (c *TechSupportCommand) Run(args []string) int { + c.InitOpts() + rest, err := c.parseOpts(args) + if err != nil { + if errwrap.Contains(err, "flag: help requested") { + return 0 + } + return 1 + } + if len(rest) != 0 { + c.Ui.Error(c.Help()) + return 1 + } + + ri, err := sockaddr.NewRouteInfo() + if err != nil { + c.Ui.Error(fmt.Sprintf("error loading route information: %v", err)) + return 1 + } + + const initNumCmds = 4 + type cmdResult struct { + cmd []string + out string + } + output := make(map[string]cmdResult, initNumCmds) + ri.VisitCommands(func(name string, cmd []string) { + out, err := exec.Command(cmd[0], cmd[1:]...).Output() + if err != nil { + out = []byte(fmt.Sprintf("ERROR: command %q failed: %v", name, err)) + } + + output[name] = cmdResult{ + cmd: cmd, + out: string(out), + } + }) + + out := c.rowWriterOutputFactory() + + for cmdName, result := range output { + switch c.outputMode { + case "md": + c.Ui.Output(fmt.Sprintf("## cmd: `%s`", cmdName)) + c.Ui.Output("") + c.Ui.Output(fmt.Sprintf("Command: `%#v`", result.cmd)) + c.Ui.Output("```") + c.Ui.Output(result.out) + c.Ui.Output("```") + c.Ui.Output("") + case "raw": + c.Ui.Output(fmt.Sprintf("cmd: %q: %#v", cmdName, result.cmd)) + c.Ui.Output("") + c.Ui.Output(result.out) + c.Ui.Output("") + default: + c.Ui.Error(fmt.Sprintf("Unsupported output type: %q", c.outputMode)) + return 1 + } + + out("s", "GOOS", runtime.GOOS) + out("s", "GOARCH", runtime.GOARCH) + out("s", "Compiler", runtime.Compiler) + out("s", "Version", runtime.Version()) + ifs, err := net.Interfaces() + if err != nil { + out("v", "net.Interfaces", err) + } else { + for i, intf := range ifs { + out("s", fmt.Sprintf("net.Interfaces[%d].Name", i), intf.Name) + out("s", fmt.Sprintf("net.Interfaces[%d].Flags", i), intf.Flags) + out("+v", fmt.Sprintf("net.Interfaces[%d].Raw", i), intf) + addrs, err := intf.Addrs() + if err != nil { + out("v", fmt.Sprintf("net.Interfaces[%d].Addrs", i), err) + } else { + for j, addr := range addrs { + out("s", fmt.Sprintf("net.Interfaces[%d].Addrs[%d]", i, j), addr) + } + } + } + } + } + + return 0 +} + +// Synopsis returns a terse description used when listing sub-commands. +func (c *TechSupportCommand) Synopsis() string { + return `Dumps diagnostic information about a platform's network` +} + +// Usage is the one-line usage description +func (c *TechSupportCommand) Usage() string { + return `sockaddr tech-support [options]` +} + +// VisitAllFlags forwards the visitor function to the FlagSet +func (c *TechSupportCommand) VisitAllFlags(fn func(*flag.Flag)) { + c.flags.VisitAll(fn) +} + +// parseOpts is responsible for parsing the options set in InitOpts(). Returns +// a list of non-parsed flags. +func (c *TechSupportCommand) parseOpts(args []string) ([]string, error) { + if err := c.flags.Parse(args); err != nil { + return nil, err + } + + switch c.outputMode { + case "md", "markdown": + c.outputMode = "md" + case "raw": + default: + return nil, fmt.Errorf(`Invalid output mode %q, supported output types are "md" (default) and "raw"`, c.outputMode) + } + return c.flags.Args(), nil +} + +func (c *TechSupportCommand) rowWriterOutputFactory() func(valueVerb, key string, val interface{}) { + type _Fmt string + type _Verb string + var lineNoFmt string + var keyVerb _Verb + var fmtMap map[_Verb]_Fmt + switch c.outputMode { + case "md": + lineNoFmt = "%02d." + keyVerb = "s" + fmtMap = map[_Verb]_Fmt{ + "s": "`%s`", + "-s": "%s", + "v": "`%v`", + "+v": "`%#v`", + } + case "raw": + lineNoFmt = "%02d:" + keyVerb = "-s" + fmtMap = map[_Verb]_Fmt{ + "s": "%q", + "-s": "%s", + "v": "%v", + "+v": "%#v", + } + default: + panic(fmt.Sprintf("Unsupported output type: %q", c.outputMode)) + } + + var count int + return func(valueVerb, key string, val interface{}) { + count++ + + keyFmt, ok := fmtMap[keyVerb] + if !ok { + panic(fmt.Sprintf("Invalid key verb: %q", keyVerb)) + } + + valFmt, ok := fmtMap[_Verb(valueVerb)] + if !ok { + panic(fmt.Sprintf("Invalid value verb: %q", valueVerb)) + } + + outputModeFmt := fmt.Sprintf("%s %s:\t%s", lineNoFmt, keyFmt, valFmt) + c.Ui.Output(fmt.Sprintf(outputModeFmt, count, key, val)) + } +} |