dotcloud/docker

View on GitHub
image/image.go

Summary

Maintainability
A
0 mins
Test Coverage
package image // import "github.com/docker/docker/image"

import (
    "context"
    "encoding/json"
    "errors"
    "io"
    "runtime"
    "strings"
    "time"

    "github.com/distribution/reference"
    "github.com/docker/docker/api/types/container"
    "github.com/docker/docker/dockerversion"
    "github.com/docker/docker/layer"
    "github.com/opencontainers/go-digest"
    ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

// 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)
}

// 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
}

// Platform generates an OCI platform from the image
func (img *Image) Platform() ocispec.Platform {
    return ocispec.Platform{
        Architecture: img.Architecture,
        OS:           img.OS,
        OSVersion:    img.OSVersion,
        OSFeatures:   img.OSFeatures,
        Variant:      img.Variant,
    }
}

// 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 = ocispec.History

// 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 {
    now := time.Now().UTC()
    return History{
        Author:     author,
        Created:    &now,
        CreatedBy:  createdBy,
        Comment:    comment,
        EmptyLayer: isEmptyLayer,
    }
}

// Exporter provides interface for loading and saving images
type Exporter interface {
    Load(context.Context, io.ReadCloser, io.Writer, bool) error
    // TODO: Load(net.Context, io.ReadCloser, <- chan StatusMessage) error
    Save(context.Context, []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
}