github.com/pritambaral/docker@v1.4.2-0.20150120174542-b2fe1b3dd952/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 flag "github.com/docker/docker/pkg/mflag" 18 "github.com/docker/docker/pkg/term" 19 "github.com/docker/docker/registry" 20 "github.com/docker/libtrust" 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 key libtrust.PrivateKey 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 flags.FlagCountUndeprecated() > 0 { 97 options = "[OPTIONS] " 98 } 99 fmt.Fprintf(cli.out, "\nUsage: docker %s %s%s\n\n%s\n\n", name, options, signature, description) 100 flags.SetOutput(cli.out) 101 flags.PrintDefaults() 102 os.Exit(0) 103 } 104 return flags 105 } 106 107 func (cli *DockerCli) LoadConfigFile() (err error) { 108 cli.configFile, err = registry.LoadConfig(os.Getenv("HOME")) 109 if err != nil { 110 fmt.Fprintf(cli.err, "WARNING: %s\n", err) 111 } 112 return err 113 } 114 115 func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error { 116 // In order to attach to a container tty, input stream for the client must 117 // be a tty itself: redirecting or piping the client standard input is 118 // incompatible with `docker run -t`, `docker exec -t` or `docker attach`. 119 if ttyMode && attachStdin && !cli.isTerminalIn { 120 return errors.New("cannot enable tty mode on non tty input") 121 } 122 return nil 123 } 124 125 func NewDockerCli(in io.ReadCloser, out, err io.Writer, key libtrust.PrivateKey, proto, addr string, tlsConfig *tls.Config) *DockerCli { 126 var ( 127 inFd uintptr 128 outFd uintptr 129 isTerminalIn = false 130 isTerminalOut = false 131 scheme = "http" 132 ) 133 134 if tlsConfig != nil { 135 scheme = "https" 136 } 137 138 if in != nil { 139 if file, ok := in.(*os.File); ok { 140 inFd = file.Fd() 141 isTerminalIn = term.IsTerminal(inFd) 142 } 143 } 144 145 if out != nil { 146 if file, ok := out.(*os.File); ok { 147 outFd = file.Fd() 148 isTerminalOut = term.IsTerminal(outFd) 149 } 150 } 151 152 if err == nil { 153 err = out 154 } 155 156 // The transport is created here for reuse during the client session 157 tr := &http.Transport{ 158 Proxy: http.ProxyFromEnvironment, 159 TLSClientConfig: tlsConfig, 160 } 161 162 // Why 32? See issue 8035 163 timeout := 32 * time.Second 164 if proto == "unix" { 165 // no need in compressing for local communications 166 tr.DisableCompression = true 167 tr.Dial = func(_, _ string) (net.Conn, error) { 168 return net.DialTimeout(proto, addr, timeout) 169 } 170 } else { 171 tr.Dial = (&net.Dialer{Timeout: timeout}).Dial 172 } 173 174 return &DockerCli{ 175 proto: proto, 176 addr: addr, 177 in: in, 178 out: out, 179 err: err, 180 key: key, 181 inFd: inFd, 182 outFd: outFd, 183 isTerminalIn: isTerminalIn, 184 isTerminalOut: isTerminalOut, 185 tlsConfig: tlsConfig, 186 scheme: scheme, 187 transport: tr, 188 } 189 }