podman
770 строк · 24.5 Кб
1// Copyright 2013 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
5package docker
6
7import (
8"context"
9"encoding/base64"
10"encoding/json"
11"errors"
12"fmt"
13"io"
14"net/http"
15"net/url"
16"os"
17"strings"
18"time"
19)
20
21// APIImages represent an image returned in the ListImages call.
22type APIImages struct {
23ID string `json:"Id" yaml:"Id" toml:"Id"`
24RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty" toml:"RepoTags,omitempty"`
25Created int64 `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"`
26Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
27VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty" toml:"VirtualSize,omitempty"`
28ParentID string `json:"ParentId,omitempty" yaml:"ParentId,omitempty" toml:"ParentId,omitempty"`
29RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty" toml:"RepoDigests,omitempty"`
30Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"`
31}
32
33// RootFS represents the underlying layers used by an image
34type RootFS struct {
35Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"`
36Layers []string `json:"Layers,omitempty" yaml:"Layers,omitempty" toml:"Layers,omitempty"`
37}
38
39// Image is the type representing a docker image and its various properties
40type Image struct {
41ID string `json:"Id" yaml:"Id" toml:"Id"`
42RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty" toml:"RepoTags,omitempty"`
43Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty" toml:"Parent,omitempty"`
44Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty" toml:"Comment,omitempty"`
45Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"`
46Container string `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"`
47ContainerConfig Config `json:"ContainerConfig,omitempty" yaml:"ContainerConfig,omitempty" toml:"ContainerConfig,omitempty"`
48DockerVersion string `json:"DockerVersion,omitempty" yaml:"DockerVersion,omitempty" toml:"DockerVersion,omitempty"`
49Author string `json:"Author,omitempty" yaml:"Author,omitempty" toml:"Author,omitempty"`
50Config *Config `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"`
51Architecture string `json:"Architecture,omitempty" yaml:"Architecture,omitempty"`
52Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
53VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty" toml:"VirtualSize,omitempty"`
54RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty" toml:"RepoDigests,omitempty"`
55RootFS *RootFS `json:"RootFS,omitempty" yaml:"RootFS,omitempty" toml:"RootFS,omitempty"`
56OS string `json:"Os,omitempty" yaml:"Os,omitempty" toml:"Os,omitempty"`
57}
58
59// ImagePre012 serves the same purpose as the Image type except that it is for
60// earlier versions of the Docker API (pre-012 to be specific)
61type ImagePre012 struct {
62ID string `json:"id"`
63Parent string `json:"parent,omitempty"`
64Comment string `json:"comment,omitempty"`
65Created time.Time `json:"created"`
66Container string `json:"container,omitempty"`
67ContainerConfig Config `json:"container_config,omitempty"`
68DockerVersion string `json:"docker_version,omitempty"`
69Author string `json:"author,omitempty"`
70Config *Config `json:"config,omitempty"`
71Architecture string `json:"architecture,omitempty"`
72Size int64 `json:"size,omitempty"`
73}
74
75var (
76// ErrNoSuchImage is the error returned when the image does not exist.
77ErrNoSuchImage = errors.New("no such image")
78
79// ErrMissingRepo is the error returned when the remote repository is
80// missing.
81ErrMissingRepo = errors.New("missing remote repository e.g. 'github.com/user/repo'")
82
83// ErrMissingOutputStream is the error returned when no output stream
84// is provided to some calls, like BuildImage.
85ErrMissingOutputStream = errors.New("missing output stream")
86
87// ErrMultipleContexts is the error returned when both a ContextDir and
88// InputStream are provided in BuildImageOptions
89ErrMultipleContexts = errors.New("image build may not be provided BOTH context dir and input stream")
90
91// ErrMustSpecifyNames is the error returned when the Names field on
92// ExportImagesOptions is nil or empty
93ErrMustSpecifyNames = errors.New("must specify at least one name to export")
94)
95
96// ListImagesOptions specify parameters to the ListImages function.
97//
98// See https://goo.gl/BVzauZ for more details.
99type ListImagesOptions struct {
100Filters map[string][]string
101All bool
102Digests bool
103Filter string
104Context context.Context
105}
106
107// ListImages returns the list of available images in the server.
108//
109// See https://goo.gl/BVzauZ for more details.
110func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
111path := "/images/json?" + queryString(opts)
112resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context})
113if err != nil {
114return nil, err
115}
116defer resp.Body.Close()
117var images []APIImages
118if err := json.NewDecoder(resp.Body).Decode(&images); err != nil {
119return nil, err
120}
121return images, nil
122}
123
124// ImageHistory represent a layer in an image's history returned by the
125// ImageHistory call.
126type ImageHistory struct {
127ID string `json:"Id" yaml:"Id" toml:"Id"`
128Tags []string `json:"Tags,omitempty" yaml:"Tags,omitempty" toml:"Tags,omitempty"`
129Created int64 `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Tags,omitempty"`
130CreatedBy string `json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty" toml:"CreatedBy,omitempty"`
131Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
132Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty" toml:"Comment,omitempty"`
133}
134
135// ImageHistory returns the history of the image by its name or ID.
136//
137// See https://goo.gl/fYtxQa for more details.
138func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
139resp, err := c.do(http.MethodGet, "/images/"+name+"/history", doOptions{})
140if err != nil {
141var e *Error
142if errors.As(err, &e) && e.Status == http.StatusNotFound {
143return nil, ErrNoSuchImage
144}
145return nil, err
146}
147defer resp.Body.Close()
148var history []ImageHistory
149if err := json.NewDecoder(resp.Body).Decode(&history); err != nil {
150return nil, err
151}
152return history, nil
153}
154
155// RemoveImage removes an image by its name or ID.
156//
157// See https://goo.gl/Vd2Pck for more details.
158func (c *Client) RemoveImage(name string) error {
159resp, err := c.do(http.MethodDelete, "/images/"+name, doOptions{})
160if err != nil {
161var e *Error
162if errors.As(err, &e) && e.Status == http.StatusNotFound {
163return ErrNoSuchImage
164}
165return err
166}
167resp.Body.Close()
168return nil
169}
170
171// RemoveImageOptions present the set of options available for removing an image
172// from a registry.
173//
174// See https://goo.gl/Vd2Pck for more details.
175type RemoveImageOptions struct {
176Force bool `qs:"force"`
177NoPrune bool `qs:"noprune"`
178Context context.Context
179}
180
181// RemoveImageExtended removes an image by its name or ID.
182// Extra params can be passed, see RemoveImageOptions
183//
184// See https://goo.gl/Vd2Pck for more details.
185func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error {
186uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts))
187resp, err := c.do(http.MethodDelete, uri, doOptions{context: opts.Context})
188if err != nil {
189var e *Error
190if errors.As(err, &e) && e.Status == http.StatusNotFound {
191return ErrNoSuchImage
192}
193return err
194}
195resp.Body.Close()
196return nil
197}
198
199// InspectImage returns an image by its name or ID.
200//
201// See https://goo.gl/ncLTG8 for more details.
202func (c *Client) InspectImage(name string) (*Image, error) {
203resp, err := c.do(http.MethodGet, "/images/"+name+"/json", doOptions{})
204if err != nil {
205var e *Error
206if errors.As(err, &e) && e.Status == http.StatusNotFound {
207return nil, ErrNoSuchImage
208}
209return nil, err
210}
211defer resp.Body.Close()
212
213var image Image
214
215// if the caller elected to skip checking the server's version, assume it's the latest
216if c.SkipServerVersionCheck || c.expectedAPIVersion.GreaterThanOrEqualTo(apiVersion112) {
217if err := json.NewDecoder(resp.Body).Decode(&image); err != nil {
218return nil, err
219}
220} else {
221var imagePre012 ImagePre012
222if err := json.NewDecoder(resp.Body).Decode(&imagePre012); err != nil {
223return nil, err
224}
225
226image.ID = imagePre012.ID
227image.Parent = imagePre012.Parent
228image.Comment = imagePre012.Comment
229image.Created = imagePre012.Created
230image.Container = imagePre012.Container
231image.ContainerConfig = imagePre012.ContainerConfig
232image.DockerVersion = imagePre012.DockerVersion
233image.Author = imagePre012.Author
234image.Config = imagePre012.Config
235image.Architecture = imagePre012.Architecture
236image.Size = imagePre012.Size
237}
238
239return &image, nil
240}
241
242// PushImageOptions represents options to use in the PushImage method.
243//
244// See https://goo.gl/BZemGg for more details.
245type PushImageOptions struct {
246// Name of the image
247Name string
248
249// Tag of the image
250Tag string
251
252// Registry server to push the image
253Registry string
254
255OutputStream io.Writer `qs:"-"`
256RawJSONStream bool `qs:"-"`
257InactivityTimeout time.Duration `qs:"-"`
258
259Context context.Context
260}
261
262// PushImage pushes an image to a remote registry, logging progress to w.
263//
264// An empty instance of AuthConfiguration may be used for unauthenticated
265// pushes.
266//
267// See https://goo.gl/BZemGg for more details.
268func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error {
269if opts.Name == "" {
270return ErrNoSuchImage
271}
272headers, err := headersWithAuth(auth)
273if err != nil {
274return err
275}
276name := opts.Name
277opts.Name = ""
278path := "/images/" + name + "/push?" + queryString(&opts)
279return c.stream(http.MethodPost, path, streamOptions{
280setRawTerminal: true,
281rawJSONStream: opts.RawJSONStream,
282headers: headers,
283stdout: opts.OutputStream,
284inactivityTimeout: opts.InactivityTimeout,
285context: opts.Context,
286})
287}
288
289// PullImageOptions present the set of options available for pulling an image
290// from a registry.
291//
292// See https://goo.gl/qkoSsn for more details.
293type PullImageOptions struct {
294All bool
295Repository string `qs:"fromImage"`
296Tag string
297Platform string `ver:"1.32"`
298
299// Only required for Docker Engine 1.9 or 1.10 w/ Remote API < 1.21
300// and Docker Engine < 1.9
301// This parameter was removed in Docker Engine 1.11
302Registry string
303
304OutputStream io.Writer `qs:"-"`
305RawJSONStream bool `qs:"-"`
306InactivityTimeout time.Duration `qs:"-"`
307Context context.Context
308}
309
310// PullImage pulls an image from a remote registry, logging progress to
311// opts.OutputStream.
312//
313// See https://goo.gl/qkoSsn for more details.
314func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error {
315if opts.Repository == "" {
316return ErrNoSuchImage
317}
318
319headers, err := headersWithAuth(auth)
320if err != nil {
321return err
322}
323if opts.Tag == "" && strings.Contains(opts.Repository, "@") {
324parts := strings.SplitN(opts.Repository, "@", 2)
325opts.Repository = parts[0]
326opts.Tag = parts[1]
327}
328return c.createImage(&opts, headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context)
329}
330
331func (c *Client) createImage(opts any, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration, context context.Context) error {
332url, err := c.getPath("/images/create", opts)
333if err != nil {
334return err
335}
336return c.streamURL(http.MethodPost, url, streamOptions{
337setRawTerminal: true,
338headers: headers,
339in: in,
340stdout: w,
341rawJSONStream: rawJSONStream,
342inactivityTimeout: timeout,
343context: context,
344})
345}
346
347// LoadImageOptions represents the options for LoadImage Docker API Call
348//
349// See https://goo.gl/rEsBV3 for more details.
350type LoadImageOptions struct {
351InputStream io.Reader
352OutputStream io.Writer
353Context context.Context
354}
355
356// LoadImage imports a tarball docker image
357//
358// See https://goo.gl/rEsBV3 for more details.
359func (c *Client) LoadImage(opts LoadImageOptions) error {
360return c.stream(http.MethodPost, "/images/load", streamOptions{
361setRawTerminal: true,
362in: opts.InputStream,
363stdout: opts.OutputStream,
364context: opts.Context,
365})
366}
367
368// ExportImageOptions represent the options for ExportImage Docker API call.
369//
370// See https://goo.gl/AuySaA for more details.
371type ExportImageOptions struct {
372Name string
373OutputStream io.Writer
374InactivityTimeout time.Duration
375Context context.Context
376}
377
378// ExportImage exports an image (as a tar file) into the stream.
379//
380// See https://goo.gl/AuySaA for more details.
381func (c *Client) ExportImage(opts ExportImageOptions) error {
382return c.stream(http.MethodGet, fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{
383setRawTerminal: true,
384stdout: opts.OutputStream,
385inactivityTimeout: opts.InactivityTimeout,
386context: opts.Context,
387})
388}
389
390// ExportImagesOptions represent the options for ExportImages Docker API call
391//
392// See https://goo.gl/N9XlDn for more details.
393type ExportImagesOptions struct {
394Names []string
395OutputStream io.Writer `qs:"-"`
396InactivityTimeout time.Duration `qs:"-"`
397Context context.Context
398}
399
400// ExportImages exports one or more images (as a tar file) into the stream
401//
402// See https://goo.gl/N9XlDn for more details.
403func (c *Client) ExportImages(opts ExportImagesOptions) error {
404if opts.Names == nil || len(opts.Names) == 0 {
405return ErrMustSpecifyNames
406}
407// API < 1.25 allows multiple name values
408// 1.25 says name must be a comma separated list
409var err error
410var exporturl string
411if c.requestedAPIVersion.GreaterThanOrEqualTo(apiVersion125) {
412str := opts.Names[0]
413for _, val := range opts.Names[1:] {
414str += "," + val
415}
416exporturl, err = c.getPath("/images/get", ExportImagesOptions{
417Names: []string{str},
418OutputStream: opts.OutputStream,
419InactivityTimeout: opts.InactivityTimeout,
420Context: opts.Context,
421})
422} else {
423exporturl, err = c.getPath("/images/get", &opts)
424}
425if err != nil {
426return err
427}
428return c.streamURL(http.MethodGet, exporturl, streamOptions{
429setRawTerminal: true,
430stdout: opts.OutputStream,
431inactivityTimeout: opts.InactivityTimeout,
432})
433}
434
435// ImportImageOptions present the set of informations available for importing
436// an image from a source file or the stdin.
437//
438// See https://goo.gl/qkoSsn for more details.
439type ImportImageOptions struct {
440Repository string `qs:"repo"`
441Source string `qs:"fromSrc"`
442Tag string `qs:"tag"`
443
444InputStream io.Reader `qs:"-"`
445OutputStream io.Writer `qs:"-"`
446RawJSONStream bool `qs:"-"`
447InactivityTimeout time.Duration `qs:"-"`
448Context context.Context
449}
450
451// ImportImage imports an image from a url, a file or stdin
452//
453// See https://goo.gl/qkoSsn for more details.
454func (c *Client) ImportImage(opts ImportImageOptions) error {
455if opts.Repository == "" {
456return ErrNoSuchImage
457}
458if opts.Source != "-" {
459opts.InputStream = nil
460}
461if opts.Source != "-" && !isURL(opts.Source) {
462f, err := os.Open(opts.Source)
463if err != nil {
464return err
465}
466opts.InputStream = f
467opts.Source = "-"
468}
469return c.createImage(&opts, nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context)
470}
471
472// BuilderVersion represents either the BuildKit or V1 ("classic") builder.
473type BuilderVersion string
474
475const (
476BuilderV1 BuilderVersion = "1"
477BuilderBuildKit BuilderVersion = "2"
478)
479
480// BuildImageOptions present the set of informations available for building an
481// image from a tarfile with a Dockerfile in it.
482//
483// For more details about the Docker building process, see
484// https://goo.gl/4nYHwV.
485type BuildImageOptions struct {
486Context context.Context
487Name string `qs:"t"`
488Dockerfile string `ver:"1.25"`
489ExtraHosts string `ver:"1.28"`
490CacheFrom []string `qs:"-" ver:"1.25"`
491Memory int64
492Memswap int64
493ShmSize int64
494CPUShares int64
495CPUQuota int64 `ver:"1.21"`
496CPUPeriod int64 `ver:"1.21"`
497CPUSetCPUs string
498Labels map[string]string
499InputStream io.Reader `qs:"-"`
500OutputStream io.Writer `qs:"-"`
501Remote string
502Auth AuthConfiguration `qs:"-"` // for older docker X-Registry-Auth header
503AuthConfigs AuthConfigurations `qs:"-"` // for newer docker X-Registry-Config header
504ContextDir string `qs:"-"`
505Ulimits []ULimit `qs:"-" ver:"1.18"`
506BuildArgs []BuildArg `qs:"-" ver:"1.21"`
507NetworkMode string `ver:"1.25"`
508Platform string `ver:"1.32"`
509InactivityTimeout time.Duration `qs:"-"`
510CgroupParent string
511SecurityOpt []string
512Target string
513Outputs string `ver:"1.40"`
514NoCache bool
515SuppressOutput bool `qs:"q"`
516Pull bool `ver:"1.16"`
517RmTmpContainer bool `qs:"rm"`
518ForceRmTmpContainer bool `qs:"forcerm" ver:"1.12"`
519RawJSONStream bool `qs:"-"`
520Version BuilderVersion `qs:"version" ver:"1.39"`
521}
522
523// BuildArg represents arguments that can be passed to the image when building
524// it from a Dockerfile.
525//
526// For more details about the Docker building process, see
527// https://goo.gl/4nYHwV.
528type BuildArg struct {
529Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
530Value string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"`
531}
532
533// BuildImage builds an image from a tarball's url or a Dockerfile in the input
534// stream.
535//
536// See https://goo.gl/4nYHwV for more details.
537func (c *Client) BuildImage(opts BuildImageOptions) error {
538if opts.OutputStream == nil {
539return ErrMissingOutputStream
540}
541headers, err := headersWithAuth(opts.Auth, c.versionedAuthConfigs(opts.AuthConfigs))
542if err != nil {
543return err
544}
545
546if opts.Remote != "" && opts.Name == "" {
547opts.Name = opts.Remote
548}
549if opts.InputStream != nil || opts.ContextDir != "" {
550headers["Content-Type"] = "application/tar"
551} else if opts.Remote == "" {
552return ErrMissingRepo
553}
554if opts.ContextDir != "" {
555if opts.InputStream != nil {
556return ErrMultipleContexts
557}
558var err error
559if opts.InputStream, err = createTarStream(opts.ContextDir, opts.Dockerfile); err != nil {
560return err
561}
562}
563qs, ver := queryStringVersion(&opts)
564
565if len(opts.CacheFrom) > 0 {
566if b, err := json.Marshal(opts.CacheFrom); err == nil {
567item := url.Values(map[string][]string{})
568item.Add("cachefrom", string(b))
569qs = fmt.Sprintf("%s&%s", qs, item.Encode())
570if ver == nil || apiVersion125.GreaterThan(ver) {
571ver = apiVersion125
572}
573}
574}
575
576if len(opts.Ulimits) > 0 {
577if b, err := json.Marshal(opts.Ulimits); err == nil {
578item := url.Values(map[string][]string{})
579item.Add("ulimits", string(b))
580qs = fmt.Sprintf("%s&%s", qs, item.Encode())
581if ver == nil || apiVersion118.GreaterThan(ver) {
582ver = apiVersion118
583}
584}
585}
586
587if len(opts.BuildArgs) > 0 {
588v := make(map[string]string)
589for _, arg := range opts.BuildArgs {
590v[arg.Name] = arg.Value
591}
592if b, err := json.Marshal(v); err == nil {
593item := url.Values(map[string][]string{})
594item.Add("buildargs", string(b))
595qs = fmt.Sprintf("%s&%s", qs, item.Encode())
596if ver == nil || apiVersion121.GreaterThan(ver) {
597ver = apiVersion121
598}
599}
600}
601
602buildURL, err := c.pathVersionCheck("/build", qs, ver)
603if err != nil {
604return err
605}
606
607return c.streamURL(http.MethodPost, buildURL, streamOptions{
608setRawTerminal: true,
609rawJSONStream: opts.RawJSONStream,
610headers: headers,
611in: opts.InputStream,
612stdout: opts.OutputStream,
613inactivityTimeout: opts.InactivityTimeout,
614context: opts.Context,
615})
616}
617
618func (c *Client) versionedAuthConfigs(authConfigs AuthConfigurations) registryAuth {
619if c.serverAPIVersion == nil {
620c.checkAPIVersion()
621}
622if c.serverAPIVersion != nil && c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion119) {
623return AuthConfigurations119(authConfigs.Configs)
624}
625return authConfigs
626}
627
628// TagImageOptions present the set of options to tag an image.
629//
630// See https://goo.gl/prHrvo for more details.
631type TagImageOptions struct {
632Repo string
633Tag string
634Force bool
635Context context.Context
636}
637
638// TagImage adds a tag to the image identified by the given name.
639//
640// See https://goo.gl/prHrvo for more details.
641func (c *Client) TagImage(name string, opts TagImageOptions) error {
642if name == "" {
643return ErrNoSuchImage
644}
645resp, err := c.do(http.MethodPost, "/images/"+name+"/tag?"+queryString(&opts), doOptions{
646context: opts.Context,
647})
648if err != nil {
649return err
650}
651
652defer resp.Body.Close()
653
654if resp.StatusCode == http.StatusNotFound {
655return ErrNoSuchImage
656}
657
658return err
659}
660
661func isURL(u string) bool {
662p, err := url.Parse(u)
663if err != nil {
664return false
665}
666return p.Scheme == "http" || p.Scheme == "https"
667}
668
669func headersWithAuth(auths ...registryAuth) (map[string]string, error) {
670headers := make(map[string]string)
671
672for _, auth := range auths {
673if auth.isEmpty() {
674continue
675}
676data, err := json.Marshal(auth)
677if err != nil {
678return nil, err
679}
680headers[auth.headerKey()] = base64.URLEncoding.EncodeToString(data)
681}
682
683return headers, nil
684}
685
686// APIImageSearch reflect the result of a search on the Docker Hub.
687//
688// See https://goo.gl/KLO9IZ for more details.
689type APIImageSearch struct {
690Description string `json:"description,omitempty" yaml:"description,omitempty" toml:"description,omitempty"`
691IsOfficial bool `json:"is_official,omitempty" yaml:"is_official,omitempty" toml:"is_official,omitempty"`
692IsAutomated bool `json:"is_automated,omitempty" yaml:"is_automated,omitempty" toml:"is_automated,omitempty"`
693Name string `json:"name,omitempty" yaml:"name,omitempty" toml:"name,omitempty"`
694StarCount int `json:"star_count,omitempty" yaml:"star_count,omitempty" toml:"star_count,omitempty"`
695}
696
697// SearchImages search the docker hub with a specific given term.
698//
699// See https://goo.gl/KLO9IZ for more details.
700func (c *Client) SearchImages(term string) ([]APIImageSearch, error) {
701resp, err := c.do(http.MethodGet, "/images/search?term="+term, doOptions{})
702if err != nil {
703return nil, err
704}
705defer resp.Body.Close()
706var searchResult []APIImageSearch
707if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil {
708return nil, err
709}
710return searchResult, nil
711}
712
713// SearchImagesEx search the docker hub with a specific given term and authentication.
714//
715// See https://goo.gl/KLO9IZ for more details.
716func (c *Client) SearchImagesEx(term string, auth AuthConfiguration) ([]APIImageSearch, error) {
717headers, err := headersWithAuth(auth)
718if err != nil {
719return nil, err
720}
721
722resp, err := c.do(http.MethodGet, "/images/search?term="+term, doOptions{
723headers: headers,
724})
725if err != nil {
726return nil, err
727}
728
729defer resp.Body.Close()
730
731var searchResult []APIImageSearch
732if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil {
733return nil, err
734}
735
736return searchResult, nil
737}
738
739// PruneImagesOptions specify parameters to the PruneImages function.
740//
741// See https://goo.gl/qfZlbZ for more details.
742type PruneImagesOptions struct {
743Filters map[string][]string
744Context context.Context
745}
746
747// PruneImagesResults specify results from the PruneImages function.
748//
749// See https://goo.gl/qfZlbZ for more details.
750type PruneImagesResults struct {
751ImagesDeleted []struct{ Untagged, Deleted string }
752SpaceReclaimed int64
753}
754
755// PruneImages deletes images which are unused.
756//
757// See https://goo.gl/qfZlbZ for more details.
758func (c *Client) PruneImages(opts PruneImagesOptions) (*PruneImagesResults, error) {
759path := "/images/prune?" + queryString(opts)
760resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
761if err != nil {
762return nil, err
763}
764defer resp.Body.Close()
765var results PruneImagesResults
766if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
767return nil, err
768}
769return &results, nil
770}
771