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