docker/image.go

229 строки
5.6 KiB
Go
Исходник Обычный вид История

2013-03-22 04:47:23 +04:00
package docker
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
2013-11-01 03:57:45 +04:00
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/graphdriver"
2013-05-15 02:37:35 +04:00
"github.com/dotcloud/docker/utils"
"io"
"io/ioutil"
"os"
"path"
2013-07-23 02:40:33 +04:00
"strconv"
"strings"
"time"
)
type Image struct {
2013-06-04 22:00:22 +04:00
ID string `json:"id"`
2013-03-21 17:35:57 +04:00
Parent string `json:"parent,omitempty"`
Comment string `json:"comment,omitempty"`
Created time.Time `json:"created"`
Container string `json:"container,omitempty"`
ContainerConfig Config `json:"container_config,omitempty"`
DockerVersion string `json:"docker_version,omitempty"`
Author string `json:"author,omitempty"`
Config *Config `json:"config,omitempty"`
2013-05-24 22:40:56 +04:00
Architecture string `json:"architecture,omitempty"`
graph *Graph
2013-05-13 17:10:26 +04:00
Size int64
}
func LoadImage(root string) (*Image, error) {
// Load the json data
jsonData, err := ioutil.ReadFile(jsonPath(root))
if err != nil {
return nil, err
}
2013-05-08 21:42:29 +04:00
img := &Image{}
if err := json.Unmarshal(jsonData, img); err != nil {
return nil, err
}
2013-06-04 22:00:22 +04:00
if err := ValidateID(img.ID); err != nil {
return nil, err
}
2013-07-23 02:40:33 +04:00
if buf, err := ioutil.ReadFile(path.Join(root, "layersize")); err != nil {
if !os.IsNotExist(err) {
return nil, err
}
} else {
if size, err := strconv.Atoi(string(buf)); err != nil {
return nil, err
} else {
img.Size = int64(size)
}
}
2013-05-08 21:42:29 +04:00
return img, nil
}
func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root, rootfs string) error {
// Store the layer
layer := rootfs
2013-07-25 19:45:15 +04:00
if err := os.MkdirAll(layer, 0755); err != nil {
return err
}
2013-05-09 06:08:11 +04:00
// If layerData is not nil, unpack it into the new layer
if layerData != nil {
if differ, ok := img.graph.driver.(graphdriver.Differ); ok {
if err := differ.ApplyDiff(img.ID, layerData); err != nil {
return err
}
} else {
start := time.Now()
utils.Debugf("Start untar layer")
if err := archive.ApplyLayer(layer, layerData); err != nil {
return err
}
utils.Debugf("Untar time: %vs", time.Now().Sub(start).Seconds())
}
}
2013-05-13 17:10:26 +04:00
2013-07-23 02:40:33 +04:00
// If raw json is provided, then use it
if jsonData != nil {
return ioutil.WriteFile(jsonPath(root), jsonData, 0600)
} else { // Otherwise, unmarshal the image
jsonData, err := json.Marshal(img)
if err != nil {
return err
}
if err := ioutil.WriteFile(jsonPath(root), jsonData, 0600); err != nil {
return err
}
}
// Compute and save the size of the rootfs
size, err := utils.TreeSize(rootfs)
if err != nil {
return fmt.Errorf("Error computing size of rootfs %s: %s", img.ID, err)
}
img.Size = size
if err := img.SaveSize(root); err != nil {
return err
}
2013-07-23 02:40:33 +04:00
return nil
}
// 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
}
func jsonPath(root string) string {
return path.Join(root, "json")
}
// TarLayer returns a tar archive of the image's filesystem layer.
2013-11-01 03:57:45 +04:00
func (image *Image) TarLayer(compression archive.Compression) (archive.Archive, error) {
if image.graph == nil {
return nil, fmt.Errorf("Can't load storage driver for unregistered image %s", image.ID)
}
layerPath, err := image.graph.driver.Get(image.ID)
if err != nil {
return nil, err
}
2013-11-01 03:57:45 +04:00
return archive.Tar(layerPath, compression)
}
2013-06-04 22:00:22 +04:00
func (image *Image) ShortID() string {
return utils.TruncateID(image.ID)
}
2013-06-04 22:00:22 +04:00
func ValidateID(id string) error {
if id == "" {
return fmt.Errorf("Image id can't be empty")
}
if strings.Contains(id, ":") {
return fmt.Errorf("Invalid character in image id: ':'")
}
return nil
}
2013-06-04 22:00:22 +04:00
func GenerateID() string {
id := make([]byte, 32)
_, err := io.ReadFull(rand.Reader, id)
if err != nil {
panic(err) // This shouldn't happen
}
return hex.EncodeToString(id)
}
// 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(
func(img *Image) error {
parents = append(parents, img)
return nil
},
); err != nil {
return nil, err
}
return parents, nil
}
func (img *Image) WalkHistory(handler func(*Image) error) (err error) {
currentImg := img
for currentImg != nil {
if handler != nil {
if err := handler(currentImg); err != nil {
return err
}
}
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")
}
2013-06-04 22:00:22 +04:00
return img.graph.imageRoot(img.ID), nil
}
2013-06-14 14:05:01 +04:00
func (img *Image) getParentsSize(size int64) int64 {
2013-05-13 17:10:26 +04:00
parentImage, err := img.GetParent()
if err != nil || parentImage == nil {
return size
}
size += parentImage.Size
2013-06-14 14:05:01 +04:00
return parentImage.getParentsSize(size)
2013-05-13 17:10:26 +04:00
}
2013-05-22 17:41:29 +04:00
2013-05-15 05:41:39 +04:00
// Build an Image object from raw json data
2013-06-04 22:00:22 +04:00
func NewImgJSON(src []byte) (*Image, error) {
2013-05-15 05:41:39 +04:00
ret := &Image{}
2013-10-26 04:50:40 +04:00
utils.Debugf("Json string: {%s}", src)
2013-05-15 05: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
}