20 "github.com/docker/docker/api"
21 dclient "github.com/docker/docker/client"
22 "github.com/docker/docker/opts"
23 "github.com/docker/docker/pkg/ioutils"
24 "github.com/docker/docker/pkg/testutil"
25 "github.com/docker/go-connections/sockets"
26 "github.com/docker/go-connections/tlsconfig"
27 "github.com/pkg/errors"
30 // Method creates a modifier that sets the specified string as the request method
31 func Method(method string) func(*http.Request) error {
32 return func(req *http.Request) error {
38 // RawString sets the specified string as body for the request
39 func RawString(content string) func(*http.Request) error {
40 return RawContent(ioutil.NopCloser(strings.NewReader(content)))
43 // RawContent sets the specified reader as body for the request
44 func RawContent(reader io.ReadCloser) func(*http.Request) error {
45 return func(req *http.Request) error {
51 // ContentType sets the specified Content-Type request header
52 func ContentType(contentType string) func(*http.Request) error {
53 return func(req *http.Request) error {
54 req.Header.Set("Content-Type", contentType)
59 // JSON sets the Content-Type request header to json
60 func JSON(req *http.Request) error {
61 return ContentType("application/json")(req)
64 // JSONBody creates a modifier that encodes the specified data to a JSON string and set it as request body. It also sets
65 // the Content-Type header of the request.
66 func JSONBody(data interface{}) func(*http.Request) error {
67 return func(req *http.Request) error {
68 jsonData := bytes.NewBuffer(nil)
69 if err := json.NewEncoder(jsonData).Encode(data); err != nil {
72 req.Body = ioutil.NopCloser(jsonData)
73 req.Header.Set("Content-Type", "application/json")
78 // Post creates and execute a POST request on the specified host and endpoint, with the specified request modifiers
79 func Post(endpoint string, modifiers ...func(*http.Request) error) (*http.Response, io.ReadCloser, error) {
80 return Do(endpoint, append(modifiers, Method(http.MethodPost))...)
83 // Delete creates and execute a DELETE request on the specified host and endpoint, with the specified request modifiers
84 func Delete(endpoint string, modifiers ...func(*http.Request) error) (*http.Response, io.ReadCloser, error) {
85 return Do(endpoint, append(modifiers, Method(http.MethodDelete))...)
88 // Get creates and execute a GET request on the specified host and endpoint, with the specified request modifiers
89 func Get(endpoint string, modifiers ...func(*http.Request) error) (*http.Response, io.ReadCloser, error) {
90 return Do(endpoint, modifiers...)
93 // Do creates and execute a request on the specified endpoint, with the specified request modifiers
94 func Do(endpoint string, modifiers ...func(*http.Request) error) (*http.Response, io.ReadCloser, error) {
95 return DoOnHost(DaemonHost(), endpoint, modifiers...)
98 // DoOnHost creates and execute a request on the specified host and endpoint, with the specified request modifiers
99 func DoOnHost(host, endpoint string, modifiers ...func(*http.Request) error) (*http.Response, io.ReadCloser, error) {
100 req, err := New(host, endpoint, modifiers...)
104 client, err := NewHTTPClient(host)
108 resp, err := client.Do(req)
109 var body io.ReadCloser
111 body = ioutils.NewReadCloserWrapper(resp.Body, func() error {
112 defer resp.Body.Close()
116 return resp, body, err
119 // New creates a new http Request to the specified host and endpoint, with the specified request modifiers
120 func New(host, endpoint string, modifiers ...func(*http.Request) error) (*http.Request, error) {
121 _, addr, _, err := dclient.ParseHost(host)
126 return nil, errors.Wrapf(err, "could not parse url %q", host)
128 req, err := http.NewRequest("GET", endpoint, nil)
130 return nil, fmt.Errorf("could not create new request: %v", err)
133 req.URL.Scheme = "http"
136 for _, config := range modifiers {
137 if err := config(req); err != nil {
144 // NewHTTPClient creates an http client for the specific host
145 func NewHTTPClient(host string) (*http.Client, error) {
146 // FIXME(vdemeester) 10*time.Second timeout of SockRequest… ?
147 proto, addr, _, err := dclient.ParseHost(host)
151 transport := new(http.Transport)
152 if proto == "tcp" && os.Getenv("DOCKER_TLS_VERIFY") != "" {
153 // Setup the socket TLS configuration.
154 tlsConfig, err := getTLSConfig()
158 transport = &http.Transport{TLSClientConfig: tlsConfig}
160 transport.DisableKeepAlives = true
161 err = sockets.ConfigureTransport(transport, proto, addr)
163 Transport: transport,
167 // NewClient returns a new Docker API client
168 func NewClient() (dclient.APIClient, error) {
170 httpClient, err := NewHTTPClient(host)
174 return dclient.NewClient(host, api.DefaultVersion, httpClient, nil)
177 // FIXME(vdemeester) httputil.ClientConn is deprecated, use http.Client instead (closer to actual client)
178 // Deprecated: Use New instead of NewRequestClient
179 // Deprecated: use request.Do (or Get, Delete, Post) instead
180 func newRequestClient(method, endpoint string, data io.Reader, ct, daemon string, modifiers ...func(*http.Request)) (*http.Request, *httputil.ClientConn, error) {
181 c, err := SockConn(time.Duration(10*time.Second), daemon)
183 return nil, nil, fmt.Errorf("could not dial docker daemon: %v", err)
186 client := httputil.NewClientConn(c, nil)
188 req, err := http.NewRequest(method, endpoint, data)
191 return nil, nil, fmt.Errorf("could not create new request: %v", err)
194 for _, opt := range modifiers {
199 req.Header.Set("Content-Type", ct)
201 return req, client, nil
204 // SockRequest create a request against the specified host (with method, endpoint and other request modifier) and
205 // returns the status code, and the content as an byte slice
206 // Deprecated: use request.Do instead
207 func SockRequest(method, endpoint string, data interface{}, daemon string, modifiers ...func(*http.Request)) (int, []byte, error) {
208 jsonData := bytes.NewBuffer(nil)
209 if err := json.NewEncoder(jsonData).Encode(data); err != nil {
213 res, body, err := SockRequestRaw(method, endpoint, jsonData, "application/json", daemon, modifiers...)
217 b, err := testutil.ReadBody(body)
218 return res.StatusCode, b, err
221 // SockRequestRaw create a request against the specified host (with method, endpoint and other request modifier) and
222 // returns the http response, the output as a io.ReadCloser
223 // Deprecated: use request.Do (or Get, Delete, Post) instead
224 func SockRequestRaw(method, endpoint string, data io.Reader, ct, daemon string, modifiers ...func(*http.Request)) (*http.Response, io.ReadCloser, error) {
225 req, client, err := newRequestClient(method, endpoint, data, ct, daemon, modifiers...)
230 resp, err := client.Do(req)
233 return resp, nil, err
235 body := ioutils.NewReadCloserWrapper(resp.Body, func() error {
236 defer resp.Body.Close()
237 return client.Close()
240 return resp, body, err
243 // SockRequestHijack creates a connection to specified host (with method, contenttype, …) and returns a hijacked connection
244 // and the output as a `bufio.Reader`
245 func SockRequestHijack(method, endpoint string, data io.Reader, ct string, daemon string, modifiers ...func(*http.Request)) (net.Conn, *bufio.Reader, error) {
246 req, client, err := newRequestClient(method, endpoint, data, ct, daemon, modifiers...)
252 conn, br := client.Hijack()
256 // SockConn opens a connection on the specified socket
257 func SockConn(timeout time.Duration, daemon string) (net.Conn, error) {
258 daemonURL, err := url.Parse(daemon)
260 return nil, errors.Wrapf(err, "could not parse url %q", daemon)
264 switch daemonURL.Scheme {
266 return npipeDial(daemonURL.Path, timeout)
268 return net.DialTimeout(daemonURL.Scheme, daemonURL.Path, timeout)
270 if os.Getenv("DOCKER_TLS_VERIFY") != "" {
271 // Setup the socket TLS configuration.
272 tlsConfig, err := getTLSConfig()
276 dialer := &net.Dialer{Timeout: timeout}
277 return tls.DialWithDialer(dialer, daemonURL.Scheme, daemonURL.Host, tlsConfig)
279 return net.DialTimeout(daemonURL.Scheme, daemonURL.Host, timeout)
281 return c, errors.Errorf("unknown scheme %v (%s)", daemonURL.Scheme, daemon)
285 func getTLSConfig() (*tls.Config, error) {
286 dockerCertPath := os.Getenv("DOCKER_CERT_PATH")
288 if dockerCertPath == "" {
289 return nil, errors.New("DOCKER_TLS_VERIFY specified, but no DOCKER_CERT_PATH environment variable")
292 option := &tlsconfig.Options{
293 CAFile: filepath.Join(dockerCertPath, "ca.pem"),
294 CertFile: filepath.Join(dockerCertPath, "cert.pem"),
295 KeyFile: filepath.Join(dockerCertPath, "key.pem"),
297 tlsConfig, err := tlsconfig.Client(*option)
302 return tlsConfig, nil
305 // DaemonHost return the daemon host string for this test execution
306 func DaemonHost() string {
307 daemonURLStr := "unix://" + opts.DefaultUnixSocket
308 if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" {
309 daemonURLStr = daemonHostVar