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
} |