1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 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))
}
}
|