image/image.go
82a54398
 package image
33f6a0aa
 
 import (
 	"encoding/json"
 	"fmt"
96d1e9bb
 	"github.com/dotcloud/docker/archive"
359b7df5
 	"github.com/dotcloud/docker/daemon/graphdriver"
6393c383
 	"github.com/dotcloud/docker/runconfig"
2e69e172
 	"github.com/dotcloud/docker/utils"
33f6a0aa
 	"io/ioutil"
 	"os"
 	"path"
0badda9f
 	"strconv"
33f6a0aa
 	"time"
 )
 
 type Image struct {
6393c383
 	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"`
a91b7109
 	Size            int64
82a54398
 
 	graph Graph
33f6a0aa
 }
 
 func LoadImage(root string) (*Image, error) {
 	// Load the json data
 	jsonData, err := ioutil.ReadFile(jsonPath(root))
 	if err != nil {
 		return nil, err
 	}
8ff17656
 	img := &Image{}
 
 	if err := json.Unmarshal(jsonData, img); err != nil {
33f6a0aa
 		return nil, err
 	}
82a54398
 	if err := utils.ValidateID(img.ID); err != nil {
33f6a0aa
 		return nil, err
 	}
0badda9f
 
 	if buf, err := ioutil.ReadFile(path.Join(root, "layersize")); err != nil {
 		if !os.IsNotExist(err) {
 			return nil, err
 		}
697707e4
 		// 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
0badda9f
 	} else {
5e941f1c
 		size, err := strconv.Atoi(string(buf))
 		if err != nil {
0badda9f
 			return nil, err
 		}
5e941f1c
 		img.Size = int64(size)
0badda9f
 	}
 
8ff17656
 	return img, nil
33f6a0aa
 }
 
f198ee52
 func StoreImage(img *Image, jsonData []byte, layerData archive.ArchiveReader, root, layer string) error {
33f6a0aa
 	// Store the layer
8fdbf46a
 	var (
 		size   int64
 		err    error
82a54398
 		driver = img.graph.Driver()
8fdbf46a
 	)
1c509f43
 	if err := os.MkdirAll(layer, 0755); err != nil {
33f6a0aa
 		return err
 	}
aaaf3f07
 
290b1973
 	// If layerData is not nil, unpack it into the new layer
 	if layerData != nil {
8fdbf46a
 		if differ, ok := driver.(graphdriver.Differ); ok {
5d972300
 			if err := differ.ApplyDiff(img.ID, layerData); err != nil {
 				return err
 			}
8fdbf46a
 
 			if size, err = differ.DiffSize(img.ID); err != nil {
 				return err
 			}
5d972300
 		} else {
19df6c32
 			start := time.Now().UTC()
5d972300
 			utils.Debugf("Start untar layer")
 			if err := archive.ApplyLayer(layer, layerData); err != nil {
 				return err
 			}
19df6c32
 			utils.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
8fdbf46a
 
 			if img.Parent == "" {
 				if size, err = utils.TreeSize(layer); err != nil {
 					return err
 				}
 			} else {
f0e6e135
 				parent, err := driver.Get(img.Parent, "")
8fdbf46a
 				if err != nil {
 					return err
 				}
bcaf6c23
 				defer driver.Put(img.Parent)
8fdbf46a
 				changes, err := archive.ChangesDirs(layer, parent)
 				if err != nil {
 					return err
 				}
697707e4
 				size = archive.ChangesSize(layer, changes)
8fdbf46a
 			}
290b1973
 		}
33f6a0aa
 	}
a91b7109
 
f2bab155
 	img.Size = size
 	if err := img.SaveSize(root); err != nil {
 		return err
33f6a0aa
 	}
0badda9f
 
697707e4
 	// If raw json is provided, then use it
 	if jsonData != nil {
 		if err := ioutil.WriteFile(jsonPath(root), jsonData, 0600); err != nil {
 			return err
 		}
 	} else {
 		if jsonData, err = json.Marshal(img); err != nil {
 			return err
 		}
 		if err := ioutil.WriteFile(jsonPath(root), jsonData, 0600); err != nil {
 			return err
 		}
 	}
33f6a0aa
 	return nil
 }
 
