github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+incompatible/image/image.go (about)

     1  package image // import "github.com/docker/docker/image"
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"io"
     7  	"reflect"
     8  	"runtime"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/docker/docker/api/types/container"
    13  	"github.com/docker/docker/dockerversion"
    14  	"github.com/docker/docker/layer"
    15  	digest "github.com/opencontainers/go-digest"
    16  )
    17  
    18  // ID is the content-addressable ID of an image.
    19  type ID digest.Digest
    20  
    21  func (id ID) String() string {
    22  	return id.Digest().String()
    23  }
    24  
    25  // Digest converts ID into a digest
    26  func (id ID) Digest() digest.Digest {
    27  	return digest.Digest(id)
    28  }
    29  
    30  // IDFromDigest creates an ID from a digest
    31  func IDFromDigest(digest digest.Digest) ID {
    32  	return ID(digest)
    33  }
    34  
    35  // V1Image stores the V1 image configuration.
    36  type V1Image struct {
    37  	// ID is a unique 64 character identifier of the image
    38  	ID string `json:"id,omitempty"`
    39  	// Parent is the ID of the parent image
    40  	Parent string `json:"parent,omitempty"`
    41  	// Comment is the commit message that was set when committing the image
    42  	Comment string `json:"comment,omitempty"`
    43  	// Created is the timestamp at which the image was created
    44  	Created time.Time `json:"created"`
    45  	// Container is the id of the container used to commit
    46  	Container string `json:"container,omitempty"`
    47  	// ContainerConfig is the configuration of the container that is committed into the image
    48  	ContainerConfig container.Config `json:"container_config,omitempty"`
    49  	// DockerVersion specifies the version of Docker that was used to build the image
    50  	DockerVersion string `json:"docker_version,omitempty"`
    51  	// Author is the name of the author that was specified when committing the image
    52  	Author string `json:"author,omitempty"`
    53  	// Config is the configuration of the container received from the client
    54  	Config *container.Config `json:"config,omitempty"`
    55  	// Architecture is the hardware that the image is built and runs on
    56  	Architecture string `json:"architecture,omitempty"`
    57  	// Variant is the CPU architecture variant (presently ARM-only)
    58  	Variant string `json:"variant,omitempty"`
    59  	// OS is the operating system used to build and run the image
    60  	OS string `json:"os,omitempty"`
    61  	// Size is the total size of the image including all layers it is composed of
    62  	Size int64 `json:",omitempty"`
    63  }
    64  
    65  // Image stores the image configuration
    66  type Image struct {
    67  	V1Image
    68  	Parent     ID        `json:"parent,omitempty"` //nolint:govet
    69  	RootFS     *RootFS   `json:"rootfs,omitempty"`
    70  	History    []History `json:"history,omitempty"`
    71  	OSVersion  string    `json:"os.version,omitempty"`
    72  	OSFeatures []string  `json:"os.features,omitempty"`
    73  
    74  	// rawJSON caches the immutable JSON associated with this image.
    75  	rawJSON []byte
    76  
    77  	// computedID is the ID computed from the hash of the image config.
    78  	// Not to be confused with the legacy V1 ID in V1Image.
    79  	computedID ID
    80  }
    81  
    82  // RawJSON returns the immutable JSON associated with the image.
    83  func (img *Image) RawJSON() []byte {
    84  	return img.rawJSON
    85  }
    86  
    87  // ID returns the image's content-addressable ID.
    88  func (img *Image) ID() ID {
    89  	return img.computedID
    90  }
    91  
    92  // ImageID stringifies ID.
    93  func (img *Image) ImageID() string {
    94  	return img.ID().String()
    95  }
    96  
    97  // RunConfig returns the image's container config.
    98  func (img *Image) RunConfig() *container.Config {
    99  	return img.Config
   100  }
   101  
   102  // BaseImgArch returns the image's architecture. If not populated, defaults to the host runtime arch.
   103  func (img *Image) BaseImgArch() string {
   104  	arch := img.Architecture
   105  	if arch == "" {
   106  		arch = runtime.GOARCH
   107  	}
   108  	return arch
   109  }
   110  
   111  // BaseImgVariant returns the image's variant, whether populated or not.
   112  // This avoids creating an inconsistency where the stored image variant
   113  // is "greater than" (i.e. v8 vs v6) the actual image variant.
   114  func (img *Image) BaseImgVariant() string {
   115  	return img.Variant
   116  }
   117  
   118  // OperatingSystem returns the image's operating system. If not populated, defaults to the host runtime OS.
   119  func (img *Image) OperatingSystem() string {
   120  	os := img.OS
   121  	if os == "" {
   122  		os = runtime.GOOS
   123  	}
   124  	return os
   125  }
   126  
   127  // MarshalJSON serializes the image to JSON. It sorts the top-level keys so
   128  // that JSON that's been manipulated by a push/pull cycle with a legacy
   129  // registry won't end up with a different key order.
   130  func (img *Image) MarshalJSON() ([]byte, error) {
   131  	type MarshalImage Image
   132  
   133  	pass1, err := json.Marshal(MarshalImage(*img))
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	var c map[string]*json.RawMessage
   139  	if err := json.Unmarshal(pass1, &c); err != nil {
   140  		return nil, err
   141  	}
   142  	return json.Marshal(c)
   143  }
   144  
   145  // ChildConfig is the configuration to apply to an Image to create a new
   146  // Child image. Other properties of the image are copied from the parent.
   147  type ChildConfig struct {
   148  	ContainerID     string
   149  	Author          string
   150  	Comment         string
   151  	DiffID          layer.DiffID
   152  	ContainerConfig *container.Config
   153  	Config          *container.Config
   154  }
   155  
   156  // NewChildImage creates a new Image as a child of this image.
   157  func NewChildImage(img *Image, child ChildConfig, os string) *Image {
   158  	isEmptyLayer := layer.IsEmpty(child.DiffID)
   159  	var rootFS *RootFS
   160  	if img.RootFS != nil {
   161  		rootFS = img.RootFS.Clone()
   162  	} else {
   163  		rootFS = NewRootFS()
   164  	}
   165  
   166  	if !isEmptyLayer {
   167  		rootFS.Append(child.DiffID)
   168  	}
   169  	imgHistory := NewHistory(
   170  		child.Author,
   171  		child.Comment,
   172  		strings.Join(child.ContainerConfig.Cmd, " "),
   173  		isEmptyLayer)
   174  
   175  	return &Image{
   176  		V1Image: V1Image{
   177  			DockerVersion:   dockerversion.Version,
   178  			Config:          child.Config,
   179  			Architecture:    img.BaseImgArch(),
   180  			Variant:         img.BaseImgVariant(),
   181  			OS:              os,
   182  			Container:       child.ContainerID,
   183  			ContainerConfig: *child.ContainerConfig,
   184  			Author:          child.Author,
   185  			Created:         imgHistory.Created,
   186  		},
   187  		RootFS:     rootFS,
   188  		History:    append(img.History, imgHistory),
   189  		OSFeatures: img.OSFeatures,
   190  		OSVersion:  img.OSVersion,
   191  	}
   192  }
   193  
   194  // History stores build commands that were used to create an image
   195  type History struct {
   196  	// Created is the timestamp at which the image was created
   197  	Created time.Time `json:"created"`
   198  	// Author is the name of the author that was specified when committing the image
   199  	Author string `json:"author,omitempty"`
   200  	// CreatedBy keeps the Dockerfile command used while building the image
   201  	CreatedBy string `json:"created_by,omitempty"`
   202  	// Comment is the commit message that was set when committing the image
   203  	Comment string `json:"comment,omitempty"`
   204  	// EmptyLayer is set to true if this history item did not generate a
   205  	// layer. Otherwise, the history item is associated with the next
   206  	// layer in the RootFS section.
   207  	EmptyLayer bool `json:"empty_layer,omitempty"`
   208  }
   209  
   210  // NewHistory creates a new history struct from arguments, and sets the created
   211  // time to the current time in UTC
   212  func NewHistory(author, comment, createdBy string, isEmptyLayer bool) History {
   213  	return History{
   214  		Author:     author,
   215  		Created:    time.Now().UTC(),
   216  		CreatedBy:  createdBy,
   217  		Comment:    comment,
   218  		EmptyLayer: isEmptyLayer,
   219  	}
   220  }
   221  
   222  // Equal compares two history structs for equality
   223  func (h History) Equal(i History) bool {
   224  	if !h.Created.Equal(i.Created) {
   225  		return false
   226  	}
   227  	i.Created = h.Created
   228  
   229  	return reflect.DeepEqual(h, i)
   230  }
   231  
   232  // Exporter provides interface for loading and saving images
   233  type Exporter interface {
   234  	Load(io.ReadCloser, io.Writer, bool) error
   235  	// TODO: Load(net.Context, io.ReadCloser, <- chan StatusMessage) error
   236  	Save([]string, io.Writer) error
   237  }
   238  
   239  // NewFromJSON creates an Image configuration from json.
   240  func NewFromJSON(src []byte) (*Image, error) {
   241  	img := &Image{}
   242  
   243  	if err := json.Unmarshal(src, img); err != nil {
   244  		return nil, err
   245  	}
   246  	if img.RootFS == nil {
   247  		return nil, errors.New("invalid image JSON, no RootFS key")
   248  	}
   249  
   250  	img.rawJSON = src
   251  
   252  	return img, nil
   253  }