Tizen_4.0 base
[platform/upstream/docker-engine.git] / integration-cli / request / request.go
1 package request
2
3 import (
4         "bufio"
5         "bytes"
6         "crypto/tls"
7         "encoding/json"
8         "fmt"
9         "io"
10         "io/ioutil"
11         "net"
12         "net/http"
13         "net/http/httputil"
14         "net/url"
15         "os"
16         "path/filepath"
17         "strings"
18         "time"
19
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"
28 )
29
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 {
33                 req.Method = method
34                 return nil
35         }
36 }
37
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)))
41 }
42
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 {
46                 req.Body = reader
47                 return nil
48         }
49 }
50
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)
55                 return nil
56         }
57 }
58
59 // JSON sets the Content-Type request header to json
60 func JSON(req *http.Request) error {
61         return ContentType("application/json")(req)
62 }
63
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 {
70                         return err
71                 }
72                 req.Body = ioutil.NopCloser(jsonData)
73                 req.Header.Set("Content-Type", "application/json")
74                 return nil
75         }
76 }
77
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))...)
81 }
82
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))...)
86 }
87
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...)
91 }
92
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...)
96 }
97
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...)
101         if err != nil {
102                 return nil, nil, err
103         }
104         client, err := NewHTTPClient(host)
105         if err != nil {
106                 return nil, nil, err
107         }
108         resp, err := client.Do(req)
109         var body io.ReadCloser
110         if resp != nil {
111                 body = ioutils.NewReadCloserWrapper(resp.Body, func() error {
112                         defer resp.Body.Close()
113                         return nil
114                 })
115         }
116         return resp, body, err
117 }
118
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)
122         if err != nil {
123                 return nil, err
124         }
125         if err != nil {
126                 return nil, errors.Wrapf(err, "could not parse url %q", host)
127         }
128         req, err := http.NewRequest("GET", endpoint, nil)
129         if err != nil {
130                 return nil, fmt.Errorf("could not create new request: %v", err)
131         }
132
133         req.URL.Scheme = "http"
134         req.URL.Host = addr
135
136         for _, config := range modifiers {
137                 if err := config(req); err != nil {
138                         return nil, err
139                 }
140         }
141         return req, nil
142 }
143
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)
148         if err != nil {
149                 return nil, err
150         }
151         transport := new(http.Transport)
152         if proto == "tcp" && os.Getenv("DOCKER_TLS_VERIFY") != "" {
153                 // Setup the socket TLS configuration.
154                 tlsConfig, err := getTLSConfig()
155                 if err != nil {
156                         return nil, err
157                 }
158                 transport = &http.Transport{TLSClientConfig: tlsConfig}
159         }
160         transport.DisableKeepAlives = true
161         err = sockets.ConfigureTransport(transport, proto, addr)
162         return &http.Client{
163                 Transport: transport,
164         }, err
165 }
166
167 // NewClient returns a new Docker API client
168 func NewClient() (dclient.APIClient, error) {
169         host := DaemonHost()
170         httpClient, err := NewHTTPClient(host)
171         if err != nil {
172                 return nil, err
173         }
174         return dclient.NewClient(host, api.DefaultVersion, httpClient, nil)
175 }
176
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)
182         if err != nil {
183                 return nil, nil, fmt.Errorf("could not dial docker daemon: %v", err)
184         }
185
186         client := httputil.NewClientConn(c, nil)
187
188         req, err := http.NewRequest(method, endpoint, data)
189         if err != nil {
190                 client.Close()
191                 return nil, nil, fmt.Errorf("could not create new request: %v", err)
192         }
193
194         for _, opt := range modifiers {
195                 opt(req)
196         }
197
198         if ct != "" {
199                 req.Header.Set("Content-Type", ct)
200         }
201         return req, client, nil
202 }
203
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 {
210                 return -1, nil, err
211         }
212
213         res, body, err := SockRequestRaw(method, endpoint, jsonData, "application/json", daemon, modifiers...)
214         if err != nil {
215                 return -1, nil, err
216         }
217         b, err := testutil.ReadBody(body)
218         return res.StatusCode, b, err
219 }
220
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...)
226         if err != nil {
227                 return nil, nil, err
228         }
229
230         resp, err := client.Do(req)
231         if err != nil {
232                 client.Close()
233                 return resp, nil, err
234         }
235         body := ioutils.NewReadCloserWrapper(resp.Body, func() error {
236                 defer resp.Body.Close()
237                 return client.Close()
238         })
239
240         return resp, body, err
241 }
242
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...)
247         if err != nil {
248                 return nil, nil, err
249         }
250
251         client.Do(req)
252         conn, br := client.Hijack()
253         return conn, br, nil
254 }
255
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)
259         if err != nil {
260                 return nil, errors.Wrapf(err, "could not parse url %q", daemon)
261         }
262
263         var c net.Conn
264         switch daemonURL.Scheme {
265         case "npipe":
266                 return npipeDial(daemonURL.Path, timeout)
267         case "unix":
268                 return net.DialTimeout(daemonURL.Scheme, daemonURL.Path, timeout)
269         case "tcp":
270                 if os.Getenv("DOCKER_TLS_VERIFY") != "" {
271                         // Setup the socket TLS configuration.
272                         tlsConfig, err := getTLSConfig()
273                         if err != nil {
274                                 return nil, err
275                         }
276                         dialer := &net.Dialer{Timeout: timeout}
277                         return tls.DialWithDialer(dialer, daemonURL.Scheme, daemonURL.Host, tlsConfig)
278                 }
279                 return net.DialTimeout(daemonURL.Scheme, daemonURL.Host, timeout)
280         default:
281                 return c, errors.Errorf("unknown scheme %v (%s)", daemonURL.Scheme, daemon)
282         }
283 }
284
285 func getTLSConfig() (*tls.Config, error) {
286         dockerCertPath := os.Getenv("DOCKER_CERT_PATH")
287
288         if dockerCertPath == "" {
289                 return nil, errors.New("DOCKER_TLS_VERIFY specified, but no DOCKER_CERT_PATH environment variable")
290         }
291
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"),
296         }
297         tlsConfig, err := tlsconfig.Client(*option)
298         if err != nil {
299                 return nil, err
300         }
301
302         return tlsConfig, nil
303 }
304
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
310         }
311         return daemonURLStr
312 }