зеркало из https://github.com/microsoft/docker.git
Separate graph from image
Move graph related functions in image to graph package. Consolidating graph functionality is the first step in refactoring graph into an image store model. Subsequent refactors will involve breaking up graph into multiple types with a strongly defined interface. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
Родитель
f20863929b
Коммит
2b58b677a5
|
@ -59,7 +59,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err = img.CheckDepth(); err != nil {
|
||||
if err = daemon.graph.CheckDepth(img); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
imgID = img.ID
|
||||
|
|
|
@ -160,7 +160,7 @@ func (daemon *Daemon) canDeleteImage(imgID string, force bool) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := parent.WalkHistory(func(p *image.Image) error {
|
||||
if err := daemon.graph.WalkHistory(parent, func(p *image.Image) error {
|
||||
if imgID == p.ID {
|
||||
if container.IsRunning() {
|
||||
if force {
|
||||
|
|
170
graph/graph.go
170
graph/graph.go
|
@ -3,12 +3,14 @@ package graph
|
|||
import (
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
@ -29,7 +31,7 @@ import (
|
|||
|
||||
// A Graph is a store for versioned filesystem images and the relationship between them.
|
||||
type Graph struct {
|
||||
Root string
|
||||
root string
|
||||
idIndex *truncindex.TruncIndex
|
||||
driver graphdriver.Driver
|
||||
}
|
||||
|
@ -47,7 +49,7 @@ func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) {
|
|||
}
|
||||
|
||||
graph := &Graph{
|
||||
Root: abspath,
|
||||
root: abspath,
|
||||
idIndex: truncindex.NewTruncIndex([]string{}),
|
||||
driver: driver,
|
||||
}
|
||||
|
@ -58,7 +60,7 @@ func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) {
|
|||
}
|
||||
|
||||
func (graph *Graph) restore() error {
|
||||
dir, err := ioutil.ReadDir(graph.Root)
|
||||
dir, err := ioutil.ReadDir(graph.root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -95,14 +97,13 @@ func (graph *Graph) Get(name string) (*image.Image, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find image: %v", err)
|
||||
}
|
||||
img, err := image.LoadImage(graph.ImageRoot(id))
|
||||
img, err := graph.loadImage(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if img.ID != id {
|
||||
return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
|
||||
}
|
||||
img.SetGraph(graph)
|
||||
|
||||
if img.Size < 0 {
|
||||
size, err := graph.driver.DiffSize(img.ID, img.Parent)
|
||||
|
@ -111,7 +112,7 @@ func (graph *Graph) Get(name string) (*image.Image, error) {
|
|||
}
|
||||
|
||||
img.Size = size
|
||||
if err := img.SaveSize(graph.ImageRoot(id)); err != nil {
|
||||
if err := graph.saveSize(graph.imageRoot(id), int(img.Size)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +165,7 @@ func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader)
|
|||
// Ensure that the image root does not exist on the filesystem
|
||||
// when it is not registered in the graph.
|
||||
// This is common when you switch from one graph driver to another
|
||||
if err := os.RemoveAll(graph.ImageRoot(img.ID)); err != nil && !os.IsNotExist(err) {
|
||||
if err := os.RemoveAll(graph.imageRoot(img.ID)); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -174,10 +175,10 @@ func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader)
|
|||
// (FIXME: make that mandatory for drivers).
|
||||
graph.driver.Remove(img.ID)
|
||||
|
||||
tmp, err := graph.Mktemp("")
|
||||
tmp, err := graph.mktemp("")
|
||||
defer os.RemoveAll(tmp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Mktemp failed: %s", err)
|
||||
return fmt.Errorf("mktemp failed: %s", err)
|
||||
}
|
||||
|
||||
// Create root filesystem in the driver
|
||||
|
@ -185,12 +186,11 @@ func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader)
|
|||
return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
|
||||
}
|
||||
// Apply the diff/layer
|
||||
img.SetGraph(graph)
|
||||
if err := image.StoreImage(img, layerData, tmp); err != nil {
|
||||
if err := graph.storeImage(img, layerData, tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
// Commit
|
||||
if err := os.Rename(tmp, graph.ImageRoot(img.ID)); err != nil {
|
||||
if err := os.Rename(tmp, graph.imageRoot(img.ID)); err != nil {
|
||||
return err
|
||||
}
|
||||
graph.idIndex.Add(img.ID)
|
||||
|
@ -200,17 +200,16 @@ func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader)
|
|||
// TempLayerArchive creates a temporary archive of the given image's filesystem layer.
|
||||
// The archive is stored on disk and will be automatically deleted as soon as has been read.
|
||||
// If output is not nil, a human-readable progress bar will be written to it.
|
||||
// FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives?
|
||||
func (graph *Graph) TempLayerArchive(id string, sf *streamformatter.StreamFormatter, output io.Writer) (*archive.TempArchive, error) {
|
||||
image, err := graph.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tmp, err := graph.Mktemp("")
|
||||
tmp, err := graph.mktemp("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, err := image.TarLayer()
|
||||
a, err := graph.TarLayer(image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -227,9 +226,9 @@ func (graph *Graph) TempLayerArchive(id string, sf *streamformatter.StreamFormat
|
|||
return archive.NewTempArchive(progressReader, tmp)
|
||||
}
|
||||
|
||||
// Mktemp creates a temporary sub-directory inside the graph's filesystem.
|
||||
func (graph *Graph) Mktemp(id string) (string, error) {
|
||||
dir := filepath.Join(graph.Root, "_tmp", stringid.GenerateRandomID())
|
||||
// mktemp creates a temporary sub-directory inside the graph's filesystem.
|
||||
func (graph *Graph) mktemp(id string) (string, error) {
|
||||
dir := filepath.Join(graph.root, "_tmp", stringid.GenerateRandomID())
|
||||
if err := system.MkdirAll(dir, 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -237,7 +236,7 @@ func (graph *Graph) Mktemp(id string) (string, error) {
|
|||
}
|
||||
|
||||
func (graph *Graph) newTempFile() (*os.File, error) {
|
||||
tmp, err := graph.Mktemp("")
|
||||
tmp, err := graph.mktemp("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -342,17 +341,17 @@ func (graph *Graph) Delete(name string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmp, err := graph.Mktemp("")
|
||||
tmp, err := graph.mktemp("")
|
||||
graph.idIndex.Delete(id)
|
||||
if err == nil {
|
||||
if err := os.Rename(graph.ImageRoot(id), tmp); err != nil {
|
||||
if err := os.Rename(graph.imageRoot(id), tmp); err != nil {
|
||||
// On err make tmp point to old dir and cleanup unused tmp dir
|
||||
os.RemoveAll(tmp)
|
||||
tmp = graph.ImageRoot(id)
|
||||
tmp = graph.imageRoot(id)
|
||||
}
|
||||
} else {
|
||||
// On err make tmp point to old dir for cleanup
|
||||
tmp = graph.ImageRoot(id)
|
||||
tmp = graph.imageRoot(id)
|
||||
}
|
||||
// Remove rootfs data from the driver
|
||||
graph.driver.Remove(id)
|
||||
|
@ -375,7 +374,7 @@ func (graph *Graph) Map() (map[string]*image.Image, error) {
|
|||
// walkAll iterates over each image in the graph, and passes it to a handler.
|
||||
// The walking order is undetermined.
|
||||
func (graph *Graph) walkAll(handler func(*image.Image)) error {
|
||||
files, err := ioutil.ReadDir(graph.Root)
|
||||
files, err := ioutil.ReadDir(graph.root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -428,10 +427,125 @@ func (graph *Graph) Heads() (map[string]*image.Image, error) {
|
|||
return heads, err
|
||||
}
|
||||
|
||||
func (graph *Graph) ImageRoot(id string) string {
|
||||
return filepath.Join(graph.Root, id)
|
||||
func (graph *Graph) imageRoot(id string) string {
|
||||
return filepath.Join(graph.root, id)
|
||||
}
|
||||
|
||||
func (graph *Graph) Driver() graphdriver.Driver {
|
||||
return graph.driver
|
||||
// storeImage stores file system layer data for the given image to the
|
||||
// graph's storage driver. Image metadata is stored in a file
|
||||
// at the specified root directory.
|
||||
func (graph *Graph) storeImage(img *image.Image, layerData archive.ArchiveReader, root string) (err error) {
|
||||
// Store the layer. If layerData is not nil, unpack it into the new layer
|
||||
if layerData != nil {
|
||||
if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := graph.saveSize(root, int(img.Size)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
return json.NewEncoder(f).Encode(img)
|
||||
}
|
||||
|
||||
// loadImage fetches the image with the given id from the graph.
|
||||
func (graph *Graph) loadImage(id string) (*image.Image, error) {
|
||||
root := graph.imageRoot(id)
|
||||
|
||||
// Open the JSON file to decode by streaming
|
||||
jsonSource, err := os.Open(jsonPath(root))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer jsonSource.Close()
|
||||
|
||||
img := &image.Image{}
|
||||
dec := json.NewDecoder(jsonSource)
|
||||
|
||||
// Decode the JSON data
|
||||
if err := dec.Decode(img); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := image.ValidateID(img.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if buf, err := ioutil.ReadFile(filepath.Join(root, "layersize")); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
// 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
|
||||
} else {
|
||||
// Using Atoi here instead would temporarily convert the size to a machine
|
||||
// dependent integer type, which causes images larger than 2^31 bytes to
|
||||
// display negative sizes on 32-bit machines:
|
||||
size, err := strconv.ParseInt(string(buf), 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.Size = int64(size)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// saveSize stores the `size` in the provided graph `img` directory `root`.
|
||||
func (graph *Graph) saveSize(root string, size int) error {
|
||||
if err := ioutil.WriteFile(filepath.Join(root, "layersize"), []byte(strconv.Itoa(size)), 0600); err != nil {
|
||||
return fmt.Errorf("Error storing image size in %s/layersize: %s", root, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetCheckSum sets the checksum for the image layer to the provided value.
|
||||
func (graph *Graph) SetCheckSum(id, checksum string) error {
|
||||
root := graph.imageRoot(id)
|
||||
if err := ioutil.WriteFile(filepath.Join(root, "checksum"), []byte(checksum), 0600); err != nil {
|
||||
return fmt.Errorf("Error storing checksum in %s/checksum: %s", root, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCheckSum gets the checksum for the provide image layer id.
|
||||
func (graph *Graph) GetCheckSum(id string) (string, error) {
|
||||
root := graph.imageRoot(id)
|
||||
cs, err := ioutil.ReadFile(filepath.Join(root, "checksum"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return string(cs), err
|
||||
}
|
||||
|
||||
// RawJSON returns the JSON representation for an image as a byte array.
|
||||
func (graph *Graph) RawJSON(id string) ([]byte, error) {
|
||||
root := graph.imageRoot(id)
|
||||
|
||||
buf, err := ioutil.ReadFile(jsonPath(root))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to read json for image %s: %s", id, err)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func jsonPath(root string) string {
|
||||
return filepath.Join(root, "json")
|
||||
}
|
||||
|
||||
// TarLayer returns a tar archive of the image's filesystem layer.
|
||||
func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error) {
|
||||
return graph.driver.Diff(img.ID, img.Parent)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
|
||||
func TestMount(t *testing.T) {
|
||||
graph, driver := tempGraph(t)
|
||||
defer os.RemoveAll(graph.Root)
|
||||
defer os.RemoveAll(graph.root)
|
||||
defer driver.Cleanup()
|
||||
|
||||
archive, err := fakeTar()
|
||||
|
@ -52,7 +52,7 @@ func TestInit(t *testing.T) {
|
|||
graph, _ := tempGraph(t)
|
||||
defer nukeGraph(graph)
|
||||
// Root should exist
|
||||
if _, err := os.Stat(graph.Root); err != nil {
|
||||
if _, err := os.Stat(graph.root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Map() should be empty
|
||||
|
@ -301,6 +301,6 @@ func tempGraph(t *testing.T) (*Graph, graphdriver.Driver) {
|
|||
}
|
||||
|
||||
func nukeGraph(graph *Graph) {
|
||||
graph.Driver().Cleanup()
|
||||
os.RemoveAll(graph.Root)
|
||||
graph.driver.Cleanup()
|
||||
os.RemoveAll(graph.root)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package graph
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
|
@ -8,6 +9,78 @@ import (
|
|||
"github.com/docker/docker/utils"
|
||||
)
|
||||
|
||||
// History returns the list of all images used to create this image.
|
||||
func (graph *Graph) History(img *image.Image) ([]*image.Image, error) {
|
||||
var parents []*image.Image
|
||||
if err := graph.WalkHistory(img,
|
||||
func(img *image.Image) error {
|
||||
parents = append(parents, img)
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parents, nil
|
||||
}
|
||||
|
||||
// WalkHistory calls the handler function for each image in the
|
||||
// provided images lineage starting from immediate parent.
|
||||
func (graph *Graph) WalkHistory(img *image.Image, handler func(*image.Image) error) (err error) {
|
||||
currentImg := img
|
||||
for currentImg != nil {
|
||||
if handler != nil {
|
||||
if err := handler(currentImg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
currentImg, err = graph.GetParent(currentImg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error while getting parent image: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// depth returns the number of parents for a
|
||||
// current image
|
||||
func (graph *Graph) depth(img *image.Image) (int, error) {
|
||||
var (
|
||||
count = 0
|
||||
parent = img
|
||||
err error
|
||||
)
|
||||
|
||||
for parent != nil {
|
||||
count++
|
||||
parent, err = graph.GetParent(parent)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// Set the max depth to the aufs default that most
|
||||
// kernels are compiled with
|
||||
// For more information see: http://sourceforge.net/p/aufs/aufs3-standalone/ci/aufs3.12/tree/config.mk
|
||||
const MaxImageDepth = 127
|
||||
|
||||
// CheckDepth returns an error if the depth of an image, as returned
|
||||
// by ImageDepth, is too large to support creating a container from it
|
||||
// on this daemon.
|
||||
func (graph *Graph) CheckDepth(img *image.Image) error {
|
||||
// We add 2 layers to the depth because the container's rw and
|
||||
// init layer add to the restriction
|
||||
depth, err := graph.depth(img)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if depth+2 >= MaxImageDepth {
|
||||
return fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
|
||||
foundImage, err := s.LookupImage(name)
|
||||
if err != nil {
|
||||
|
@ -27,7 +100,7 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
|
|||
|
||||
history := []*types.ImageHistory{}
|
||||
|
||||
err = foundImage.WalkHistory(func(img *image.Image) error {
|
||||
err = s.graph.WalkHistory(foundImage, func(img *image.Image) error {
|
||||
history = append(history, &types.ImageHistory{
|
||||
ID: img.ID,
|
||||
Created: img.Created.Unix(),
|
||||
|
@ -41,3 +114,19 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
|
|||
|
||||
return history, err
|
||||
}
|
||||
|
||||
func (graph *Graph) GetParent(img *image.Image) (*image.Image, error) {
|
||||
if img.Parent == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return graph.Get(img.Parent)
|
||||
}
|
||||
|
||||
func (graph *Graph) GetParentsSize(img *image.Image, size int64) int64 {
|
||||
parentImage, err := graph.GetParent(img)
|
||||
if err != nil || parentImage == nil {
|
||||
return size
|
||||
}
|
||||
size += parentImage.Size
|
||||
return graph.GetParentsSize(parentImage, size)
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) {
|
|||
newImage.ID = image.ID
|
||||
newImage.Created = int(image.Created.Unix())
|
||||
newImage.Size = int(image.Size)
|
||||
newImage.VirtualSize = int(image.GetParentsSize(0) + image.Size)
|
||||
newImage.VirtualSize = int(s.graph.GetParentsSize(image, 0) + image.Size)
|
||||
newImage.Labels = image.ContainerConfig.Labels
|
||||
|
||||
if utils.DigestReference(ref) {
|
||||
|
@ -140,7 +140,7 @@ func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) {
|
|||
newImage.ID = image.ID
|
||||
newImage.Created = int(image.Created.Unix())
|
||||
newImage.Size = int(image.Size)
|
||||
newImage.VirtualSize = int(image.GetParentsSize(0) + image.Size)
|
||||
newImage.VirtualSize = int(s.graph.GetParentsSize(image, 0) + image.Size)
|
||||
newImage.Labels = image.ContainerConfig.Labels
|
||||
|
||||
images = append(images, newImage)
|
||||
|
|
|
@ -57,7 +57,7 @@ func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error
|
|||
metadata = *layer.Config
|
||||
}
|
||||
|
||||
for ; layer != nil; layer, err = layer.GetParent() {
|
||||
for ; layer != nil; layer, err = s.graph.GetParent(layer) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -72,12 +72,12 @@ func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error
|
|||
}
|
||||
}
|
||||
|
||||
checksum, err := layer.GetCheckSum(s.graph.ImageRoot(layer.ID))
|
||||
checksum, err := s.graph.GetCheckSum(layer.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting image checksum: %s", err)
|
||||
}
|
||||
if tarsum.VersionLabelForChecksum(checksum) != tarsum.Version1.String() {
|
||||
archive, err := layer.TarLayer()
|
||||
archive, err := s.graph.TarLayer(layer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -95,12 +95,12 @@ func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error
|
|||
checksum = tarSum.Sum(nil)
|
||||
|
||||
// Save checksum value
|
||||
if err := layer.SaveCheckSum(s.graph.ImageRoot(layer.ID), checksum); err != nil {
|
||||
if err := s.graph.SetCheckSum(layer.ID, checksum); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
jsonData, err := layer.RawJson()
|
||||
jsonData, err := s.graph.RawJSON(layer.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Cannot retrieve the path for {%s}: %s", layer.ID, err)
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ func TestManifestTarsumCache(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if cs, err := img.GetCheckSum(store.graph.ImageRoot(testManifestImageID)); err != nil {
|
||||
if cs, err := store.graph.GetCheckSum(testManifestImageID); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if cs != "" {
|
||||
t.Fatalf("Non-empty checksum file after register")
|
||||
|
@ -153,7 +153,7 @@ func TestManifestTarsumCache(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
manifestChecksum, err := img.GetCheckSum(store.graph.ImageRoot(testManifestImageID))
|
||||
manifestChecksum, err := store.graph.GetCheckSum(testManifestImageID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ func TestManifestTarsumCache(t *testing.T) {
|
|||
t.Fatalf("Unexpected number of layer history, expecting 1: %d", len(manifest.History))
|
||||
}
|
||||
|
||||
v1compat, err := img.RawJson()
|
||||
v1compat, err := store.graph.RawJSON(img.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ func TestManifestDigestCheck(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if cs, err := img.GetCheckSum(store.graph.ImageRoot(testManifestImageID)); err != nil {
|
||||
if cs, err := store.graph.GetCheckSum(testManifestImageID); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if cs != "" {
|
||||
t.Fatalf("Non-empty checksum file after register")
|
||||
|
|
|
@ -5,9 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
|
@ -57,7 +55,7 @@ func (s *TagStore) getImageList(localRepo map[string]string, requestedTag string
|
|||
|
||||
tagsByImage[id] = append(tagsByImage[id], tag)
|
||||
|
||||
for img, err := s.graph.Get(id); img != nil; img, err = img.GetParent() {
|
||||
for img, err := s.graph.Get(id); img != nil; img, err = s.graph.GetParent(img) {
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -248,7 +246,7 @@ func (s *TagStore) pushRepository(r *registry.Session, out io.Writer,
|
|||
|
||||
func (s *TagStore) pushImage(r *registry.Session, out io.Writer, imgID, ep string, token []string, sf *streamformatter.StreamFormatter) (checksum string, err error) {
|
||||
out = ioutils.NewWriteFlusher(out)
|
||||
jsonRaw, err := ioutil.ReadFile(filepath.Join(s.graph.Root, imgID, "json"))
|
||||
jsonRaw, err := s.graph.RawJSON(imgID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Cannot retrieve the path for {%s}: %s", imgID, err)
|
||||
}
|
||||
|
@ -349,7 +347,7 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o
|
|||
|
||||
layersSeen := make(map[string]bool)
|
||||
layers := []*image.Image{layer}
|
||||
for ; layer != nil; layer, err = layer.GetParent() {
|
||||
for ; layer != nil; layer, err = s.graph.GetParent(layer) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -372,12 +370,12 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o
|
|||
return err
|
||||
}
|
||||
}
|
||||
jsonData, err := layer.RawJson()
|
||||
jsonData, err := s.graph.RawJSON(layer.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot retrieve the path for %s: %s", layer.ID, err)
|
||||
}
|
||||
|
||||
checksum, err := layer.GetCheckSum(s.graph.ImageRoot(layer.ID))
|
||||
checksum, err := s.graph.GetCheckSum(layer.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting image checksum: %s", err)
|
||||
}
|
||||
|
@ -401,7 +399,7 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o
|
|||
return err
|
||||
} else if cs != checksum {
|
||||
// Cache new checksum
|
||||
if err := layer.SaveCheckSum(s.graph.ImageRoot(layer.ID), cs); err != nil {
|
||||
if err := s.graph.SetCheckSum(layer.ID, cs); err != nil {
|
||||
return err
|
||||
}
|
||||
checksum = cs
|
||||
|
@ -456,7 +454,7 @@ func (s *TagStore) pushV2Image(r *registry.Session, img *image.Image, endpoint *
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
arch, err := image.TarLayer()
|
||||
arch, err := s.graph.TarLayer(image)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ func (s *TagStore) LookupRaw(name string) ([]byte, error) {
|
|||
return nil, fmt.Errorf("No such image %s", name)
|
||||
}
|
||||
|
||||
imageInspectRaw, err := image.RawJson()
|
||||
imageInspectRaw, err := s.graph.RawJSON(image.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ func (s *TagStore) Lookup(name string) (*types.ImageInspect, error) {
|
|||
Architecture: image.Architecture,
|
||||
Os: image.OS,
|
||||
Size: image.Size,
|
||||
VirtualSize: image.GetParentsSize(0) + image.Size,
|
||||
VirtualSize: s.graph.GetParentsSize(image, 0) + image.Size,
|
||||
}
|
||||
|
||||
return imageInspect, nil
|
||||
|
@ -51,7 +51,7 @@ func (s *TagStore) Lookup(name string) (*types.ImageInspect, error) {
|
|||
// ImageTarLayer return the tarLayer of the image
|
||||
func (s *TagStore) ImageTarLayer(name string, dest io.Writer) error {
|
||||
if image, err := s.LookupImage(name); err == nil && image != nil {
|
||||
fs, err := image.TarLayer()
|
||||
fs, err := s.graph.TarLayer(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/daemon/graphdriver"
|
||||
)
|
||||
|
||||
type Graph interface {
|
||||
Get(id string) (*Image, error)
|
||||
ImageRoot(id string) string
|
||||
Driver() graphdriver.Driver
|
||||
}
|
229
image/image.go
229
image/image.go
|
@ -3,22 +3,12 @@ package image
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
// Set the max depth to the aufs default that most
|
||||
// kernels are compiled with
|
||||
// For more information see: http://sourceforge.net/p/aufs/aufs3-standalone/ci/aufs3.12/tree/config.mk
|
||||
const MaxImageDepth = 127
|
||||
|
||||
var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
|
||||
|
||||
type Image struct {
|
||||
|
@ -34,225 +24,6 @@ type Image struct {
|
|||
Architecture string `json:"architecture,omitempty"`
|
||||
OS string `json:"os,omitempty"`
|
||||
Size int64
|
||||
|
||||
graph Graph
|
||||
}
|
||||
|
||||
func LoadImage(root string) (*Image, error) {
|
||||
// Open the JSON file to decode by streaming
|
||||
jsonSource, err := os.Open(jsonPath(root))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer jsonSource.Close()
|
||||
|
||||
img := &Image{}
|
||||
dec := json.NewDecoder(jsonSource)
|
||||
|
||||
// Decode the JSON data
|
||||
if err := dec.Decode(img); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ValidateID(img.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if buf, err := ioutil.ReadFile(filepath.Join(root, "layersize")); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
// 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
|
||||
} else {
|
||||
// Using Atoi here instead would temporarily convert the size to a machine
|
||||
// dependent integer type, which causes images larger than 2^31 bytes to
|
||||
// display negative sizes on 32-bit machines:
|
||||
size, err := strconv.ParseInt(string(buf), 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.Size = int64(size)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// StoreImage stores file system layer data for the given image to the
|
||||
// image's registered storage driver. Image metadata is stored in a file
|
||||
// at the specified root directory.
|
||||
func StoreImage(img *Image, layerData archive.ArchiveReader, root string) (err error) {
|
||||
// Store the layer. If layerData is not nil, unpack it into the new layer
|
||||
if layerData != nil {
|
||||
if img.Size, err = img.graph.Driver().ApplyDiff(img.ID, img.Parent, layerData); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := img.SaveSize(root); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
return json.NewEncoder(f).Encode(img)
|
||||
}
|
||||
|
||||
func (img *Image) SetGraph(graph Graph) {
|
||||
img.graph = graph
|
||||
}
|
||||
|
||||
// SaveSize stores the current `size` value of `img` in the directory `root`.
|
||||
func (img *Image) SaveSize(root string) error {
|
||||
if err := ioutil.WriteFile(filepath.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 (img *Image) SaveCheckSum(root, checksum string) error {
|
||||
if err := ioutil.WriteFile(filepath.Join(root, "checksum"), []byte(checksum), 0600); err != nil {
|
||||
return fmt.Errorf("Error storing checksum in %s/checksum: %s", root, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (img *Image) GetCheckSum(root string) (string, error) {
|
||||
cs, err := ioutil.ReadFile(filepath.Join(root, "checksum"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return string(cs), err
|
||||
}
|
||||
|
||||
func jsonPath(root string) string {
|
||||
return filepath.Join(root, "json")
|
||||
}
|
||||
|
||||
func (img *Image) RawJson() ([]byte, error) {
|
||||
root, err := img.root()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to get root for image %s: %s", img.ID, err)
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(jsonPath(root))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to read json for image %s: %s", img.ID, err)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// TarLayer returns a tar archive of the image's filesystem layer.
|
||||
func (img *Image) TarLayer() (arch archive.Archive, err error) {
|
||||
if img.graph == nil {
|
||||
return nil, fmt.Errorf("Can't load storage driver for unregistered image %s", img.ID)
|
||||
}
|
||||
|
||||
driver := img.graph.Driver()
|
||||
|
||||
return driver.Diff(img.ID, img.Parent)
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
return img.graph.ImageRoot(img.ID), nil
|
||||
}
|
||||
|
||||
func (img *Image) GetParentsSize(size int64) int64 {
|
||||
parentImage, err := img.GetParent()
|
||||
if err != nil || parentImage == nil {
|
||||
return size
|
||||
}
|
||||
size += parentImage.Size
|
||||
return parentImage.GetParentsSize(size)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// CheckDepth returns an error if the depth of an image, as returned
|
||||
// by ImageDepth, is too large to support creating a container from it
|
||||
// on this daemon.
|
||||
func (img *Image) CheckDepth() error {
|
||||
// We add 2 layers to the depth because the container's rw and
|
||||
// init layer add to the restriction
|
||||
depth, err := img.Depth()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if depth+2 >= MaxImageDepth {
|
||||
return fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build an Image object from raw json data
|
||||
|
|
Загрузка…
Ссылка в новой задаче