github.com/tompao/docker@v1.9.1/image/image.go (about) 1 package image 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "regexp" 7 "time" 8 9 "github.com/Sirupsen/logrus" 10 "github.com/docker/distribution/digest" 11 derr "github.com/docker/docker/errors" 12 "github.com/docker/docker/pkg/version" 13 "github.com/docker/docker/runconfig" 14 ) 15 16 var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`) 17 18 // noFallbackMinVersion is the minimum version for which v1compatibility 19 // information will not be marshaled through the Image struct to remove 20 // blank fields. 21 var noFallbackMinVersion = version.Version("1.8.3") 22 23 // Descriptor provides the information necessary to register an image in 24 // the graph. 25 type Descriptor interface { 26 ID() string 27 Parent() string 28 MarshalConfig() ([]byte, error) 29 } 30 31 // Image stores the image configuration. 32 // All fields in this struct must be marked `omitempty` to keep getting 33 // predictable hashes from the old `v1Compatibility` configuration. 34 type Image struct { 35 // ID a unique 64 character identifier of the image 36 ID string `json:"id,omitempty"` 37 // Parent id of the image 38 Parent string `json:"parent,omitempty"` 39 // Comment user added comment 40 Comment string `json:"comment,omitempty"` 41 // Created timestamp when image was created 42 Created time.Time `json:"created"` 43 // Container is the id of the container used to commit 44 Container string `json:"container,omitempty"` 45 // ContainerConfig is the configuration of the container that is committed into the image 46 ContainerConfig runconfig.Config `json:"container_config,omitempty"` 47 // DockerVersion specifies version on which image is built 48 DockerVersion string `json:"docker_version,omitempty"` 49 // Author of the image 50 Author string `json:"author,omitempty"` 51 // Config is the configuration of the container received from the client 52 Config *runconfig.Config `json:"config,omitempty"` 53 // Architecture is the hardware that the image is build and runs on 54 Architecture string `json:"architecture,omitempty"` 55 // OS is the operating system used to build and run the image 56 OS string `json:"os,omitempty"` 57 // Size is the total size of the image including all layers it is composed of 58 Size int64 `json:",omitempty"` // capitalized for backwards compatibility 59 // ParentID specifies the strong, content address of the parent configuration. 60 ParentID digest.Digest `json:"parent_id,omitempty"` 61 // LayerID provides the content address of the associated layer. 62 LayerID digest.Digest `json:"layer_id,omitempty"` 63 } 64 65 // NewImgJSON creates an Image configuration from json. 66 func NewImgJSON(src []byte) (*Image, error) { 67 ret := &Image{} 68 69 // FIXME: Is there a cleaner way to "purify" the input json? 70 if err := json.Unmarshal(src, ret); err != nil { 71 return nil, err 72 } 73 return ret, nil 74 } 75 76 // ValidateID checks whether an ID string is a valid image ID. 77 func ValidateID(id string) error { 78 if ok := validHex.MatchString(id); !ok { 79 return derr.ErrorCodeInvalidImageID.WithArgs(id) 80 } 81 return nil 82 } 83 84 // MakeImageConfig returns immutable configuration JSON for image based on the 85 // v1Compatibility object, layer digest and parent StrongID. SHA256() of this 86 // config is the new image ID (strongID). 87 func MakeImageConfig(v1Compatibility []byte, layerID, parentID digest.Digest) ([]byte, error) { 88 89 // Detect images created after 1.8.3 90 img, err := NewImgJSON(v1Compatibility) 91 if err != nil { 92 return nil, err 93 } 94 useFallback := version.Version(img.DockerVersion).LessThan(noFallbackMinVersion) 95 96 if useFallback { 97 // Fallback for pre-1.8.3. Calculate base config based on Image struct 98 // so that fields with default values added by Docker will use same ID 99 logrus.Debugf("Using fallback hash for %v", layerID) 100 101 v1Compatibility, err = json.Marshal(img) 102 if err != nil { 103 return nil, err 104 } 105 } 106 107 var c map[string]*json.RawMessage 108 if err := json.Unmarshal(v1Compatibility, &c); err != nil { 109 return nil, err 110 } 111 112 if err := layerID.Validate(); err != nil { 113 return nil, fmt.Errorf("invalid layerID: %v", err) 114 } 115 116 c["layer_id"] = rawJSON(layerID) 117 118 if parentID != "" { 119 if err := parentID.Validate(); err != nil { 120 return nil, fmt.Errorf("invalid parentID %v", err) 121 } 122 c["parent_id"] = rawJSON(parentID) 123 } 124 125 delete(c, "id") 126 delete(c, "parent") 127 delete(c, "Size") // Size is calculated from data on disk and is inconsitent 128 129 return json.Marshal(c) 130 } 131 132 // StrongID returns image ID for the config JSON. 133 func StrongID(configJSON []byte) (digest.Digest, error) { 134 digester := digest.Canonical.New() 135 if _, err := digester.Hash().Write(configJSON); err != nil { 136 return "", err 137 } 138 dgst := digester.Digest() 139 logrus.Debugf("H(%v) = %v", string(configJSON), dgst) 140 return dgst, nil 141 } 142 143 func rawJSON(value interface{}) *json.RawMessage { 144 jsonval, err := json.Marshal(value) 145 if err != nil { 146 return nil 147 } 148 return (*json.RawMessage)(&jsonval) 149 }