podman

Форк
0
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

5
package docker
6

7
import (
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.
22
type APIImages struct {
23
	ID          string            `json:"Id" yaml:"Id" toml:"Id"`
24
	RepoTags    []string          `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty" toml:"RepoTags,omitempty"`
25
	Created     int64             `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"`
26
	Size        int64             `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
27
	VirtualSize int64             `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty" toml:"VirtualSize,omitempty"`
28
	ParentID    string            `json:"ParentId,omitempty" yaml:"ParentId,omitempty" toml:"ParentId,omitempty"`
29
	RepoDigests []string          `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty" toml:"RepoDigests,omitempty"`
30
	Labels      map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"`
31
}
32

33
// RootFS represents the underlying layers used by an image
34
type RootFS struct {
35
	Type   string   `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"`
36
	Layers []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
40
type Image struct {
41
	ID              string    `json:"Id" yaml:"Id" toml:"Id"`
42
	RepoTags        []string  `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty" toml:"RepoTags,omitempty"`
43
	Parent          string    `json:"Parent,omitempty" yaml:"Parent,omitempty" toml:"Parent,omitempty"`
44
	Comment         string    `json:"Comment,omitempty" yaml:"Comment,omitempty" toml:"Comment,omitempty"`
45
	Created         time.Time `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"`
46
	Container       string    `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"`
47
	ContainerConfig Config    `json:"ContainerConfig,omitempty" yaml:"ContainerConfig,omitempty" toml:"ContainerConfig,omitempty"`
48
	DockerVersion   string    `json:"DockerVersion,omitempty" yaml:"DockerVersion,omitempty" toml:"DockerVersion,omitempty"`
49
	Author          string    `json:"Author,omitempty" yaml:"Author,omitempty" toml:"Author,omitempty"`
50
	Config          *Config   `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"`
51
	Architecture    string    `json:"Architecture,omitempty" yaml:"Architecture,omitempty"`
52
	Size            int64     `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
53
	VirtualSize     int64     `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty" toml:"VirtualSize,omitempty"`
54
	RepoDigests     []string  `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty" toml:"RepoDigests,omitempty"`
55
	RootFS          *RootFS   `json:"RootFS,omitempty" yaml:"RootFS,omitempty" toml:"RootFS,omitempty"`
56
	OS              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)
61
type ImagePre012 struct {
62
	ID              string    `json:"id"`
63
	Parent          string    `json:"parent,omitempty"`
64
	Comment         string    `json:"comment,omitempty"`
65
	Created         time.Time `json:"created"`
66
	Container       string    `json:"container,omitempty"`
67
	ContainerConfig Config    `json:"container_config,omitempty"`
68
	DockerVersion   string    `json:"docker_version,omitempty"`
69
	Author          string    `json:"author,omitempty"`
70
	Config          *Config   `json:"config,omitempty"`
71
	Architecture    string    `json:"architecture,omitempty"`
72
	Size            int64     `json:"size,omitempty"`
73
}
74

75
var (
76
	// ErrNoSuchImage is the error returned when the image does not exist.
77
	ErrNoSuchImage = errors.New("no such image")
78

79
	// ErrMissingRepo is the error returned when the remote repository is
80
	// missing.
81
	ErrMissingRepo = 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.
85
	ErrMissingOutputStream = errors.New("missing output stream")
86

87
	// ErrMultipleContexts is the error returned when both a ContextDir and
88
	// InputStream are provided in BuildImageOptions
89
	ErrMultipleContexts = 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
93
	ErrMustSpecifyNames = 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.
99
type ListImagesOptions struct {
100
	Filters map[string][]string
101
	All     bool
102
	Digests bool
103
	Filter  string
104
	Context 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.
110
func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
111
	path := "/images/json?" + queryString(opts)
112
	resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context})
113
	if err != nil {
114
		return nil, err
115
	}
116
	defer resp.Body.Close()
117
	var images []APIImages
118
	if err := json.NewDecoder(resp.Body).Decode(&images); err != nil {
119
		return nil, err
120
	}
121
	return images, nil
122
}
123

124
// ImageHistory represent a layer in an image's history returned by the
125
// ImageHistory call.
126
type ImageHistory struct {
127
	ID        string   `json:"Id" yaml:"Id" toml:"Id"`
128
	Tags      []string `json:"Tags,omitempty" yaml:"Tags,omitempty" toml:"Tags,omitempty"`
129
	Created   int64    `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Tags,omitempty"`
130
	CreatedBy string   `json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty" toml:"CreatedBy,omitempty"`
131
	Size      int64    `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
132
	Comment   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.
138
func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
139
	resp, err := c.do(http.MethodGet, "/images/"+name+"/history", doOptions{})
140
	if err != nil {
141
		var e *Error
142
		if errors.As(err, &e) && e.Status == http.StatusNotFound {
143
			return nil, ErrNoSuchImage
144
		}
145
		return nil, err
146
	}
147
	defer resp.Body.Close()
148
	var history []ImageHistory
149
	if err := json.NewDecoder(resp.Body).Decode(&history); err != nil {
150
		return nil, err
151
	}
152
	return history, nil
153
}
154

155
// RemoveImage removes an image by its name or ID.
156
//
157
// See https://goo.gl/Vd2Pck for more details.
158
func (c *Client) RemoveImage(name string) error {
159
	resp, err := c.do(http.MethodDelete, "/images/"+name, doOptions{})
160
	if err != nil {
161
		var e *Error
162
		if errors.As(err, &e) && e.Status == http.StatusNotFound {
163
			return ErrNoSuchImage
164
		}
165
		return err
166
	}
167
	resp.Body.Close()
168
	return 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.
175
type RemoveImageOptions struct {
176
	Force   bool `qs:"force"`
177
	NoPrune bool `qs:"noprune"`
178
	Context 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.
185
func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error {
186
	uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts))
187
	resp, err := c.do(http.MethodDelete, uri, doOptions{context: opts.Context})
188
	if err != nil {
189
		var e *Error
190
		if errors.As(err, &e) && e.Status == http.StatusNotFound {
191
			return ErrNoSuchImage
192
		}
193
		return err
194
	}
195
	resp.Body.Close()
196
	return nil
197
}
198

199
// InspectImage returns an image by its name or ID.
200
//
201
// See https://goo.gl/ncLTG8 for more details.
202
func (c *Client) InspectImage(name string) (*Image, error) {
203
	resp, err := c.do(http.MethodGet, "/images/"+name+"/json", doOptions{})
204
	if err != nil {
205
		var e *Error
206
		if errors.As(err, &e) && e.Status == http.StatusNotFound {
207
			return nil, ErrNoSuchImage
208
		}
209
		return nil, err
210
	}
211
	defer resp.Body.Close()
212

213
	var image Image
214

215
	// if the caller elected to skip checking the server's version, assume it's the latest
216
	if c.SkipServerVersionCheck || c.expectedAPIVersion.GreaterThanOrEqualTo(apiVersion112) {
217
		if err := json.NewDecoder(resp.Body).Decode(&image); err != nil {
218
			return nil, err
219
		}
220
	} else {
221
		var imagePre012 ImagePre012
222
		if err := json.NewDecoder(resp.Body).Decode(&imagePre012); err != nil {
223
			return nil, err
224
		}
225

226
		image.ID = imagePre012.ID
227
		image.Parent = imagePre012.Parent
228
		image.Comment = imagePre012.Comment
229
		image.Created = imagePre012.Created
230
		image.Container = imagePre012.Container
231
		image.ContainerConfig = imagePre012.ContainerConfig
232
		image.DockerVersion = imagePre012.DockerVersion
233
		image.Author = imagePre012.Author
234
		image.Config = imagePre012.Config
235
		image.Architecture = imagePre012.Architecture
236
		image.Size = imagePre012.Size
237
	}
238

239
	return &image, nil
240
}
241

242
// PushImageOptions represents options to use in the PushImage method.
243
//
244
// See https://goo.gl/BZemGg for more details.
245
type PushImageOptions struct {
246
	// Name of the image
247
	Name string
248

249
	// Tag of the image
250
	Tag string
251

252
	// Registry server to push the image
253
	Registry string
254

255
	OutputStream      io.Writer     `qs:"-"`
256
	RawJSONStream     bool          `qs:"-"`
257
	InactivityTimeout time.Duration `qs:"-"`
258

259
	Context 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.
268
func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error {
269
	if opts.Name == "" {
270
		return ErrNoSuchImage
271
	}
272
	headers, err := headersWithAuth(auth)
273
	if err != nil {
274
		return err
275
	}
276
	name := opts.Name
277
	opts.Name = ""
278
	path := "/images/" + name + "/push?" + queryString(&opts)
279
	return c.stream(http.MethodPost, path, streamOptions{
280
		setRawTerminal:    true,
281
		rawJSONStream:     opts.RawJSONStream,
282
		headers:           headers,
283
		stdout:            opts.OutputStream,
284
		inactivityTimeout: opts.InactivityTimeout,
285
		context:           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.
293
type PullImageOptions struct {
294
	All        bool
295
	Repository string `qs:"fromImage"`
296
	Tag        string
297
	Platform   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
302
	Registry string
303

304
	OutputStream      io.Writer     `qs:"-"`
305
	RawJSONStream     bool          `qs:"-"`
306
	InactivityTimeout time.Duration `qs:"-"`
307
	Context           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.
314
func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error {
315
	if opts.Repository == "" {
316
		return ErrNoSuchImage
317
	}
318

319
	headers, err := headersWithAuth(auth)
320
	if err != nil {
321
		return err
322
	}
323
	if opts.Tag == "" && strings.Contains(opts.Repository, "@") {
324
		parts := strings.SplitN(opts.Repository, "@", 2)
325
		opts.Repository = parts[0]
326
		opts.Tag = parts[1]
327
	}
328
	return c.createImage(&opts, headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context)
329
}
330

331
func (c *Client) createImage(opts any, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration, context context.Context) error {
332
	url, err := c.getPath("/images/create", opts)
333
	if err != nil {
334
		return err
335
	}
336
	return c.streamURL(http.MethodPost, url, streamOptions{
337
		setRawTerminal:    true,
338
		headers:           headers,
339
		in:                in,
340
		stdout:            w,
341
		rawJSONStream:     rawJSONStream,
342
		inactivityTimeout: timeout,
343
		context:           context,
344
	})
345
}
346

347
// LoadImageOptions represents the options for LoadImage Docker API Call
348
//
349
// See https://goo.gl/rEsBV3 for more details.
350
type LoadImageOptions struct {
351
	InputStream  io.Reader
352
	OutputStream io.Writer
353
	Context      context.Context
354
}
355

356
// LoadImage imports a tarball docker image
357
//
358
// See https://goo.gl/rEsBV3 for more details.
359
func (c *Client) LoadImage(opts LoadImageOptions) error {
360
	return c.stream(http.MethodPost, "/images/load", streamOptions{
361
		setRawTerminal: true,
362
		in:             opts.InputStream,
363
		stdout:         opts.OutputStream,
364
		context:        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.
371
type ExportImageOptions struct {
372
	Name              string
373
	OutputStream      io.Writer
374
	InactivityTimeout time.Duration
375
	Context           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.
381
func (c *Client) ExportImage(opts ExportImageOptions) error {
382
	return c.stream(http.MethodGet, fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{
383
		setRawTerminal:    true,
384
		stdout:            opts.OutputStream,
385
		inactivityTimeout: opts.InactivityTimeout,
386
		context:           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.
393
type ExportImagesOptions struct {
394
	Names             []string
395
	OutputStream      io.Writer     `qs:"-"`
396
	InactivityTimeout time.Duration `qs:"-"`
397
	Context           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.
403
func (c *Client) ExportImages(opts ExportImagesOptions) error {
404
	if opts.Names == nil || len(opts.Names) == 0 {
405
		return ErrMustSpecifyNames
406
	}
407
	// API < 1.25 allows multiple name values
408
	// 1.25 says name must be a comma separated list
409
	var err error
410
	var exporturl string
411
	if c.requestedAPIVersion.GreaterThanOrEqualTo(apiVersion125) {
412
		str := opts.Names[0]
413
		for _, val := range opts.Names[1:] {
414
			str += "," + val
415
		}
416
		exporturl, err = c.getPath("/images/get", ExportImagesOptions{
417
			Names:             []string{str},
418
			OutputStream:      opts.OutputStream,
419
			InactivityTimeout: opts.InactivityTimeout,
420
			Context:           opts.Context,
421
		})
422
	} else {
423
		exporturl, err = c.getPath("/images/get", &opts)
424
	}
425
	if err != nil {
426
		return err
427
	}
428
	return c.streamURL(http.MethodGet, exporturl, streamOptions{
429
		setRawTerminal:    true,
430
		stdout:            opts.OutputStream,
431
		inactivityTimeout: 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.
439
type ImportImageOptions struct {
440
	Repository string `qs:"repo"`
441
	Source     string `qs:"fromSrc"`
442
	Tag        string `qs:"tag"`
443

444
	InputStream       io.Reader     `qs:"-"`
445
	OutputStream      io.Writer     `qs:"-"`
446
	RawJSONStream     bool          `qs:"-"`
447
	InactivityTimeout time.Duration `qs:"-"`
448
	Context           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.
454
func (c *Client) ImportImage(opts ImportImageOptions) error {
455
	if opts.Repository == "" {
456
		return ErrNoSuchImage
457
	}
458
	if opts.Source != "-" {
459
		opts.InputStream = nil
460
	}
461
	if opts.Source != "-" && !isURL(opts.Source) {
462
		f, err := os.Open(opts.Source)
463
		if err != nil {
464
			return err
465
		}
466
		opts.InputStream = f
467
		opts.Source = "-"
468
	}
469
	return 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.
473
type BuilderVersion string
474

475
const (
476
	BuilderV1       BuilderVersion = "1"
477
	BuilderBuildKit 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.
485
type BuildImageOptions struct {
486
	Context             context.Context
487
	Name                string   `qs:"t"`
488
	Dockerfile          string   `ver:"1.25"`
489
	ExtraHosts          string   `ver:"1.28"`
490
	CacheFrom           []string `qs:"-" ver:"1.25"`
491
	Memory              int64
492
	Memswap             int64
493
	ShmSize             int64
494
	CPUShares           int64
495
	CPUQuota            int64 `ver:"1.21"`
496
	CPUPeriod           int64 `ver:"1.21"`
497
	CPUSetCPUs          string
498
	Labels              map[string]string
499
	InputStream         io.Reader `qs:"-"`
500
	OutputStream        io.Writer `qs:"-"`
501
	Remote              string
502
	Auth                AuthConfiguration  `qs:"-"` // for older docker X-Registry-Auth header
503
	AuthConfigs         AuthConfigurations `qs:"-"` // for newer docker X-Registry-Config header
504
	ContextDir          string             `qs:"-"`
505
	Ulimits             []ULimit           `qs:"-" ver:"1.18"`
506
	BuildArgs           []BuildArg         `qs:"-" ver:"1.21"`
507
	NetworkMode         string             `ver:"1.25"`
508
	Platform            string             `ver:"1.32"`
509
	InactivityTimeout   time.Duration      `qs:"-"`
510
	CgroupParent        string
511
	SecurityOpt         []string
512
	Target              string
513
	Outputs             string `ver:"1.40"`
514
	NoCache             bool
515
	SuppressOutput      bool           `qs:"q"`
516
	Pull                bool           `ver:"1.16"`
517
	RmTmpContainer      bool           `qs:"rm"`
518
	ForceRmTmpContainer bool           `qs:"forcerm" ver:"1.12"`
519
	RawJSONStream       bool           `qs:"-"`
520
	Version             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.
528
type BuildArg struct {
529
	Name  string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
530
	Value 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.
537
func (c *Client) BuildImage(opts BuildImageOptions) error {
538
	if opts.OutputStream == nil {
539
		return ErrMissingOutputStream
540
	}
541
	headers, err := headersWithAuth(opts.Auth, c.versionedAuthConfigs(opts.AuthConfigs))
542
	if err != nil {
543
		return err
544
	}
545

546
	if opts.Remote != "" && opts.Name == "" {
547
		opts.Name = opts.Remote
548
	}
549
	if opts.InputStream != nil || opts.ContextDir != "" {
550
		headers["Content-Type"] = "application/tar"
551
	} else if opts.Remote == "" {
552
		return ErrMissingRepo
553
	}
554
	if opts.ContextDir != "" {
555
		if opts.InputStream != nil {
556
			return ErrMultipleContexts
557
		}
558
		var err error
559
		if opts.InputStream, err = createTarStream(opts.ContextDir, opts.Dockerfile); err != nil {
560
			return err
561
		}
562
	}
563
	qs, ver := queryStringVersion(&opts)
564

565
	if len(opts.CacheFrom) > 0 {
566
		if b, err := json.Marshal(opts.CacheFrom); err == nil {
567
			item := url.Values(map[string][]string{})
568
			item.Add("cachefrom", string(b))
569
			qs = fmt.Sprintf("%s&%s", qs, item.Encode())
570
			if ver == nil || apiVersion125.GreaterThan(ver) {
571
				ver = apiVersion125
572
			}
573
		}
574
	}
575

576
	if len(opts.Ulimits) > 0 {
577
		if b, err := json.Marshal(opts.Ulimits); err == nil {
578
			item := url.Values(map[string][]string{})
579
			item.Add("ulimits", string(b))
580
			qs = fmt.Sprintf("%s&%s", qs, item.Encode())
581
			if ver == nil || apiVersion118.GreaterThan(ver) {
582
				ver = apiVersion118
583
			}
584
		}
585
	}
586

587
	if len(opts.BuildArgs) > 0 {
588
		v := make(map[string]string)
589
		for _, arg := range opts.BuildArgs {
590
			v[arg.Name] = arg.Value
591
		}
592
		if b, err := json.Marshal(v); err == nil {
593
			item := url.Values(map[string][]string{})
594
			item.Add("buildargs", string(b))
595
			qs = fmt.Sprintf("%s&%s", qs, item.Encode())
596
			if ver == nil || apiVersion121.GreaterThan(ver) {
597
				ver = apiVersion121
598
			}
599
		}
600
	}
601

602
	buildURL, err := c.pathVersionCheck("/build", qs, ver)
603
	if err != nil {
604
		return err
605
	}
606

607
	return c.streamURL(http.MethodPost, buildURL, streamOptions{
608
		setRawTerminal:    true,
609
		rawJSONStream:     opts.RawJSONStream,
610
		headers:           headers,
611
		in:                opts.InputStream,
612
		stdout:            opts.OutputStream,
613
		inactivityTimeout: opts.InactivityTimeout,
614
		context:           opts.Context,
615
	})
616
}
617

618
func (c *Client) versionedAuthConfigs(authConfigs AuthConfigurations) registryAuth {
619
	if c.serverAPIVersion == nil {
620
		c.checkAPIVersion()
621
	}
622
	if c.serverAPIVersion != nil && c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion119) {
623
		return AuthConfigurations119(authConfigs.Configs)
624
	}
625
	return authConfigs
626
}
627

628
// TagImageOptions present the set of options to tag an image.
629
//
630
// See https://goo.gl/prHrvo for more details.
631
type TagImageOptions struct {
632
	Repo    string
633
	Tag     string
634
	Force   bool
635
	Context 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.
641
func (c *Client) TagImage(name string, opts TagImageOptions) error {
642
	if name == "" {
643
		return ErrNoSuchImage
644
	}
645
	resp, err := c.do(http.MethodPost, "/images/"+name+"/tag?"+queryString(&opts), doOptions{
646
		context: opts.Context,
647
	})
648
	if err != nil {
649
		return err
650
	}
651

652
	defer resp.Body.Close()
653

654
	if resp.StatusCode == http.StatusNotFound {
655
		return ErrNoSuchImage
656
	}
657

658
	return err
659
}
660

661
func isURL(u string) bool {
662
	p, err := url.Parse(u)
663
	if err != nil {
664
		return false
665
	}
666
	return p.Scheme == "http" || p.Scheme == "https"
667
}
668

669
func headersWithAuth(auths ...registryAuth) (map[string]string, error) {
670
	headers := make(map[string]string)
671

672
	for _, auth := range auths {
673
		if auth.isEmpty() {
674
			continue
675
		}
676
		data, err := json.Marshal(auth)
677
		if err != nil {
678
			return nil, err
679
		}
680
		headers[auth.headerKey()] = base64.URLEncoding.EncodeToString(data)
681
	}
682

683
	return 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.
689
type APIImageSearch struct {
690
	Description string `json:"description,omitempty" yaml:"description,omitempty" toml:"description,omitempty"`
691
	IsOfficial  bool   `json:"is_official,omitempty" yaml:"is_official,omitempty" toml:"is_official,omitempty"`
692
	IsAutomated bool   `json:"is_automated,omitempty" yaml:"is_automated,omitempty" toml:"is_automated,omitempty"`
693
	Name        string `json:"name,omitempty" yaml:"name,omitempty" toml:"name,omitempty"`
694
	StarCount   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.
700
func (c *Client) SearchImages(term string) ([]APIImageSearch, error) {
701
	resp, err := c.do(http.MethodGet, "/images/search?term="+term, doOptions{})
702
	if err != nil {
703
		return nil, err
704
	}
705
	defer resp.Body.Close()
706
	var searchResult []APIImageSearch
707
	if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil {
708
		return nil, err
709
	}
710
	return 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.
716
func (c *Client) SearchImagesEx(term string, auth AuthConfiguration) ([]APIImageSearch, error) {
717
	headers, err := headersWithAuth(auth)
718
	if err != nil {
719
		return nil, err
720
	}
721

722
	resp, err := c.do(http.MethodGet, "/images/search?term="+term, doOptions{
723
		headers: headers,
724
	})
725
	if err != nil {
726
		return nil, err
727
	}
728

729
	defer resp.Body.Close()
730

731
	var searchResult []APIImageSearch
732
	if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil {
733
		return nil, err
734
	}
735

736
	return searchResult, nil
737
}
738

739
// PruneImagesOptions specify parameters to the PruneImages function.
740
//
741
// See https://goo.gl/qfZlbZ for more details.
742
type PruneImagesOptions struct {
743
	Filters map[string][]string
744
	Context context.Context
745
}
746

747
// PruneImagesResults specify results from the PruneImages function.
748
//
749
// See https://goo.gl/qfZlbZ for more details.
750
type PruneImagesResults struct {
751
	ImagesDeleted  []struct{ Untagged, Deleted string }
752
	SpaceReclaimed int64
753
}
754

755
// PruneImages deletes images which are unused.
756
//
757
// See https://goo.gl/qfZlbZ for more details.
758
func (c *Client) PruneImages(opts PruneImagesOptions) (*PruneImagesResults, error) {
759
	path := "/images/prune?" + queryString(opts)
760
	resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context})
761
	if err != nil {
762
		return nil, err
763
	}
764
	defer resp.Body.Close()
765
	var results PruneImagesResults
766
	if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
767
		return nil, err
768
	}
769
	return &results, nil
770
}
771

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.