github.com/jandre/docker@v1.7.0/image/image.go (about) 1 package image 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "regexp" 10 "strconv" 11 "time" 12 13 "github.com/docker/docker/pkg/archive" 14 "github.com/docker/docker/runconfig" 15 ) 16 17 // Set the max depth to the aufs default that most 18 // kernels are compiled with 19 // For more information see: http://sourceforge.net/p/aufs/aufs3-standalone/ci/aufs3.12/tree/config.mk 20 const MaxImageDepth = 127 21 22 var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`) 23 24 type Image struct { 25 ID string `json:"id"` 26 Parent string `json:"parent,omitempty"` 27 Comment string `json:"comment,omitempty"` 28 Created time.Time `json:"created"` 29 Container string `json:"container,omitempty"` 30 ContainerConfig runconfig.Config `json:"container_config,omitempty"` 31 DockerVersion string `json:"docker_version,omitempty"` 32 Author string `json:"author,omitempty"` 33 Config *runconfig.Config `json:"config,omitempty"` 34 Architecture string `json:"architecture,omitempty"` 35 OS string `json:"os,omitempty"` 36 Size int64 37 38 graph Graph 39 } 40 41 func LoadImage(root string) (*Image, error) { 42 // Open the JSON file to decode by streaming 43 jsonSource, err := os.Open(jsonPath(root)) 44 if err != nil { 45 return nil, err 46 } 47 defer jsonSource.Close() 48 49 img := &Image{} 50 dec := json.NewDecoder(jsonSource) 51 52 // Decode the JSON data 53 if err := dec.Decode(img); err != nil { 54 return nil, err 55 } 56 if err := ValidateID(img.ID); err != nil { 57 return nil, err 58 } 59 60 if buf, err := ioutil.ReadFile(filepath.Join(root, "layersize")); err != nil { 61 if !os.IsNotExist(err) { 62 return nil, err 63 } 64 // If the layersize file does not exist then set the size to a negative number 65 // because a layer size of 0 (zero) is valid 66 img.Size = -1 67 } else { 68 // Using Atoi here instead would temporarily convert the size to a machine 69 // dependent integer type, which causes images larger than 2^31 bytes to 70 // display negative sizes on 32-bit machines: 71 size, err := strconv.ParseInt(string(buf), 10, 64) 72 if err != nil { 73 return nil, err 74 } 75 img.Size = int64(size) 76 } 77 78 return img, nil 79 } 80 81 // StoreImage stores file system layer data for the given image to the 82 // image's registered storage driver. Image metadata is stored in a file 83 // at the specified root directory. 84 func StoreImage(img *Image, layerData archive.ArchiveReader, root string) (err error) { 85 // Store the layer. If layerData is not nil, unpack it into the new layer 86 if layerData != nil { 87 if img.Size, err = img.graph.Driver().ApplyDiff(img.ID, img.Parent, layerData); err != nil { 88 return err 89 } 90 } 91 92 if err := img.SaveSize(root); err != nil { 93 return err 94 } 95 96 f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600)) 97 if err != nil { 98 return err 99 } 100 101 defer f.Close() 102 103 return json.NewEncoder(f).Encode(img) 104 } 105 106 func (img *Image) SetGraph(graph Graph) { 107 img.graph = graph 108 } 109 110 // SaveSize stores the current `size` value of `img` in the directory `root`. 111 func (img *Image) SaveSize(root string) error { 112 if err := ioutil.WriteFile(filepath.Join(root, "layersize"), []byte(strconv.Itoa(int(img.Size))), 0600); err != nil { 113 return fmt.Errorf("Error storing image size in %s/layersize: %s", root, err) 114 } 115 return nil 116 } 117 118 func (img *Image) SaveCheckSum(root, checksum string) error { 119 if err := ioutil.WriteFile(filepath.Join(root, "checksum"), []byte(checksum), 0600); err != nil { 120 return fmt.Errorf("Error storing checksum in %s/checksum: %s", root, err) 121 } 122 return nil 123 } 124 125 func (img *Image) GetCheckSum(root string) (string, error) { 126 cs, err := ioutil.ReadFile(filepath.Join(root, "checksum")) 127 if err != nil { 128 if os.IsNotExist(err) { 129 return "", nil 130 } 131 return "", err 132 } 133 return string(cs), err 134 } 135 136 func jsonPath(root string) string { 137 return filepath.Join(root, "json") 138 } 139 140 func (img *Image) RawJson() ([]byte, error) { 141 root, err := img.root() 142 if err != nil { 143 return nil, fmt.Errorf("Failed to get root for image %s: %s", img.ID, err) 144 } 145 146 buf, err := ioutil.ReadFile(jsonPath(root)) 147 if err != nil { 148 return nil, fmt.Errorf("Failed to read json for image %s: %s", img.ID, err) 149 } 150 151 return buf, nil 152 } 153 154 // TarLayer returns a tar archive of the image's filesystem layer. 155 func (img *Image) TarLayer() (arch archive.Archive, err error) { 156 if img.graph == nil { 157 return nil, fmt.Errorf("Can't load storage driver for unregistered image %s", img.ID) 158 } 159 160 driver := img.graph.Driver() 161 162 return driver.Diff(img.ID, img.Parent) 163 } 164 165 // Image includes convenience proxy functions to its graph 166 // These functions will return an error if the image is not registered 167 // (ie. if image.graph == nil) 168 func (img *Image) History() ([]*Image, error) { 169 var parents []*Image 170 if err := img.WalkHistory( 171 func(img *Image) error { 172 parents = append(parents, img) 173 return nil 174 }, 175 ); err != nil { 176 return nil, err 177 } 178 return parents, nil 179 } 180 181 func (img *Image) WalkHistory(handler func(*Image) error) (err error) { 182 currentImg := img 183 for currentImg != nil { 184 if handler != nil { 185 if err := handler(currentImg); err != nil { 186 return err 187 } 188 } 189 currentImg, err = currentImg.GetParent() 190 if err != nil { 191 return fmt.Errorf("Error while getting parent image: %v", err) 192 } 193 } 194 return nil 195 } 196 197 func (img *Image) GetParent() (*Image, error) { 198 if img.Parent == "" { 199 return nil, nil 200 } 201 if img.graph == nil { 202 return nil, fmt.Errorf("Can't lookup parent of unregistered image") 203 } 204 return img.graph.Get(img.Parent) 205 } 206 207 func (img *Image) root() (string, error) { 208 if img.graph == nil { 209 return "", fmt.Errorf("Can't lookup root of unregistered image") 210 } 211 return img.graph.ImageRoot(img.ID), nil 212 } 213 214 func (img *Image) GetParentsSize(size int64) int64 { 215 parentImage, err := img.GetParent() 216 if err != nil || parentImage == nil { 217 return size 218 } 219 size += parentImage.Size 220 return parentImage.GetParentsSize(size) 221 } 222 223 // Depth returns the number of parents for a 224 // current image 225 func (img *Image) Depth() (int, error) { 226 var ( 227 count = 0 228 parent = img 229 err error 230 ) 231 232 for parent != nil { 233 count++ 234 parent, err = parent.GetParent() 235 if err != nil { 236 return -1, err 237 } 238 } 239 return count, nil 240 } 241 242 // CheckDepth returns an error if the depth of an image, as returned 243 // by ImageDepth, is too large to support creating a container from it 244 // on this daemon. 245 func (img *Image) CheckDepth() error { 246 // We add 2 layers to the depth because the container's rw and 247 // init layer add to the restriction 248 depth, err := img.Depth() 249 if err != nil { 250 return err 251 } 252 if depth+2 >= MaxImageDepth { 253 return fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth) 254 } 255 return nil 256 } 257 258 // Build an Image object from raw json data 259 func NewImgJSON(src []byte) (*Image, error) { 260 ret := &Image{} 261 262 // FIXME: Is there a cleaner way to "purify" the input json? 263 if err := json.Unmarshal(src, ret); err != nil { 264 return nil, err 265 } 266 return ret, nil 267 } 268 269 // Check wheather id is a valid image ID or not 270 func ValidateID(id string) error { 271 if ok := validHex.MatchString(id); !ok { 272 return fmt.Errorf("image ID '%s' is invalid", id) 273 } 274 return nil 275 }