diff --git a/builder/internals.go b/builder/internals.go index 6fac572939..b951e59ea1 100644 --- a/builder/internals.go +++ b/builder/internals.go @@ -241,20 +241,12 @@ func (b *builder) runContextCommand(args []string, allowRemote bool, allowDecomp } defer container.Unmount() - if err := container.PrepareStorage(); err != nil { - return err - } - for _, ci := range copyInfos { if err := b.addContext(container, ci.origPath, ci.destPath, ci.decompress); err != nil { return err } } - if err := container.CleanupStorage(); err != nil { - return err - } - if err := b.commit(container.ID, cmd, fmt.Sprintf("%s %s in %s", cmdName, origPaths, dest)); err != nil { return err } diff --git a/daemon/container.go b/daemon/container.go index f56b8cbe99..855ce0296c 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -227,6 +227,21 @@ func (container *Container) GetRootResourcePath(path string) (string, error) { return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root) } +func (container *Container) ExportRw() (archive.Archive, error) { + if container.daemon == nil { + return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID) + } + archive, err := container.daemon.Diff(container) + if err != nil { + return nil, err + } + return ioutils.NewReadCloserWrapper(archive, func() error { + err := archive.Close() + return err + }), + nil +} + func (container *Container) Start() (err error) { container.Lock() defer container.Unlock() @@ -258,12 +273,6 @@ func (container *Container) Start() (err error) { return err } - // No-op if non-Windows. Once the container filesystem is mounted, - // prepare the layer to boot using the Windows driver. - if err := container.PrepareStorage(); err != nil { - return err - } - // Make sure NetworkMode has an acceptable value. We do this to ensure // backwards API compatibility. container.hostConfig = runconfig.SetDefaultNetModeIfBlank(container.hostConfig) @@ -345,10 +354,6 @@ func (container *Container) isNetworkAllocated() bool { func (container *Container) cleanup() { container.ReleaseNetwork() - if err := container.CleanupStorage(); err != nil { - logrus.Errorf("%v: Failed to cleanup storage: %v", container.ID, err) - } - if err := container.Unmount(); err != nil { logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err) } @@ -658,14 +663,8 @@ func (container *Container) Copy(resource string) (rc io.ReadCloser, err error) return nil, err } - if err := container.PrepareStorage(); err != nil { - container.Unmount() - return nil, err - } - reader := ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() - container.CleanupStorage() container.UnmountVolumes(true) container.Unmount() container.Unlock() diff --git a/daemon/container_unix.go b/daemon/container_unix.go index b52f14c5de..245a0762cc 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -18,9 +18,7 @@ import ( "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/daemon/links" "github.com/docker/docker/daemon/network" - "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/directory" - "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/nat" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/system" @@ -948,21 +946,6 @@ func (container *Container) initializeNetworking() error { return container.buildHostnameFile() } -func (container *Container) ExportRw() (archive.Archive, error) { - if container.daemon == nil { - return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID) - } - archive, err := container.daemon.Diff(container) - if err != nil { - return nil, err - } - return ioutils.NewReadCloserWrapper(archive, func() error { - err := archive.Close() - return err - }), - nil -} - func (container *Container) getIpcContainer() (*Container, error) { containerID := container.hostConfig.IpcMode.Container() c, err := container.daemon.Get(containerID) @@ -1107,14 +1090,6 @@ func (container *Container) UnmountVolumes(forceSyscall bool) error { return nil } -func (container *Container) PrepareStorage() error { - return nil -} - -func (container *Container) CleanupStorage() error { - return nil -} - func (container *Container) networkMounts() []execdriver.Mount { var mounts []execdriver.Mount mode := "Z" diff --git a/daemon/container_windows.go b/daemon/container_windows.go index 425e1abe54..cea0268565 100644 --- a/daemon/container_windows.go +++ b/daemon/container_windows.go @@ -4,14 +4,9 @@ package daemon import ( "fmt" - "path/filepath" "strings" "github.com/docker/docker/daemon/execdriver" - "github.com/docker/docker/daemon/graphdriver/windows" - "github.com/docker/docker/image" - "github.com/docker/docker/pkg/archive" - "github.com/microsoft/hcsshim" ) // This is deliberately empty on Windows as the default path will be set by @@ -98,25 +93,27 @@ func populateCommand(c *Container, env []string) error { processConfig.Env = env - var layerFolder string var layerPaths []string - - // The following is specific to the Windows driver. We do this to - // enable VFS to continue operating for development purposes. - if wd, ok := c.daemon.driver.(*windows.WindowsGraphDriver); ok { - var err error - var img *image.Image - var ids []string - - if img, err = c.daemon.graph.Get(c.ImageID); err != nil { - return fmt.Errorf("Failed to graph.Get on ImageID %s - %s", c.ImageID, err) - } - if ids, err = c.daemon.graph.ParentLayerIds(img); err != nil { - return fmt.Errorf("Failed to get parentlayer ids %s", img.ID) - } - layerPaths = wd.LayerIdsToPaths(ids) - layerFolder = filepath.Join(wd.Info().HomeDir, filepath.Base(c.ID)) + img, err := c.daemon.graph.Get(c.ImageID) + if err != nil { + return fmt.Errorf("Failed to graph.Get on ImageID %s - %s", c.ImageID, err) } + for i := img; i != nil && err == nil; i, err = c.daemon.graph.GetParent(i) { + lp, err := c.daemon.driver.Get(i.ID, "") + if err != nil { + return fmt.Errorf("Failed to get layer path from graphdriver %s for ImageID %s - %s", c.daemon.driver.String(), i.ID, err) + } + layerPaths = append(layerPaths, lp) + err = c.daemon.driver.Put(i.ID) + if err != nil { + return fmt.Errorf("Failed to put layer path from graphdriver %s for ImageID %s - %s", c.daemon.driver.String(), i.ID, err) + } + } + m, err := c.daemon.driver.GetMetadata(c.ID) + if err != nil { + return fmt.Errorf("Failed to get layer metadata - %s", err) + } + layerFolder := m["dir"] // TODO Windows: Factor out remainder of unused fields. c.command = &execdriver.Command{ @@ -151,14 +148,6 @@ func (container *Container) AllocateNetwork() error { return nil } -func (container *Container) ExportRw() (archive.Archive, error) { - if container.IsRunning() { - return nil, fmt.Errorf("Cannot export a running container.") - } - // TODO Windows. Implementation (different to Linux) - return nil, nil -} - func (container *Container) UpdateNetwork() error { return nil } @@ -174,36 +163,6 @@ func (container *Container) UnmountVolumes(forceSyscall bool) error { return nil } -func (container *Container) PrepareStorage() error { - if wd, ok := container.daemon.driver.(*windows.WindowsGraphDriver); ok { - // Get list of paths to parent layers. - var ids []string - if container.ImageID != "" { - img, err := container.daemon.graph.Get(container.ImageID) - if err != nil { - return err - } - - ids, err = container.daemon.graph.ParentLayerIds(img) - if err != nil { - return err - } - } - - if err := hcsshim.PrepareLayer(wd.Info(), container.ID, wd.LayerIdsToPaths(ids)); err != nil { - return err - } - } - return nil -} - -func (container *Container) CleanupStorage() error { - if wd, ok := container.daemon.driver.(*windows.WindowsGraphDriver); ok { - return hcsshim.UnprepareLayer(wd.Info(), container.ID) - } - return nil -} - // prepareMountPoints is a no-op on Windows func (container *Container) prepareMountPoints() error { return nil diff --git a/daemon/daemon.go b/daemon/daemon.go index 4d6d43dc31..5b75aa1199 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -24,6 +24,7 @@ import ( "github.com/docker/docker/daemon/network" "github.com/docker/docker/graph" "github.com/docker/docker/image" + "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/broadcastwriter" "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/graphdb" @@ -683,6 +684,12 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo return nil, fmt.Errorf("Couldn't create Tag store repositories-%s: %s", d.driver.String(), err) } + if restorer, ok := d.driver.(graphdriver.ImageRestorer); ok { + if _, err := restorer.RestoreCustomImages(repositories, g); err != nil { + return nil, fmt.Errorf("Couldn't restore custom images: %s", err) + } + } + d.netController, err = initNetworkController(config) if err != nil { return nil, fmt.Errorf("Error initializing network controller: %v", err) @@ -840,6 +847,46 @@ func (daemon *Daemon) UnsubscribeToContainerStats(name string, ch chan interface return nil } +func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) { + initID := fmt.Sprintf("%s-init", container.ID) + return daemon.driver.Changes(container.ID, initID) +} + +func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) { + initID := fmt.Sprintf("%s-init", container.ID) + return daemon.driver.Diff(container.ID, initID) +} + +func (daemon *Daemon) createRootfs(container *Container) error { + // Step 1: create the container directory. + // This doubles as a barrier to avoid race conditions. + if err := os.Mkdir(container.root, 0700); err != nil { + return err + } + initID := fmt.Sprintf("%s-init", container.ID) + if err := daemon.driver.Create(initID, container.ImageID); err != nil { + return err + } + initPath, err := daemon.driver.Get(initID, "") + if err != nil { + return err + } + + if err := setupInitLayer(initPath); err != nil { + daemon.driver.Put(initID) + return err + } + + // We want to unmount init layer before we take snapshot of it + // for the actual container. + daemon.driver.Put(initID) + + if err := daemon.driver.Create(container.ID, initID); err != nil { + return err + } + return nil +} + // FIXME: this is a convenience function for integration tests // which need direct access to daemon.graph. // Once the tests switch to using engine and jobs, this method diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index ead54dece0..82f993aab6 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -14,7 +14,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/autogen/dockerversion" "github.com/docker/docker/daemon/graphdriver" - "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/parsers/kernel" @@ -39,16 +38,6 @@ const ( platformSupported = true ) -func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) { - initID := fmt.Sprintf("%s-init", container.ID) - return daemon.driver.Changes(container.ID, initID) -} - -func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) { - initID := fmt.Sprintf("%s-init", container.ID) - return daemon.driver.Diff(container.ID, initID) -} - func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error { var ( labelOpts []string @@ -74,36 +63,6 @@ func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error return err } -func (daemon *Daemon) createRootfs(container *Container) error { - // Step 1: create the container directory. - // This doubles as a barrier to avoid race conditions. - if err := os.Mkdir(container.root, 0700); err != nil { - return err - } - initID := fmt.Sprintf("%s-init", container.ID) - if err := daemon.driver.Create(initID, container.ImageID); err != nil { - return err - } - initPath, err := daemon.driver.Get(initID, "") - if err != nil { - return err - } - - if err := setupInitLayer(initPath); err != nil { - daemon.driver.Put(initID) - return err - } - - // We want to unmount init layer before we take snapshot of it - // for the actual container. - daemon.driver.Put(initID) - - if err := daemon.driver.Create(container.ID, initID); err != nil { - return err - } - return nil -} - func checkKernel() error { // Check for unsupported kernel versions // FIXME: it would be cleaner to not test for specific versions, but rather diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index da2d2e570b..fdd3b8ff32 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -5,14 +5,11 @@ import ( "os" "syscall" - "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/graphdriver" - "github.com/docker/docker/daemon/graphdriver/windows" - "github.com/docker/docker/pkg/archive" + _ "github.com/docker/docker/daemon/graphdriver/windows" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/runconfig" "github.com/docker/libnetwork" - "github.com/microsoft/hcsshim" ) const ( @@ -20,55 +17,11 @@ const ( platformSupported = true ) -func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) { - return daemon.driver.Changes(container.ID, container.ImageID) -} - -func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) { - return daemon.driver.Diff(container.ID, container.ImageID) -} - func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error { return nil } -func (daemon *Daemon) createRootfs(container *Container) error { - // Step 1: create the container directory. - // This doubles as a barrier to avoid race conditions. - if err := os.Mkdir(container.root, 0700); err != nil { - return err - } - - if wd, ok := daemon.driver.(*windows.WindowsGraphDriver); ok { - if container.ImageID != "" { - // Get list of paths to parent layers. - logrus.Debugln("createRootfs: Container has parent image:", container.ImageID) - img, err := daemon.graph.Get(container.ImageID) - if err != nil { - return err - } - - ids, err := daemon.graph.ParentLayerIds(img) - if err != nil { - return err - } - logrus.Debugf("Got image ids: %d", len(ids)) - - if err := hcsshim.CreateSandboxLayer(wd.Info(), container.ID, container.ImageID, wd.LayerIdsToPaths(ids)); err != nil { - return err - } - } else { - if err := daemon.driver.Create(container.ID, container.ImageID); err != nil { - return err - } - } - } else { - // Fall-back code path to allow the use of the VFS driver for development - if err := daemon.driver.Create(container.ID, container.ImageID); err != nil { - return err - } - - } +func setupInitLayer(initLayer string) error { return nil } diff --git a/daemon/delete.go b/daemon/delete.go index 30bfeddd82..3eeb5a282f 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "path" - "runtime" "github.com/Sirupsen/logrus" ) @@ -117,12 +116,9 @@ func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) { return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", daemon.driver, container.ID, err) } - // There will not be an -init on Windows, so don't fail by not attempting to delete it - if runtime.GOOS != "windows" { - initID := fmt.Sprintf("%s-init", container.ID) - if err := daemon.driver.Remove(initID); err != nil { - return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", daemon.driver, initID, err) - } + initID := fmt.Sprintf("%s-init", container.ID) + if err := daemon.driver.Remove(initID); err != nil { + return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", daemon.driver, initID, err) } if err = os.RemoveAll(container.root); err != nil { diff --git a/daemon/execdriver/windows/run.go b/daemon/execdriver/windows/run.go index 2757de14b3..a17ba79320 100644 --- a/daemon/execdriver/windows/run.go +++ b/daemon/execdriver/windows/run.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "strconv" "strings" @@ -100,8 +101,13 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba } for i := 0; i < len(c.LayerPaths); i++ { + _, filename := filepath.Split(c.LayerPaths[i]) + g, err := hcsshim.NameToGuid(filename) + if err != nil { + return execdriver.ExitStatus{ExitCode: -1}, err + } cu.Layers = append(cu.Layers, layer{ - ID: hcsshim.NewGUID(c.LayerPaths[i]).ToString(), + ID: g.ToString(), Path: c.LayerPaths[i], }) } diff --git a/daemon/graphdriver/driver_windows.go b/daemon/graphdriver/driver_windows.go index e5297ebb71..d0c255f3b9 100644 --- a/daemon/graphdriver/driver_windows.go +++ b/daemon/graphdriver/driver_windows.go @@ -1,19 +1,5 @@ package graphdriver -import ( - "github.com/docker/docker/pkg/archive" - "github.com/microsoft/hcsshim" -) - -type WindowsGraphDriver interface { - Driver - CopyDiff(id, sourceId string, parentLayerPaths []string) error - LayerIdsToPaths(ids []string) []string - Info() hcsshim.DriverInfo - Export(id string, parentLayerPaths []string) (archive.Archive, error) - Import(id string, layerData archive.Reader, parentLayerPaths []string) (int64, error) -} - var ( // Slice of drivers that should be used in order priority = []string{ diff --git a/daemon/graphdriver/imagerestorer.go b/daemon/graphdriver/imagerestorer.go new file mode 100644 index 0000000000..33c3da1c29 --- /dev/null +++ b/daemon/graphdriver/imagerestorer.go @@ -0,0 +1,30 @@ +package graphdriver + +import ( + "github.com/docker/docker/image" + "github.com/docker/docker/pkg/archive" +) + +// NOTE: These interfaces are used for implementing specific features of the Windows +// graphdriver implementation. The current versions are a short-term solution and +// likely to change or possibly be eliminated, so avoid using them outside of the Windows +// graphdriver code. + +// ImageRestorer interface allows the implementer to add a custom image to +// the graph and tagstore. +type ImageRestorer interface { + RestoreCustomImages(tagger Tagger, recorder Recorder) ([]string, error) +} + +// Tagger is an interface that exposes the TagStore.Tag function without needing +// to import graph. +type Tagger interface { + Tag(repoName, tag, imageName string, force bool) error +} + +// Recorder is an interface that exposes the Graph.Register and Graph.Exists +// functions without needing to import graph. +type Recorder interface { + Exists(id string) bool + Register(img *image.Image, layerData archive.Reader) error +} diff --git a/daemon/graphdriver/windows/windows.go b/daemon/graphdriver/windows/windows.go index 5c52db12c9..de26cbf718 100644 --- a/daemon/graphdriver/windows/windows.go +++ b/daemon/graphdriver/windows/windows.go @@ -3,17 +3,26 @@ package windows import ( + "crypto/sha512" + "encoding/json" "fmt" + "io/ioutil" "os" "path/filepath" + "runtime" + "strconv" + "strings" "sync" "time" "github.com/Sirupsen/logrus" + "github.com/docker/docker/autogen/dockerversion" "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/docker/image" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/pkg/random" "github.com/microsoft/hcsshim" ) @@ -27,7 +36,7 @@ const ( filterDriver ) -type WindowsGraphDriver struct { +type Driver struct { info hcsshim.DriverInfo sync.Mutex // Protects concurrent modification to active active map[string]int @@ -36,7 +45,7 @@ type WindowsGraphDriver struct { // New returns a new Windows storage filter driver. func InitFilter(home string, options []string) (graphdriver.Driver, error) { logrus.Debugf("WindowsGraphDriver InitFilter at %s", home) - d := &WindowsGraphDriver{ + d := &Driver{ info: hcsshim.DriverInfo{ HomeDir: home, Flavour: filterDriver, @@ -49,7 +58,7 @@ func InitFilter(home string, options []string) (graphdriver.Driver, error) { // New returns a new Windows differencing disk driver. func InitDiff(home string, options []string) (graphdriver.Driver, error) { logrus.Debugf("WindowsGraphDriver InitDiff at %s", home) - d := &WindowsGraphDriver{ + d := &Driver{ info: hcsshim.DriverInfo{ HomeDir: home, Flavour: diffDriver, @@ -59,11 +68,7 @@ func InitDiff(home string, options []string) (graphdriver.Driver, error) { return d, nil } -func (d *WindowsGraphDriver) Info() hcsshim.DriverInfo { - return d.info -} - -func (d *WindowsGraphDriver) String() string { +func (d *Driver) String() string { switch d.info.Flavour { case diffDriver: return "windowsdiff" @@ -74,7 +79,7 @@ func (d *WindowsGraphDriver) String() string { } } -func (d *WindowsGraphDriver) Status() [][2]string { +func (d *Driver) Status() [][2]string { return [][2]string{ {"Windows", ""}, } @@ -82,45 +87,137 @@ func (d *WindowsGraphDriver) Status() [][2]string { // Exists returns true if the given id is registered with // this driver -func (d *WindowsGraphDriver) Exists(id string) bool { - result, err := hcsshim.LayerExists(d.info, id) +func (d *Driver) Exists(id string) bool { + rId, err := d.resolveId(id) + if err != nil { + return false + } + result, err := hcsshim.LayerExists(d.info, rId) if err != nil { return false } return result } -func (d *WindowsGraphDriver) Create(id, parent string) error { - return hcsshim.CreateLayer(d.info, id, parent) +func (d *Driver) Create(id, parent string) error { + rPId, err := d.resolveId(parent) + if err != nil { + return err + } + + parentChain, err := d.getLayerChain(rPId) + if err != nil { + return err + } + + var layerChain []string + + parentIsInit := strings.HasSuffix(rPId, "-init") + + if !parentIsInit && rPId != "" { + parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId) + if err != nil { + return err + } + layerChain = []string{parentPath} + } + + layerChain = append(layerChain, parentChain...) + + if parentIsInit { + if len(layerChain) == 0 { + return fmt.Errorf("Cannot create a read/write layer without a parent layer.") + } + if err := hcsshim.CreateSandboxLayer(d.info, id, layerChain[0], layerChain); err != nil { + return err + } + } else { + if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil { + return err + } + } + + if _, err := os.Lstat(d.dir(parent)); err == nil { + if err := d.setLayerChain(id, layerChain); err != nil { + if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil { + logrus.Warnf("Failed to DestroyLayer %s: %s", id, err) + } + return err + } + } else if os.IsNotExist(err) { + // If the parent doesn't exist, this must be a special creation for an image + // registered at an alternate location. Use the parent id as the alternate ID. + if err := d.setId(id, parent); err != nil { + if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil { + logrus.Warnf("Failed to DestroyLayer %s: %s", id, err) + } + return err + } + } else { + if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil { + logrus.Warnf("Failed to DestroyLayer %s: %s", id, err) + } + return err + } + + return nil } -func (d *WindowsGraphDriver) dir(id string) string { +func (d *Driver) dir(id string) string { return filepath.Join(d.info.HomeDir, filepath.Base(id)) } // Remove unmounts and removes the dir information -func (d *WindowsGraphDriver) Remove(id string) error { - return hcsshim.DestroyLayer(d.info, id) +func (d *Driver) Remove(id string) error { + rId, err := d.resolveId(id) + if err != nil { + return err + } + + return hcsshim.DestroyLayer(d.info, rId) } // Get returns the rootfs path for the id. This will mount the dir at it's given path -func (d *WindowsGraphDriver) Get(id, mountLabel string) (string, error) { +func (d *Driver) Get(id, mountLabel string) (string, error) { + logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel) var dir string d.Lock() defer d.Unlock() - if d.active[id] == 0 { - if err := hcsshim.ActivateLayer(d.info, id); err != nil { + rId, err := d.resolveId(id) + if err != nil { + return "", err + } + + // Getting the layer paths must be done outside of the lock. + layerChain, err := d.getLayerChain(rId) + if err != nil { + return "", err + } + + if d.active[rId] == 0 { + if err := hcsshim.ActivateLayer(d.info, rId); err != nil { + return "", err + } + if err := hcsshim.PrepareLayer(d.info, rId, layerChain); err != nil { + if err2 := hcsshim.DeactivateLayer(d.info, rId); err2 != nil { + logrus.Warnf("Failed to Deactivate %s: %s", id, err) + } return "", err } } - mountPath, err := hcsshim.GetLayerMountPath(d.info, id) + mountPath, err := hcsshim.GetLayerMountPath(d.info, rId) if err != nil { + if err2 := hcsshim.DeactivateLayer(d.info, rId); err2 != nil { + logrus.Warnf("Failed to Deactivate %s: %s", id, err) + } return "", err } + d.active[rId]++ + // If the layer has a mount path, use that. Otherwise, use the // folder path. if mountPath != "" { @@ -129,61 +226,131 @@ func (d *WindowsGraphDriver) Get(id, mountLabel string) (string, error) { dir = d.dir(id) } - d.active[id]++ - return dir, nil } -func (d *WindowsGraphDriver) Put(id string) error { +func (d *Driver) Put(id string) error { logrus.Debugf("WindowsGraphDriver Put() id %s", id) + rId, err := d.resolveId(id) + if err != nil { + return err + } + d.Lock() defer d.Unlock() - if d.active[id] > 1 { - d.active[id]-- - } else if d.active[id] == 1 { - if err := hcsshim.DeactivateLayer(d.info, id); err != nil { + if d.active[rId] > 1 { + d.active[rId]-- + } else if d.active[rId] == 1 { + if err := hcsshim.UnprepareLayer(d.info, rId); err != nil { return err } - delete(d.active, id) + if err := hcsshim.DeactivateLayer(d.info, rId); err != nil { + return err + } + delete(d.active, rId) } return nil } -func (d *WindowsGraphDriver) Cleanup() error { +func (d *Driver) Cleanup() error { return nil } // Diff produces an archive of the changes between the specified // layer and its parent layer which may be "". -func (d *WindowsGraphDriver) Diff(id, parent string) (arch archive.Archive, err error) { - return nil, fmt.Errorf("The Windows graphdriver does not support Diff()") +func (d *Driver) Diff(id, parent string) (arch archive.Archive, err error) { + rId, err := d.resolveId(id) + if err != nil { + return + } + + // Getting the layer paths must be done outside of the lock. + layerChain, err := d.getLayerChain(rId) + if err != nil { + return + } + + d.Lock() + + // To support export, a layer must be activated but not prepared. + if d.info.Flavour == filterDriver { + if d.active[rId] == 0 { + if err = hcsshim.ActivateLayer(d.info, rId); err != nil { + d.Unlock() + return + } + defer func() { + if err := hcsshim.DeactivateLayer(d.info, rId); err != nil { + logrus.Warnf("Failed to Deactivate %s: %s", rId, err) + } + }() + } else { + if err = hcsshim.UnprepareLayer(d.info, rId); err != nil { + d.Unlock() + return + } + defer func() { + if err := hcsshim.PrepareLayer(d.info, rId, layerChain); err != nil { + logrus.Warnf("Failed to re-PrepareLayer %s: %s", rId, err) + } + }() + } + } + + d.Unlock() + + return d.exportLayer(rId, layerChain) } // Changes produces a list of changes between the specified layer // and its parent layer. If parent is "", then all changes will be ADD changes. -func (d *WindowsGraphDriver) Changes(id, parent string) ([]archive.Change, error) { +func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { return nil, fmt.Errorf("The Windows graphdriver does not support Changes()") } // ApplyDiff extracts the changeset from the given diff into the // layer with the specified id and parent, returning the size of the // new layer in bytes. -func (d *WindowsGraphDriver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) { - start := time.Now().UTC() - logrus.Debugf("WindowsGraphDriver ApplyDiff: Start untar layer") - - destination := d.dir(id) - if d.info.Flavour == diffDriver { - destination = filepath.Dir(destination) - } - - if size, err = chrootarchive.ApplyLayer(destination, diff); err != nil { +func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error) { + rPId, err := d.resolveId(parent) + if err != nil { + return + } + + if d.info.Flavour == diffDriver { + start := time.Now().UTC() + logrus.Debugf("WindowsGraphDriver ApplyDiff: Start untar layer") + destination := d.dir(id) + destination = filepath.Dir(destination) + if size, err = chrootarchive.ApplyUncompressedLayer(destination, diff); err != nil { + return + } + logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds()) + + return + } + + parentChain, err := d.getLayerChain(rPId) + if err != nil { + return + } + parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId) + if err != nil { + return + } + layerChain := []string{parentPath} + layerChain = append(layerChain, parentChain...) + + if size, err = d.importLayer(id, diff, layerChain); err != nil { + return + } + + if err = d.setLayerChain(id, layerChain); err != nil { return } - logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds()) return } @@ -191,8 +358,13 @@ func (d *WindowsGraphDriver) ApplyDiff(id, parent string, diff archive.Reader) ( // DiffSize calculates the changes between the specified layer // and its parent and returns the size in bytes of the changes // relative to its base filesystem directory. -func (d *WindowsGraphDriver) DiffSize(id, parent string) (size int64, err error) { - changes, err := d.Changes(id, parent) +func (d *Driver) DiffSize(id, parent string) (size int64, err error) { + rPId, err := d.resolveId(parent) + if err != nil { + return + } + + changes, err := d.Changes(id, rPId) if err != nil { return } @@ -206,65 +378,88 @@ func (d *WindowsGraphDriver) DiffSize(id, parent string) (size int64, err error) return archive.ChangesSize(layerFs, changes), nil } -func (d *WindowsGraphDriver) CopyDiff(sourceId, id string, parentLayerPaths []string) error { - d.Lock() - defer d.Unlock() - - if d.info.Flavour == filterDriver && d.active[sourceId] == 0 { - if err := hcsshim.ActivateLayer(d.info, sourceId); err != nil { - return err - } - defer func() { - err := hcsshim.DeactivateLayer(d.info, sourceId) - if err != nil { - logrus.Warnf("Failed to Deactivate %s: %s", sourceId, err) - } - }() - } - - return hcsshim.CopyLayer(d.info, sourceId, id, parentLayerPaths) -} - -func (d *WindowsGraphDriver) LayerIdsToPaths(ids []string) []string { - var paths []string - for _, id := range ids { - path, err := d.Get(id, "") - if err != nil { - logrus.Debug("LayerIdsToPaths: Error getting mount path for id", id, ":", err.Error()) - return nil - } - if d.Put(id) != nil { - logrus.Debug("LayerIdsToPaths: Error putting mount path for id", id, ":", err.Error()) - return nil - } - paths = append(paths, path) - } - return paths -} - -func (d *WindowsGraphDriver) GetMetadata(id string) (map[string]string, error) { - return nil, nil -} - -func (d *WindowsGraphDriver) Export(id string, parentLayerPaths []string) (arch archive.Archive, err error) { - layerFs, err := d.Get(id, "") +func (d *Driver) RestoreCustomImages(tagger graphdriver.Tagger, recorder graphdriver.Recorder) (imageIDs []string, err error) { + strData, err := hcsshim.GetSharedBaseImages() if err != nil { - return + return nil, fmt.Errorf("Failed to restore base images: %s", err) } - defer func() { - if err != nil { - d.Put(id) - } - }() - tempFolder := layerFs + "-temp" + type customImageInfo struct { + Name string + Version string + Path string + Size int64 + CreatedTime time.Time + } + type customImageInfoList struct { + Images []customImageInfo + } + + var infoData customImageInfoList + + if err = json.Unmarshal([]byte(strData), &infoData); err != nil { + err = fmt.Errorf("JSON unmarshal returned error=%s", err) + logrus.Error(err) + return nil, err + } + + for _, imageData := range infoData.Images { + _, folderName := filepath.Split(imageData.Path) + + // Use crypto hash of the foldername to generate a docker style id. + h := sha512.Sum384([]byte(folderName)) + id := fmt.Sprintf("%x", h[:32]) + + if !recorder.Exists(id) { + // Register the image. + img := &image.Image{ + ID: id, + Created: imageData.CreatedTime, + DockerVersion: dockerversion.VERSION, + Architecture: runtime.GOARCH, + OS: runtime.GOOS, + Size: imageData.Size, + } + + if err := recorder.Register(img, nil); err != nil { + return nil, err + } + + // Create tags for the new image. + if err := tagger.Tag(strings.ToLower(imageData.Name), imageData.Version, img.ID, true); err != nil { + return nil, err + } + + // Create the alternate ID file. + if err := d.setId(img.ID, folderName); err != nil { + return nil, err + } + + imageIDs = append(imageIDs, img.ID) + } + } + + return imageIDs, nil +} + +func (d *Driver) GetMetadata(id string) (map[string]string, error) { + m := make(map[string]string) + m["dir"] = d.dir(id) + return m, nil +} + +func (d *Driver) exportLayer(id string, parentLayerPaths []string) (arch archive.Archive, err error) { + layerFolder := d.dir(id) + + tempFolder := layerFolder + "-" + strconv.FormatUint(uint64(random.Rand.Uint32()), 10) if err = os.MkdirAll(tempFolder, 0755); err != nil { logrus.Errorf("Could not create %s %s", tempFolder, err) return } defer func() { if err != nil { - if err2 := os.RemoveAll(tempFolder); err2 != nil { + _, folderName := filepath.Split(tempFolder) + if err2 := hcsshim.DestroyLayer(d.info, folderName); err2 != nil { logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2) } } @@ -281,7 +476,8 @@ func (d *WindowsGraphDriver) Export(id string, parentLayerPaths []string) (arch return ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() d.Put(id) - if err2 := os.RemoveAll(tempFolder); err2 != nil { + _, folderName := filepath.Split(tempFolder) + if err2 := hcsshim.DestroyLayer(d.info, folderName); err2 != nil { logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2) } return err @@ -289,24 +485,17 @@ func (d *WindowsGraphDriver) Export(id string, parentLayerPaths []string) (arch } -func (d *WindowsGraphDriver) Import(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) { - layerFs, err := d.Get(id, "") - if err != nil { - return - } - defer func() { - if err != nil { - d.Put(id) - } - }() +func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) { + layerFolder := d.dir(id) - tempFolder := layerFs + "-temp" + tempFolder := layerFolder + "-" + strconv.FormatUint(uint64(random.Rand.Uint32()), 10) if err = os.MkdirAll(tempFolder, 0755); err != nil { logrus.Errorf("Could not create %s %s", tempFolder, err) return } defer func() { - if err2 := os.RemoveAll(tempFolder); err2 != nil { + _, folderName := filepath.Split(tempFolder) + if err2 := hcsshim.DestroyLayer(d.info, folderName); err2 != nil { logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2) } }() @@ -324,3 +513,54 @@ func (d *WindowsGraphDriver) Import(id string, layerData archive.Reader, parentL return } + +func (d *Driver) resolveId(id string) (string, error) { + content, err := ioutil.ReadFile(filepath.Join(d.dir(id), "layerId")) + if os.IsNotExist(err) { + return id, nil + } else if err != nil { + return "", err + } + return string(content), nil +} + +func (d *Driver) setId(id, altId string) error { + err := ioutil.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altId), 0600) + if err != nil { + return err + } + return nil +} + +func (d *Driver) getLayerChain(id string) ([]string, error) { + jPath := filepath.Join(d.dir(id), "layerchain.json") + content, err := ioutil.ReadFile(jPath) + if os.IsNotExist(err) { + return nil, nil + } else if err != nil { + return nil, fmt.Errorf("Unable to read layerchain file - %s", err) + } + + var layerChain []string + err = json.Unmarshal(content, &layerChain) + if err != nil { + return nil, fmt.Errorf("Failed to unmarshall layerchain json - %s", err) + } + + return layerChain, nil +} + +func (d *Driver) setLayerChain(id string, chain []string) error { + content, err := json.Marshal(&chain) + if err != nil { + return fmt.Errorf("Failed to marshall layerchain json - %s", err) + } + + jPath := filepath.Join(d.dir(id), "layerchain.json") + err = ioutil.WriteFile(jPath, content, 0600) + if err != nil { + return fmt.Errorf("Unable to write layerchain file - %s", err) + } + + return nil +} diff --git a/graph/graph.go b/graph/graph.go index 80386053bc..0f15b0bbc6 100644 --- a/graph/graph.go +++ b/graph/graph.go @@ -77,11 +77,12 @@ func (r *retainedLayers) Exists(layerID string) bool { // A Graph is a store for versioned filesystem images and the relationship between them. type Graph struct { - root string - idIndex *truncindex.TruncIndex - driver graphdriver.Driver - imageMutex imageMutex // protect images in driver. - retained *retainedLayers + root string + idIndex *truncindex.TruncIndex + driver graphdriver.Driver + imageMutex imageMutex // protect images in driver. + retained *retainedLayers + tarSplitDisabled bool } // file names for ./graph// @@ -117,6 +118,12 @@ func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) { driver: driver, retained: &retainedLayers{layerHolders: make(map[string]map[string]struct{})}, } + + // Windows does not currently support tarsplit functionality. + if runtime.GOOS == "windows" { + graph.tarSplitDisabled = true + } + if err := graph.restore(); err != nil { return nil, err } @@ -141,12 +148,6 @@ func (graph *Graph) restore() error { } } - 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 @@ -285,6 +286,13 @@ func (graph *Graph) Register(img *image.Image, layerData archive.Reader) (err er return nil } +func createRootFilesystemInDriver(graph *Graph, img *image.Image, layerData archive.Reader) 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 +} + // 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. @@ -442,6 +450,16 @@ func (graph *Graph) Heads() map[string]*image.Image { return heads } +// TarLayer returns a tar archive of the image's filesystem layer. +func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error) { + rdr, err := graph.assembleTarLayer(img) + if err != nil { + logrus.Debugf("[graph] TarLayer with traditional differ: %s", img.ID) + return graph.driver.Diff(img.ID, img.Parent) + } + return rdr, nil +} + func (graph *Graph) imageRoot(id string) string { return filepath.Join(graph.root, id) } @@ -535,30 +553,64 @@ func jsonPath(root string) string { return filepath.Join(root, jsonFileName) } -func (graph *Graph) disassembleAndApplyTarLayer(img *image.Image, layerData archive.Reader, root string) error { - // this is saving the tar-split metadata - mf, err := os.OpenFile(filepath.Join(root, tarDataFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600)) - if err != 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.Image, layerData archive.Reader, root string) (err error) { + // Store the layer. If layerData is not nil, unpack it into the new layer + if layerData != nil { + if err := graph.disassembleAndApplyTarLayer(img, layerData, root); err != nil { + return err + } + } + + if err := graph.saveSize(root, img.Size); err != nil { return err } - mfz := gzip.NewWriter(mf) - metaPacker := storage.NewJSONPacker(mfz) - defer mf.Close() - defer mfz.Close() - inflatedLayerData, err := archive.DecompressStream(layerData) + f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600)) if err != nil { return err } - // we're passing nil here for the file putter, because the ApplyDiff will - // handle the extraction of the archive - rdr, err := asm.NewInputTarStream(inflatedLayerData, metaPacker, nil) - if err != nil { - return err + defer f.Close() + + return json.NewEncoder(f).Encode(img) +} + +func (graph *Graph) disassembleAndApplyTarLayer(img *image.Image, layerData archive.Reader, root string) (err error) { + var ar archive.Reader + + if graph.tarSplitDisabled { + ar = layerData + } else { + // this is saving the tar-split metadata + mf, err := os.OpenFile(filepath.Join(root, tarDataFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600)) + if err != nil { + return err + } + + mfz := gzip.NewWriter(mf) + metaPacker := storage.NewJSONPacker(mfz) + defer mf.Close() + defer mfz.Close() + + inflatedLayerData, err := archive.DecompressStream(layerData) + if err != nil { + return err + } + + // we're passing nil here for the file putter, because the ApplyDiff will + // handle the extraction of the archive + rdr, err := asm.NewInputTarStream(inflatedLayerData, metaPacker, nil) + if err != nil { + return err + } + + ar = archive.Reader(rdr) } - if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, archive.Reader(rdr)); err != nil { + if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, ar); err != nil { return err } diff --git a/graph/graph_unix.go b/graph/graph_unix.go deleted file mode 100644 index aed0499bca..0000000000 --- a/graph/graph_unix.go +++ /dev/null @@ -1,120 +0,0 @@ -// +build !windows - -package graph - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - "syscall" - - "github.com/Sirupsen/logrus" - "github.com/docker/docker/image" - "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.Image, layerData archive.Reader) 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.Image, layerData archive.Reader, root string) (err error) { - // Store the layer. If layerData is not nil, unpack it into the new layer - if layerData != nil { - if err := graph.disassembleAndApplyTarLayer(img, layerData, root); err != nil { - return err - } - } - - if err := graph.saveSize(root, 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.Image) (arch archive.Archive, err error) { - rdr, err := graph.assembleTarLayer(img) - if err != nil { - logrus.Debugf("[graph] TarLayer with traditional differ: %s", img.ID) - return graph.driver.Diff(img.ID, img.Parent) - } - return rdr, nil -} diff --git a/graph/graph_windows.go b/graph/graph_windows.go deleted file mode 100644 index bf9e565867..0000000000 --- a/graph/graph_windows.go +++ /dev/null @@ -1,164 +0,0 @@ -// +build windows - -package graph - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/Sirupsen/logrus" - "github.com/docker/docker/daemon/graphdriver/windows" - "github.com/docker/docker/image" - "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.Image, layerData archive.Reader) 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.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.Image, layerData archive.Reader, 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, 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) - } - // 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 err := graph.disassembleAndApplyTarLayer(img, layerData, root); err != nil { - return err - } - } - - if err := graph.saveSize(root, 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.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)) - } - // 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. - rdr, err := graph.assembleTarLayer(img) - if err != nil { - logrus.Debugf("[graph] TarLayer with traditional differ: %s", img.ID) - return graph.driver.Diff(img.ID, img.Parent) - } - return rdr, nil -} diff --git a/hack/make/.integration-daemon-start b/hack/make/.integration-daemon-start index a8d3e9bde8..7c89a27d24 100644 --- a/hack/make/.integration-daemon-start +++ b/hack/make/.integration-daemon-start @@ -55,8 +55,8 @@ else export DOCKER_HOST="$DOCKER_TEST_HOST" fi -# give it a second to come up so it's "ready" -tries=10 +# give it a little time to come up so it's "ready" +tries=30 while ! docker version &> /dev/null; do (( tries-- )) if [ $tries -le 0 ]; then