github.com/lmars/docker@v1.6.0-rc2/api/client/utils.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "net/http" 12 "net/url" 13 "os" 14 gosignal "os/signal" 15 "strconv" 16 "strings" 17 18 log "github.com/Sirupsen/logrus" 19 "github.com/docker/docker/api" 20 "github.com/docker/docker/autogen/dockerversion" 21 "github.com/docker/docker/engine" 22 "github.com/docker/docker/pkg/signal" 23 "github.com/docker/docker/pkg/stdcopy" 24 "github.com/docker/docker/pkg/term" 25 "github.com/docker/docker/registry" 26 "github.com/docker/docker/utils" 27 ) 28 29 var ( 30 ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") 31 ) 32 33 func (cli *DockerCli) HTTPClient() *http.Client { 34 return &http.Client{Transport: cli.transport} 35 } 36 37 func (cli *DockerCli) encodeData(data interface{}) (*bytes.Buffer, error) { 38 params := bytes.NewBuffer(nil) 39 if data != nil { 40 if env, ok := data.(engine.Env); ok { 41 if err := env.Encode(params); err != nil { 42 return nil, err 43 } 44 } else { 45 buf, err := json.Marshal(data) 46 if err != nil { 47 return nil, err 48 } 49 if _, err := params.Write(buf); err != nil { 50 return nil, err 51 } 52 } 53 } 54 return params, nil 55 } 56 57 func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) { 58 params, err := cli.encodeData(data) 59 if err != nil { 60 return nil, -1, err 61 } 62 req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params) 63 if err != nil { 64 return nil, -1, err 65 } 66 if passAuthInfo { 67 cli.LoadConfigFile() 68 // Resolve the Auth config relevant for this server 69 authConfig := cli.configFile.Configs[registry.IndexServerAddress()] 70 getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) { 71 buf, err := json.Marshal(authConfig) 72 if err != nil { 73 return nil, err 74 } 75 registryAuthHeader := []string{ 76 base64.URLEncoding.EncodeToString(buf), 77 } 78 return map[string][]string{"X-Registry-Auth": registryAuthHeader}, nil 79 } 80 if headers, err := getHeaders(authConfig); err == nil && headers != nil { 81 for k, v := range headers { 82 req.Header[k] = v 83 } 84 } 85 } 86 req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION) 87 req.URL.Host = cli.addr 88 req.URL.Scheme = cli.scheme 89 if data != nil { 90 req.Header.Set("Content-Type", "application/json") 91 } else if method == "POST" { 92 req.Header.Set("Content-Type", "text/plain") 93 } 94 resp, err := cli.HTTPClient().Do(req) 95 if err != nil { 96 if strings.Contains(err.Error(), "connection refused") { 97 return nil, -1, ErrConnectionRefused 98 } 99 100 if cli.tlsConfig == nil { 101 return nil, -1, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err) 102 } 103 return nil, -1, fmt.Errorf("An error occurred trying to connect: %v", err) 104 105 } 106 107 if resp.StatusCode < 200 || resp.StatusCode >= 400 { 108 body, err := ioutil.ReadAll(resp.Body) 109 if err != nil { 110 return nil, -1, err 111 } 112 if len(body) == 0 { 113 return nil, resp.StatusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(resp.StatusCode), req.URL) 114 } 115 return nil, resp.StatusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body)) 116 } 117 118 return resp.Body, resp.StatusCode, nil 119 } 120 121 func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error { 122 return cli.streamHelper(method, path, true, in, out, nil, headers) 123 } 124 125 func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error { 126 if (method == "POST" || method == "PUT") && in == nil { 127 in = bytes.NewReader([]byte{}) 128 } 129 130 req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in) 131 if err != nil { 132 return err 133 } 134 req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION) 135 req.URL.Host = cli.addr 136 req.URL.Scheme = cli.scheme 137 if method == "POST" { 138 req.Header.Set("Content-Type", "text/plain") 139 } 140 141 if headers != nil { 142 for k, v := range headers { 143 req.Header[k] = v 144 } 145 } 146 resp, err := cli.HTTPClient().Do(req) 147 if err != nil { 148 if strings.Contains(err.Error(), "connection refused") { 149 return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?") 150 } 151 return err 152 } 153 defer resp.Body.Close() 154 155 if resp.StatusCode < 200 || resp.StatusCode >= 400 { 156 body, err := ioutil.ReadAll(resp.Body) 157 if err != nil { 158 return err 159 } 160 if len(body) == 0 { 161 return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode)) 162 } 163 return fmt.Errorf("Error: %s", bytes.TrimSpace(body)) 164 } 165 166 if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") { 167 return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.outFd, cli.isTerminalOut) 168 } 169 if stdout != nil || stderr != nil { 170 // When TTY is ON, use regular copy 171 if setRawTerminal { 172 _, err = io.Copy(stdout, resp.Body) 173 } else { 174 _, err = stdcopy.StdCopy(stdout, stderr, resp.Body) 175 } 176 log.Debugf("[stream] End of stdout") 177 return err 178 } 179 return nil 180 } 181 182 func (cli *DockerCli) resizeTty(id string, isExec bool) { 183 height, width := cli.getTtySize() 184 if height == 0 && width == 0 { 185 return 186 } 187 v := url.Values{} 188 v.Set("h", strconv.Itoa(height)) 189 v.Set("w", strconv.Itoa(width)) 190 191 path := "" 192 if !isExec { 193 path = "/containers/" + id + "/resize?" 194 } else { 195 path = "/exec/" + id + "/resize?" 196 } 197 198 if _, _, err := readBody(cli.call("POST", path+v.Encode(), nil, false)); err != nil { 199 log.Debugf("Error resize: %s", err) 200 } 201 } 202 203 func waitForExit(cli *DockerCli, containerId string) (int, error) { 204 stream, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil, false) 205 if err != nil { 206 return -1, err 207 } 208 209 var out engine.Env 210 if err := out.Decode(stream); err != nil { 211 return -1, err 212 } 213 return out.GetInt("StatusCode"), nil 214 } 215 216 // getExitCode perform an inspect on the container. It returns 217 // the running state and the exit code. 218 func getExitCode(cli *DockerCli, containerId string) (bool, int, error) { 219 stream, _, err := cli.call("GET", "/containers/"+containerId+"/json", nil, false) 220 if err != nil { 221 // If we can't connect, then the daemon probably died. 222 if err != ErrConnectionRefused { 223 return false, -1, err 224 } 225 return false, -1, nil 226 } 227 228 var result engine.Env 229 if err := result.Decode(stream); err != nil { 230 return false, -1, err 231 } 232 233 state := result.GetSubEnv("State") 234 return state.GetBool("Running"), state.GetInt("ExitCode"), nil 235 } 236 237 // getExecExitCode perform an inspect on the exec command. It returns 238 // the running state and the exit code. 239 func getExecExitCode(cli *DockerCli, execId string) (bool, int, error) { 240 stream, _, err := cli.call("GET", "/exec/"+execId+"/json", nil, false) 241 if err != nil { 242 // If we can't connect, then the daemon probably died. 243 if err != ErrConnectionRefused { 244 return false, -1, err 245 } 246 return false, -1, nil 247 } 248 249 var result engine.Env 250 if err := result.Decode(stream); err != nil { 251 return false, -1, err 252 } 253 254 return result.GetBool("Running"), result.GetInt("ExitCode"), nil 255 } 256 257 func (cli *DockerCli) monitorTtySize(id string, isExec bool) error { 258 cli.resizeTty(id, isExec) 259 260 sigchan := make(chan os.Signal, 1) 261 gosignal.Notify(sigchan, signal.SIGWINCH) 262 go func() { 263 for _ = range sigchan { 264 cli.resizeTty(id, isExec) 265 } 266 }() 267 return nil 268 } 269 270 func (cli *DockerCli) getTtySize() (int, int) { 271 if !cli.isTerminalOut { 272 return 0, 0 273 } 274 ws, err := term.GetWinsize(cli.outFd) 275 if err != nil { 276 log.Debugf("Error getting size: %s", err) 277 if ws == nil { 278 return 0, 0 279 } 280 } 281 return int(ws.Height), int(ws.Width) 282 } 283 284 func readBody(stream io.ReadCloser, statusCode int, err error) ([]byte, int, error) { 285 if stream != nil { 286 defer stream.Close() 287 } 288 if err != nil { 289 return nil, statusCode, err 290 } 291 body, err := ioutil.ReadAll(stream) 292 if err != nil { 293 return nil, -1, err 294 } 295 return body, statusCode, nil 296 }