123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- // Copyright 2015 go-dockerclient authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package docker
- import (
- "bytes"
- "encoding/base64"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "os"
- "time"
- )
- // APIImages represent an image returned in the ListImages call.
- type APIImages struct {
- ID string `json:"Id" yaml:"Id"`
- RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty"`
- Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"`
- Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"`
- VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty"`
- ParentID string `json:"ParentId,omitempty" yaml:"ParentId,omitempty"`
- RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty"`
- Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"`
- }
- // RootFS represents the underlying layers used by an image
- type RootFS struct {
- Type string `json:"Type,omitempty" yaml:"Type,omitempty"`
- Layers []string `json:"Layers,omitempty" yaml:"Layers,omitempty"`
- }
- // Image is the type representing a docker image and its various properties
- type Image struct {
- ID string `json:"Id" yaml:"Id"`
- RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty"`
- Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty"`
- Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty"`
- Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty"`
- Container string `json:"Container,omitempty" yaml:"Container,omitempty"`
- ContainerConfig Config `json:"ContainerConfig,omitempty" yaml:"ContainerConfig,omitempty"`
- DockerVersion string `json:"DockerVersion,omitempty" yaml:"DockerVersion,omitempty"`
- Author string `json:"Author,omitempty" yaml:"Author,omitempty"`
- Config *Config `json:"Config,omitempty" yaml:"Config,omitempty"`
- Architecture string `json:"Architecture,omitempty" yaml:"Architecture,omitempty"`
- Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"`
- VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty"`
- RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty"`
- RootFS *RootFS `json:"RootFS,omitempty" yaml:"RootFS,omitempty"`
- }
- // ImagePre012 serves the same purpose as the Image type except that it is for
- // earlier versions of the Docker API (pre-012 to be specific)
- type ImagePre012 struct {
- ID string `json:"id"`
- Parent string `json:"parent,omitempty"`
- Comment string `json:"comment,omitempty"`
- Created time.Time `json:"created"`
- Container string `json:"container,omitempty"`
- ContainerConfig Config `json:"container_config,omitempty"`
- DockerVersion string `json:"docker_version,omitempty"`
- Author string `json:"author,omitempty"`
- Config *Config `json:"config,omitempty"`
- Architecture string `json:"architecture,omitempty"`
- Size int64 `json:"size,omitempty"`
- }
- var (
- // ErrNoSuchImage is the error returned when the image does not exist.
- ErrNoSuchImage = errors.New("no such image")
- // ErrMissingRepo is the error returned when the remote repository is
- // missing.
- ErrMissingRepo = errors.New("missing remote repository e.g. 'github.com/user/repo'")
- // ErrMissingOutputStream is the error returned when no output stream
- // is provided to some calls, like BuildImage.
- ErrMissingOutputStream = errors.New("missing output stream")
- // ErrMultipleContexts is the error returned when both a ContextDir and
- // InputStream are provided in BuildImageOptions
- ErrMultipleContexts = errors.New("image build may not be provided BOTH context dir and input stream")
- // ErrMustSpecifyNames is the error rreturned when the Names field on
- // ExportImagesOptions is nil or empty
- ErrMustSpecifyNames = errors.New("must specify at least one name to export")
- )
- // ListImagesOptions specify parameters to the ListImages function.
- //
- // See https://goo.gl/xBe1u3 for more details.
- type ListImagesOptions struct {
- All bool
- Filters map[string][]string
- Digests bool
- Filter string
- }
- // ListImages returns the list of available images in the server.
- //
- // See https://goo.gl/xBe1u3 for more details.
- func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
- path := "/images/json?" + queryString(opts)
- resp, err := c.do("GET", path, doOptions{})
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- var images []APIImages
- if err := json.NewDecoder(resp.Body).Decode(&images); err != nil {
- return nil, err
- }
- return images, nil
- }
- // ImageHistory represent a layer in an image's history returned by the
- // ImageHistory call.
- type ImageHistory struct {
- ID string `json:"Id" yaml:"Id"`
- Tags []string `json:"Tags,omitempty" yaml:"Tags,omitempty"`
- Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"`
- CreatedBy string `json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty"`
- Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"`
- }
- // ImageHistory returns the history of the image by its name or ID.
- //
- // See https://goo.gl/8bnTId for more details.
- func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
- resp, err := c.do("GET", "/images/"+name+"/history", doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return nil, ErrNoSuchImage
- }
- return nil, err
- }
- defer resp.Body.Close()
- var history []ImageHistory
- if err := json.NewDecoder(resp.Body).Decode(&history); err != nil {
- return nil, err
- }
- return history, nil
- }
- // RemoveImage removes an image by its name or ID.
- //
- // See https://goo.gl/V3ZWnK for more details.
- func (c *Client) RemoveImage(name string) error {
- resp, err := c.do("DELETE", "/images/"+name, doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return ErrNoSuchImage
- }
- return err
- }
- resp.Body.Close()
- return nil
- }
- // RemoveImageOptions present the set of options available for removing an image
- // from a registry.
- //
- // See https://goo.gl/V3ZWnK for more details.
- type RemoveImageOptions struct {
- Force bool `qs:"force"`
- NoPrune bool `qs:"noprune"`
- }
- // RemoveImageExtended removes an image by its name or ID.
- // Extra params can be passed, see RemoveImageOptions
- //
- // See https://goo.gl/V3ZWnK for more details.
- func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error {
- uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts))
- resp, err := c.do("DELETE", uri, doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return ErrNoSuchImage
- }
- return err
- }
- resp.Body.Close()
- return nil
- }
- // InspectImage returns an image by its name or ID.
- //
- // See https://goo.gl/jHPcg6 for more details.
- func (c *Client) InspectImage(name string) (*Image, error) {
- resp, err := c.do("GET", "/images/"+name+"/json", doOptions{})
- if err != nil {
- if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
- return nil, ErrNoSuchImage
- }
- return nil, err
- }
- defer resp.Body.Close()
- var image Image
- // if the caller elected to skip checking the server's version, assume it's the latest
- if c.SkipServerVersionCheck || c.expectedAPIVersion.GreaterThanOrEqualTo(apiVersion112) {
- if err := json.NewDecoder(resp.Body).Decode(&image); err != nil {
- return nil, err
- }
- } else {
- var imagePre012 ImagePre012
- if err := json.NewDecoder(resp.Body).Decode(&imagePre012); err != nil {
- return nil, err
- }
- image.ID = imagePre012.ID
- image.Parent = imagePre012.Parent
- image.Comment = imagePre012.Comment
- image.Created = imagePre012.Created
- image.Container = imagePre012.Container
- image.ContainerConfig = imagePre012.ContainerConfig
- image.DockerVersion = imagePre012.DockerVersion
- image.Author = imagePre012.Author
- image.Config = imagePre012.Config
- image.Architecture = imagePre012.Architecture
- image.Size = imagePre012.Size
- }
- return &image, nil
- }
- // PushImageOptions represents options to use in the PushImage method.
- //
- // See https://goo.gl/zPtZaT for more details.
- type PushImageOptions struct {
- // Name of the image
- Name string
- // Tag of the image
- Tag string
- // Registry server to push the image
- Registry string
- OutputStream io.Writer `qs:"-"`
- RawJSONStream bool `qs:"-"`
- InactivityTimeout time.Duration `qs:"-"`
- }
- // PushImage pushes an image to a remote registry, logging progress to w.
- //
- // An empty instance of AuthConfiguration may be used for unauthenticated
- // pushes.
- //
- // See https://goo.gl/zPtZaT for more details.
- func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error {
- if opts.Name == "" {
- return ErrNoSuchImage
- }
- headers, err := headersWithAuth(auth)
- if err != nil {
- return err
- }
- name := opts.Name
- opts.Name = ""
- path := "/images/" + name + "/push?" + queryString(&opts)
- return c.stream("POST", path, streamOptions{
- setRawTerminal: true,
- rawJSONStream: opts.RawJSONStream,
- headers: headers,
- stdout: opts.OutputStream,
- inactivityTimeout: opts.InactivityTimeout,
- })
- }
- // PullImageOptions present the set of options available for pulling an image
- // from a registry.
- //
- // See https://goo.gl/iJkZjD for more details.
- type PullImageOptions struct {
- Repository string `qs:"fromImage"`
- Registry string
- Tag string
- OutputStream io.Writer `qs:"-"`
- RawJSONStream bool `qs:"-"`
- InactivityTimeout time.Duration `qs:"-"`
- }
- // PullImage pulls an image from a remote registry, logging progress to
- // opts.OutputStream.
- //
- // See https://goo.gl/iJkZjD for more details.
- func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error {
- if opts.Repository == "" {
- return ErrNoSuchImage
- }
- headers, err := headersWithAuth(auth)
- if err != nil {
- return err
- }
- return c.createImage(queryString(&opts), headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout)
- }
- func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration) error {
- path := "/images/create?" + qs
- return c.stream("POST", path, streamOptions{
- setRawTerminal: true,
- headers: headers,
- in: in,
- stdout: w,
- rawJSONStream: rawJSONStream,
- inactivityTimeout: timeout,
- })
- }
- // LoadImageOptions represents the options for LoadImage Docker API Call
- //
- // See https://goo.gl/JyClMX for more details.
- type LoadImageOptions struct {
- InputStream io.Reader
- }
- // LoadImage imports a tarball docker image
- //
- // See https://goo.gl/JyClMX for more details.
- func (c *Client) LoadImage(opts LoadImageOptions) error {
- return c.stream("POST", "/images/load", streamOptions{
- setRawTerminal: true,
- in: opts.InputStream,
- })
- }
- // ExportImageOptions represent the options for ExportImage Docker API call.
- //
- // See https://goo.gl/le7vK8 for more details.
- type ExportImageOptions struct {
- Name string
- OutputStream io.Writer
- InactivityTimeout time.Duration `qs:"-"`
- }
- // ExportImage exports an image (as a tar file) into the stream.
- //
- // See https://goo.gl/le7vK8 for more details.
- func (c *Client) ExportImage(opts ExportImageOptions) error {
- return c.stream("GET", fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{
- setRawTerminal: true,
- stdout: opts.OutputStream,
- inactivityTimeout: opts.InactivityTimeout,
- })
- }
- // ExportImagesOptions represent the options for ExportImages Docker API call
- //
- // See https://goo.gl/huC7HA for more details.
- type ExportImagesOptions struct {
- Names []string
- OutputStream io.Writer `qs:"-"`
- InactivityTimeout time.Duration `qs:"-"`
- }
- // ExportImages exports one or more images (as a tar file) into the stream
- //
- // See https://goo.gl/huC7HA for more details.
- func (c *Client) ExportImages(opts ExportImagesOptions) error {
- if opts.Names == nil || len(opts.Names) == 0 {
- return ErrMustSpecifyNames
- }
- return c.stream("GET", "/images/get?"+queryString(&opts), streamOptions{
- setRawTerminal: true,
- stdout: opts.OutputStream,
- inactivityTimeout: opts.InactivityTimeout,
- })
- }
- // ImportImageOptions present the set of informations available for importing
- // an image from a source file or the stdin.
- //
- // See https://goo.gl/iJkZjD for more details.
- type ImportImageOptions struct {
- Repository string `qs:"repo"`
- Source string `qs:"fromSrc"`
- Tag string `qs:"tag"`
- InputStream io.Reader `qs:"-"`
- OutputStream io.Writer `qs:"-"`
- RawJSONStream bool `qs:"-"`
- InactivityTimeout time.Duration `qs:"-"`
- }
- // ImportImage imports an image from a url, a file or stdin
- //
- // See https://goo.gl/iJkZjD for more details.
- func (c *Client) ImportImage(opts ImportImageOptions) error {
- if opts.Repository == "" {
- return ErrNoSuchImage
- }
- if opts.Source != "-" {
- opts.InputStream = nil
- }
- if opts.Source != "-" && !isURL(opts.Source) {
- f, err := os.Open(opts.Source)
- if err != nil {
- return err
- }
- opts.InputStream = f
- opts.Source = "-"
- }
- return c.createImage(queryString(&opts), nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout)
- }
- // BuildImageOptions present the set of informations available for building an
- // image from a tarfile with a Dockerfile in it.
- //
- // For more details about the Docker building process, see
- // http://goo.gl/tlPXPu.
- type BuildImageOptions struct {
- Name string `qs:"t"`
- Dockerfile string `qs:"dockerfile"`
- NoCache bool `qs:"nocache"`
- SuppressOutput bool `qs:"q"`
- Pull bool `qs:"pull"`
- RmTmpContainer bool `qs:"rm"`
- ForceRmTmpContainer bool `qs:"forcerm"`
- Memory int64 `qs:"memory"`
- Memswap int64 `qs:"memswap"`
- CPUShares int64 `qs:"cpushares"`
- CPUQuota int64 `qs:"cpuquota"`
- CPUPeriod int64 `qs:"cpuperiod"`
- CPUSetCPUs string `qs:"cpusetcpus"`
- InputStream io.Reader `qs:"-"`
- OutputStream io.Writer `qs:"-"`
- RawJSONStream bool `qs:"-"`
- Remote string `qs:"remote"`
- Auth AuthConfiguration `qs:"-"` // for older docker X-Registry-Auth header
- AuthConfigs AuthConfigurations `qs:"-"` // for newer docker X-Registry-Config header
- ContextDir string `qs:"-"`
- Ulimits []ULimit `qs:"-"`
- BuildArgs []BuildArg `qs:"-"`
- InactivityTimeout time.Duration `qs:"-"`
- }
- // BuildArg represents arguments that can be passed to the image when building
- // it from a Dockerfile.
- //
- // For more details about the Docker building process, see
- // http://goo.gl/tlPXPu.
- type BuildArg struct {
- Name string `json:"Name,omitempty" yaml:"Name,omitempty"`
- Value string `json:"Value,omitempty" yaml:"Value,omitempty"`
- }
- // BuildImage builds an image from a tarball's url or a Dockerfile in the input
- // stream.
- //
- // See https://goo.gl/xySxCe for more details.
- func (c *Client) BuildImage(opts BuildImageOptions) error {
- if opts.OutputStream == nil {
- return ErrMissingOutputStream
- }
- headers, err := headersWithAuth(opts.Auth, c.versionedAuthConfigs(opts.AuthConfigs))
- if err != nil {
- return err
- }
- if opts.Remote != "" && opts.Name == "" {
- opts.Name = opts.Remote
- }
- if opts.InputStream != nil || opts.ContextDir != "" {
- headers["Content-Type"] = "application/tar"
- } else if opts.Remote == "" {
- return ErrMissingRepo
- }
- if opts.ContextDir != "" {
- if opts.InputStream != nil {
- return ErrMultipleContexts
- }
- var err error
- if opts.InputStream, err = createTarStream(opts.ContextDir, opts.Dockerfile); err != nil {
- return err
- }
- }
- qs := queryString(&opts)
- if len(opts.Ulimits) > 0 {
- if b, err := json.Marshal(opts.Ulimits); err == nil {
- item := url.Values(map[string][]string{})
- item.Add("ulimits", string(b))
- qs = fmt.Sprintf("%s&%s", qs, item.Encode())
- }
- }
- if len(opts.BuildArgs) > 0 {
- v := make(map[string]string)
- for _, arg := range opts.BuildArgs {
- v[arg.Name] = arg.Value
- }
- if b, err := json.Marshal(v); err == nil {
- item := url.Values(map[string][]string{})
- item.Add("buildargs", string(b))
- qs = fmt.Sprintf("%s&%s", qs, item.Encode())
- }
- }
- return c.stream("POST", fmt.Sprintf("/build?%s", qs), streamOptions{
- setRawTerminal: true,
- rawJSONStream: opts.RawJSONStream,
- headers: headers,
- in: opts.InputStream,
- stdout: opts.OutputStream,
- inactivityTimeout: opts.InactivityTimeout,
- })
- }
- func (c *Client) versionedAuthConfigs(authConfigs AuthConfigurations) interface{} {
- if c.serverAPIVersion == nil {
- c.checkAPIVersion()
- }
- if c.serverAPIVersion != nil && c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion119) {
- return AuthConfigurations119(authConfigs.Configs)
- }
- return authConfigs
- }
- // TagImageOptions present the set of options to tag an image.
- //
- // See https://goo.gl/98ZzkU for more details.
- type TagImageOptions struct {
- Repo string
- Tag string
- Force bool
- }
- // TagImage adds a tag to the image identified by the given name.
- //
- // See https://goo.gl/98ZzkU for more details.
- func (c *Client) TagImage(name string, opts TagImageOptions) error {
- if name == "" {
- return ErrNoSuchImage
- }
- resp, err := c.do("POST", fmt.Sprintf("/images/"+name+"/tag?%s",
- queryString(&opts)), doOptions{})
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- if resp.StatusCode == http.StatusNotFound {
- return ErrNoSuchImage
- }
- return err
- }
- func isURL(u string) bool {
- p, err := url.Parse(u)
- if err != nil {
- return false
- }
- return p.Scheme == "http" || p.Scheme == "https"
- }
- func headersWithAuth(auths ...interface{}) (map[string]string, error) {
- var headers = make(map[string]string)
- for _, auth := range auths {
- switch auth.(type) {
- case AuthConfiguration:
- var buf bytes.Buffer
- if err := json.NewEncoder(&buf).Encode(auth); err != nil {
- return nil, err
- }
- headers["X-Registry-Auth"] = base64.URLEncoding.EncodeToString(buf.Bytes())
- case AuthConfigurations, AuthConfigurations119:
- var buf bytes.Buffer
- if err := json.NewEncoder(&buf).Encode(auth); err != nil {
- return nil, err
- }
- headers["X-Registry-Config"] = base64.URLEncoding.EncodeToString(buf.Bytes())
- }
- }
- return headers, nil
- }
- // APIImageSearch reflect the result of a search on the Docker Hub.
- //
- // See https://goo.gl/AYjyrF for more details.
- type APIImageSearch struct {
- Description string `json:"description,omitempty" yaml:"description,omitempty"`
- IsOfficial bool `json:"is_official,omitempty" yaml:"is_official,omitempty"`
- IsAutomated bool `json:"is_automated,omitempty" yaml:"is_automated,omitempty"`
- Name string `json:"name,omitempty" yaml:"name,omitempty"`
- StarCount int `json:"star_count,omitempty" yaml:"star_count,omitempty"`
- }
- // SearchImages search the docker hub with a specific given term.
- //
- // See https://goo.gl/AYjyrF for more details.
- func (c *Client) SearchImages(term string) ([]APIImageSearch, error) {
- resp, err := c.do("GET", "/images/search?term="+term, doOptions{})
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- var searchResult []APIImageSearch
- if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil {
- return nil, err
- }
- return searchResult, nil
- }
- // SearchImagesEx search the docker hub with a specific given term and authentication.
- //
- // See https://goo.gl/AYjyrF for more details.
- func (c *Client) SearchImagesEx(term string, auth AuthConfiguration) ([]APIImageSearch, error) {
- headers, err := headersWithAuth(auth)
- if err != nil {
- return nil, err
- }
- resp, err := c.do("GET", "/images/search?term="+term, doOptions{
- headers: headers,
- })
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- var searchResult []APIImageSearch
- if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil {
- return nil, err
- }
- return searchResult, nil
- }
|