image.go
ef711962
 package docker
33f6a0aa
 
 import (
141b5fc7
 	"crypto/rand"
6e2ddf6f
 	"crypto/sha256"
141b5fc7
 	"encoding/hex"
33f6a0aa
 	"encoding/json"
 	"fmt"
2e69e172
 	"github.com/dotcloud/docker/utils"
0208b6ac
 	"io"
33f6a0aa
 	"io/ioutil"
ac49a797
 	"log"
33f6a0aa
 	"os"
ac49a797
 	"os/exec"
33f6a0aa
 	"path"
a91b7109
 	"path/filepath"
33f6a0aa
 	"strings"
 	"time"
 )
 
 type Image struct {
fd224ee5
 	ID              string    `json:"id"`
eef9659c
 	Parent          string    `json:"parent,omitempty"`
 	Comment         string    `json:"comment,omitempty"`
 	Created         time.Time `json:"created"`
0146c80c
 	Container       string    `json:"container,omitempty"`
 	ContainerConfig Config    `json:"container_config,omitempty"`
8bfbdd7a
 	DockerVersion   string    `json:"docker_version,omitempty"`
4ef2d5c1
 	Author          string    `json:"author,omitempty"`
51d62282
 	Config          *Config   `json:"config,omitempty"`
2cf92abf
 	Architecture    string    `json:"architecture,omitempty"`
05ae69a6
 	graph           *Graph
a91b7109
 	Size            int64
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
 	}
fd224ee5
 	if err := ValidateID(img.ID); err != nil {
33f6a0aa
 		return nil, err
 	}
 	// Check that the filesystem layer exists
 	if stat, err := os.Stat(layerPath(root)); err != nil {
 		if os.IsNotExist(err) {
fd224ee5
 			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
33f6a0aa
 		}
86ada2fa
 		return nil, err
33f6a0aa
 	} else if !stat.IsDir() {
fd224ee5
 		return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.ID, layerPath(root))
33f6a0aa
 	}
8ff17656
 	return img, nil
33f6a0aa
 }
 
aaaf3f07
 func StoreImage(img *Image, layerData Archive, root string, store bool) error {
33f6a0aa
 	// Check that root doesn't already exist
 	if _, err := os.Stat(root); err == nil {
fd224ee5
 		return fmt.Errorf("Image %s already exists", img.ID)
33f6a0aa
 	} else if !os.IsNotExist(err) {
 		return err
 	}
 	// Store the layer
 	layer := layerPath(root)
 	if err := os.MkdirAll(layer, 0700); err != nil {
 		return err
 	}
aaaf3f07
 
 	if store {
 		layerArchive := layerArchivePath(root)
 		file, err := os.OpenFile(layerArchive, os.O_WRONLY|os.O_CREATE, 0600)
 		if err != nil {
 			return err
 		}
 		// FIXME: Retrieve the image layer size from here?
 		if _, err := io.Copy(file, layerData); err != nil {
 			return err
 		}
 		// FIXME: Don't close/open, read/write instead of Copy
 		file.Close()
 
 		file, err = os.Open(layerArchive)
 		if err != nil {
 			return err
 		}
 		defer file.Close()
 		layerData = file
 	}
290b1973
 	// If layerData is not nil, unpack it into the new layer
 	if layerData != nil {
 		if err := Untar(layerData, layer); err != nil {
 			return err
 		}
33f6a0aa
 	}
a91b7109
 
 	return StoreSize(img, root)
 }
 
 func StoreSize(img *Image, root string) error {
 	layer := layerPath(root)
 
 	filepath.Walk(layer, func(path string, fileInfo os.FileInfo, err error) error {
 		img.Size += fileInfo.Size()
 		return nil
 	})
 
33f6a0aa
 	// Store the json ball
 	jsonData, err := json.Marshal(img)
 	if err != nil {
 		return err
 	}
 	if err := ioutil.WriteFile(jsonPath(root), jsonData, 0600); err != nil {
 		return err
 	}
 	return nil
 }
 
 func layerPath(root string) string {
 	return path.Join(root, "layer")
 }
 
