github.com/tsuna/docker@v1.7.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/http" 10 "path/filepath" 11 "reflect" 12 "strings" 13 "text/template" 14 15 "github.com/docker/docker/cliconfig" 16 "github.com/docker/docker/pkg/homedir" 17 flag "github.com/docker/docker/pkg/mflag" 18 "github.com/docker/docker/pkg/term" 19 "github.com/docker/docker/utils" 20 ) 21 22 // DockerCli represents the docker command line client. 23 // Instances of the client can be returned from NewDockerCli. 24 type DockerCli struct { 25 // proto holds the client protocol i.e. unix. 26 proto string 27 // addr holds the client address. 28 addr string 29 30 // configFile has the client configuration file 31 configFile *cliconfig.ConfigFile 32 // in holds the input stream and closer (io.ReadCloser) for the client. 33 in io.ReadCloser 34 // out holds the output stream (io.Writer) for the client. 35 out io.Writer 36 // err holds the error stream (io.Writer) for the client. 37 err io.Writer 38 // keyFile holds the key file as a string. 39 keyFile string 40 // tlsConfig holds the TLS configuration for the client, and will 41 // set the scheme to https in NewDockerCli if present. 42 tlsConfig *tls.Config 43 // scheme holds the scheme of the client i.e. https. 44 scheme string 45 // inFd holds the file descriptor of the client's STDIN (if valid). 46 inFd uintptr 47 // outFd holds file descriptor of the client's STDOUT (if valid). 48 outFd uintptr 49 // isTerminalIn indicates whether the client's STDIN is a TTY 50 isTerminalIn bool 51 // isTerminalOut dindicates whether the client's STDOUT is a TTY 52 isTerminalOut bool 53 // transport holds the client transport instance. 54 transport *http.Transport 55 } 56 57 var funcMap = template.FuncMap{ 58 "json": func(v interface{}) string { 59 a, _ := json.Marshal(v) 60 return string(a) 61 }, 62 } 63 64 func (cli *DockerCli) Out() io.Writer { 65 return cli.out 66 } 67 68 func (cli *DockerCli) Err() io.Writer { 69 return cli.err 70 } 71 72 func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) { 73 camelArgs := make([]string, len(args)) 74 for i, s := range args { 75 if len(s) == 0 { 76 return nil, false 77 } 78 camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) 79 } 80 methodName := "Cmd" + strings.Join(camelArgs, "") 81 method := reflect.ValueOf(cli).MethodByName(methodName) 82 if !method.IsValid() { 83 return nil, false 84 } 85 return method.Interface().(func(...string) error), true 86 } 87 88 // Cmd executes the specified command. 89 func (cli *DockerCli) Cmd(args ...string) error { 90 if len(args) > 1 { 91 method, exists := cli.getMethod(args[:2]...) 92 if exists { 93 return method(args[2:]...) 94 } 95 } 96 if len(args) > 0 { 97 method, exists := cli.getMethod(args[0]) 98 if !exists { 99 return fmt.Errorf("docker: '%s' is not a docker command.\nSee 'docker --help'.", args[0]) 100 } 101 return method(args[1:]...) 102 } 103 return cli.CmdHelp() 104 } 105 106 // Subcmd is a subcommand of the main "docker" command. 107 // A subcommand represents an action that can be performed 108 // from the Docker command line client. 109 // 110 // To see all available subcommands, run "docker --help". 111 func (cli *DockerCli) Subcmd(name, signature, description string, exitOnError bool) *flag.FlagSet { 112 var errorHandling flag.ErrorHandling 113 if exitOnError { 114 errorHandling = flag.ExitOnError 115 } else { 116 errorHandling = flag.ContinueOnError 117 } 118 flags := flag.NewFlagSet(name, errorHandling) 119 if signature != "" { 120 signature = " " + signature 121 } 122 flags.Usage = func() { 123 flags.ShortUsage() 124 flags.PrintDefaults() 125 } 126 flags.ShortUsage = func() { 127 options := "" 128 if flags.FlagCountUndeprecated() > 0 { 129 options = " [OPTIONS]" 130 } 131 fmt.Fprintf(flags.Out(), "\nUsage: docker %s%s%s\n\n%s\n", name, options, signature, description) 132 } 133 return flags 134 } 135 136 // CheckTtyInput checks if we are trying to attach to a container tty 137 // from a non-tty client input stream, and if so, returns an error. 138 func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error { 139 // In order to attach to a container tty, input stream for the client must 140 // be a tty itself: redirecting or piping the client standard input is 141 // incompatible with `docker run -t`, `docker exec -t` or `docker attach`. 142 if ttyMode && attachStdin && !cli.isTerminalIn { 143 return errors.New("cannot enable tty mode on non tty input") 144 } 145 return nil 146 } 147 148 // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. 149 // The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config 150 // is set the client scheme will be set to https. 151 // The client will be given a 32-second timeout (see https://github.com/docker/docker/pull/8035). 152 func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, addr string, tlsConfig *tls.Config) *DockerCli { 153 var ( 154 inFd uintptr 155 outFd uintptr 156 isTerminalIn = false 157 isTerminalOut = false 158 scheme = "http" 159 ) 160 161 if tlsConfig != nil { 162 scheme = "https" 163 } 164 if in != nil { 165 inFd, isTerminalIn = term.GetFdInfo(in) 166 } 167 168 if out != nil { 169 outFd, isTerminalOut = term.GetFdInfo(out) 170 } 171 172 if err == nil { 173 err = out 174 } 175 176 // The transport is created here for reuse during the client session. 177 tr := &http.Transport{ 178 TLSClientConfig: tlsConfig, 179 } 180 utils.ConfigureTCPTransport(tr, proto, addr) 181 182 configFile, e := cliconfig.Load(filepath.Join(homedir.Get(), ".docker")) 183 if e != nil { 184 fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e) 185 } 186 187 return &DockerCli{ 188 proto: proto, 189 addr: addr, 190 configFile: configFile, 191 in: in, 192 out: out, 193 err: err, 194 keyFile: keyFile, 195 inFd: inFd, 196 outFd: outFd, 197 isTerminalIn: isTerminalIn, 198 isTerminalOut: isTerminalOut, 199 tlsConfig: tlsConfig, 200 scheme: scheme, 201 transport: tr, 202 } 203 }