diff options
author | Jake Champlin <jake@gnu.space> | 2017-06-09 17:54:32 +0000 |
---|---|---|
committer | Jake Champlin <jake@gnu.space> | 2017-06-09 17:54:32 +0000 |
commit | 9b12e4fe6f3c95986f1f3ec791636c58ca7e7583 (patch) | |
tree | 38f5f12bec0e488a12f0459a7356e6b7de7d8f84 /vendor/github.com/fsouza/go-dockerclient/image.go | |
parent | cec3de8a3bcaffd21dedd1bf42da4b490cae7e16 (diff) | |
download | terraform-provider-statuscake-9b12e4fe6f3c95986f1f3ec791636c58ca7e7583.tar.gz terraform-provider-statuscake-9b12e4fe6f3c95986f1f3ec791636c58ca7e7583.tar.zst terraform-provider-statuscake-9b12e4fe6f3c95986f1f3ec791636c58ca7e7583.zip |
Transfer of provider code
Diffstat (limited to 'vendor/github.com/fsouza/go-dockerclient/image.go')
-rw-r--r-- | vendor/github.com/fsouza/go-dockerclient/image.go | 642 |
1 files changed, 642 insertions, 0 deletions
diff --git a/vendor/github.com/fsouza/go-dockerclient/image.go b/vendor/github.com/fsouza/go-dockerclient/image.go new file mode 100644 index 0000000..fd51c3f --- /dev/null +++ b/vendor/github.com/fsouza/go-dockerclient/image.go | |||
@@ -0,0 +1,642 @@ | |||
1 | // Copyright 2015 go-dockerclient authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package docker | ||
6 | |||
7 | import ( | ||
8 | "bytes" | ||
9 | "encoding/base64" | ||
10 | "encoding/json" | ||
11 | "errors" | ||
12 | "fmt" | ||
13 | "io" | ||
14 | "net/http" | ||
15 | "net/url" | ||
16 | "os" | ||
17 | "time" | ||
18 | ) | ||
19 | |||
20 | // APIImages represent an image returned in the ListImages call. | ||
21 | type APIImages struct { | ||
22 | ID string `json:"Id" yaml:"Id"` | ||
23 | RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty"` | ||
24 | Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"` | ||
25 | Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"` | ||
26 | VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty"` | ||
27 | ParentID string `json:"ParentId,omitempty" yaml:"ParentId,omitempty"` | ||
28 | RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty"` | ||
29 | Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"` | ||
30 | } | ||
31 | |||
32 | // RootFS represents the underlying layers used by an image | ||
33 | type RootFS struct { | ||
34 | Type string `json:"Type,omitempty" yaml:"Type,omitempty"` | ||
35 | Layers []string `json:"Layers,omitempty" yaml:"Layers,omitempty"` | ||
36 | } | ||
37 | |||
38 | // Image is the type representing a docker image and its various properties | ||
39 | type Image struct { | ||
40 | ID string `json:"Id" yaml:"Id"` | ||
41 | RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty"` | ||
42 | Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty"` | ||
43 | Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty"` | ||
44 | Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty"` | ||
45 | Container string `json:"Container,omitempty" yaml:"Container,omitempty"` | ||
46 | ContainerConfig Config `json:"ContainerConfig,omitempty" yaml:"ContainerConfig,omitempty"` | ||
47 | DockerVersion string `json:"DockerVersion,omitempty" yaml:"DockerVersion,omitempty"` | ||
48 | Author string `json:"Author,omitempty" yaml:"Author,omitempty"` | ||
49 | Config *Config `json:"Config,omitempty" yaml:"Config,omitempty"` | ||
50 | Architecture string `json:"Architecture,omitempty" yaml:"Architecture,omitempty"` | ||
51 | Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"` | ||
52 | VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty"` | ||
53 | RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty"` | ||
54 | RootFS *RootFS `json:"RootFS,omitempty" yaml:"RootFS,omitempty"` | ||
55 | } | ||
56 | |||
57 | // ImagePre012 serves the same purpose as the Image type except that it is for | ||
58 | // earlier versions of the Docker API (pre-012 to be specific) | ||
59 | type ImagePre012 struct { | ||
60 | ID string `json:"id"` | ||
61 | Parent string `json:"parent,omitempty"` | ||
62 | Comment string `json:"comment,omitempty"` | ||
63 | Created time.Time `json:"created"` | ||
64 | Container string `json:"container,omitempty"` | ||
65 | ContainerConfig Config `json:"container_config,omitempty"` | ||
66 | DockerVersion string `json:"docker_version,omitempty"` | ||
67 | Author string `json:"author,omitempty"` | ||
68 | Config *Config `json:"config,omitempty"` | ||
69 | Architecture string `json:"architecture,omitempty"` | ||
70 | Size int64 `json:"size,omitempty"` | ||
71 | } | ||
72 | |||
73 | var ( | ||
74 | // ErrNoSuchImage is the error returned when the image does not exist. | ||
75 | ErrNoSuchImage = errors.New("no such image") | ||
76 | |||
77 | // ErrMissingRepo is the error returned when the remote repository is | ||
78 | // missing. | ||
79 | ErrMissingRepo = errors.New("missing remote repository e.g. 'github.com/user/repo'") | ||
80 | |||
81 | // ErrMissingOutputStream is the error returned when no output stream | ||
82 | // is provided to some calls, like BuildImage. | ||
83 | ErrMissingOutputStream = errors.New("missing output stream") | ||
84 | |||
85 | // ErrMultipleContexts is the error returned when both a ContextDir and | ||
86 | // InputStream are provided in BuildImageOptions | ||
87 | ErrMultipleContexts = errors.New("image build may not be provided BOTH context dir and input stream") | ||
88 | |||
89 | // ErrMustSpecifyNames is the error rreturned when the Names field on | ||
90 | // ExportImagesOptions is nil or empty | ||
91 | ErrMustSpecifyNames = errors.New("must specify at least one name to export") | ||
92 | ) | ||
93 | |||
94 | // ListImagesOptions specify parameters to the ListImages function. | ||
95 | // | ||
96 | // See https://goo.gl/xBe1u3 for more details. | ||
97 | type ListImagesOptions struct { | ||
98 | All bool | ||
99 | Filters map[string][]string | ||
100 | Digests bool | ||
101 | Filter string | ||
102 | } | ||
103 | |||
104 | // ListImages returns the list of available images in the server. | ||
105 | // | ||
106 | // See https://goo.gl/xBe1u3 for more details. | ||
107 | func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) { | ||
108 | path := "/images/json?" + queryString(opts) | ||
109 | resp, err := c.do("GET", path, doOptions{}) | ||
110 | if err != nil { | ||
111 | return nil, err | ||
112 | } | ||
113 | defer resp.Body.Close() | ||
114 | var images []APIImages | ||
115 | if err := json.NewDecoder(resp.Body).Decode(&images); err != nil { | ||
116 | return nil, err | ||
117 | } | ||
118 | return images, nil | ||
119 | } | ||
120 | |||
121 | // ImageHistory represent a layer in an image's history returned by the | ||
122 | // ImageHistory call. | ||
123 | type ImageHistory struct { | ||
124 | ID string `json:"Id" yaml:"Id"` | ||
125 | Tags []string `json:"Tags,omitempty" yaml:"Tags,omitempty"` | ||
126 | Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"` | ||
127 | CreatedBy string `json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty"` | ||
128 | Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"` | ||
129 | } | ||
130 | |||
131 | // ImageHistory returns the history of the image by its name or ID. | ||
132 | // | ||
133 | // See https://goo.gl/8bnTId for more details. | ||
134 | func (c *Client) ImageHistory(name string) ([]ImageHistory, error) { | ||
135 | resp, err := c.do("GET", "/images/"+name+"/history", doOptions{}) | ||
136 | if err != nil { | ||
137 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
138 | return nil, ErrNoSuchImage | ||
139 | } | ||
140 | return nil, err | ||
141 | } | ||
142 | defer resp.Body.Close() | ||
143 | var history []ImageHistory | ||
144 | if err := json.NewDecoder(resp.Body).Decode(&history); err != nil { | ||
145 | return nil, err | ||
146 | } | ||
147 | return history, nil | ||
148 | } | ||
149 | |||
150 | // RemoveImage removes an image by its name or ID. | ||
151 | // | ||
152 | // See https://goo.gl/V3ZWnK for more details. | ||
153 | func (c *Client) RemoveImage(name string) error { | ||
154 | resp, err := c.do("DELETE", "/images/"+name, doOptions{}) | ||
155 | if err != nil { | ||
156 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
157 | return ErrNoSuchImage | ||
158 | } | ||
159 | return err | ||
160 | } | ||
161 | resp.Body.Close() | ||
162 | return nil | ||
163 | } | ||
164 | |||
165 | // RemoveImageOptions present the set of options available for removing an image | ||
166 | // from a registry. | ||
167 | // | ||
168 | // See https://goo.gl/V3ZWnK for more details. | ||
169 | type RemoveImageOptions struct { | ||
170 | Force bool `qs:"force"` | ||
171 | NoPrune bool `qs:"noprune"` | ||
172 | } | ||
173 | |||
174 | // RemoveImageExtended removes an image by its name or ID. | ||
175 | // Extra params can be passed, see RemoveImageOptions | ||
176 | // | ||
177 | // See https://goo.gl/V3ZWnK for more details. | ||
178 | func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error { | ||
179 | uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts)) | ||
180 | resp, err := c.do("DELETE", uri, doOptions{}) | ||
181 | if err != nil { | ||
182 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
183 | return ErrNoSuchImage | ||
184 | } | ||
185 | return err | ||
186 | } | ||
187 | resp.Body.Close() | ||
188 | return nil | ||
189 | } | ||
190 | |||
191 | // InspectImage returns an image by its name or ID. | ||
192 | // | ||
193 | // See https://goo.gl/jHPcg6 for more details. | ||
194 | func (c *Client) InspectImage(name string) (*Image, error) { | ||
195 | resp, err := c.do("GET", "/images/"+name+"/json", doOptions{}) | ||
196 | if err != nil { | ||
197 | if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound { | ||
198 | return nil, ErrNoSuchImage | ||
199 | } | ||
200 | return nil, err | ||
201 | } | ||
202 | defer resp.Body.Close() | ||
203 | |||
204 | var image Image | ||
205 | |||
206 | // if the caller elected to skip checking the server's version, assume it's the latest | ||
207 | if c.SkipServerVersionCheck || c.expectedAPIVersion.GreaterThanOrEqualTo(apiVersion112) { | ||
208 | if err := json.NewDecoder(resp.Body).Decode(&image); err != nil { | ||
209 | return nil, err | ||
210 | } | ||
211 | } else { | ||
212 | var imagePre012 ImagePre012 | ||
213 | if err := json.NewDecoder(resp.Body).Decode(&imagePre012); err != nil { | ||
214 | return nil, err | ||
215 | } | ||
216 | |||
217 | image.ID = imagePre012.ID | ||
218 | image.Parent = imagePre012.Parent | ||
219 | image.Comment = imagePre012.Comment | ||
220 | image.Created = imagePre012.Created | ||
221 | image.Container = imagePre012.Container | ||
222 | image.ContainerConfig = imagePre012.ContainerConfig | ||
223 | image.DockerVersion = imagePre012.DockerVersion | ||
224 | image.Author = imagePre012.Author | ||
225 | image.Config = imagePre012.Config | ||
226 | image.Architecture = imagePre012.Architecture | ||
227 | image.Size = imagePre012.Size | ||
228 | } | ||
229 | |||
230 | return &image, nil | ||
231 | } | ||
232 | |||
233 | // PushImageOptions represents options to use in the PushImage method. | ||
234 | // | ||
235 | // See https://goo.gl/zPtZaT for more details. | ||
236 | type PushImageOptions struct { | ||
237 | // Name of the image | ||
238 | Name string | ||
239 | |||
240 | // Tag of the image | ||
241 | Tag string | ||
242 | |||
243 | // Registry server to push the image | ||
244 | Registry string | ||
245 | |||
246 | OutputStream io.Writer `qs:"-"` | ||
247 | RawJSONStream bool `qs:"-"` | ||
248 | InactivityTimeout time.Duration `qs:"-"` | ||
249 | } | ||
250 | |||
251 | // PushImage pushes an image to a remote registry, logging progress to w. | ||
252 | // | ||
253 | // An empty instance of AuthConfiguration may be used for unauthenticated | ||
254 | // pushes. | ||
255 | // | ||
256 | // See https://goo.gl/zPtZaT for more details. | ||
257 | func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error { | ||
258 | if opts.Name == "" { | ||
259 | return ErrNoSuchImage | ||
260 | } | ||
261 | headers, err := headersWithAuth(auth) | ||
262 | if err != nil { | ||
263 | return err | ||
264 | } | ||
265 | name := opts.Name | ||
266 | opts.Name = "" | ||
267 | path := "/images/" + name + "/push?" + queryString(&opts) | ||
268 | return c.stream("POST", path, streamOptions{ | ||
269 | setRawTerminal: true, | ||
270 | rawJSONStream: opts.RawJSONStream, | ||
271 | headers: headers, | ||
272 | stdout: opts.OutputStream, | ||
273 | inactivityTimeout: opts.InactivityTimeout, | ||
274 | }) | ||
275 | } | ||
276 | |||
277 | // PullImageOptions present the set of options available for pulling an image | ||
278 | // from a registry. | ||
279 | // | ||
280 | // See https://goo.gl/iJkZjD for more details. | ||
281 | type PullImageOptions struct { | ||
282 | Repository string `qs:"fromImage"` | ||
283 | Registry string | ||
284 | Tag string | ||
285 | |||
286 | OutputStream io.Writer `qs:"-"` | ||
287 | RawJSONStream bool `qs:"-"` | ||
288 | InactivityTimeout time.Duration `qs:"-"` | ||
289 | } | ||
290 | |||
291 | // PullImage pulls an image from a remote registry, logging progress to | ||
292 | // opts.OutputStream. | ||
293 | // | ||
294 | // See https://goo.gl/iJkZjD for more details. | ||
295 | func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error { | ||
296 | if opts.Repository == "" { | ||
297 | return ErrNoSuchImage | ||
298 | } | ||
299 | |||
300 | headers, err := headersWithAuth(auth) | ||
301 | if err != nil { | ||
302 | return err | ||
303 | } | ||
304 | return c.createImage(queryString(&opts), headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout) | ||
305 | } | ||
306 | |||
307 | func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration) error { | ||
308 | path := "/images/create?" + qs | ||
309 | return c.stream("POST", path, streamOptions{ | ||
310 | setRawTerminal: true, | ||
311 | headers: headers, | ||
312 | in: in, | ||
313 | stdout: w, | ||
314 | rawJSONStream: rawJSONStream, | ||
315 | inactivityTimeout: timeout, | ||
316 | }) | ||
317 | } | ||
318 | |||
319 | // LoadImageOptions represents the options for LoadImage Docker API Call | ||
320 | // | ||
321 | // See https://goo.gl/JyClMX for more details. | ||
322 | type LoadImageOptions struct { | ||
323 | InputStream io.Reader | ||
324 | } | ||
325 | |||
326 | // LoadImage imports a tarball docker image | ||
327 | // | ||
328 | // See https://goo.gl/JyClMX for more details. | ||
329 | func (c *Client) LoadImage(opts LoadImageOptions) error { | ||
330 | return c.stream("POST", "/images/load", streamOptions{ | ||
331 | setRawTerminal: true, | ||
332 | in: opts.InputStream, | ||
333 | }) | ||
334 | } | ||
335 | |||
336 | // ExportImageOptions represent the options for ExportImage Docker API call. | ||
337 | // | ||
338 | // See https://goo.gl/le7vK8 for more details. | ||
339 | type ExportImageOptions struct { | ||
340 | Name string | ||
341 | OutputStream io.Writer | ||
342 | InactivityTimeout time.Duration `qs:"-"` | ||
343 | } | ||
344 | |||
345 | // ExportImage exports an image (as a tar file) into the stream. | ||
346 | // | ||
347 | // See https://goo.gl/le7vK8 for more details. | ||
348 | func (c *Client) ExportImage(opts ExportImageOptions) error { | ||
349 | return c.stream("GET", fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{ | ||
350 | setRawTerminal: true, | ||
351 | stdout: opts.OutputStream, | ||
352 | inactivityTimeout: opts.InactivityTimeout, | ||
353 | }) | ||
354 | } | ||
355 | |||
356 | // ExportImagesOptions represent the options for ExportImages Docker API call | ||
357 | // | ||
358 | // See https://goo.gl/huC7HA for more details. | ||
359 | type ExportImagesOptions struct { | ||
360 | Names []string | ||
361 | OutputStream io.Writer `qs:"-"` | ||
362 | InactivityTimeout time.Duration `qs:"-"` | ||
363 | } | ||
364 | |||
365 | // ExportImages exports one or more images (as a tar file) into the stream | ||
366 | // | ||
367 | // See https://goo.gl/huC7HA for more details. | ||
368 | func (c *Client) ExportImages(opts ExportImagesOptions) error { | ||
369 | if opts.Names == nil || len(opts.Names) == 0 { | ||
370 | return ErrMustSpecifyNames | ||
371 | } | ||
372 | return c.stream("GET", "/images/get?"+queryString(&opts), streamOptions{ | ||
373 | setRawTerminal: true, | ||
374 | stdout: opts.OutputStream, | ||
375 | inactivityTimeout: opts.InactivityTimeout, | ||
376 | }) | ||
377 | } | ||
378 | |||
379 | // ImportImageOptions present the set of informations available for importing | ||
380 | // an image from a source file or the stdin. | ||
381 | // | ||
382 | // See https://goo.gl/iJkZjD for more details. | ||
383 | type ImportImageOptions struct { | ||
384 | Repository string `qs:"repo"` | ||
385 | Source string `qs:"fromSrc"` | ||
386 | Tag string `qs:"tag"` | ||
387 | |||
388 | InputStream io.Reader `qs:"-"` | ||
389 | OutputStream io.Writer `qs:"-"` | ||
390 | RawJSONStream bool `qs:"-"` | ||
391 | InactivityTimeout time.Duration `qs:"-"` | ||
392 | } | ||
393 | |||
394 | // ImportImage imports an image from a url, a file or stdin | ||
395 | // | ||
396 | // See https://goo.gl/iJkZjD for more details. | ||
397 | func (c *Client) ImportImage(opts ImportImageOptions) error { | ||
398 | if opts.Repository == "" { | ||
399 | return ErrNoSuchImage | ||
400 | } | ||
401 | if opts.Source != "-" { | ||
402 | opts.InputStream = nil | ||
403 | } | ||
404 | if opts.Source != "-" && !isURL(opts.Source) { | ||
405 | f, err := os.Open(opts.Source) | ||
406 | if err != nil { | ||
407 | return err | ||
408 | } | ||
409 | opts.InputStream = f | ||
410 | opts.Source = "-" | ||
411 | } | ||
412 | return c.createImage(queryString(&opts), nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout) | ||
413 | } | ||
414 | |||
415 | // BuildImageOptions present the set of informations available for building an | ||
416 | // image from a tarfile with a Dockerfile in it. | ||
417 | // | ||
418 | // For more details about the Docker building process, see | ||
419 | // http://goo.gl/tlPXPu. | ||
420 | type BuildImageOptions struct { | ||
421 | Name string `qs:"t"` | ||
422 | Dockerfile string `qs:"dockerfile"` | ||
423 | NoCache bool `qs:"nocache"` | ||
424 | SuppressOutput bool `qs:"q"` | ||
425 | Pull bool `qs:"pull"` | ||
426 | RmTmpContainer bool `qs:"rm"` | ||
427 | ForceRmTmpContainer bool `qs:"forcerm"` | ||
428 | Memory int64 `qs:"memory"` | ||
429 | Memswap int64 `qs:"memswap"` | ||
430 | CPUShares int64 `qs:"cpushares"` | ||
431 | CPUQuota int64 `qs:"cpuquota"` | ||
432 | CPUPeriod int64 `qs:"cpuperiod"` | ||
433 | CPUSetCPUs string `qs:"cpusetcpus"` | ||
434 | InputStream io.Reader `qs:"-"` | ||
435 | OutputStream io.Writer `qs:"-"` | ||
436 | RawJSONStream bool `qs:"-"` | ||
437 | Remote string `qs:"remote"` | ||
438 | Auth AuthConfiguration `qs:"-"` // for older docker X-Registry-Auth header | ||
439 | AuthConfigs AuthConfigurations `qs:"-"` // for newer docker X-Registry-Config header | ||
440 | ContextDir string `qs:"-"` | ||
441 | Ulimits []ULimit `qs:"-"` | ||
442 | BuildArgs []BuildArg `qs:"-"` | ||
443 | InactivityTimeout time.Duration `qs:"-"` | ||
444 | } | ||
445 | |||
446 | // BuildArg represents arguments that can be passed to the image when building | ||
447 | // it from a Dockerfile. | ||
448 | // | ||
449 | // For more details about the Docker building process, see | ||
450 | // http://goo.gl/tlPXPu. | ||
451 | type BuildArg struct { | ||
452 | Name string `json:"Name,omitempty" yaml:"Name,omitempty"` | ||
453 | Value string `json:"Value,omitempty" yaml:"Value,omitempty"` | ||
454 | } | ||
455 | |||
456 | // BuildImage builds an image from a tarball's url or a Dockerfile in the input | ||
457 | // stream. | ||
458 | // | ||
459 | // See https://goo.gl/xySxCe for more details. | ||
460 | func (c *Client) BuildImage(opts BuildImageOptions) error { | ||
461 | if opts.OutputStream == nil { | ||
462 | return ErrMissingOutputStream | ||
463 | } | ||
464 | headers, err := headersWithAuth(opts.Auth, c.versionedAuthConfigs(opts.AuthConfigs)) | ||
465 | if err != nil { | ||
466 | return err | ||
467 | } | ||
468 | |||
469 | if opts.Remote != "" && opts.Name == "" { | ||
470 | opts.Name = opts.Remote | ||
471 | } | ||
472 | if opts.InputStream != nil || opts.ContextDir != "" { | ||
473 | headers["Content-Type"] = "application/tar" | ||
474 | } else if opts.Remote == "" { | ||
475 | return ErrMissingRepo | ||
476 | } | ||
477 | if opts.ContextDir != "" { | ||
478 | if opts.InputStream != nil { | ||
479 | return ErrMultipleContexts | ||
480 | } | ||
481 | var err error | ||
482 | if opts.InputStream, err = createTarStream(opts.ContextDir, opts.Dockerfile); err != nil { | ||
483 | return err | ||
484 | } | ||
485 | } | ||
486 | |||
487 | qs := queryString(&opts) | ||
488 | if len(opts.Ulimits) > 0 { | ||
489 | if b, err := json.Marshal(opts.Ulimits); err == nil { | ||
490 | item := url.Values(map[string][]string{}) | ||
491 | item.Add("ulimits", string(b)) | ||
492 | qs = fmt.Sprintf("%s&%s", qs, item.Encode()) | ||
493 | } | ||
494 | } | ||
495 | |||
496 | if len(opts.BuildArgs) > 0 { | ||
497 | v := make(map[string]string) | ||
498 | for _, arg := range opts.BuildArgs { | ||
499 | v[arg.Name] = arg.Value | ||
500 | } | ||
501 | if b, err := json.Marshal(v); err == nil { | ||
502 | item := url.Values(map[string][]string{}) | ||
503 | item.Add("buildargs", string(b)) | ||
504 | qs = fmt.Sprintf("%s&%s", qs, item.Encode()) | ||
505 | } | ||
506 | } | ||
507 | |||
508 | return c.stream("POST", fmt.Sprintf("/build?%s", qs), streamOptions{ | ||
509 | setRawTerminal: true, | ||
510 | rawJSONStream: opts.RawJSONStream, | ||
511 | headers: headers, | ||
512 | in: opts.InputStream, | ||
513 | stdout: opts.OutputStream, | ||
514 | inactivityTimeout: opts.InactivityTimeout, | ||
515 | }) | ||
516 | } | ||
517 | |||
518 | func (c *Client) versionedAuthConfigs(authConfigs AuthConfigurations) interface{} { | ||
519 | if c.serverAPIVersion == nil { | ||
520 | c.checkAPIVersion() | ||
521 | } | ||
522 | if c.serverAPIVersion != nil && c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion119) { | ||
523 | return AuthConfigurations119(authConfigs.Configs) | ||
524 | } | ||
525 | return authConfigs | ||
526 | } | ||
527 | |||
528 | // TagImageOptions present the set of options to tag an image. | ||
529 | // | ||
530 | // See https://goo.gl/98ZzkU for more details. | ||
531 | type TagImageOptions struct { | ||
532 | Repo string | ||
533 | Tag string | ||
534 | Force bool | ||
535 | } | ||
536 | |||
537 | // TagImage adds a tag to the image identified by the given name. | ||
538 | // | ||
539 | // See https://goo.gl/98ZzkU for more details. | ||
540 | func (c *Client) TagImage(name string, opts TagImageOptions) error { | ||
541 | if name == "" { | ||
542 | return ErrNoSuchImage | ||
543 | } | ||
544 | resp, err := c.do("POST", fmt.Sprintf("/images/"+name+"/tag?%s", | ||
545 | queryString(&opts)), doOptions{}) | ||
546 | |||
547 | if err != nil { | ||
548 | return err | ||
549 | } | ||
550 | |||
551 | defer resp.Body.Close() | ||
552 | |||
553 | if resp.StatusCode == http.StatusNotFound { | ||
554 | return ErrNoSuchImage | ||
555 | } | ||
556 | |||
557 | return err | ||
558 | } | ||
559 | |||
560 | func isURL(u string) bool { | ||
561 | p, err := url.Parse(u) | ||
562 | if err != nil { | ||
563 | return false | ||
564 | } | ||
565 | return p.Scheme == "http" || p.Scheme == "https" | ||
566 | } | ||
567 | |||
568 | func headersWithAuth(auths ...interface{}) (map[string]string, error) { | ||
569 | var headers = make(map[string]string) | ||
570 | |||
571 | for _, auth := range auths { | ||
572 | switch auth.(type) { | ||
573 | case AuthConfiguration: | ||
574 | var buf bytes.Buffer | ||
575 | if err := json.NewEncoder(&buf).Encode(auth); err != nil { | ||
576 | return nil, err | ||
577 | } | ||
578 | headers["X-Registry-Auth"] = base64.URLEncoding.EncodeToString(buf.Bytes()) | ||
579 | case AuthConfigurations, AuthConfigurations119: | ||
580 | var buf bytes.Buffer | ||
581 | if err := json.NewEncoder(&buf).Encode(auth); err != nil { | ||
582 | return nil, err | ||
583 | } | ||
584 | headers["X-Registry-Config"] = base64.URLEncoding.EncodeToString(buf.Bytes()) | ||
585 | } | ||
586 | } | ||
587 | |||
588 | return headers, nil | ||
589 | } | ||
590 | |||
591 | // APIImageSearch reflect the result of a search on the Docker Hub. | ||
592 | // | ||
593 | // See https://goo.gl/AYjyrF for more details. | ||
594 | type APIImageSearch struct { | ||
595 | Description string `json:"description,omitempty" yaml:"description,omitempty"` | ||
596 | IsOfficial bool `json:"is_official,omitempty" yaml:"is_official,omitempty"` | ||
597 | IsAutomated bool `json:"is_automated,omitempty" yaml:"is_automated,omitempty"` | ||
598 | Name string `json:"name,omitempty" yaml:"name,omitempty"` | ||
599 | StarCount int `json:"star_count,omitempty" yaml:"star_count,omitempty"` | ||
600 | } | ||
601 | |||
602 | // SearchImages search the docker hub with a specific given term. | ||
603 | // | ||
604 | // See https://goo.gl/AYjyrF for more details. | ||
605 | func (c *Client) SearchImages(term string) ([]APIImageSearch, error) { | ||
606 | resp, err := c.do("GET", "/images/search?term="+term, doOptions{}) | ||
607 | if err != nil { | ||
608 | return nil, err | ||
609 | } | ||
610 | defer resp.Body.Close() | ||
611 | var searchResult []APIImageSearch | ||
612 | if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil { | ||
613 | return nil, err | ||
614 | } | ||
615 | return searchResult, nil | ||
616 | } | ||
617 | |||
618 | // SearchImagesEx search the docker hub with a specific given term and authentication. | ||
619 | // | ||
620 | // See https://goo.gl/AYjyrF for more details. | ||
621 | func (c *Client) SearchImagesEx(term string, auth AuthConfiguration) ([]APIImageSearch, error) { | ||
622 | headers, err := headersWithAuth(auth) | ||
623 | if err != nil { | ||
624 | return nil, err | ||
625 | } | ||
626 | |||
627 | resp, err := c.do("GET", "/images/search?term="+term, doOptions{ | ||
628 | headers: headers, | ||
629 | }) | ||
630 | if err != nil { | ||
631 | return nil, err | ||
632 | } | ||
633 | |||
634 | defer resp.Body.Close() | ||
635 | |||
636 | var searchResult []APIImageSearch | ||
637 | if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil { | ||
638 | return nil, err | ||
639 | } | ||
640 | |||
641 | return searchResult, nil | ||
642 | } | ||