aaaf3f07
 func layerArchivePath(root string) string {
 	return path.Join(root, "layer.tar.xz")
 }
 
33f6a0aa
 func jsonPath(root string) string {
 	return path.Join(root, "json")
 }
 
 func MountAUFS(ro []string, rw string, target string) error {
 	// FIXME: Now mount the layers
 	rwBranch := fmt.Sprintf("%v=rw", rw)
 	roBranches := ""
 	for _, layer := range ro {
e49af5b6
 		roBranches += fmt.Sprintf("%v=ro+wh:", layer)
33f6a0aa
 	}
 	branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
ac49a797
 
f67ea78c
 	branches += ",xino=/dev/shm/aufs.xino"
 
ac49a797
 	//if error, try to load aufs kernel module
 	if err := mount("none", target, "aufs", 0, branches); err != nil {
 		log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
 		if err := exec.Command("modprobe", "aufs").Run(); err != nil {
 			return fmt.Errorf("Unable to load the AUFS module")
 		}
 		log.Printf("...module loaded.")
 		if err := mount("none", target, "aufs", 0, branches); err != nil {
 			return fmt.Errorf("Unable to mount using aufs")
 		}
 	}
 	return nil
33f6a0aa
 }
 
baacae83
 // TarLayer returns a tar archive of the image's filesystem layer.
 func (image *Image) TarLayer(compression Compression) (Archive, error) {
 	layerPath, err := image.layer()
 	if err != nil {
 		return nil, err
 	}
 	return Tar(layerPath, compression)
 }
 
24033327
 func (image *Image) Mount(root, rw string) error {
75c866d6
 	if mounted, err := Mounted(root); err != nil {
33f6a0aa
 		return err
75c866d6
 	} else if mounted {
24033327
 		return fmt.Errorf("%s is already mounted", root)
33f6a0aa
 	}
 	layers, err := image.layers()
 	if err != nil {
 		return err
 	}
9e827813
 	// Create the target directories if they don't exist
 	if err := os.Mkdir(root, 0755); err != nil && !os.IsExist(err) {
 		return err
 	}
 	if err := os.Mkdir(rw, 0755); err != nil && !os.IsExist(err) {
 		return err
 	}
33f6a0aa
 	if err := MountAUFS(layers, rw, root); err != nil {
 		return err
 	}
 	return nil
 }
 
c8db980a
 func (image *Image) Changes(rw string) ([]Change, error) {
 	layers, err := image.layers()
 	if err != nil {
 		return nil, err
 	}
 	return Changes(layers, rw)
 }
 
fd224ee5
 func (image *Image) ShortID() string {
 	return utils.TruncateID(image.ID)
1632566e
 }
 
fd224ee5
 func ValidateID(id string) error {
33f6a0aa
 	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
 }
 
fd224ee5
 func GenerateID() string {
141b5fc7
 	id := make([]byte, 32)
 	_, err := io.ReadFull(rand.Reader, id)
 	if err != nil {
 		panic(err) // This shouldn't happen
0208b6ac
 	}
141b5fc7
 	return hex.EncodeToString(id)
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
 }
 
 // layers returns all the filesystem layers needed to mount an image
da266e6c
 // FIXME: @shykes refactor this function with the new error handling
 //        (I'll do it if I have time tonight, I focus on the rest)
33f6a0aa
 func (img *Image) layers() ([]string, error) {
 	var list []string
 	var e error
 	if err := img.WalkHistory(
da266e6c
 		func(img *Image) (err error) {
33f6a0aa
 			if layer, err := img.layer(); err != nil {
 				e = err
 			} else if layer != "" {
 				list = append(list, layer)
 			}
da266e6c
 			return err
33f6a0aa
 		},
 	); err != nil {
 		return nil, err
 	} else if e != nil { // Did an error occur inside the handler?
 		return nil, e
 	}
 	if len(list) == 0 {
fd224ee5
 		return nil, fmt.Errorf("No layer found for image %s\n", img.ID)
33f6a0aa
 	}
 	return list, 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")
 	}