82a54398
 func (img *Image) SetGraph(graph Graph) {
 	img.graph = graph
 }
 
f2bab155
 // 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
33f6a0aa
 }
 
 func jsonPath(root string) string {
 	return path.Join(root, "json")
 }
 
baacae83
 // TarLayer returns a tar archive of the image's filesystem layer.
bcaf6c23
 func (img *Image) TarLayer() (arch archive.Archive, err error) {
28d4cbbc
 	if img.graph == nil {
 		return nil, fmt.Errorf("Can't load storage driver for unregistered image %s", img.ID)
9e827813
 	}
82a54398
 	driver := img.graph.Driver()
4e0c76b3
 	if differ, ok := driver.(graphdriver.Differ); ok {
 		return differ.Diff(img.ID)
33f6a0aa
 	}
 
f0e6e135
 	imgFs, err := driver.Get(img.ID, "")
c8db980a
 	if err != nil {
 		return nil, err
 	}
bcaf6c23
 
 	defer func() {
d277a4c0
 		if err != nil {
bcaf6c23
 			driver.Put(img.ID)
4e0c76b3
 		}
bcaf6c23
 	}()
 
 	if img.Parent == "" {
 		archive, err := archive.Tar(imgFs, archive.Uncompressed)
4e0c76b3
 		if err != nil {
 			return nil, err
 		}
f198ee52
 		return utils.NewReadCloserWrapper(archive, func() error {
 			err := archive.Close()
 			driver.Put(img.ID)
 			return err
 		}), nil
bcaf6c23
 	}
 
f0e6e135
 	parentFs, err := driver.Get(img.Parent, "")
bcaf6c23
 	if err != nil {
 		return nil, err
 	}
 	defer driver.Put(img.Parent)
 	changes, err := archive.ChangesDirs(imgFs, parentFs)
 	if err != nil {
 		return nil, err
 	}
 	archive, err := archive.ExportChanges(imgFs, changes)
 	if err != nil {
 		return nil, err
4e0c76b3
 	}
f198ee52
 	return utils.NewReadCloserWrapper(archive, func() error {
 		err := archive.Close()
 		driver.Put(img.ID)
 		return err
 	}), nil
c8db980a
 }
 
33f6a0aa
 // 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(
da266e6c
 		func(img *Image) error {
33f6a0aa
 			parents = append(parents, img)
da266e6c
 			return nil
33f6a0aa
 		},
 	); err != nil {
 		return nil, err
 	}
 	return parents, nil
 }
 
da266e6c
 func (img *Image) WalkHistory(handler func(*Image) error) (err error) {
33f6a0aa
 	currentImg := img
 	for currentImg != nil {
 		if handler != nil {
da266e6c
 			if err := handler(currentImg); err != nil {
 				return err
 			}
33f6a0aa
 		}
 		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")
 	}
82a54398
 	return img.graph.ImageRoot(img.ID), nil
33f6a0aa
 }
 
82a54398
 func (img *Image) GetParentsSize(size int64) int64 {
a91b7109
 	parentImage, err := img.GetParent()
 	if err != nil || parentImage == nil {
 		return size
 	}
 	size += parentImage.Size
82a54398
 	return parentImage.GetParentsSize(size)
a91b7109
 }
6fce89e6
 
af753cba
 // 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
 }
 
9bb3dc98
 // Build an Image object from raw json data
fd224ee5
 func NewImgJSON(src []byte) (*Image, error) {
9bb3dc98
 	ret := &Image{}
 
8f39f0b5
 	utils.Debugf("Json string: {%s}", src)
9bb3dc98
 	// 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
 }