github.com/grange74/docker@v1.6.0-rc3/api/client/cli.go (about) 1 package client 2 3 import ( 4 "crypto/tls" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "net" 10 "net/http" 11 "os" 12 "reflect" 13 "strings" 14 "text/template" 15 "time" 16 17 "github.com/docker/docker/pkg/homedir" 18 flag "github.com/docker/docker/pkg/mflag" 19 "github.com/docker/docker/pkg/term" 20 "github.com/docker/docker/registry" 21 ) 22 23 type DockerCli struct { 24 proto string 25 addr string 26 configFile *registry.ConfigFile 27 in io.ReadCloser 28 out io.Writer 29 err io.Writer 30 keyFile string 31 tlsConfig *tls.Config 32 scheme string 33 // inFd holds file descriptor of the client's STDIN, if it's a valid file 34 inFd uintptr 35 // outFd holds file descriptor of the client's STDOUT, if it's a valid file 36 outFd uintptr 37 // isTerminalIn describes if client's STDIN is a TTY 38 isTerminalIn bool 39 // isTerminalOut describes if client's STDOUT is a TTY 40 isTerminalOut bool 41 transport *http.Transport 42 } 43 44 var funcMap = template.FuncMap{ 45 "json": func(v interface{}) string { 46 a, _ := json.Marshal(v) 47 return string(a) 48 }, 49 } 50 51 func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) { 52 camelArgs := make([]string, len(args)) 53 for i, s := range args { 54 if len(s) == 0 { 55 return nil, false 56 } 57 camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) 58 } 59 methodName := "Cmd" + strings.Join(camelArgs, "") 60 method := reflect.ValueOf(cli).MethodByName(methodName) 61 if !method.IsValid() { 62 return nil, false 63 } 64 return method.Interface().(func(...string) error), true 65 } 66 67 // Cmd executes the specified command 68 func (cli *DockerCli) Cmd(args ...string) error { 69 if len(args) > 1 { 70 method, exists := cli.getMethod(args[:2]...) 71 if exists { 72 return method(args[2:]...) 73 } 74 } 75 if len(args) > 0 { 76 method, exists := cli.getMethod(args[0]) 77 if !exists { 78 fmt.Fprintf(cli.err, "docker: '%s' is not a docker command. See 'docker --help'.\n", args[0]) 79 os.Exit(1) 80 } 81 return method(args[1:]...) 82 } 83 return cli.CmdHelp() 84 } 85 86 func (cli *DockerCli) Subcmd(name, signature, description string, exitOnError bool) *flag.FlagSet { 87 var errorHandling flag.ErrorHandling 88 if exitOnError { 89 errorHandling = flag.ExitOnError 90 } else { 91 errorHandling = flag.ContinueOnError 92 } 93 flags := flag.NewFlagSet(name, errorHandling) 94 flags.Usage = func() { 95 options := "" 96 if signature != "" { 97 signature = " " + signature 98 } 99 if flags.FlagCountUndeprecated() > 0 { 100 options = " [OPTIONS]" 101 } 102 fmt.Fprintf(cli.out, "\nUsage: docker %s%s%s\n\n%s\n\n", name, options, signature, description) 103 flags.SetOutput(cli.out) 104 flags.PrintDefaults() 105 os.Exit(0) 106 } 107 return flags 108 } 109 110 func (cli *DockerCli) LoadConfigFile() (err error) { 111 cli.configFile, err = registry.LoadConfig(homedir.Get()) 112 if err != nil { 113 fmt.Fprintf(cli.err, "WARNING: %s\n", err) 114 } 115 return err 116 } 117 118 func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error { 119 // In order to attach to a container tty, input stream for the client must 120 // be a tty itself: redirecting or piping the client standard input is 121 // incompatible with `docker run -t`, `docker exec -t` or `docker attach`. 122 if ttyMode && attachStdin && !cli.isTerminalIn { 123 return errors.New("cannot enable tty mode on non tty input") 124 } 125 return nil 126 } 127 128 func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, addr string, tlsConfig *tls.Config) *DockerCli { 129 var ( 130 inFd uintptr 131 outFd uintptr 132 isTerminalIn = false 133 isTerminalOut = false 134 scheme = "http" 135 ) 136 137 if tlsConfig != nil { 138 scheme = "https" 139 } 140 if in != nil { 141 inFd, isTerminalIn = term.GetFdInfo(in) 142 } 143 144 if out != nil { 145 outFd, isTerminalOut = term.GetFdInfo(out) 146 } 147 148 if err == nil { 149 err = out 150 } 151 152 // The transport is created here for reuse during the client session 153 tr := &http.Transport{ 154 TLSClientConfig: tlsConfig, 155 } 156 157 // Why 32? See issue 8035 158 timeout := 32 * time.Second 159 if proto == "unix" { 160 // no need in compressing for local communications 161 tr.DisableCompression = true 162 tr.Dial = func(_, _ string) (net.Conn, error) { 163 return net.DialTimeout(proto, addr, timeout) 164 } 165 } else { 166 tr.Proxy = http.ProxyFromEnvironment 167 tr.Dial = (&net.Dialer{Timeout: timeout}).Dial 168 } 169 170 return &DockerCli{ 171 proto: proto, 172 addr: addr, 173 in: in, 174 out: out, 175 err: err, 176 keyFile: keyFile, 177 inFd: inFd, 178 outFd: outFd, 179 isTerminalIn: isTerminalIn, 180 isTerminalOut: isTerminalOut, 181 tlsConfig: tlsConfig, 182 scheme: scheme, 183 transport: tr, 184 } 185 }