зеркало из https://github.com/microsoft/docker.git
Windows: Graph driver implementation
Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
Родитель
b7e8169274
Коммит
52f4d09ffb
|
@ -24,7 +24,6 @@ import (
|
|||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/graph"
|
||||
imagepkg "github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/httputils"
|
||||
|
@ -229,12 +228,20 @@ 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
|
||||
}
|
||||
|
@ -455,7 +462,7 @@ func ContainsWildcards(name string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
|
||||
func (b *Builder) pullImage(name string) (*graph.Image, error) {
|
||||
remote, tag := parsers.ParseRepositoryTag(name)
|
||||
if tag == "" {
|
||||
tag = "latest"
|
||||
|
@ -493,7 +500,7 @@ func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
|
|||
return image, nil
|
||||
}
|
||||
|
||||
func (b *Builder) processImageFrom(img *imagepkg.Image) error {
|
||||
func (b *Builder) processImageFrom(img *graph.Image) error {
|
||||
b.image = img.ID
|
||||
|
||||
if img.Config != nil {
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -177,7 +178,7 @@ func (configFile *ConfigFile) Save() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
|
||||
if err := system.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
|
@ -16,7 +16,7 @@ type ContainerCommitConfig struct {
|
|||
|
||||
// Commit creates a new filesystem image from the current state of a container.
|
||||
// The image can optionally be tagged into a repository
|
||||
func (daemon *Daemon) Commit(container *Container, c *ContainerCommitConfig) (*image.Image, error) {
|
||||
func (daemon *Daemon) Commit(container *Container, c *ContainerCommitConfig) (*graph.Image, error) {
|
||||
if c.Pause && !container.IsPaused() {
|
||||
container.Pause()
|
||||
defer container.Unpause()
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/daemon/logger/jsonfilelog"
|
||||
"github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/broadcastwriter"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
|
@ -259,6 +259,13 @@ func (container *Container) Start() (err error) {
|
|||
if err := container.Mount(); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
if err := container.initializeNetworking(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -350,6 +357,10 @@ func (container *Container) cleanup() {
|
|||
|
||||
disableAllActiveLinks(container)
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -573,7 +584,7 @@ func (container *Container) Changes() ([]archive.Change, error) {
|
|||
return container.changes()
|
||||
}
|
||||
|
||||
func (container *Container) GetImage() (*image.Image, error) {
|
||||
func (container *Container) GetImage() (*graph.Image, error) {
|
||||
if container.daemon == nil {
|
||||
return nil, fmt.Errorf("Can't get image of unregistered container")
|
||||
}
|
||||
|
@ -666,8 +677,15 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
|||
if err != nil {
|
||||
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()
|
||||
return err
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build linux
|
||||
// +build !windows
|
||||
|
||||
package daemon
|
||||
|
||||
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/nat"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/pkg/ulimit"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
|
@ -970,7 +971,7 @@ func (container *Container) setupWorkingDirectory() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(pth, 0755); err != nil {
|
||||
if err := system.MkdirAll(pth, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -1109,3 +1110,11 @@ func (container *Container) UnmountVolumes(forceSyscall bool) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) PrepareStorage() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) CleanupStorage() error {
|
||||
return nil
|
||||
}
|
|
@ -4,10 +4,14 @@ package daemon
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/daemon/graphdriver/windows"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/microsoft/hcsshim"
|
||||
)
|
||||
|
||||
// This is deliberately empty on Windows as the default path will be set by
|
||||
|
@ -102,6 +106,26 @@ 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 *graph.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))
|
||||
}
|
||||
|
||||
// TODO Windows: Factor out remainder of unused fields.
|
||||
c.command = &execdriver.Command{
|
||||
ID: c.ID,
|
||||
|
@ -118,6 +142,8 @@ func populateCommand(c *Container, env []string) error {
|
|||
ProcessLabel: c.GetProcessLabel(),
|
||||
MountLabel: c.GetMountLabel(),
|
||||
FirstStart: !c.HasBeenStartedBefore,
|
||||
LayerFolder: layerFolder,
|
||||
LayerPaths: layerPaths,
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -165,3 +191,33 @@ func (container *Container) DisableLink(name string) {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/runconfig"
|
||||
|
@ -47,7 +46,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
|
|||
var (
|
||||
container *Container
|
||||
warnings []string
|
||||
img *image.Image
|
||||
img *graph.Image
|
||||
imgID string
|
||||
err error
|
||||
)
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/broadcastwriter"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
"github.com/docker/docker/pkg/graphdb"
|
||||
|
@ -338,7 +337,7 @@ func (daemon *Daemon) restore() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) error {
|
||||
func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *graph.Image) error {
|
||||
if img != nil && img.Config != nil {
|
||||
if err := runconfig.Merge(config, img.Config); err != nil {
|
||||
return err
|
||||
|
@ -879,7 +878,7 @@ func (daemon *Daemon) ContainerGraph() *graphdb.Database {
|
|||
return daemon.containerGraph
|
||||
}
|
||||
|
||||
func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*image.Image, error) {
|
||||
func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*graph.Image, error) {
|
||||
// Retrieve all images
|
||||
images, err := daemon.Graph().Map()
|
||||
if err != nil {
|
||||
|
@ -896,7 +895,7 @@ func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*i
|
|||
}
|
||||
|
||||
// Loop on the children of the given image and check the config
|
||||
var match *image.Image
|
||||
var match *graph.Image
|
||||
for elem := range imageMap[imgID] {
|
||||
img, ok := images[elem]
|
||||
if !ok {
|
||||
|
|
|
@ -6,11 +6,14 @@ import (
|
|||
"runtime"
|
||||
"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/pkg/parsers"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/microsoft/hcsshim"
|
||||
)
|
||||
|
||||
func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) {
|
||||
|
@ -31,8 +34,36 @@ func (daemon *Daemon) createRootfs(container *Container) error {
|
|||
if err := os.Mkdir(container.root, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := daemon.driver.Create(container.ID, container.ImageID); 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
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -101,6 +101,7 @@ func GetDriver(name, home string, options []string) (Driver, error) {
|
|||
if initFunc, exists := drivers[name]; exists {
|
||||
return initFunc(filepath.Join(home, name), options)
|
||||
}
|
||||
logrus.Errorf("Failed to GetDriver graph %s %s", name, home)
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
package graphdriver
|
||||
|
||||
type DiffDiskDriver interface {
|
||||
import (
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/microsoft/hcsshim"
|
||||
)
|
||||
|
||||
type WindowsGraphDriver interface {
|
||||
Driver
|
||||
CopyDiff(id, sourceId string) error
|
||||
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.ArchiveReader, parentLayerPaths []string) (int64, error)
|
||||
}
|
||||
|
||||
var (
|
||||
// Slice of drivers that should be used in order
|
||||
priority = []string{
|
||||
"windows",
|
||||
"windowsfilter",
|
||||
"windowsdiff",
|
||||
"vfs",
|
||||
}
|
||||
)
|
||||
|
|
|
@ -0,0 +1,326 @@
|
|||
//+build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/graphdriver"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/microsoft/hcsshim"
|
||||
)
|
||||
|
||||
func init() {
|
||||
graphdriver.Register("windowsfilter", InitFilter)
|
||||
graphdriver.Register("windowsdiff", InitDiff)
|
||||
}
|
||||
|
||||
const (
|
||||
diffDriver = iota
|
||||
filterDriver
|
||||
)
|
||||
|
||||
type WindowsGraphDriver struct {
|
||||
info hcsshim.DriverInfo
|
||||
sync.Mutex // Protects concurrent modification to active
|
||||
active map[string]int
|
||||
}
|
||||
|
||||
// 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{
|
||||
info: hcsshim.DriverInfo{
|
||||
HomeDir: home,
|
||||
Flavour: filterDriver,
|
||||
},
|
||||
active: make(map[string]int),
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// 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{
|
||||
info: hcsshim.DriverInfo{
|
||||
HomeDir: home,
|
||||
Flavour: diffDriver,
|
||||
},
|
||||
active: make(map[string]int),
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (d *WindowsGraphDriver) Info() hcsshim.DriverInfo {
|
||||
return d.info
|
||||
}
|
||||
|
||||
func (d *WindowsGraphDriver) String() string {
|
||||
switch d.info.Flavour {
|
||||
case diffDriver:
|
||||
return "windowsdiff"
|
||||
case filterDriver:
|
||||
return "windowsfilter"
|
||||
default:
|
||||
return "Unknown driver flavour"
|
||||
}
|
||||
}
|
||||
|
||||
func (d *WindowsGraphDriver) Status() [][2]string {
|
||||
return [][2]string{
|
||||
{"Windows", ""},
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (d *WindowsGraphDriver) Create(id, parent string) error {
|
||||
return hcsshim.CreateLayer(d.info, id, parent)
|
||||
}
|
||||
|
||||
func (d *WindowsGraphDriver) 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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
var dir string
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
if d.active[id] == 0 {
|
||||
if err := hcsshim.ActivateLayer(d.info, id); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
mountPath, err := hcsshim.GetLayerMountPath(d.info, id)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If the layer has a mount path, use that. Otherwise, use the
|
||||
// folder path.
|
||||
if mountPath != "" {
|
||||
dir = mountPath
|
||||
} else {
|
||||
dir = d.dir(id)
|
||||
}
|
||||
|
||||
d.active[id]++
|
||||
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func (d *WindowsGraphDriver) Put(id string) error {
|
||||
logrus.Debugf("WindowsGraphDriver Put() id %s", id)
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
delete(d.active, id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *WindowsGraphDriver) 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()")
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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.ArchiveReader) (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 {
|
||||
return
|
||||
}
|
||||
logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
layerFs, err := d.Get(id, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer d.Put(id)
|
||||
|
||||
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, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
d.Put(id)
|
||||
}
|
||||
}()
|
||||
|
||||
tempFolder := layerFs + "-temp"
|
||||
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 {
|
||||
logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = hcsshim.ExportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
archive, err := archive.Tar(tempFolder, archive.Uncompressed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return ioutils.NewReadCloserWrapper(archive, func() error {
|
||||
err := archive.Close()
|
||||
d.Put(id)
|
||||
if err2 := os.RemoveAll(tempFolder); err2 != nil {
|
||||
logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
|
||||
}
|
||||
return err
|
||||
}), nil
|
||||
|
||||
}
|
||||
|
||||
func (d *WindowsGraphDriver) Import(id string, layerData archive.ArchiveReader, parentLayerPaths []string) (size int64, err error) {
|
||||
layerFs, err := d.Get(id, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
d.Put(id)
|
||||
}
|
||||
}()
|
||||
|
||||
tempFolder := layerFs + "-temp"
|
||||
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 {
|
||||
logrus.Warnf("Couldn't clean-up tempFolder: %s %s", tempFolder, err2)
|
||||
}
|
||||
}()
|
||||
|
||||
start := time.Now().UTC()
|
||||
logrus.Debugf("Start untar layer")
|
||||
if size, err = chrootarchive.ApplyLayer(tempFolder, layerData); err != nil {
|
||||
return
|
||||
}
|
||||
logrus.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
|
||||
|
||||
if err = hcsshim.ImportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/utils"
|
||||
|
@ -160,7 +159,7 @@ func (daemon *Daemon) canDeleteImage(imgID string, force bool) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := daemon.graph.WalkHistory(parent, func(p image.Image) error {
|
||||
if err := daemon.graph.WalkHistory(parent, func(p graph.Image) error {
|
||||
if imgID == p.ID {
|
||||
if container.IsRunning() {
|
||||
if force {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/volume"
|
||||
"github.com/docker/docker/volume/local"
|
||||
|
@ -35,7 +36,7 @@ func (m *mountPoint) Setup() (string, error) {
|
|||
if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
if err := os.MkdirAll(m.Source, 0755); err != nil {
|
||||
if err := system.MkdirAll(m.Source, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/pidfile"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/pkg/timeutils"
|
||||
"github.com/docker/docker/pkg/tlsconfig"
|
||||
"github.com/docker/docker/registry"
|
||||
|
@ -51,7 +52,7 @@ func migrateKey() (err error) {
|
|||
}
|
||||
}()
|
||||
|
||||
if err := os.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil {
|
||||
if err := system.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil {
|
||||
return fmt.Errorf("Unable to create daemon configuration directory: %s", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,22 @@ type Graph struct {
|
|||
imageMutex imageMutex // protect images in driver.
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
ID string `json:"id"`
|
||||
Parent string `json:"parent,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
Created time.Time `json:"created"`
|
||||
Container string `json:"container,omitempty"`
|
||||
ContainerConfig runconfig.Config `json:"container_config,omitempty"`
|
||||
DockerVersion string `json:"docker_version,omitempty"`
|
||||
Author string `json:"author,omitempty"`
|
||||
Config *runconfig.Config `json:"config,omitempty"`
|
||||
Architecture string `json:"architecture,omitempty"`
|
||||
OS string `json:"os,omitempty"`
|
||||
Size int64
|
||||
graph Graph
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrDigestNotSet is used when request the digest for a layer
|
||||
// but the layer has no digest value or content to compute the
|
||||
|
@ -79,6 +95,13 @@ func (graph *Graph) restore() error {
|
|||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
|
||||
baseIds, err := graph.restoreBaseImages()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ids = append(ids, baseIds...)
|
||||
|
||||
graph.idIndex = truncindex.NewTruncIndex(ids)
|
||||
logrus.Debugf("Restored %d elements", len(ids))
|
||||
return nil
|
||||
|
@ -100,7 +123,7 @@ func (graph *Graph) Exists(id string) bool {
|
|||
}
|
||||
|
||||
// Get returns the image with the given id, or an error if the image doesn't exist.
|
||||
func (graph *Graph) Get(name string) (*image.Image, error) {
|
||||
func (graph *Graph) Get(name string) (*Image, error) {
|
||||
id, err := graph.idIndex.Get(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find image: %v", err)
|
||||
|
@ -128,8 +151,8 @@ func (graph *Graph) Get(name string) (*image.Image, error) {
|
|||
}
|
||||
|
||||
// Create creates a new image and registers it in the graph.
|
||||
func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*image.Image, error) {
|
||||
img := &image.Image{
|
||||
func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*Image, error) {
|
||||
img := &Image{
|
||||
ID: stringid.GenerateRandomID(),
|
||||
Comment: comment,
|
||||
Created: time.Now().UTC(),
|
||||
|
@ -153,7 +176,7 @@ func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, contain
|
|||
}
|
||||
|
||||
// Register imports a pre-existing image into the graph.
|
||||
func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader) (err error) {
|
||||
func (graph *Graph) Register(img *Image, layerData archive.ArchiveReader) (err error) {
|
||||
if err := image.ValidateID(img.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -197,9 +220,10 @@ func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader)
|
|||
}
|
||||
|
||||
// Create root filesystem in the driver
|
||||
if err := graph.driver.Create(img.ID, img.Parent); err != nil {
|
||||
return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
|
||||
if err := createRootFilesystemInDriver(graph, img, layerData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Apply the diff/layer
|
||||
if err := graph.storeImage(img, layerData, tmp); err != nil {
|
||||
return err
|
||||
|
@ -303,9 +327,9 @@ func (graph *Graph) Delete(name string) error {
|
|||
}
|
||||
|
||||
// Map returns a list of all images in the graph, addressable by ID.
|
||||
func (graph *Graph) Map() (map[string]*image.Image, error) {
|
||||
images := make(map[string]*image.Image)
|
||||
err := graph.walkAll(func(image *image.Image) {
|
||||
func (graph *Graph) Map() (map[string]*Image, error) {
|
||||
images := make(map[string]*Image)
|
||||
err := graph.walkAll(func(image *Image) {
|
||||
images[image.ID] = image
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -316,7 +340,7 @@ func (graph *Graph) Map() (map[string]*image.Image, error) {
|
|||
|
||||
// walkAll iterates over each image in the graph, and passes it to a handler.
|
||||
// The walking order is undetermined.
|
||||
func (graph *Graph) walkAll(handler func(*image.Image)) error {
|
||||
func (graph *Graph) walkAll(handler func(*Image)) error {
|
||||
files, err := ioutil.ReadDir(graph.root)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -336,9 +360,9 @@ func (graph *Graph) walkAll(handler func(*image.Image)) error {
|
|||
// If an image of id ID has 3 children images, then the value for key ID
|
||||
// will be a list of 3 images.
|
||||
// If an image has no children, it will not have an entry in the table.
|
||||
func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
|
||||
byParent := make(map[string][]*image.Image)
|
||||
err := graph.walkAll(func(img *image.Image) {
|
||||
func (graph *Graph) ByParent() (map[string][]*Image, error) {
|
||||
byParent := make(map[string][]*Image)
|
||||
err := graph.walkAll(func(img *Image) {
|
||||
parent, err := graph.Get(img.Parent)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -346,7 +370,7 @@ func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
|
|||
if children, exists := byParent[parent.ID]; exists {
|
||||
byParent[parent.ID] = append(children, img)
|
||||
} else {
|
||||
byParent[parent.ID] = []*image.Image{img}
|
||||
byParent[parent.ID] = []*Image{img}
|
||||
}
|
||||
})
|
||||
return byParent, err
|
||||
|
@ -354,13 +378,13 @@ func (graph *Graph) ByParent() (map[string][]*image.Image, error) {
|
|||
|
||||
// Heads returns all heads in the graph, keyed by id.
|
||||
// A head is an image which is not the parent of another image in the graph.
|
||||
func (graph *Graph) Heads() (map[string]*image.Image, error) {
|
||||
heads := make(map[string]*image.Image)
|
||||
func (graph *Graph) Heads() (map[string]*Image, error) {
|
||||
heads := make(map[string]*Image)
|
||||
byParent, err := graph.ByParent()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = graph.walkAll(func(image *image.Image) {
|
||||
err = graph.walkAll(func(image *Image) {
|
||||
// If it's not in the byParent lookup table, then
|
||||
// it's not a parent -> so it's a head!
|
||||
if _, exists := byParent[image.ID]; !exists {
|
||||
|
@ -374,33 +398,8 @@ func (graph *Graph) imageRoot(id string) string {
|
|||
return filepath.Join(graph.root, id)
|
||||
}
|
||||
|
||||
// storeImage stores file system layer data for the given image to the
|
||||
// graph's storage driver. Image metadata is stored in a file
|
||||
// at the specified root directory.
|
||||
func (graph *Graph) storeImage(img *image.Image, layerData archive.ArchiveReader, root string) (err error) {
|
||||
// Store the layer. If layerData is not nil, unpack it into the new layer
|
||||
if layerData != nil {
|
||||
if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := graph.saveSize(root, int(img.Size)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
return json.NewEncoder(f).Encode(img)
|
||||
}
|
||||
|
||||
// loadImage fetches the image with the given id from the graph.
|
||||
func (graph *Graph) loadImage(id string) (*image.Image, error) {
|
||||
func (graph *Graph) loadImage(id string) (*Image, error) {
|
||||
root := graph.imageRoot(id)
|
||||
|
||||
// Open the JSON file to decode by streaming
|
||||
|
@ -410,7 +409,7 @@ func (graph *Graph) loadImage(id string) (*image.Image, error) {
|
|||
}
|
||||
defer jsonSource.Close()
|
||||
|
||||
img := &image.Image{}
|
||||
img := &Image{}
|
||||
dec := json.NewDecoder(jsonSource)
|
||||
|
||||
// Decode the JSON data
|
||||
|
@ -488,7 +487,13 @@ func jsonPath(root string) string {
|
|||
return filepath.Join(root, "json")
|
||||
}
|
||||
|
||||
// TarLayer returns a tar archive of the image's filesystem layer.
|
||||
func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error) {
|
||||
return graph.driver.Diff(img.ID, img.Parent)
|
||||
// Build an Image object from raw json data
|
||||
func NewImgJSON(src []byte) (*Image, error) {
|
||||
ret := &Image{}
|
||||
|
||||
// FIXME: Is there a cleaner way to "purify" the input json?
|
||||
if err := json.Unmarshal(src, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ func TestInterruptedRegister(t *testing.T) {
|
|||
graph, _ := tempGraph(t)
|
||||
defer nukeGraph(graph)
|
||||
badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
|
||||
image := &image.Image{
|
||||
image := &Image{
|
||||
ID: stringid.GenerateRandomID(),
|
||||
Comment: "testing",
|
||||
Created: time.Now(),
|
||||
|
@ -128,7 +128,7 @@ func TestRegister(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
image := &image.Image{
|
||||
image := &Image{
|
||||
ID: stringid.GenerateRandomID(),
|
||||
Comment: "testing",
|
||||
Created: time.Now(),
|
||||
|
@ -232,19 +232,19 @@ func TestByParent(t *testing.T) {
|
|||
|
||||
graph, _ := tempGraph(t)
|
||||
defer nukeGraph(graph)
|
||||
parentImage := &image.Image{
|
||||
parentImage := &Image{
|
||||
ID: stringid.GenerateRandomID(),
|
||||
Comment: "parent",
|
||||
Created: time.Now(),
|
||||
Parent: "",
|
||||
}
|
||||
childImage1 := &image.Image{
|
||||
childImage1 := &Image{
|
||||
ID: stringid.GenerateRandomID(),
|
||||
Comment: "child1",
|
||||
Created: time.Now(),
|
||||
Parent: parentImage.ID,
|
||||
}
|
||||
childImage2 := &image.Image{
|
||||
childImage2 := &Image{
|
||||
ID: stringid.GenerateRandomID(),
|
||||
Comment: "child2",
|
||||
Created: time.Now(),
|
||||
|
@ -264,7 +264,7 @@ func TestByParent(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func createTestImage(graph *Graph, t *testing.T) *image.Image {
|
||||
func createTestImage(graph *Graph, t *testing.T) *Image {
|
||||
archive, err := fakeTar()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
// +build !windows
|
||||
|
||||
package graph
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
)
|
||||
|
||||
// setupInitLayer populates a directory with mountpoints suitable
|
||||
// for bind-mounting dockerinit into the container. The mountpoint is simply an
|
||||
// empty file at /.dockerinit
|
||||
//
|
||||
// This extra layer is used by all containers as the top-most ro layer. It protects
|
||||
// the container from unwanted side-effects on the rw layer.
|
||||
func SetupInitLayer(initLayer string) error {
|
||||
for pth, typ := range map[string]string{
|
||||
"/dev/pts": "dir",
|
||||
"/dev/shm": "dir",
|
||||
"/proc": "dir",
|
||||
"/sys": "dir",
|
||||
"/.dockerinit": "file",
|
||||
"/.dockerenv": "file",
|
||||
"/etc/resolv.conf": "file",
|
||||
"/etc/hosts": "file",
|
||||
"/etc/hostname": "file",
|
||||
"/dev/console": "file",
|
||||
"/etc/mtab": "/proc/mounts",
|
||||
} {
|
||||
parts := strings.Split(pth, "/")
|
||||
prev := "/"
|
||||
for _, p := range parts[1:] {
|
||||
prev = filepath.Join(prev, p)
|
||||
syscall.Unlink(filepath.Join(initLayer, prev))
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err := system.MkdirAll(filepath.Join(initLayer, filepath.Dir(pth)), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
switch typ {
|
||||
case "dir":
|
||||
if err := system.MkdirAll(filepath.Join(initLayer, pth), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
case "file":
|
||||
f, err := os.OpenFile(filepath.Join(initLayer, pth), os.O_CREATE, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
default:
|
||||
if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Layer is ready to use, if it wasn't before.
|
||||
return nil
|
||||
}
|
||||
|
||||
func createRootFilesystemInDriver(graph *Graph, img *Image, layerData archive.ArchiveReader) error {
|
||||
if err := graph.driver.Create(img.ID, img.Parent); err != nil {
|
||||
return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (graph *Graph) restoreBaseImages() ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// storeImage stores file system layer data for the given image to the
|
||||
// graph's storage driver. Image metadata is stored in a file
|
||||
// at the specified root directory.
|
||||
func (graph *Graph) storeImage(img *Image, layerData archive.ArchiveReader, root string) (err error) {
|
||||
// Store the layer. If layerData is not nil, unpack it into the new layer
|
||||
if layerData != nil {
|
||||
if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := graph.saveSize(root, int(img.Size)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
return json.NewEncoder(f).Encode(img)
|
||||
}
|
||||
|
||||
// TarLayer returns a tar archive of the image's filesystem layer.
|
||||
func (graph *Graph) TarLayer(img *Image) (arch archive.Archive, err error) {
|
||||
return graph.driver.Diff(img.ID, img.Parent)
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
// +build windows
|
||||
|
||||
package graph
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/graphdriver/windows"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
)
|
||||
|
||||
// setupInitLayer populates a directory with mountpoints suitable
|
||||
// for bind-mounting dockerinit into the container. T
|
||||
func SetupInitLayer(initLayer string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createRootFilesystemInDriver(graph *Graph, img *Image, layerData archive.ArchiveReader) error {
|
||||
if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
|
||||
if img.Container != "" && layerData == nil {
|
||||
logrus.Debugf("Copying from container %s.", img.Container)
|
||||
|
||||
var ids []string
|
||||
if img.Parent != "" {
|
||||
parentImg, err := graph.Get(img.Parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ids, err = graph.ParentLayerIds(parentImg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := wd.CopyDiff(img.Container, img.ID, wd.LayerIdsToPaths(ids)); err != nil {
|
||||
return fmt.Errorf("Driver %s failed to copy image rootfs %s: %s", graph.driver, img.Container, err)
|
||||
}
|
||||
} else if img.Parent == "" {
|
||||
if err := graph.driver.Create(img.ID, img.Parent); err != nil {
|
||||
return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This fallback allows the use of VFS during daemon development.
|
||||
if err := graph.driver.Create(img.ID, img.Parent); err != nil {
|
||||
return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (graph *Graph) restoreBaseImages() ([]string, error) {
|
||||
// TODO Windows. This needs implementing (@swernli)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ParentLayerIds returns a list of all parent image IDs for the given image.
|
||||
func (graph *Graph) ParentLayerIds(img *Image) (ids []string, err error) {
|
||||
for i := img; i != nil && err == nil; i, err = graph.GetParent(i) {
|
||||
ids = append(ids, i.ID)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// storeImage stores file system layer data for the given image to the
|
||||
// graph's storage driver. Image metadata is stored in a file
|
||||
// at the specified root directory.
|
||||
func (graph *Graph) storeImage(img *Image, layerData archive.ArchiveReader, root string) (err error) {
|
||||
|
||||
if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
|
||||
// Store the layer. If layerData is not nil and this isn't a base image,
|
||||
// unpack it into the new layer
|
||||
if layerData != nil && img.Parent != "" {
|
||||
var ids []string
|
||||
if img.Parent != "" {
|
||||
parentImg, err := graph.Get(img.Parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ids, err = graph.ParentLayerIds(parentImg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if img.Size, err = wd.Import(img.ID, layerData, wd.LayerIdsToPaths(ids)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := graph.saveSize(root, int(img.Size)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
return json.NewEncoder(f).Encode(img)
|
||||
} else {
|
||||
// We keep this functionality here so that we can still work with the
|
||||
// VFS driver during development. This will not be used for actual running
|
||||
// of Windows containers. Without this code, it would not be possible to
|
||||
// docker pull using the VFS driver.
|
||||
|
||||
// Store the layer. If layerData is not nil, unpack it into the new layer
|
||||
if layerData != nil {
|
||||
if img.Size, err = graph.driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := graph.saveSize(root, int(img.Size)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
return json.NewEncoder(f).Encode(img)
|
||||
}
|
||||
}
|
||||
|
||||
// TarLayer returns a tar archive of the image's filesystem layer.
|
||||
func (graph *Graph) TarLayer(img *Image) (arch archive.Archive, err error) {
|
||||
if wd, ok := graph.driver.(*windows.WindowsGraphDriver); ok {
|
||||
var ids []string
|
||||
if img.Parent != "" {
|
||||
parentImg, err := graph.Get(img.Parent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ids, err = graph.ParentLayerIds(parentImg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return wd.Export(img.ID, wd.LayerIdsToPaths(ids))
|
||||
} else {
|
||||
// We keep this functionality here so that we can still work with the VFS
|
||||
// driver during development. VFS is not supported (and just will not work)
|
||||
// for Windows containers.
|
||||
return graph.driver.Diff(img.ID, img.Parent)
|
||||
}
|
||||
}
|
|
@ -5,13 +5,12 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/utils"
|
||||
)
|
||||
|
||||
// WalkHistory calls the handler function for each image in the
|
||||
// provided images lineage starting from immediate parent.
|
||||
func (graph *Graph) WalkHistory(img *image.Image, handler func(image.Image) error) (err error) {
|
||||
func (graph *Graph) WalkHistory(img *Image, handler func(Image) error) (err error) {
|
||||
currentImg := img
|
||||
for currentImg != nil {
|
||||
if handler != nil {
|
||||
|
@ -29,7 +28,7 @@ func (graph *Graph) WalkHistory(img *image.Image, handler func(image.Image) erro
|
|||
|
||||
// depth returns the number of parents for a
|
||||
// current image
|
||||
func (graph *Graph) depth(img *image.Image) (int, error) {
|
||||
func (graph *Graph) depth(img *Image) (int, error) {
|
||||
var (
|
||||
count = 0
|
||||
parent = img
|
||||
|
@ -54,7 +53,7 @@ const MaxImageDepth = 127
|
|||
// CheckDepth returns an error if the depth of an image, as returned
|
||||
// by ImageDepth, is too large to support creating a container from it
|
||||
// on this daemon.
|
||||
func (graph *Graph) CheckDepth(img *image.Image) error {
|
||||
func (graph *Graph) CheckDepth(img *Image) error {
|
||||
// We add 2 layers to the depth because the container's rw and
|
||||
// init layer add to the restriction
|
||||
depth, err := graph.depth(img)
|
||||
|
@ -86,7 +85,7 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
|
|||
|
||||
history := []*types.ImageHistory{}
|
||||
|
||||
err = s.graph.WalkHistory(foundImage, func(img image.Image) error {
|
||||
err = s.graph.WalkHistory(foundImage, func(img Image) error {
|
||||
history = append(history, &types.ImageHistory{
|
||||
ID: img.ID,
|
||||
Created: img.Created.Unix(),
|
||||
|
@ -101,14 +100,14 @@ func (s *TagStore) History(name string) ([]*types.ImageHistory, error) {
|
|||
return history, err
|
||||
}
|
||||
|
||||
func (graph *Graph) GetParent(img *image.Image) (*image.Image, error) {
|
||||
func (graph *Graph) GetParent(img *Image) (*Image, error) {
|
||||
if img.Parent == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return graph.Get(img.Parent)
|
||||
}
|
||||
|
||||
func (graph *Graph) GetParentsSize(img *image.Image, size int64) int64 {
|
||||
func (graph *Graph) GetParentsSize(img *Image, size int64) int64 {
|
||||
parentImage, err := graph.GetParent(img)
|
||||
if err != nil || parentImage == nil {
|
||||
return size
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/parsers/filters"
|
||||
"github.com/docker/docker/utils"
|
||||
)
|
||||
|
@ -32,7 +31,7 @@ func (r ByCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
|
|||
|
||||
func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) {
|
||||
var (
|
||||
allImages map[string]*image.Image
|
||||
allImages map[string]*Image
|
||||
err error
|
||||
filtTagged = true
|
||||
filtLabel = false
|
||||
|
|
|
@ -98,7 +98,7 @@ func (s *TagStore) recursiveLoad(address, tmpImageDir string) error {
|
|||
logrus.Debugf("Error reading embedded tar", err)
|
||||
return err
|
||||
}
|
||||
img, err := image.NewImgJSON(imageJson)
|
||||
img, err := NewImgJSON(imageJson)
|
||||
if err != nil {
|
||||
logrus.Debugf("Error unmarshalling json", err)
|
||||
return err
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
|
@ -124,7 +123,7 @@ func TestManifestTarsumCache(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
img := &image.Image{ID: testManifestImageID}
|
||||
img := &Image{ID: testManifestImageID}
|
||||
if err := store.graph.Register(img, archive); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -190,7 +189,7 @@ func TestManifestDigestCheck(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
img := &image.Image{ID: testManifestImageID}
|
||||
img := &Image{ID: testManifestImageID}
|
||||
if err := store.graph.Register(img, archive); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/progressreader"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
|
@ -387,7 +386,7 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
|
|||
imgJSON []byte
|
||||
imgSize int
|
||||
err error
|
||||
img *image.Image
|
||||
img *Image
|
||||
)
|
||||
retries := 5
|
||||
for j := 1; j <= retries; j++ {
|
||||
|
@ -399,7 +398,7 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
|
|||
time.Sleep(time.Duration(j) * 500 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
img, err = image.NewImgJSON(imgJSON)
|
||||
img, err = NewImgJSON(imgJSON)
|
||||
layersDownloaded = true
|
||||
if err != nil && j == retries {
|
||||
out.Write(sf.FormatProgress(stringid.TruncateID(id), "Error pulling dependent layers", nil))
|
||||
|
@ -540,7 +539,7 @@ func (s *TagStore) pullV2Tag(r *registry.Session, out io.Writer, endpoint *regis
|
|||
// downloadInfo is used to pass information from download to extractor
|
||||
type downloadInfo struct {
|
||||
imgJSON []byte
|
||||
img *image.Image
|
||||
img *Image
|
||||
digest digest.Digest
|
||||
tmpFile *os.File
|
||||
length int64
|
||||
|
@ -556,7 +555,7 @@ func (s *TagStore) pullV2Tag(r *registry.Session, out io.Writer, endpoint *regis
|
|||
imgJSON = []byte(manifest.History[i].V1Compatibility)
|
||||
)
|
||||
|
||||
img, err := image.NewImgJSON(imgJSON)
|
||||
img, err := NewImgJSON(imgJSON)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to parse json: %s", err)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/progressreader"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
|
@ -349,7 +348,7 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o
|
|||
}
|
||||
|
||||
layersSeen := make(map[string]bool)
|
||||
layers := []*image.Image{}
|
||||
layers := []*Image{}
|
||||
for ; layer != nil; layer, err = s.graph.GetParent(layer) {
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -445,7 +444,7 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o
|
|||
}
|
||||
|
||||
// PushV2Image pushes the image content to the v2 registry, first buffering the contents to disk
|
||||
func (s *TagStore) pushV2Image(r *registry.Session, img *image.Image, endpoint *registry.Endpoint, imageName string, sf *streamformatter.StreamFormatter, out io.Writer, auth *registry.RequestAuthorization) (digest.Digest, error) {
|
||||
func (s *TagStore) pushV2Image(r *registry.Session, img *Image, endpoint *registry.Endpoint, imageName string, sf *streamformatter.StreamFormatter, out io.Writer, auth *registry.RequestAuthorization) (digest.Digest, error) {
|
||||
out.Write(sf.FormatProgress(stringid.TruncateID(img.ID), "Buffering to Disk", nil))
|
||||
|
||||
image, err := s.graph.Get(img.ID)
|
||||
|
|
|
@ -3,6 +3,7 @@ package graph
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/types"
|
||||
|
@ -58,17 +59,21 @@ func (s *TagStore) Lookup(name string) (*types.ImageInspect, error) {
|
|||
// ImageTarLayer return the tarLayer of the image
|
||||
func (s *TagStore) ImageTarLayer(name string, dest io.Writer) error {
|
||||
if image, err := s.LookupImage(name); err == nil && image != nil {
|
||||
fs, err := s.graph.TarLayer(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fs.Close()
|
||||
// On Windows, the base layer cannot be exported
|
||||
if runtime.GOOS != "windows" || image.Parent != "" {
|
||||
|
||||
written, err := io.Copy(dest, fs)
|
||||
if err != nil {
|
||||
return err
|
||||
fs, err := s.graph.TarLayer(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fs.Close()
|
||||
|
||||
written, err := io.Copy(dest, fs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("rendered layer for %s of [%d] size", image.ID, written)
|
||||
}
|
||||
logrus.Debugf("rendered layer for %s of [%d] size", image.ID, written)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("No such image: %s", name)
|
||||
|
|
|
@ -15,7 +15,6 @@ import (
|
|||
|
||||
"github.com/docker/docker/daemon/events"
|
||||
"github.com/docker/docker/graph/tags"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/registry"
|
||||
|
@ -126,7 +125,7 @@ func (store *TagStore) reload() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (store *TagStore) LookupImage(name string) (*image.Image, error) {
|
||||
func (store *TagStore) LookupImage(name string) (*Image, error) {
|
||||
// FIXME: standardize on returning nil when the image doesn't exist, and err for everything else
|
||||
// (so we can pass all errors here)
|
||||
repoName, ref := parsers.ParseRepositoryTag(name)
|
||||
|
@ -135,7 +134,7 @@ func (store *TagStore) LookupImage(name string) (*image.Image, error) {
|
|||
}
|
||||
var (
|
||||
err error
|
||||
img *image.Image
|
||||
img *Image
|
||||
)
|
||||
|
||||
img, err = store.GetImage(repoName, ref)
|
||||
|
@ -330,7 +329,7 @@ func (store *TagStore) Get(repoName string) (Repository, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error) {
|
||||
func (store *TagStore) GetImage(repoName, refOrID string) (*Image, error) {
|
||||
repo, err := store.Get(repoName)
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/docker/docker/daemon/events"
|
||||
"github.com/docker/docker/daemon/graphdriver"
|
||||
_ "github.com/docker/docker/daemon/graphdriver/vfs" // import the vfs driver so it is used in the tests
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/trust"
|
||||
"github.com/docker/docker/utils"
|
||||
)
|
||||
|
@ -80,7 +79,7 @@ func mkTestTagStore(root string, t *testing.T) *TagStore {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
img := &image.Image{ID: testOfficialImageID}
|
||||
img := &Image{ID: testOfficialImageID}
|
||||
if err := graph.Register(img, officialArchive); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -91,7 +90,7 @@ func mkTestTagStore(root string, t *testing.T) *TagStore {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
img = &image.Image{ID: testPrivateImageID}
|
||||
img = &Image{ID: testPrivateImageID}
|
||||
if err := graph.Register(img, privateArchive); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,42 +1,12 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
|
||||
|
||||
type Image struct {
|
||||
ID string `json:"id"`
|
||||
Parent string `json:"parent,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
Created time.Time `json:"created"`
|
||||
Container string `json:"container,omitempty"`
|
||||
ContainerConfig runconfig.Config `json:"container_config,omitempty"`
|
||||
DockerVersion string `json:"docker_version,omitempty"`
|
||||
Author string `json:"author,omitempty"`
|
||||
Config *runconfig.Config `json:"config,omitempty"`
|
||||
Architecture string `json:"architecture,omitempty"`
|
||||
OS string `json:"os,omitempty"`
|
||||
Size int64
|
||||
}
|
||||
|
||||
// Build an Image object from raw json data
|
||||
func NewImgJSON(src []byte) (*Image, error) {
|
||||
ret := &Image{}
|
||||
|
||||
// FIXME: Is there a cleaner way to "purify" the input json?
|
||||
if err := json.Unmarshal(src, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Check wheather id is a valid image ID or not
|
||||
func ValidateID(id string) error {
|
||||
if ok := validHex.MatchString(id); !ok {
|
||||
|
|
Загрузка…
Ссылка в новой задаче