fd224ee5
 	return img.graph.imageRoot(img.ID), nil
33f6a0aa
 }
 
 // Return the path of an image's layer
 func (img *Image) layer() (string, error) {
 	root, err := img.root()
 	if err != nil {
 		return "", err
 	}
 	return layerPath(root), nil
 }
be756089
 
6e2ddf6f
 func (img *Image) Checksum() (string, error) {
fd224ee5
 	img.graph.checksumLock[img.ID].Lock()
 	defer img.graph.checksumLock[img.ID].Unlock()
8ff17656
 
6e2ddf6f
 	root, err := img.root()
 	if err != nil {
 		return "", err
 	}
 
44b33b44
 	checksums, err := img.graph.getStoredChecksums()
 	if err != nil {
 		return "", err
 	}
fd224ee5
 	if checksum, ok := checksums[img.ID]; ok {
44b33b44
 		return checksum, nil
6e2ddf6f
 	}
 
be756089
 	layer, err := img.layer()
 	if err != nil {
6e2ddf6f
 		return "", err
be756089
 	}
f10b0f75
 	jsonData, err := ioutil.ReadFile(jsonPath(root))
 	if err != nil {
 		return "", err
 	}
 
aaaf3f07
 	var layerData io.Reader
 
 	if file, err := os.Open(layerArchivePath(root)); err != nil {
 		if os.IsNotExist(err) {
 			layerData, err = Tar(layer, Xz)
 			if err != nil {
 				return "", err
 			}
 		} else {
 			return "", err
 		}
 	} else {
 		defer file.Close()
 		layerData = file
be756089
 	}
f10b0f75
 
6e2ddf6f
 	h := sha256.New()
be791a22
 	if _, err := h.Write(jsonData); err != nil {
6e2ddf6f
 		return "", err
be756089
 	}
be791a22
 	if _, err := h.Write([]byte("\n")); err != nil {
6e2ddf6f
 		return "", err
be756089
 	}
8ff17656
 
f10b0f75
 	if _, err := io.Copy(h, layerData); err != nil {
6e2ddf6f
 		return "", err
 	}
d985050a
 	hash := "sha256:" + hex.EncodeToString(h.Sum(nil))
c4ebf870
 
8ff17656
 	// Reload the json file to make sure not to overwrite faster sums
 	img.graph.lockSumFile.Lock()
 	defer img.graph.lockSumFile.Unlock()
44b33b44
 
 	checksums, err = img.graph.getStoredChecksums()
be756089
 	if err != nil {
44b33b44
 		return "", err
be756089
 	}
44b33b44
 
fd224ee5
 	checksums[img.ID] = hash
44b33b44
 
 	// Dump the checksums to disc
 	if err := img.graph.storeChecksums(checksums); err != nil {
6e2ddf6f
 		return hash, err
be756089
 	}
44b33b44
 
6e2ddf6f
 	return hash, nil
be756089
 }
a91b7109
 
00cf2a1f
 func (img *Image) getParentsSize(size int64) int64 {
a91b7109
 	parentImage, err := img.GetParent()
 	if err != nil || parentImage == nil {
 		return size
 	}
 	size += parentImage.Size
00cf2a1f
 	return parentImage.getParentsSize(size)
a91b7109
 }
6fce89e6
 
9bb3dc98
 // Build an Image object from raw json data
fd224ee5
 func NewImgJSON(src []byte) (*Image, error) {
9bb3dc98
 	ret := &Image{}
 
 	utils.Debugf("Json string: {%s}\n", src)
 	// 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
 }