github.com/marinho/drone@v0.2.1-0.20140504195434-d3ba962e89a7/pkg/build/docker/client.go (about) 1 package docker 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net" 11 "net/http" 12 "net/http/httputil" 13 "os" 14 "strings" 15 16 "github.com/dotcloud/docker/pkg/term" 17 "github.com/dotcloud/docker/utils" 18 ) 19 20 const ( 21 APIVERSION = 1.9 22 DEFAULTHTTPPORT = 4243 23 DEFAULTUNIXSOCKET = "/var/run/docker.sock" 24 DEFAULTPROTOCOL = "unix" 25 DEFAULTTAG = "latest" 26 VERSION = "0.8.0" 27 ) 28 29 // Enables verbose logging to the Terminal window 30 var Logging = true 31 32 // New creates an instance of the Docker Client 33 func New() *Client { 34 c := &Client{} 35 36 c.setHost(DEFAULTUNIXSOCKET) 37 38 c.Images = &ImageService{c} 39 c.Containers = &ContainerService{c} 40 return c 41 } 42 43 type Client struct { 44 proto string 45 addr string 46 47 Images *ImageService 48 Containers *ContainerService 49 } 50 51 var ( 52 // Returned if the specified resource does not exist. 53 ErrNotFound = errors.New("Not Found") 54 55 // Returned if the caller attempts to make a call or modify a resource 56 // for which the caller is not authorized. 57 // 58 // The request was a valid request, the caller's authentication credentials 59 // succeeded but those credentials do not grant the caller permission to 60 // access the resource. 61 ErrForbidden = errors.New("Forbidden") 62 63 // Returned if the call requires authentication and either the credentials 64 // provided failed or no credentials were provided. 65 ErrNotAuthorized = errors.New("Unauthorized") 66 67 // Returned if the caller submits a badly formed request. For example, 68 // the caller can receive this return if you forget a required parameter. 69 ErrBadRequest = errors.New("Bad Request") 70 ) 71 72 func (c *Client) setHost(defaultUnixSocket string) { 73 c.proto = DEFAULTPROTOCOL 74 c.addr = defaultUnixSocket 75 76 if os.Getenv("DOCKER_HOST") != "" { 77 pieces := strings.Split(os.Getenv("DOCKER_HOST"), "://") 78 if len(pieces) == 2 { 79 c.proto = pieces[0] 80 c.addr = pieces[1] 81 } else if len(pieces) == 1 { 82 c.addr = pieces[0] 83 } 84 } else { 85 // if the default socket doesn't exist then 86 // we'll try to connect to the default tcp address 87 if _, err := os.Stat(defaultUnixSocket); err != nil { 88 c.proto = "tcp" 89 c.addr = "0.0.0.0:4243" 90 } 91 } 92 } 93 94 // helper function used to make HTTP requests to the Docker daemon. 95 func (c *Client) do(method, path string, in, out interface{}) error { 96 // if data input is provided, serialize to JSON 97 var payload io.Reader 98 if in != nil { 99 buf, err := json.Marshal(in) 100 if err != nil { 101 return err 102 } 103 payload = bytes.NewBuffer(buf) 104 } 105 106 // create the request 107 req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), payload) 108 if err != nil { 109 return err 110 } 111 112 // set the appropariate headers 113 req.Header = http.Header{} 114 req.Header.Set("User-Agent", "Docker-Client/"+VERSION) 115 req.Header.Set("Content-Type", "application/json") 116 117 // dial the host server 118 req.Host = c.addr 119 dial, err := net.Dial(c.proto, c.addr) 120 if err != nil { 121 return err 122 } 123 124 // make the request 125 conn := httputil.NewClientConn(dial, nil) 126 resp, err := conn.Do(req) 127 defer conn.Close() 128 if err != nil { 129 return err 130 } 131 132 // Read the bytes from the body (make sure we defer close the body) 133 defer resp.Body.Close() 134 body, err := ioutil.ReadAll(resp.Body) 135 if err != nil { 136 return err 137 } 138 139 // Check for an http error status (ie not 200 StatusOK) 140 switch resp.StatusCode { 141 case 404: 142 return ErrNotFound 143 case 403: 144 return ErrForbidden 145 case 401: 146 return ErrNotAuthorized 147 case 400: 148 return ErrBadRequest 149 } 150 151 // Unmarshall the JSON response 152 if out != nil { 153 return json.Unmarshal(body, out) 154 } 155 156 return nil 157 } 158 159 func (c *Client) hijack(method, path string, setRawTerminal bool, out io.Writer) error { 160 req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), nil) 161 if err != nil { 162 return err 163 } 164 165 req.Header.Set("User-Agent", "Docker-Client/"+VERSION) 166 req.Header.Set("Content-Type", "plain/text") 167 req.Host = c.addr 168 169 dial, err := net.Dial(c.proto, c.addr) 170 if err != nil { 171 if strings.Contains(err.Error(), "connection refused") { 172 return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?") 173 } 174 return err 175 } 176 clientconn := httputil.NewClientConn(dial, nil) 177 defer clientconn.Close() 178 179 // Server hijacks the connection, error 'connection closed' expected 180 clientconn.Do(req) 181 182 // Hijack the connection to read / write 183 rwc, br := clientconn.Hijack() 184 defer rwc.Close() 185 186 // launch a goroutine to copy the stream 187 // of build output to the writer. 188 errStdout := make(chan error, 1) 189 go func() { 190 var err error 191 if setRawTerminal { 192 _, err = io.Copy(out, br) 193 } else { 194 _, err = utils.StdCopy(out, out, br) 195 } 196 197 errStdout <- err 198 }() 199 200 // wait for a response 201 if err := <-errStdout; err != nil { 202 return err 203 } 204 return nil 205 } 206 207 func (c *Client) stream(method, path string, in io.Reader, out io.Writer, headers http.Header) error { 208 if (method == "POST" || method == "PUT") && in == nil { 209 in = bytes.NewReader(nil) 210 } 211 212 // setup the request 213 req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in) 214 if err != nil { 215 return err 216 } 217 218 // set default headers 219 req.Header = headers 220 req.Header.Set("User-Agent", "Docker-Client/0.6.4") 221 req.Header.Set("Content-Type", "plain/text") 222 223 // dial the host server 224 req.Host = c.addr 225 dial, err := net.Dial(c.proto, c.addr) 226 if err != nil { 227 return err 228 } 229 230 // make the request 231 conn := httputil.NewClientConn(dial, nil) 232 resp, err := conn.Do(req) 233 defer conn.Close() 234 if err != nil { 235 return err 236 } 237 238 // make sure we defer close the body 239 defer resp.Body.Close() 240 241 // Check for an http error status (ie not 200 StatusOK) 242 switch resp.StatusCode { 243 case 404: 244 return ErrNotFound 245 case 403: 246 return ErrForbidden 247 case 401: 248 return ErrNotAuthorized 249 case 400: 250 return ErrBadRequest 251 } 252 253 // If no output we exit now with no errors 254 if out == nil { 255 return nil 256 } 257 258 // copy the output stream to the writer 259 if resp.Header.Get("Content-Type") == "application/json" { 260 var terminalFd = os.Stdin.Fd() 261 var isTerminal = term.IsTerminal(terminalFd) 262 263 // it may not make sense to put this code here, but it works for 264 // us at the moment, and I don't feel like refactoring 265 return utils.DisplayJSONMessagesStream(resp.Body, out, terminalFd, isTerminal) 266 } 267 // otherwise plain text 268 if _, err := io.Copy(out, resp.Body); err != nil { 269 return err 270 } 271 272 return nil 273 }