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
}
|
33f6a0aa |
if err := Untar(layerData, layer); err != nil {
return err
} |
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
} |