| package image // import "github.com/docker/docker/image" | |
| import ( | |
| "encoding/json" | |
| "errors" | |
| "io" | |
| "reflect" | |
| "runtime" | |
| "strings" | |
| "time" | |
| "github.com/docker/distribution/reference" | |
| "github.com/docker/docker/api/types/container" | |
| "github.com/docker/docker/dockerversion" | |
| "github.com/docker/docker/layer" | |
| "github.com/opencontainers/go-digest" | |
| ) | |
| // ID is the content-addressable ID of an image. | |
| type ID digest.Digest | |
| func (id ID) String() string { | |
| return id.Digest().String() | |
| } | |
| // Digest converts ID into a digest | |
| func (id ID) Digest() digest.Digest { | |
| return digest.Digest(id) | |
| } | |
| // IDFromDigest creates an ID from a digest | |
| // | |
| // Deprecated: cast to an ID using ID(digest). | |
| func IDFromDigest(digest digest.Digest) ID { | |
| return ID(digest) | |
| } | |
| // V1Image stores the V1 image configuration. | |
| type V1Image struct { | |
| // ID is a unique 64 character identifier of the image | |
| ID string `json:"id,omitempty"` | |
| // Parent is the ID of the parent image. | |
| // | |
| // Depending on how the image was created, this field may be empty and | |
| // is only set for images that were built/created locally. This field | |
| // is empty if the image was pulled from an image registry. | |
| Parent string `json:"parent,omitempty"` | |
| // Comment is an optional message that can be set when committing or | |
| // importing the image. | |
| Comment string `json:"comment,omitempty"` | |
| // Created is the timestamp at which the image was created | |
| Created time.Time `json:"created"` | |
| // Container is the ID of the container that was used to create the image. | |
| // | |
| // Depending on how the image was created, this field may be empty. | |
| Container string `json:"container,omitempty"` | |
| // ContainerConfig is the configuration of the container that was committed | |
| // into the image. | |
| ContainerConfig container.Config `json:"container_config,omitempty"` | |
| // DockerVersion is the version of Docker that was used to build the image. | |
| // | |
| // Depending on how the image was created, this field may be empty. | |
| DockerVersion string `json:"docker_version,omitempty"` | |
| // Author is the name of the author that was specified when committing the | |
| // image, or as specified through MAINTAINER (deprecated) in the Dockerfile. | |
| Author string `json:"author,omitempty"` | |
| // Config is the configuration of the container received from the client. | |
| Config *container.Config `json:"config,omitempty"` | |
| // Architecture is the hardware CPU architecture that the image runs on. | |
| Architecture string `json:"architecture,omitempty"` | |
| // Variant is the CPU architecture variant (presently ARM-only). | |
| Variant string `json:"variant,omitempty"` | |
| // OS is the Operating System the image is built to run on. | |
| OS string `json:"os,omitempty"` | |
| // Size is the total size of the image including all layers it is composed of. | |
| Size int64 `json:",omitempty"` | |
| } | |
| // Image stores the image configuration | |
| type Image struct { | |
| V1Image | |
| // Parent is the ID of the parent image. | |
| // | |
| // Depending on how the image was created, this field may be empty and | |
| // is only set for images that were built/created locally. This field | |
| // is empty if the image was pulled from an image registry. | |
| Parent ID `json:"parent,omitempty"` //nolint:govet | |
| // RootFS contains information about the image's RootFS, including the | |
| // layer IDs. | |
| RootFS *RootFS `json:"rootfs,omitempty"` | |
| History []History `json:"history,omitempty"` | |
| // OsVersion is the version of the Operating System the image is built to | |
| // run on (especially for Windows). | |
| OSVersion string `json:"os.version,omitempty"` | |
| OSFeatures []string `json:"os.features,omitempty"` | |
| // rawJSON caches the immutable JSON associated with this image. | |
| rawJSON []byte | |
| // computedID is the ID computed from the hash of the image config. | |
| // Not to be confused with the legacy V1 ID in V1Image. | |
| computedID ID | |
| // Details holds additional details about image | |
| Details *Details `json:"-"` | |
| } | |
| // Details provides additional image data | |
| type Details struct { | |
| References []reference.Named | |
| Size int64 | |
| Metadata map[string]string | |
| Driver string | |
| LastUpdated time.Time | |
| } | |
| // RawJSON returns the immutable JSON associated with the image. | |
| func (img *Image) RawJSON() []byte { | |
| return img.rawJSON | |
| } | |
| // ID returns the image's content-addressable ID. | |
| func (img *Image) ID() ID { | |
| return img.computedID | |
| } | |
| // ImageID stringifies ID. | |
| func (img *Image) ImageID() string { | |
| return img.ID().String() | |
| } | |
| // RunConfig returns the image's container config. | |
| func (img *Image) RunConfig() *container.Config { | |
| return img.Config | |
| } | |
| // BaseImgArch returns the image's architecture. If not populated, defaults to the host runtime arch. | |
| func (img *Image) BaseImgArch() string { | |
| arch := img.Architecture | |
| if arch == "" { | |
| arch = runtime.GOARCH | |
| } | |
| return arch | |
| } | |
| // BaseImgVariant returns the image's variant, whether populated or not. | |
| // This avoids creating an inconsistency where the stored image variant | |
| // is "greater than" (i.e. v8 vs v6) the actual image variant. | |
| func (img *Image) BaseImgVariant() string { | |
| return img.Variant | |
| } | |
| // OperatingSystem returns the image's operating system. If not populated, defaults to the host runtime OS. | |
| func (img *Image) OperatingSystem() string { | |
| os := img.OS | |
| if os == "" { | |
| os = runtime.GOOS | |
| } | |
| return os | |
| } | |
| // MarshalJSON serializes the image to JSON. It sorts the top-level keys so | |
| // that JSON that's been manipulated by a push/pull cycle with a legacy | |
| // registry won't end up with a different key order. | |
| func (img *Image) MarshalJSON() ([]byte, error) { | |
| type MarshalImage Image | |
| pass1, err := json.Marshal(MarshalImage(*img)) | |
| if err != nil { | |
| return nil, err | |
| } | |
| var c map[string]*json.RawMessage | |
| if err := json.Unmarshal(pass1, &c); err != nil { | |
| return nil, err | |
| } | |
| return json.Marshal(c) | |
| } | |
| // ChildConfig is the configuration to apply to an Image to create a new | |
| // Child image. Other properties of the image are copied from the parent. | |
| type ChildConfig struct { | |
| ContainerID string | |
| Author string | |
| Comment string | |
| DiffID layer.DiffID | |
| ContainerConfig *container.Config | |
| Config *container.Config | |
| } | |
| // NewImage creates a new image with the given ID | |
| func NewImage(id ID) *Image { | |
| return &Image{ | |
| computedID: id, | |
| } | |
| } | |
| // NewChildImage creates a new Image as a child of this image. | |
| func NewChildImage(img *Image, child ChildConfig, os string) *Image { | |
| isEmptyLayer := layer.IsEmpty(child.DiffID) | |
| var rootFS *RootFS | |
| if img.RootFS != nil { | |
| rootFS = img.RootFS.Clone() | |
| } else { | |
| rootFS = NewRootFS() | |
| } | |
| if !isEmptyLayer { | |
| rootFS.Append(child.DiffID) | |
| } | |
| imgHistory := NewHistory( | |
| child.Author, | |
| child.Comment, | |
| strings.Join(child.ContainerConfig.Cmd, " "), | |
| isEmptyLayer) | |
| return &Image{ | |
| V1Image: V1Image{ | |
| DockerVersion: dockerversion.Version, | |
| Config: child.Config, | |
| Architecture: img.BaseImgArch(), | |
| Variant: img.BaseImgVariant(), | |
| OS: os, | |
| Container: child.ContainerID, | |
| ContainerConfig: *child.ContainerConfig, | |
| Author: child.Author, | |
| Created: imgHistory.Created, | |
| }, | |
| RootFS: rootFS, | |
| History: append(img.History, imgHistory), | |
| OSFeatures: img.OSFeatures, | |
| OSVersion: img.OSVersion, | |
| } | |
| } | |
| // Clone clones an image and changes ID. | |
| func Clone(base *Image, id ID) *Image { | |
| img := *base | |
| img.RootFS = img.RootFS.Clone() | |
| img.V1Image.ID = id.String() | |
| img.computedID = id | |
| return &img | |
| } | |
| // History stores build commands that were used to create an image | |
| type History struct { | |
| // Created is the timestamp at which the image was created | |
| Created time.Time `json:"created"` | |
| // Author is the name of the author that was specified when committing the | |
| // image, or as specified through MAINTAINER (deprecated) in the Dockerfile. | |
| Author string `json:"author,omitempty"` | |
| // CreatedBy keeps the Dockerfile command used while building the image | |
| CreatedBy string `json:"created_by,omitempty"` | |
| // Comment is the commit message that was set when committing the image | |
| Comment string `json:"comment,omitempty"` | |
| // EmptyLayer is set to true if this history item did not generate a | |
| // layer. Otherwise, the history item is associated with the next | |
| // layer in the RootFS section. | |
| EmptyLayer bool `json:"empty_layer,omitempty"` | |
| } | |
| // NewHistory creates a new history struct from arguments, and sets the created | |
| // time to the current time in UTC | |
| func NewHistory(author, comment, createdBy string, isEmptyLayer bool) History { | |
| return History{ | |
| Author: author, | |
| Created: time.Now().UTC(), | |
| CreatedBy: createdBy, | |
| Comment: comment, | |
| EmptyLayer: isEmptyLayer, | |
| } | |
| } | |
| // Equal compares two history structs for equality | |
| func (h History) Equal(i History) bool { | |
| if !h.Created.Equal(i.Created) { | |
| return false | |
| } | |
| i.Created = h.Created | |
| return reflect.DeepEqual(h, i) | |
| } | |
| // Exporter provides interface for loading and saving images | |
| type Exporter interface { | |
| Load(io.ReadCloser, io.Writer, bool) error | |
| // TODO: Load(net.Context, io.ReadCloser, <- chan StatusMessage) error | |
| Save([]string, io.Writer) error | |
| } | |
| // NewFromJSON creates an Image configuration from json. | |
| func NewFromJSON(src []byte) (*Image, error) { | |
| img := &Image{} | |
| if err := json.Unmarshal(src, img); err != nil { | |
| return nil, err | |
| } | |
| if img.RootFS == nil { | |
| return nil, errors.New("invalid image JSON, no RootFS key") | |
| } | |
| img.rawJSON = src | |
| return img, nil | |
| } |