2014-03-07 20:36:47 -05:00
|
|
|
package image
|
2013-03-18 03:15:35 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path"
|
2013-07-22 18:40:33 -04:00
|
|
|
"strconv"
|
2013-03-18 03:15:35 -04:00
|
|
|
"time"
|
2014-07-24 16:37:44 -04:00
|
|
|
|
2014-10-24 13:12:35 -04:00
|
|
|
log "github.com/Sirupsen/logrus"
|
2014-10-24 18:11:48 -04:00
|
|
|
"github.com/docker/docker/pkg/archive"
|
2014-11-04 18:23:21 -05:00
|
|
|
"github.com/docker/docker/pkg/tarsum"
|
2014-07-24 16:37:44 -04:00
|
|
|
"github.com/docker/docker/runconfig"
|
|
|
|
"github.com/docker/docker/utils"
|
2013-03-18 03:15:35 -04:00
|
|
|
)
|
|
|
|
|
2014-08-14 20:43:12 -04:00
|
|
|
// Set the max depth to the aufs default that most
|
|
|
|
// kernels are compiled with
|
|
|
|
// For more information see: http://sourceforge.net/p/aufs/aufs3-standalone/ci/aufs3.12/tree/config.mk
|
|
|
|
const MaxImageDepth = 127
|
|
|
|
|
2013-03-18 03:15:35 -04:00
|
|
|
type Image struct {
|
2014-02-11 23:04:39 -05:00
|
|
|
ID string `json:"id"`
|
|
|
|
Parent string `json:"parent,omitempty"`
|
|
|
|
Comment string `json:"comment,omitempty"`
|
|
|
|
Created time.Time `json:"created"`
|
|
|
|
Container string `json:"container,omitempty"`
|
|
|
|
ContainerConfig runconfig.Config `json:"container_config,omitempty"`
|
|
|
|
DockerVersion string `json:"docker_version,omitempty"`
|
|
|
|
Author string `json:"author,omitempty"`
|
|
|
|
Config *runconfig.Config `json:"config,omitempty"`
|
|
|
|
Architecture string `json:"architecture,omitempty"`
|
|
|
|
OS string `json:"os,omitempty"`
|
2014-11-04 18:23:21 -05:00
|
|
|
Checksum string `json:"checksum"`
|
2013-05-13 09:10:26 -04:00
|
|
|
Size int64
|
2014-03-07 20:36:47 -05:00
|
|
|
|
|
|
|
graph Graph
|
2013-03-18 03:15:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func LoadImage(root string) (*Image, error) {
|
2014-11-04 08:45:46 -05:00
|
|
|
// Open the JSON file to decode by streaming
|
|
|
|
jsonSource, err := os.Open(jsonPath(root))
|
2013-03-18 03:15:35 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-11-04 08:45:46 -05:00
|
|
|
defer jsonSource.Close()
|
|
|
|
|
2013-05-08 13:42:29 -04:00
|
|
|
img := &Image{}
|
2014-11-04 08:45:46 -05:00
|
|
|
dec := json.NewDecoder(jsonSource)
|
2013-05-08 13:42:29 -04:00
|
|
|
|
2014-11-04 08:45:46 -05:00
|
|
|
// Decode the JSON data
|
|
|
|
if err := dec.Decode(img); err != nil {
|
2013-03-18 03:15:35 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-03-07 20:36:47 -05:00
|
|
|
if err := utils.ValidateID(img.ID); err != nil {
|
2013-03-18 03:15:35 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
2013-07-22 18:40:33 -04:00
|
|
|
|
|
|
|
if buf, err := ioutil.ReadFile(path.Join(root, "layersize")); err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return nil, err
|
|
|
|
}
|
2013-12-05 17:03:23 -05:00
|
|
|
// If the layersize file does not exist then set the size to a negative number
|
|
|
|
// because a layer size of 0 (zero) is valid
|
|
|
|
img.Size = -1
|
2013-07-22 18:40:33 -04:00
|
|
|
} else {
|
2014-11-10 12:14:12 -05:00
|
|
|
// Using Atoi here instead would temporarily convert the size to a machine
|
|
|
|
// dependent integer type, which causes images larger than 2^31 bytes to
|
|
|
|
// display negative sizes on 32-bit machines:
|
|
|
|
size, err := strconv.ParseInt(string(buf), 10, 64)
|
2013-11-18 18:35:56 -05:00
|
|
|
if err != nil {
|
2013-07-22 18:40:33 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
2013-11-18 18:35:56 -05:00
|
|
|
img.Size = int64(size)
|
2013-07-22 18:40:33 -04:00
|
|
|
}
|
|
|
|
|
2013-05-08 13:42:29 -04:00
|
|
|
return img, nil
|
2013-03-18 03:15:35 -04:00
|
|
|
}
|
|
|
|
|
2014-11-04 18:23:21 -05:00
|
|
|
// StoreImage stores file system layer data for the given image to the
|
|
|
|
// image's registered storage driver. Image metadata is stored in a file
|
|
|
|
// at the specified root directory. This function also computes the TarSum
|
|
|
|
// of `layerData` (currently using tarsum.dev).
|
2014-10-27 14:00:29 -04:00
|
|
|
func StoreImage(img *Image, layerData archive.ArchiveReader, root string) error {
|
2013-03-18 03:15:35 -04:00
|
|
|
// Store the layer
|
2013-11-20 17:51:04 -05:00
|
|
|
var (
|
2014-11-04 18:23:21 -05:00
|
|
|
size int64
|
|
|
|
err error
|
|
|
|
driver = img.graph.Driver()
|
|
|
|
layerTarSum tarsum.TarSum
|
2013-11-20 17:51:04 -05:00
|
|
|
)
|
2013-05-08 22:08:11 -04:00
|
|
|
|
2013-06-22 15:29:42 -04:00
|
|
|
// If layerData is not nil, unpack it into the new layer
|
|
|
|
if layerData != nil {
|
2014-11-04 18:23:21 -05:00
|
|
|
layerDataDecompressed, err := archive.DecompressStream(layerData)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer layerDataDecompressed.Close()
|
|
|
|
|
|
|
|
if layerTarSum, err = tarsum.NewTarSum(layerDataDecompressed, true, tarsum.VersionDev); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if size, err = driver.ApplyDiff(img.ID, img.Parent, layerTarSum); err != nil {
|
2014-09-10 23:30:52 -04:00
|
|
|
return err
|
2013-06-22 15:29:42 -04:00
|
|
|
}
|
2014-11-04 18:23:21 -05:00
|
|
|
|
|
|
|
checksum := layerTarSum.Sum(nil)
|
|
|
|
|
|
|
|
if img.Checksum != "" && img.Checksum != checksum {
|
|
|
|
log.Warn("image layer checksum mismatch: computed %q, expected %q", checksum, img.Checksum)
|
|
|
|
}
|
|
|
|
|
|
|
|
img.Checksum = checksum
|
2013-03-18 03:15:35 -04:00
|
|
|
}
|
2013-05-13 09:10:26 -04:00
|
|
|
|
2013-11-07 15:34:01 -05:00
|
|
|
img.Size = size
|
|
|
|
if err := img.SaveSize(root); err != nil {
|
|
|
|
return err
|
2013-03-18 03:15:35 -04:00
|
|
|
}
|
2013-07-22 18:40:33 -04:00
|
|
|
|
2014-10-27 14:00:29 -04:00
|
|
|
f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2013-12-05 17:03:23 -05:00
|
|
|
}
|
2014-10-27 14:00:29 -04:00
|
|
|
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
return json.NewEncoder(f).Encode(img)
|
2013-03-18 03:15:35 -04:00
|
|
|
}
|
|
|
|
|
2014-03-07 20:36:47 -05:00
|
|
|
func (img *Image) SetGraph(graph Graph) {
|
|
|
|
img.graph = graph
|
|
|
|
}
|
|
|
|
|
2013-11-07 15:34:01 -05:00
|
|
|
// SaveSize stores the current `size` value of `img` in the directory `root`.
|
|
|
|
func (img *Image) SaveSize(root string) error {
|
|
|
|
if err := ioutil.WriteFile(path.Join(root, "layersize"), []byte(strconv.Itoa(int(img.Size))), 0600); err != nil {
|
|
|
|
return fmt.Errorf("Error storing image size in %s/layersize: %s", root, err)
|
|
|
|
}
|
|
|
|
return nil
|
2013-03-18 03:15:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func jsonPath(root string) string {
|
|
|
|
return path.Join(root, "json")
|
|
|
|
}
|
|
|
|
|
2014-06-17 09:05:29 -04:00
|
|
|
func (img *Image) RawJson() ([]byte, error) {
|
|
|
|
root, err := img.root()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed to get root for image %s: %s", img.ID, err)
|
|
|
|
}
|
|
|
|
fh, err := os.Open(jsonPath(root))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed to open json for image %s: %s", img.ID, err)
|
|
|
|
}
|
|
|
|
buf, err := ioutil.ReadAll(fh)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed to read json for image %s: %s", img.ID, err)
|
|
|
|
}
|
|
|
|
return buf, nil
|
|
|
|
}
|
|
|
|
|
2013-04-21 17:23:55 -04:00
|
|
|
// TarLayer returns a tar archive of the image's filesystem layer.
|
2013-12-05 16:18:02 -05:00
|
|
|
func (img *Image) TarLayer() (arch archive.Archive, err error) {
|
2013-11-19 17:57:53 -05:00
|
|
|
if img.graph == nil {
|
|
|
|
return nil, fmt.Errorf("Can't load storage driver for unregistered image %s", img.ID)
|
2013-03-21 01:13:28 -04:00
|
|
|
}
|
2013-12-05 16:18:02 -05:00
|
|
|
|
2014-09-10 23:30:52 -04:00
|
|
|
driver := img.graph.Driver()
|
2013-12-05 16:18:02 -05:00
|
|
|
|
2014-09-10 23:30:52 -04:00
|
|
|
return driver.Diff(img.ID, img.Parent)
|
2013-03-21 01:12:38 -04:00
|
|
|
}
|
|
|
|
|
2013-03-18 03:15:35 -04:00
|
|
|
// Image includes convenience proxy functions to its graph
|
|
|
|
// These functions will return an error if the image is not registered
|
|
|
|
// (ie. if image.graph == nil)
|
|
|
|
func (img *Image) History() ([]*Image, error) {
|
|
|
|
var parents []*Image
|
|
|
|
if err := img.WalkHistory(
|
2013-03-21 13:10:14 -04:00
|
|
|
func(img *Image) error {
|
2013-03-18 03:15:35 -04:00
|
|
|
parents = append(parents, img)
|
2013-03-21 13:10:14 -04:00
|
|
|
return nil
|
2013-03-18 03:15:35 -04:00
|
|
|
},
|
|
|
|
); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return parents, nil
|
|
|
|
}
|
|
|
|
|
2013-03-21 13:10:14 -04:00
|
|
|
func (img *Image) WalkHistory(handler func(*Image) error) (err error) {
|
2013-03-18 03:15:35 -04:00
|
|
|
currentImg := img
|
|
|
|
for currentImg != nil {
|
|
|
|
if handler != nil {
|
2013-03-21 13:10:14 -04:00
|
|
|
if err := handler(currentImg); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-03-18 03:15:35 -04:00
|
|
|
}
|
|
|
|
currentImg, err = currentImg.GetParent()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error while getting parent image: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (img *Image) GetParent() (*Image, error) {
|
|
|
|
if img.Parent == "" {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
if img.graph == nil {
|
|
|
|
return nil, fmt.Errorf("Can't lookup parent of unregistered image")
|
|
|
|
}
|
|
|
|
return img.graph.Get(img.Parent)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (img *Image) root() (string, error) {
|
|
|
|
if img.graph == nil {
|
|
|
|
return "", fmt.Errorf("Can't lookup root of unregistered image")
|
|
|
|
}
|
2014-03-07 20:36:47 -05:00
|
|
|
return img.graph.ImageRoot(img.ID), nil
|
2013-03-18 03:15:35 -04:00
|
|
|
}
|
|
|
|
|
2014-03-07 20:36:47 -05:00
|
|
|
func (img *Image) GetParentsSize(size int64) int64 {
|
2013-05-13 09:10:26 -04:00
|
|
|
parentImage, err := img.GetParent()
|
|
|
|
if err != nil || parentImage == nil {
|
|
|
|
return size
|
|
|
|
}
|
|
|
|
size += parentImage.Size
|
2014-03-07 20:36:47 -05:00
|
|
|
return parentImage.GetParentsSize(size)
|
2013-05-13 09:10:26 -04:00
|
|
|
}
|
2013-05-22 09:41:29 -04:00
|
|
|
|
2013-11-19 03:51:16 -05:00
|
|
|
// Depth returns the number of parents for a
|
|
|
|
// current image
|
|
|
|
func (img *Image) Depth() (int, error) {
|
|
|
|
var (
|
|
|
|
count = 0
|
|
|
|
parent = img
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
for parent != nil {
|
|
|
|
count++
|
|
|
|
parent, err = parent.GetParent()
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count, nil
|
|
|
|
}
|
|
|
|
|
2014-08-14 20:43:12 -04:00
|
|
|
// CheckDepth returns an error if the depth of an image, as returned
|
|
|
|
// by ImageDepth, is too large to support creating a container from it
|
|
|
|
// on this daemon.
|
|
|
|
func (img *Image) CheckDepth() error {
|
|
|
|
// We add 2 layers to the depth because the container's rw and
|
|
|
|
// init layer add to the restriction
|
|
|
|
depth, err := img.Depth()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if depth+2 >= MaxImageDepth {
|
|
|
|
return fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2013-05-14 21:41:39 -04:00
|
|
|
// Build an Image object from raw json data
|
2013-06-04 14:00:22 -04:00
|
|
|
func NewImgJSON(src []byte) (*Image, error) {
|
2013-05-14 21:41:39 -04:00
|
|
|
ret := &Image{}
|
|
|
|
|
2014-07-24 16:37:44 -04:00
|
|
|
log.Debugf("Json string: {%s}", src)
|
2013-05-14 21:41:39 -04:00
|
|
|
// FIXME: Is there a cleaner way to "purify" the input json?
|
|
|
|
if err := json.Unmarshal(src, ret); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return ret, nil
|
|
|
|
}
|