Windows: Graph driver implementation

Signed-off-by: John Howard <jhoward@microsoft.com>
Upstream-commit: 52f4d09ffb376ffaa6677cb1e0413c6a97f53f24
Component: engine
This commit is contained in:
John Howard
2015-07-10 14:33:11 -07:00
parent 576b45a55d
commit 1296feade8
29 changed files with 850 additions and 145 deletions
+52 -47
View File
@@ -37,6 +37,22 @@ type Graph struct {
imageMutex imageMutex // protect images in driver.
}
type Image struct {
ID string `json:"id"`
Parent string `json:"parent,omitempty"`
Comment string `json:"comment,omitempty"`
Created time.Time `json:"created"`
Container string `json:"container,omitempty"`
ContainerConfig runconfig.Config `json:"container_config,omitempty"`
DockerVersion string `json:"docker_version,omitempty"`
Author string `json:"author,omitempty"`
Config *runconfig.Config `json:"config,omitempty"`
Architecture string `json:"architecture,omitempty"`
OS string `json:"os,omitempty"`
Size int64
graph Graph
}
var (
// ErrDigestNotSet is used when request the digest for a layer
// but the layer has no digest value or content to compute the
@@ -79,6 +95,13 @@ func (graph *Graph) restore() error {
ids = append(ids, id)
}
}
baseIds, err := graph.restoreBaseImages()
if err != nil {
return err
}
ids = append(ids, baseIds...)
graph.idIndex = truncindex.NewTruncIndex(ids)
logrus.Debugf("Restored %d elements", len(ids))
return nil
@@ -100,7 +123,7 @@ func (graph *Graph) Exists(id string) bool {
}
// Get returns the image with the given id, or an error if the image doesn't exist.
func (graph *Graph) Get(name string) (*image.Image, error) {
func (graph *Graph) Get(name string) (*Image, error) {
id, err := graph.idIndex.Get(name)
if err != nil {
return nil, fmt.Errorf("could not find image: %v", err)
@@ -128,8 +151,8 @@ func (graph *Graph) Get(name string) (*image.Image, error) {
}
// Create creates a new image and registers it in the graph.
func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*image.Image, error) {
img := &image.Image{
func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*Image, error) {
img := &Image{
ID: stringid.GenerateRandomID(),
Comment: comment,
Created: time.Now().UTC(),
@@ -153,7 +176,7 @@ func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, contain
}
// Register imports a pre-existing image into the graph.
func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader) (err error) {
func (graph *Graph) Register(img *Image, layerData archive.ArchiveReader) (err error) {
if err := image.ValidateID(img.ID); err != nil {
return err
}
@@ -197,9 +220,10 @@ func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader)
}
// Create root filesystem in the driver
if err := graph.driver.Create(img.ID, img.Parent); err != nil {
return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
if err := createRootFilesystemInDriver(graph, img, layerData); err != nil {
return err
}
// Apply the diff/layer
if err := graph.storeImage(img, layerData, tmp); err != nil {
return err
@@ -303,9 +327,9 @@ func (graph *Graph) Delete(name string) error {
}
// Map returns a list of all images in the graph, addressable by ID.
func (graph *Graph) Map() (map[string]*image.Image, error) {
images := make(map[string]*image.Image)
err := graph.walkAll(func(image *image.Image) {
func (graph *Graph) Map() (map[string]*Image, error) {
images := make(map[string]*Image)
err := graph.walkAll(func(image *Image) {
images[image.ID] = image
})
if err != nil {
@@ -316,7 +340,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 {
func (graph *Graph) walkAll(handler func(*Image)) error {
files, err := ioutil.ReadDir(graph.root)
if err != nil {
return err
@@ -336,9 +360,9 @@ func (graph *Graph) walkAll(handler func(*image.Image)) error {
// If an image of id ID has 3 children images, then the value for key ID
// will be a list of 3 images.
// If an image has no children, it will not have an entry in the table.
func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
byParent := make(map[string][]*image.Image)
err := graph.walkAll(func(img *image.Image) {
func (graph *Graph) ByParent() (map[string][]*Image, error) {
byParent := make(map[string][]*Image)
err := graph.walkAll(func(img *Image) {
parent, err := graph.Get(img.Parent)
if err != nil {
return
@@ -346,7 +370,7 @@ func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
if children, exists := byParent[parent.ID]; exists {
byParent[parent.ID] = append(children, img)
} else {
byParent[parent.ID] = []*image.Image{img}
byParent[parent.ID] = []*Image{img}
}
})
return byParent, err
@@ -354,13 +378,13 @@ func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
// Heads returns all heads in the graph, keyed by id.
// A head is an image which is not the parent of another image in the graph.
func (graph *Graph) Heads() (map[string]*image.Image, error) {
heads := make(map[string]*image.Image)
func (graph *Graph) Heads() (map[string]*Image, error) {
heads := make(map[string]*Image)
byParent, err := graph.ByParent()
if err != nil {
return nil, err
}
err = graph.walkAll(func(image *image.Image) {
err = graph.walkAll(func(image *Image) {
// If it's not in the byParent lookup table, then
// it's not a parent -> so it's a head!
if _, exists := byParent[image.ID]; !exists {
@@ -374,33 +398,8 @@ func (graph *Graph) imageRoot(id string) string {
return filepath.Join(graph.root, id)
}
// 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) {
func (graph *Graph) loadImage(id string) (*Image, error) {
root := graph.imageRoot(id)
// Open the JSON file to decode by streaming
@@ -410,7 +409,7 @@ func (graph *Graph) loadImage(id string) (*image.Image, error) {
}
defer jsonSource.Close()
img := &image.Image{}
img := &Image{}
dec := json.NewDecoder(jsonSource)
// Decode the JSON data
@@ -488,7 +487,13 @@ 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)
// Build an Image object from raw json data
func NewImgJSON(src []byte) (*Image, error) {
ret := &Image{}
// 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
}
+6 -6
View File
@@ -68,7 +68,7 @@ func TestInterruptedRegister(t *testing.T) {
graph, _ := tempGraph(t)
defer nukeGraph(graph)
badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
image := &image.Image{
image := &Image{
ID: stringid.GenerateRandomID(),
Comment: "testing",
Created: time.Now(),
@@ -128,7 +128,7 @@ func TestRegister(t *testing.T) {
if err != nil {
t.Fatal(err)
}
image := &image.Image{
image := &Image{
ID: stringid.GenerateRandomID(),
Comment: "testing",
Created: time.Now(),
@@ -232,19 +232,19 @@ func TestByParent(t *testing.T) {
graph, _ := tempGraph(t)
defer nukeGraph(graph)
parentImage := &image.Image{
parentImage := &Image{
ID: stringid.GenerateRandomID(),
Comment: "parent",
Created: time.Now(),
Parent: "",
}
childImage1 := &image.Image{
childImage1 := &Image{
ID: stringid.GenerateRandomID(),
Comment: "child1",
Created: time.Now(),
Parent: parentImage.ID,
}
childImage2 := &image.Image{
childImage2 := &Image{
ID: stringid.GenerateRandomID(),
Comment: "child2",
Created: time.Now(),
@@ -264,7 +264,7 @@ func TestByParent(t *testing.T) {
}
}
func createTestImage(graph *Graph, t *testing.T) *image.Image {
func createTestImage(graph *Graph, t *testing.T) *Image {
archive, err := fakeTar()
if err != nil {
t.Fatal(err)
+114
View File
@@ -0,0 +1,114 @@
// +build !windows
package graph
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/system"
)
// setupInitLayer populates a directory with mountpoints suitable
// for bind-mounting dockerinit into the container. The mountpoint is simply an
// empty file at /.dockerinit
//
// This extra layer is used by all containers as the top-most ro layer. It protects
// the container from unwanted side-effects on the rw layer.
func SetupInitLayer(initLayer string) error {
for pth, typ := range map[string]string{
"/dev/pts": "dir",
"/dev/shm": "dir",
"/proc": "dir",
"/sys": "dir",
"/.dockerinit": "file",
"/.dockerenv": "file",
"/etc/resolv.conf": "file",
"/etc/hosts": "file",
"/etc/hostname": "file",
"/dev/console": "file",
"/etc/mtab": "/proc/mounts",
} {
parts := strings.Split(pth, "/")
prev := "/"
for _, p := range parts[1:] {
prev = filepath.Join(prev, p)
syscall.Unlink(filepath.Join(initLayer, prev))
}
if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil {
if os.IsNotExist(err) {
if err := system.MkdirAll(filepath.Join(initLayer, filepath.Dir(pth)), 0755); err != nil {
return err
}
switch typ {
case "dir":
if err := system.MkdirAll(filepath.Join(initLayer, pth), 0755); err != nil {
return err
}
case "file":
f, err := os.OpenFile(filepath.Join(initLayer, pth), os.O_CREATE, 0755)
if err != nil {
return err
}
f.Close()
default:
if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil {
return err
}
}
} else {
return err
}
}
}
// Layer is ready to use, if it wasn't before.
return nil
}
func createRootFilesystemInDriver(graph *Graph, img *Image, layerData archive.ArchiveReader) error {
if err := graph.driver.Create(img.ID, img.Parent); err != nil {
return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
}
return nil
}
func (graph *Graph) restoreBaseImages() ([]string, error) {
return nil, nil
}
// 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, 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)
}
// TarLayer returns a tar archive of the image's filesystem layer.
func (graph *Graph) TarLayer(img *Image) (arch archive.Archive, err error) {
return graph.driver.Diff(img.ID, img.Parent)
}
+160
View File
@@ -0,0 +1,160 @@
// +build windows
package graph
import (
"encoding/json"
"fmt"
"os"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/graphdriver/windows"
"github.com/docker/docker/pkg/archive"
)
// setupInitLayer populates a directory with mountpoints suitable
// for bind-mounting dockerinit into the container. T
func SetupInitLayer(initLayer string) error {
return nil
}
func createRootFilesystemInDriver(graph *Graph, img *Image, layerData archive.ArchiveReader) error {
if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
if img.Container != "" && layerData == nil {
logrus.Debugf("Copying from container %s.", img.Container)
var ids []string
if img.Parent != "" {
parentImg, err := graph.Get(img.Parent)
if err != nil {
return err
}
ids, err = graph.ParentLayerIds(parentImg)
if err != nil {
return err
}
}
if err := wd.CopyDiff(img.Container, img.ID, wd.LayerIdsToPaths(ids)); err != nil {
return fmt.Errorf("Driver %s failed to copy image rootfs %s: %s", graph.driver, img.Container, err)
}
} else if img.Parent == "" {
if err := graph.driver.Create(img.ID, img.Parent); err != nil {
return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
}
}
} else {
// This fallback allows the use of VFS during daemon development.
if err := graph.driver.Create(img.ID, img.Parent); err != nil {
return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
}
}
return nil
}
func (graph *Graph) restoreBaseImages() ([]string, error) {
// TODO Windows. This needs implementing (@swernli)
return nil, nil
}
// ParentLayerIds returns a list of all parent image IDs for the given image.
func (graph *Graph) ParentLayerIds(img *Image) (ids []string, err error) {
for i := img; i != nil && err == nil; i, err = graph.GetParent(i) {
ids = append(ids, i.ID)
}
return
}
// 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, layerData archive.ArchiveReader, root string) (err error) {
if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
// Store the layer. If layerData is not nil and this isn't a base image,
// unpack it into the new layer
if layerData != nil && img.Parent != "" {
var ids []string
if img.Parent != "" {
parentImg, err := graph.Get(img.Parent)
if err != nil {
return err
}
ids, err = graph.ParentLayerIds(parentImg)
if err != nil {
return err
}
}
if img.Size, err = wd.Import(img.ID, layerData, wd.LayerIdsToPaths(ids)); 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)
} else {
// We keep this functionality here so that we can still work with the
// VFS driver during development. This will not be used for actual running
// of Windows containers. Without this code, it would not be possible to
// docker pull using the VFS driver.
// 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)
}
}
// TarLayer returns a tar archive of the image's filesystem layer.
func (graph *Graph) TarLayer(img *Image) (arch archive.Archive, err error) {
if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
var ids []string
if img.Parent != "" {
parentImg, err := graph.Get(img.Parent)
if err != nil {
return nil, err
}
ids, err = graph.ParentLayerIds(parentImg)
if err != nil {
return nil, err
}
}
return wd.Export(img.ID, wd.LayerIdsToPaths(ids))
} else {
// We keep this functionality here so that we can still work with the VFS
// driver during development. VFS is not supported (and just will not work)
// for Windows containers.
return graph.driver.Diff(img.ID, img.Parent)
}
}
+6 -7
View File
@@ -5,13 +5,12 @@ import (
"strings"
"github.com/docker/docker/api/types"
"github.com/docker/docker/image"
"github.com/docker/docker/utils"
)
// 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) {
func (graph *Graph) WalkHistory(img *Image, handler func(Image) error) (err error) {
currentImg := img
for currentImg != nil {
if handler != nil {
@@ -29,7 +28,7 @@ func (graph *Graph) WalkHistory(img *image.Image, handler func(image.Image) erro
// depth returns the number of parents for a
// current image
func (graph *Graph) depth(img *image.Image) (int, error) {
func (graph *Graph) depth(img *Image) (int, error) {
var (
count = 0
parent = img
@@ -54,7 +53,7 @@ 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 {
func (graph *Graph) CheckDepth(img *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)
@@ -86,7 +85,7 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
history := []*types.ImageHistory{}
err = s.graph.WalkHistory(foundImage, func(img image.Image) error {
err = s.graph.WalkHistory(foundImage, func(img Image) error {
history = append(history, &types.ImageHistory{
ID: img.ID,
Created: img.Created.Unix(),
@@ -101,14 +100,14 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
return history, err
}
func (graph *Graph) GetParent(img *image.Image) (*image.Image, error) {
func (graph *Graph) GetParent(img *Image) (*Image, error) {
if img.Parent == "" {
return nil, nil
}
return graph.Get(img.Parent)
}
func (graph *Graph) GetParentsSize(img *image.Image, size int64) int64 {
func (graph *Graph) GetParentsSize(img *Image, size int64) int64 {
parentImage, err := graph.GetParent(img)
if err != nil || parentImage == nil {
return size
+1 -2
View File
@@ -8,7 +8,6 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/parsers/filters"
"github.com/docker/docker/utils"
)
@@ -32,7 +31,7 @@ func (r ByCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) {
var (
allImages map[string]*image.Image
allImages map[string]*Image
err error
filtTagged = true
filtLabel = false
+1 -1
View File
@@ -98,7 +98,7 @@ func (s *TagStore) recursiveLoad(address, tmpImageDir string) error {
logrus.Debugf("Error reading embedded tar", err)
return err
}
img, err := image.NewImgJSON(imageJson)
img, err := NewImgJSON(imageJson)
if err != nil {
logrus.Debugf("Error unmarshalling json", err)
return err
+2 -3
View File
@@ -7,7 +7,6 @@ import (
"testing"
"github.com/docker/distribution/digest"
"github.com/docker/docker/image"
"github.com/docker/docker/registry"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/utils"
@@ -124,7 +123,7 @@ func TestManifestTarsumCache(t *testing.T) {
if err != nil {
t.Fatal(err)
}
img := &image.Image{ID: testManifestImageID}
img := &Image{ID: testManifestImageID}
if err := store.graph.Register(img, archive); err != nil {
t.Fatal(err)
}
@@ -190,7 +189,7 @@ func TestManifestDigestCheck(t *testing.T) {
if err != nil {
t.Fatal(err)
}
img := &image.Image{ID: testManifestImageID}
img := &Image{ID: testManifestImageID}
if err := store.graph.Register(img, archive); err != nil {
t.Fatal(err)
}
+4 -5
View File
@@ -13,7 +13,6 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/digest"
"github.com/docker/docker/cliconfig"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/progressreader"
"github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/pkg/stringid"
@@ -387,7 +386,7 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
imgJSON []byte
imgSize int
err error
img *image.Image
img *Image
)
retries := 5
for j := 1; j <= retries; j++ {
@@ -399,7 +398,7 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
time.Sleep(time.Duration(j) * 500 * time.Millisecond)
continue
}
img, err = image.NewImgJSON(imgJSON)
img, err = NewImgJSON(imgJSON)
layersDownloaded = true
if err != nil && j == retries {
out.Write(sf.FormatProgress(stringid.TruncateID(id), "Error pulling dependent layers", nil))
@@ -540,7 +539,7 @@ func (s *TagStore) pullV2Tag(r *registry.Session, out io.Writer, endpoint *regis
// downloadInfo is used to pass information from download to extractor
type downloadInfo struct {
imgJSON []byte
img *image.Image
img *Image
digest digest.Digest
tmpFile *os.File
length int64
@@ -556,7 +555,7 @@ func (s *TagStore) pullV2Tag(r *registry.Session, out io.Writer, endpoint *regis
imgJSON = []byte(manifest.History[i].V1Compatibility)
)
img, err := image.NewImgJSON(imgJSON)
img, err := NewImgJSON(imgJSON)
if err != nil {
return false, fmt.Errorf("failed to parse json: %s", err)
}
+2 -3
View File
@@ -11,7 +11,6 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/distribution/digest"
"github.com/docker/docker/cliconfig"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/progressreader"
"github.com/docker/docker/pkg/streamformatter"
@@ -349,7 +348,7 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o
}
layersSeen := make(map[string]bool)
layers := []*image.Image{}
layers := []*Image{}
for ; layer != nil; layer, err = s.graph.GetParent(layer) {
if err != nil {
return err
@@ -445,7 +444,7 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o
}
// PushV2Image pushes the image content to the v2 registry, first buffering the contents to disk
func (s *TagStore) pushV2Image(r *registry.Session, img *image.Image, endpoint *registry.Endpoint, imageName string, sf *streamformatter.StreamFormatter, out io.Writer, auth *registry.RequestAuthorization) (digest.Digest, error) {
func (s *TagStore) pushV2Image(r *registry.Session, img *Image, endpoint *registry.Endpoint, imageName string, sf *streamformatter.StreamFormatter, out io.Writer, auth *registry.RequestAuthorization) (digest.Digest, error) {
out.Write(sf.FormatProgress(stringid.TruncateID(img.ID), "Buffering to Disk", nil))
image, err := s.graph.Get(img.ID)
+14 -9
View File
@@ -3,6 +3,7 @@ package graph
import (
"fmt"
"io"
"runtime"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types"
@@ -58,17 +59,21 @@ 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 := s.graph.TarLayer(image)
if err != nil {
return err
}
defer fs.Close()
// On Windows, the base layer cannot be exported
if runtime.GOOS != "windows" || image.Parent != "" {
written, err := io.Copy(dest, fs)
if err != nil {
return err
fs, err := s.graph.TarLayer(image)
if err != nil {
return err
}
defer fs.Close()
written, err := io.Copy(dest, fs)
if err != nil {
return err
}
logrus.Debugf("rendered layer for %s of [%d] size", image.ID, written)
}
logrus.Debugf("rendered layer for %s of [%d] size", image.ID, written)
return nil
}
return fmt.Errorf("No such image: %s", name)
+3 -4
View File
@@ -15,7 +15,6 @@ import (
"github.com/docker/docker/daemon/events"
"github.com/docker/docker/graph/tags"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/registry"
@@ -126,7 +125,7 @@ func (store *TagStore) reload() error {
return nil
}
func (store *TagStore) LookupImage(name string) (*image.Image, error) {
func (store *TagStore) LookupImage(name string) (*Image, error) {
// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
// (so we can pass all errors here)
repoName, ref := parsers.ParseRepositoryTag(name)
@@ -135,7 +134,7 @@ func (store *TagStore) LookupImage(name string) (*image.Image, error) {
}
var (
err error
img *image.Image
img *Image
)
img, err = store.GetImage(repoName, ref)
@@ -330,7 +329,7 @@ func (store *TagStore) Get(repoName string) (Repository, error) {
return nil, nil
}
func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error) {
func (store *TagStore) GetImage(repoName, refOrID string) (*Image, error) {
repo, err := store.Get(repoName)
if err != nil {
+2 -3
View File
@@ -11,7 +11,6 @@ import (
"github.com/docker/docker/daemon/events"
"github.com/docker/docker/daemon/graphdriver"
_ "github.com/docker/docker/daemon/graphdriver/vfs" // import the vfs driver so it is used in the tests
"github.com/docker/docker/image"
"github.com/docker/docker/trust"
"github.com/docker/docker/utils"
)
@@ -80,7 +79,7 @@ func mkTestTagStore(root string, t *testing.T) *TagStore {
if err != nil {
t.Fatal(err)
}
img := &image.Image{ID: testOfficialImageID}
img := &Image{ID: testOfficialImageID}
if err := graph.Register(img, officialArchive); err != nil {
t.Fatal(err)
}
@@ -91,7 +90,7 @@ func mkTestTagStore(root string, t *testing.T) *TagStore {
if err != nil {
t.Fatal(err)
}
img = &image.Image{ID: testPrivateImageID}
img = &Image{ID: testPrivateImageID}
if err := graph.Register(img, privateArchive); err != nil {
t.Fatal(err)
}