image.go
ef711962
 package docker
33f6a0aa
 
 import (
141b5fc7
 	"crypto/rand"
 	"encoding/hex"
33f6a0aa
 	"encoding/json"
 	"fmt"
0208b6ac
 	"io"
33f6a0aa
 	"io/ioutil"
ac49a797
 	"log"
33f6a0aa
 	"os"
ac49a797
 	"os/exec"
33f6a0aa
 	"path"
 	"strings"
 	"time"
 )
 
 type Image struct {
eef9659c
 	Id              string    `json:"id"`
 	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"`
05ae69a6
 	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
 	}
 	var img Image
 	if err := json.Unmarshal(jsonData, &img); err != nil {
 		return nil, err
 	}
 	if err := ValidateId(img.Id); err != nil {
 		return nil, err
 	}
 	// Check that the filesystem layer exists
 	if stat, err := os.Stat(layerPath(root)); err != nil {
 		if os.IsNotExist(err) {
 			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.Id)
 		} else {
 			return nil, err
 		}
 	} else if !stat.IsDir() {
 		return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.Id, layerPath(root))
 	}
 	return &img, nil
 }
 
 func StoreImage(img *Image, layerData Archive, root string) error {
 	// Check that root doesn't already exist
 	if _, err := os.Stat(root); err == nil {
 		return fmt.Errorf("Image %s already exists", img.Id)
 	} else if !os.IsNotExist(err) {
 		return err
 	}
 	// Store the layer
 	layer := layerPath(root)
 	if err := os.MkdirAll(layer, 0700); err != nil {
 		return err
 	}
 	if err := Untar(layerData, layer); err != nil {
 		return err
 	}
 	// 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")
 }
 
 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
 
 	//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)
 }
 
1632566e
 func (image *Image) ShortId() string {
 	return TruncateId(image.Id)
 }
 
33f6a0aa
 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
 }
 
 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 {
 		return nil, fmt.Errorf("No layer found for image %s\n", img.Id)
 	}
 	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")
 	}
 	return img.graph.imageRoot(img.Id), nil
 }
 
 // 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
 }