mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #15579 from Microsoft/10662-graph
Windows: Graph remove custom interface, add central store
This commit is contained in:
commit
2e7b088164
16 changed files with 551 additions and 641 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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],
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
30
daemon/graphdriver/imagerestorer.go
Normal file
30
daemon/graphdriver/imagerestorer.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
104
graph/graph.go
104
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/<ID>/
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue