diff --git a/components/engine/container/container.go b/components/engine/container/container.go index a076e80746..4e1fa918dd 100644 --- a/components/engine/container/container.go +++ b/components/engine/container/container.go @@ -37,6 +37,7 @@ import ( "github.com/docker/docker/restartmanager" "github.com/docker/docker/runconfig" "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/docker/go-connections/nat" units "github.com/docker/go-units" "github.com/docker/libnetwork" @@ -94,7 +95,7 @@ type Container struct { RestartCount int HasBeenStartedBefore bool HasBeenManuallyStopped bool // used for unless-stopped restart policy - MountPoints map[string]*volume.MountPoint + MountPoints map[string]*volumemounts.MountPoint HostConfig *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable ExecCommands *exec.Store `json:"-"` DependencyStore agentexec.DependencyGetter `json:"-"` @@ -128,7 +129,7 @@ func NewBaseContainer(id, root string) *Container { State: NewState(), ExecCommands: exec.NewStore(), Root: root, - MountPoints: make(map[string]*volume.MountPoint), + MountPoints: make(map[string]*volumemounts.MountPoint), StreamConfig: stream.NewConfig(), attachContext: &attachContext{}, } @@ -450,8 +451,8 @@ func (container *Container) AddMountPointWithVolume(destination string, vol volu if operatingSystem == "" { operatingSystem = runtime.GOOS } - volumeParser := volume.NewParser(operatingSystem) - container.MountPoints[destination] = &volume.MountPoint{ + volumeParser := volumemounts.NewParser(operatingSystem) + container.MountPoints[destination] = &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Name: vol.Name(), Driver: vol.DriverName(), diff --git a/components/engine/container/container_unix.go b/components/engine/container/container_unix.go index e5cecf3166..9397cdf60b 100644 --- a/components/engine/container/container_unix.go +++ b/components/engine/container/container_unix.go @@ -15,6 +15,7 @@ import ( "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -61,7 +62,7 @@ func (container *Container) BuildHostnameFile() error { func (container *Container) NetworkMounts() []Mount { var mounts []Mount shared := container.HostConfig.NetworkMode.IsContainer() - parser := volume.NewParser(container.OS) + parser := volumemounts.NewParser(container.OS) if container.ResolvConfPath != "" { if _, err := os.Stat(container.ResolvConfPath); err != nil { logrus.Warnf("ResolvConfPath set to %q, but can't stat this filename (err = %v); skipping", container.ResolvConfPath, err) @@ -198,7 +199,7 @@ func (container *Container) UnmountIpcMount(unmount func(pth string) error) erro // IpcMounts returns the list of IPC mounts func (container *Container) IpcMounts() []Mount { var mounts []Mount - parser := volume.NewParser(container.OS) + parser := volumemounts.NewParser(container.OS) if container.HasMountFor("/dev/shm") { return mounts @@ -402,7 +403,7 @@ func copyExistingContents(source, destination string) error { // TmpfsMounts returns the list of tmpfs mounts func (container *Container) TmpfsMounts() ([]Mount, error) { - parser := volume.NewParser(container.OS) + parser := volumemounts.NewParser(container.OS) var mounts []Mount for dest, data := range container.HostConfig.Tmpfs { mounts = append(mounts, Mount{ diff --git a/components/engine/daemon/archive_unix.go b/components/engine/daemon/archive_unix.go index ca33e7ac9a..50e6fe24be 100644 --- a/components/engine/daemon/archive_unix.go +++ b/components/engine/daemon/archive_unix.go @@ -4,7 +4,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( "github.com/docker/docker/container" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" ) // checkIfPathIsInAVolume checks if the path is in a volume. If it is, it @@ -12,7 +12,7 @@ import ( // cannot be configured with a read-only rootfs. func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) { var toVolume bool - parser := volume.NewParser(container.OS) + parser := volumemounts.NewParser(container.OS) for _, mnt := range container.MountPoints { if toVolume = parser.HasResource(mnt, absPath); toVolume { if mnt.RW { diff --git a/components/engine/daemon/container.go b/components/engine/daemon/container.go index b1ae3daa94..c8e2053970 100644 --- a/components/engine/daemon/container.go +++ b/components/engine/daemon/container.go @@ -20,7 +20,7 @@ import ( "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/truncindex" "github.com/docker/docker/runconfig" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/docker/go-connections/nat" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" @@ -296,7 +296,7 @@ func (daemon *Daemon) verifyContainerSettings(platform string, hostConfig *conta } // Validate mounts; check if host directories still exist - parser := volume.NewParser(platform) + parser := volumemounts.NewParser(platform) for _, cfg := range hostConfig.Mounts { if err := parser.ValidateMountConfig(&cfg); err != nil { return nil, err diff --git a/components/engine/daemon/create_windows.go b/components/engine/daemon/create_windows.go index a4cf7df939..a2e7a94d4e 100644 --- a/components/engine/daemon/create_windows.go +++ b/components/engine/daemon/create_windows.go @@ -7,7 +7,7 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" ) // createContainerOSSpecificSettings performs host-OS specific container create functionality @@ -26,7 +26,7 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con } hostConfig.Isolation = "hyperv" } - parser := volume.NewParser(container.OS) + parser := volumemounts.NewParser(container.OS) for spec := range config.Volumes { mp, err := parser.ParseMountRaw(spec, hostConfig.VolumeDriver) diff --git a/components/engine/daemon/daemon.go b/components/engine/daemon/daemon.go index 8c36f042ea..9812c0be6c 100644 --- a/components/engine/daemon/daemon.go +++ b/components/engine/daemon/daemon.go @@ -179,11 +179,6 @@ func (daemon *Daemon) restore() error { delete(containers, id) continue } - // verify that all volumes valid and have been migrated from the pre-1.7 layout - if err := daemon.verifyVolumesInfo(c); err != nil { - // don't skip the container due to error - logrus.Errorf("Failed to verify volumes for container '%s': %v", c.ID, err) - } if err := daemon.Register(c); err != nil { logrus.Errorf("Failed to register container %s: %s", c.ID, err) delete(containers, id) @@ -1150,17 +1145,15 @@ func setDefaultMtu(conf *config.Config) { } func (daemon *Daemon) configureVolumes(rootIDs idtools.IDPair) (*store.VolumeStore, error) { - volumesDriver, err := local.New(daemon.configStore.Root, rootIDs) + volumeDriver, err := local.New(daemon.configStore.Root, rootIDs) if err != nil { return nil, err } - - volumedrivers.RegisterPluginGetter(daemon.PluginStore) - - if !volumedrivers.Register(volumesDriver, volumesDriver.Name()) { + drivers := volumedrivers.NewStore(daemon.PluginStore) + if !drivers.Register(volumeDriver, volumeDriver.Name()) { return nil, errors.New("local volume driver could not be registered") } - return store.New(daemon.configStore.Root) + return store.New(daemon.configStore.Root, drivers) } // IsShuttingDown tells whether the daemon is shutting down or not diff --git a/components/engine/daemon/daemon_test.go b/components/engine/daemon/daemon_test.go index 2fb4ff902a..2fe4276d7a 100644 --- a/components/engine/daemon/daemon_test.go +++ b/components/engine/daemon/daemon_test.go @@ -13,7 +13,6 @@ import ( _ "github.com/docker/docker/pkg/discovery/memory" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/truncindex" - "github.com/docker/docker/volume" volumedrivers "github.com/docker/docker/volume/drivers" "github.com/docker/docker/volume/local" "github.com/docker/docker/volume/store" @@ -121,7 +120,8 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) { repository: tmp, root: tmp, } - daemon.volumes, err = store.New(tmp) + drivers := volumedrivers.NewStore(nil) + daemon.volumes, err = store.New(tmp, drivers) if err != nil { return nil, err } @@ -130,7 +130,7 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) { if err != nil { return nil, err } - volumedrivers.Register(volumesDriver, volumesDriver.Name()) + drivers.Register(volumesDriver, volumesDriver.Name()) return daemon, nil } @@ -208,7 +208,6 @@ func TestContainerInitDNS(t *testing.T) { if err != nil { t.Fatal(err) } - defer volumedrivers.Unregister(volume.DefaultDriverName) c, err := daemon.load(containerID) if err != nil { diff --git a/components/engine/daemon/daemon_unix.go b/components/engine/daemon/daemon_unix.go index 670d564b8a..82a38839a2 100644 --- a/components/engine/daemon/daemon_unix.go +++ b/components/engine/daemon/daemon_unix.go @@ -33,7 +33,7 @@ import ( "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/runconfig" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/docker/libnetwork" nwconfig "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/drivers/bridge" @@ -626,7 +626,7 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes. return warnings, fmt.Errorf("Unknown runtime specified %s", hostConfig.Runtime) } - parser := volume.NewParser(runtime.GOOS) + parser := volumemounts.NewParser(runtime.GOOS) for dest := range hostConfig.Tmpfs { if err := parser.ValidateTmpfsMountDestination(dest); err != nil { return warnings, err diff --git a/components/engine/daemon/daemon_unix_test.go b/components/engine/daemon/daemon_unix_test.go index 84281c0b8c..36c6030988 100644 --- a/components/engine/daemon/daemon_unix_test.go +++ b/components/engine/daemon/daemon_unix_test.go @@ -6,18 +6,11 @@ import ( "errors" "io/ioutil" "os" - "path/filepath" "testing" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/daemon/config" - "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/volume" - "github.com/docker/docker/volume/drivers" - "github.com/docker/docker/volume/local" - "github.com/docker/docker/volume/store" - "github.com/gotestyourself/gotestyourself/assert" ) type fakeContainerGetter struct { @@ -273,85 +266,3 @@ func TestNetworkOptions(t *testing.T) { t.Fatal("Expected networkOptions error, got nil") } } - -func TestMigratePre17Volumes(t *testing.T) { - rootDir, err := ioutil.TempDir("", "test-daemon-volumes") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(rootDir) - - volumeRoot := filepath.Join(rootDir, "volumes") - err = os.MkdirAll(volumeRoot, 0755) - if err != nil { - t.Fatal(err) - } - - containerRoot := filepath.Join(rootDir, "containers") - cid := "1234" - err = os.MkdirAll(filepath.Join(containerRoot, cid), 0755) - assert.NilError(t, err) - - vid := "5678" - vfsPath := filepath.Join(rootDir, "vfs", "dir", vid) - err = os.MkdirAll(vfsPath, 0755) - assert.NilError(t, err) - - config := []byte(` - { - "ID": "` + cid + `", - "Volumes": { - "/foo": "` + vfsPath + `", - "/bar": "/foo", - "/quux": "/quux" - }, - "VolumesRW": { - "/foo": true, - "/bar": true, - "/quux": false - } - } - `) - - volStore, err := store.New(volumeRoot) - if err != nil { - t.Fatal(err) - } - drv, err := local.New(volumeRoot, idtools.IDPair{UID: 0, GID: 0}) - if err != nil { - t.Fatal(err) - } - volumedrivers.Register(drv, volume.DefaultDriverName) - - daemon := &Daemon{ - root: rootDir, - repository: containerRoot, - volumes: volStore, - } - err = ioutil.WriteFile(filepath.Join(containerRoot, cid, "config.v2.json"), config, 600) - if err != nil { - t.Fatal(err) - } - c, err := daemon.load(cid) - if err != nil { - t.Fatal(err) - } - if err := daemon.verifyVolumesInfo(c); err != nil { - t.Fatal(err) - } - - expected := map[string]volume.MountPoint{ - "/foo": {Destination: "/foo", RW: true, Name: vid}, - "/bar": {Source: "/foo", Destination: "/bar", RW: true}, - "/quux": {Source: "/quux", Destination: "/quux", RW: false}, - } - for id, mp := range c.MountPoints { - x, exists := expected[id] - if !exists { - t.Fatal("volume not migrated") - } - if mp.Source != x.Source || mp.Destination != x.Destination || mp.RW != x.RW || mp.Name != x.Name { - t.Fatalf("got unexpected mountpoint, expected: %+v, got: %+v", x, mp) - } - } -} diff --git a/components/engine/daemon/daemon_windows.go b/components/engine/daemon/daemon_windows.go index 50507e5fad..1f801032df 100644 --- a/components/engine/daemon/daemon_windows.go +++ b/components/engine/daemon/daemon_windows.go @@ -632,13 +632,6 @@ func setupDaemonProcess(config *config.Config) error { return nil } -// verifyVolumesInfo is a no-op on windows. -// This is called during daemon initialization to migrate volumes from pre-1.7. -// volumes were not supported on windows pre-1.7 -func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error { - return nil -} - func (daemon *Daemon) setupSeccompProfile() error { return nil } diff --git a/components/engine/daemon/disk_usage.go b/components/engine/daemon/disk_usage.go index cad441f9cb..344627afa0 100644 --- a/components/engine/daemon/disk_usage.go +++ b/components/engine/daemon/disk_usage.go @@ -35,41 +35,39 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er return nil, fmt.Errorf("failed to retrieve image list: %v", err) } - // Get all local volumes - allVolumes := []*types.Volume{} - getLocalVols := func(v volume.Volume) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - if d, ok := v.(volume.DetailedVolume); ok { - // skip local volumes with mount options since these could have external - // mounted filesystems that will be slow to enumerate. - if len(d.Options()) > 0 { - return nil - } - } - name := v.Name() - refs := daemon.volumes.Refs(v) - - tv := volumeToAPIType(v) - sz, err := directory.Size(ctx, v.Path()) - if err != nil { - logrus.Warnf("failed to determine size of volume %v", name) - sz = -1 - } - tv.UsageData = &types.VolumeUsageData{Size: sz, RefCount: int64(len(refs))} - allVolumes = append(allVolumes, tv) - } - - return nil - } - - err = daemon.traverseLocalVolumes(getLocalVols) + volumes, err := daemon.volumes.FilterByDriver(volume.DefaultDriverName) if err != nil { return nil, err } + var allVolumes []*types.Volume + for _, v := range volumes { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + if d, ok := v.(volume.DetailedVolume); ok { + if len(d.Options()) > 0 { + // skip local volumes with mount options since these could have external + // mounted filesystems that will be slow to enumerate. + continue + } + } + + name := v.Name() + refs := daemon.volumes.Refs(v) + + tv := volumeToAPIType(v) + sz, err := directory.Size(ctx, v.Path()) + if err != nil { + logrus.Warnf("failed to determine size of volume %v", name) + sz = -1 + } + tv.UsageData = &types.VolumeUsageData{Size: sz, RefCount: int64(len(refs))} + allVolumes = append(allVolumes, tv) + } + allLayersSize, err := daemon.imageService.LayerDiskUsage(ctx) if err != nil { return nil, err diff --git a/components/engine/daemon/graphdriver/aufs/aufs.go b/components/engine/daemon/graphdriver/aufs/aufs.go index a46cb31d34..2fa5c6952a 100644 --- a/components/engine/daemon/graphdriver/aufs/aufs.go +++ b/components/engine/daemon/graphdriver/aufs/aufs.go @@ -109,7 +109,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap switch fsMagic { case graphdriver.FsMagicAufs, graphdriver.FsMagicBtrfs, graphdriver.FsMagicEcryptfs: - logrus.Errorf("AUFS is not supported over %s", backingFs) + logrus.WithField("storage-driver", "aufs").Errorf("AUFS is not supported over %s", backingFs) return nil, graphdriver.ErrIncompatibleFS } @@ -143,10 +143,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, err } } - logger := logrus.WithFields(logrus.Fields{ - "module": "graphdriver", - "driver": "aufs", - }) + logger := logrus.WithField("storage-driver", "aufs") for _, path := range []string{"mnt", "diff"} { p := filepath.Join(root, path) @@ -310,9 +307,8 @@ func (a *Driver) Remove(id string) error { } logger := logrus.WithFields(logrus.Fields{ - "module": "graphdriver", - "driver": "aufs", - "layer": id, + "storage-driver": "aufs", + "layer": id, }) var retries int @@ -443,7 +439,7 @@ func (a *Driver) Put(id string) error { err := a.unmount(m) if err != nil { - logrus.Debugf("Failed to unmount %s aufs: %v", id, err) + logrus.WithField("storage-driver", "aufs").Debugf("Failed to unmount %s aufs: %v", id, err) } return err } @@ -601,7 +597,7 @@ func (a *Driver) Cleanup() error { for _, m := range dirs { if err := a.unmount(m); err != nil { - logrus.Debugf("aufs error unmounting %s: %s", m, err) + logrus.WithField("storage-driver", "aufs").Debugf("error unmounting %s: %s", m, err) } } return mountpk.RecursiveUnmount(a.root) @@ -656,17 +652,18 @@ func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err erro // useDirperm checks dirperm1 mount option can be used with the current // version of aufs. func useDirperm() bool { + logger := logrus.WithField("storage-driver", "aufs") enableDirpermLock.Do(func() { base, err := ioutil.TempDir("", "docker-aufs-base") if err != nil { - logrus.Errorf("error checking dirperm1: %v", err) + logger.Errorf("error checking dirperm1: %v", err) return } defer os.RemoveAll(base) union, err := ioutil.TempDir("", "docker-aufs-union") if err != nil { - logrus.Errorf("error checking dirperm1: %v", err) + logger.Errorf("error checking dirperm1: %v", err) return } defer os.RemoveAll(union) @@ -677,7 +674,7 @@ func useDirperm() bool { } enableDirperm = true if err := Unmount(union); err != nil { - logrus.Errorf("error checking dirperm1: failed to unmount %v", err) + logger.Errorf("error checking dirperm1: failed to unmount %v", err) } }) return enableDirperm diff --git a/components/engine/daemon/graphdriver/aufs/mount.go b/components/engine/daemon/graphdriver/aufs/mount.go index 4781988488..c0a89c1a01 100644 --- a/components/engine/daemon/graphdriver/aufs/mount.go +++ b/components/engine/daemon/graphdriver/aufs/mount.go @@ -12,7 +12,7 @@ import ( // Unmount the target specified. func Unmount(target string) error { if err := exec.Command("auplink", target, "flush").Run(); err != nil { - logrus.Warnf("Couldn't run auplink before unmount %s: %s", target, err) + logrus.WithField("storage-driver", "aufs").Warnf("Couldn't run auplink before unmount %s: %s", target, err) } return unix.Unmount(target, 0) } diff --git a/components/engine/daemon/graphdriver/btrfs/btrfs.go b/components/engine/daemon/graphdriver/btrfs/btrfs.go index eaed2c7471..cac6240303 100644 --- a/components/engine/daemon/graphdriver/btrfs/btrfs.go +++ b/components/engine/daemon/graphdriver/btrfs/btrfs.go @@ -291,10 +291,10 @@ func subvolDelete(dirpath, name string, quotaEnabled bool) error { _, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_QGROUP_CREATE, uintptr(unsafe.Pointer(&args))) if errno != 0 { - logrus.Errorf("Failed to delete btrfs qgroup %v for %s: %v", qgroupid, fullPath, errno.Error()) + logrus.WithField("storage-driver", "btrfs").Errorf("Failed to delete btrfs qgroup %v for %s: %v", qgroupid, fullPath, errno.Error()) } } else { - logrus.Errorf("Failed to lookup btrfs qgroup for %s: %v", fullPath, err.Error()) + logrus.WithField("storage-driver", "btrfs").Errorf("Failed to lookup btrfs qgroup for %s: %v", fullPath, err.Error()) } } diff --git a/components/engine/daemon/graphdriver/devmapper/deviceset.go b/components/engine/daemon/graphdriver/devmapper/deviceset.go index ca6251023b..dafe7661a2 100644 --- a/components/engine/daemon/graphdriver/devmapper/deviceset.go +++ b/components/engine/daemon/graphdriver/devmapper/deviceset.go @@ -276,7 +276,7 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) { if !os.IsNotExist(err) { return "", err } - logrus.Debugf("devmapper: Creating loopback file %s for device-manage use", filename) + logrus.WithField("storage-driver", "devicemapper").Debugf("Creating loopback file %s for device-manage use", filename) file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0600) if err != nil { return "", err @@ -297,7 +297,7 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) { return "", fmt.Errorf("devmapper: Unable to grow loopback file %s: %v", filename, err) } } else if fi.Size() > size { - logrus.Warnf("devmapper: Can't shrink loopback file %s", filename) + logrus.WithField("storage-driver", "devicemapper").Warnf("Can't shrink loopback file %s", filename) } } return filename, nil @@ -403,39 +403,40 @@ func (devices *DeviceSet) lookupDeviceWithLock(hash string) (*devInfo, error) { // This function relies on that device hash map has been loaded in advance. // Should be called with devices.Lock() held. func (devices *DeviceSet) constructDeviceIDMap() { - logrus.Debug("devmapper: constructDeviceIDMap()") - defer logrus.Debug("devmapper: constructDeviceIDMap() END") + logrus.WithField("storage-driver", "devicemapper").Debug("constructDeviceIDMap()") + defer logrus.WithField("storage-driver", "devicemapper").Debug("constructDeviceIDMap() END") for _, info := range devices.Devices { devices.markDeviceIDUsed(info.DeviceID) - logrus.Debugf("devmapper: Added deviceId=%d to DeviceIdMap", info.DeviceID) + logrus.WithField("storage-driver", "devicemapper").Debugf("Added deviceId=%d to DeviceIdMap", info.DeviceID) } } func (devices *DeviceSet) deviceFileWalkFunction(path string, finfo os.FileInfo) error { + logger := logrus.WithField("storage-driver", "devicemapper") // Skip some of the meta files which are not device files. if strings.HasSuffix(finfo.Name(), ".migrated") { - logrus.Debugf("devmapper: Skipping file %s", path) + logger.Debugf("Skipping file %s", path) return nil } if strings.HasPrefix(finfo.Name(), ".") { - logrus.Debugf("devmapper: Skipping file %s", path) + logger.Debugf("Skipping file %s", path) return nil } if finfo.Name() == deviceSetMetaFile { - logrus.Debugf("devmapper: Skipping file %s", path) + logger.Debugf("Skipping file %s", path) return nil } if finfo.Name() == transactionMetaFile { - logrus.Debugf("devmapper: Skipping file %s", path) + logger.Debugf("Skipping file %s", path) return nil } - logrus.Debugf("devmapper: Loading data for file %s", path) + logger.Debugf("Loading data for file %s", path) hash := finfo.Name() if hash == "base" { @@ -452,12 +453,12 @@ func (devices *DeviceSet) deviceFileWalkFunction(path string, finfo os.FileInfo) } func (devices *DeviceSet) loadDeviceFilesOnStart() error { - logrus.Debug("devmapper: loadDeviceFilesOnStart()") - defer logrus.Debug("devmapper: loadDeviceFilesOnStart() END") + logrus.WithField("storage-driver", "devicemapper").Debug("loadDeviceFilesOnStart()") + defer logrus.WithField("storage-driver", "devicemapper").Debug("loadDeviceFilesOnStart() END") var scan = func(path string, info os.FileInfo, err error) error { if err != nil { - logrus.Debugf("devmapper: Can't walk the file %s", path) + logrus.WithField("storage-driver", "devicemapper").Debugf("Can't walk the file %s", path) return nil } @@ -474,7 +475,7 @@ func (devices *DeviceSet) loadDeviceFilesOnStart() error { // Should be called with devices.Lock() held. func (devices *DeviceSet) unregisterDevice(hash string) error { - logrus.Debugf("devmapper: unregisterDevice(%v)", hash) + logrus.WithField("storage-driver", "devicemapper").Debugf("unregisterDevice(%v)", hash) info := &devInfo{ Hash: hash, } @@ -482,7 +483,7 @@ func (devices *DeviceSet) unregisterDevice(hash string) error { delete(devices.Devices, hash) if err := devices.removeMetadata(info); err != nil { - logrus.Debugf("devmapper: Error removing metadata: %s", err) + logrus.WithField("storage-driver", "devicemapper").Debugf("Error removing metadata: %s", err) return err } @@ -491,7 +492,7 @@ func (devices *DeviceSet) unregisterDevice(hash string) error { // Should be called with devices.Lock() held. func (devices *DeviceSet) registerDevice(id int, hash string, size uint64, transactionID uint64) (*devInfo, error) { - logrus.Debugf("devmapper: registerDevice(%v, %v)", id, hash) + logrus.WithField("storage-driver", "devicemapper").Debugf("registerDevice(%v, %v)", id, hash) info := &devInfo{ Hash: hash, DeviceID: id, @@ -513,7 +514,7 @@ func (devices *DeviceSet) registerDevice(id int, hash string, size uint64, trans } func (devices *DeviceSet) activateDeviceIfNeeded(info *devInfo, ignoreDeleted bool) error { - logrus.Debugf("devmapper: activateDeviceIfNeeded(%v)", info.Hash) + logrus.WithField("storage-driver", "devicemapper").Debugf("activateDeviceIfNeeded(%v)", info.Hash) if info.Deleted && !ignoreDeleted { return fmt.Errorf("devmapper: Can't activate device %v as it is marked for deletion", info.Hash) @@ -568,7 +569,7 @@ func determineDefaultFS() string { return "xfs" } - logrus.Warnf("devmapper: XFS is not supported in your system (%v). Defaulting to ext4 filesystem", err) + logrus.WithField("storage-driver", "devicemapper").Warnf("XFS is not supported in your system (%v). Defaulting to ext4 filesystem", err) return "ext4" } @@ -597,12 +598,12 @@ func (devices *DeviceSet) createFilesystem(info *devInfo) (err error) { args = append(args, devices.mkfsArgs...) args = append(args, devname) - logrus.Infof("devmapper: Creating filesystem %s on device %s, mkfs args: %v", devices.filesystem, info.Name(), args) + logrus.WithField("storage-driver", "devicemapper").Infof("Creating filesystem %s on device %s, mkfs args: %v", devices.filesystem, info.Name(), args) defer func() { if err != nil { - logrus.Infof("devmapper: Error while creating filesystem %s on device %s: %v", devices.filesystem, info.Name(), err) + logrus.WithField("storage-driver", "devicemapper").Infof("Error while creating filesystem %s on device %s: %v", devices.filesystem, info.Name(), err) } else { - logrus.Infof("devmapper: Successfully created filesystem %s on device %s", devices.filesystem, info.Name()) + logrus.WithField("storage-driver", "devicemapper").Infof("Successfully created filesystem %s on device %s", devices.filesystem, info.Name()) } }() @@ -668,7 +669,7 @@ func (devices *DeviceSet) cleanupDeletedDevices() error { if !info.Deleted { continue } - logrus.Debugf("devmapper: Found deleted device %s.", info.Hash) + logrus.WithField("storage-driver", "devicemapper").Debugf("Found deleted device %s.", info.Hash) deletedDevices = append(deletedDevices, info) } @@ -679,7 +680,7 @@ func (devices *DeviceSet) cleanupDeletedDevices() error { for _, info := range deletedDevices { // This will again try deferred deletion. if err := devices.DeleteDevice(info.Hash, false); err != nil { - logrus.Warnf("devmapper: Deletion of device %s, device_id=%v failed:%v", info.Hash, info.DeviceID, err) + logrus.WithField("storage-driver", "devicemapper").Warnf("Deletion of device %s, device_id=%v failed:%v", info.Hash, info.DeviceID, err) } } @@ -701,7 +702,7 @@ func (devices *DeviceSet) startDeviceDeletionWorker() { return } - logrus.Debug("devmapper: Worker to cleanup deleted devices started") + logrus.WithField("storage-driver", "devicemapper").Debug("Worker to cleanup deleted devices started") for range devices.deletionWorkerTicker.C { devices.cleanupDeletedDevices() } @@ -797,8 +798,10 @@ func (devices *DeviceSet) createRegisterDevice(hash string) (*devInfo, error) { return nil, err } + logger := logrus.WithField("storage-driver", "devicemapper") + if err := devices.openTransaction(hash, deviceID); err != nil { - logrus.Debugf("devmapper: Error opening transaction hash = %s deviceID = %d", hash, deviceID) + logger.Debugf("Error opening transaction hash = %s deviceID = %d", hash, deviceID) devices.markDeviceIDFree(deviceID) return nil, err } @@ -810,7 +813,7 @@ func (devices *DeviceSet) createRegisterDevice(hash string) (*devInfo, error) { // happen. Now we have a mechanism to find // a free device ID. So something is not right. // Give a warning and continue. - logrus.Errorf("devmapper: Device ID %d exists in pool but it is supposed to be unused", deviceID) + logger.Errorf("Device ID %d exists in pool but it is supposed to be unused", deviceID) deviceID, err = devices.getNextFreeDeviceID() if err != nil { return nil, err @@ -819,14 +822,14 @@ func (devices *DeviceSet) createRegisterDevice(hash string) (*devInfo, error) { devices.refreshTransaction(deviceID) continue } - logrus.Debugf("devmapper: Error creating device: %s", err) + logger.Debugf("Error creating device: %s", err) devices.markDeviceIDFree(deviceID) return nil, err } break } - logrus.Debugf("devmapper: Registering device (id %v) with FS size %v", deviceID, devices.baseFsSize) + logger.Debugf("Registering device (id %v) with FS size %v", deviceID, devices.baseFsSize) info, err := devices.registerDevice(deviceID, hash, devices.baseFsSize, devices.OpenTransactionID) if err != nil { _ = devicemapper.DeleteDevice(devices.getPoolDevName(), deviceID) @@ -895,8 +898,10 @@ func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInf return err } + logger := logrus.WithField("storage-driver", "devicemapper") + if err := devices.openTransaction(hash, deviceID); err != nil { - logrus.Debugf("devmapper: Error opening transaction hash = %s deviceID = %d", hash, deviceID) + logger.Debugf("Error opening transaction hash = %s deviceID = %d", hash, deviceID) devices.markDeviceIDFree(deviceID) return err } @@ -908,7 +913,7 @@ func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInf // happen. Now we have a mechanism to find // a free device ID. So something is not right. // Give a warning and continue. - logrus.Errorf("devmapper: Device ID %d exists in pool but it is supposed to be unused", deviceID) + logger.Errorf("Device ID %d exists in pool but it is supposed to be unused", deviceID) deviceID, err = devices.getNextFreeDeviceID() if err != nil { return err @@ -917,7 +922,7 @@ func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInf devices.refreshTransaction(deviceID) continue } - logrus.Debugf("devmapper: Error creating snap device: %s", err) + logger.Debugf("Error creating snap device: %s", err) devices.markDeviceIDFree(deviceID) return err } @@ -927,7 +932,7 @@ func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInf if _, err := devices.registerDevice(deviceID, hash, size, devices.OpenTransactionID); err != nil { devicemapper.DeleteDevice(devices.getPoolDevName(), deviceID) devices.markDeviceIDFree(deviceID) - logrus.Debugf("devmapper: Error registering device: %s", err) + logger.Debugf("Error registering device: %s", err) return err } @@ -942,20 +947,21 @@ func (devices *DeviceSet) createRegisterSnapDevice(hash string, baseInfo *devInf func (devices *DeviceSet) loadMetadata(hash string) *devInfo { info := &devInfo{Hash: hash, devices: devices} + logger := logrus.WithField("storage-driver", "devicemapper") jsonData, err := ioutil.ReadFile(devices.metadataFile(info)) if err != nil { - logrus.Debugf("devmapper: Failed to read %s with err: %v", devices.metadataFile(info), err) + logger.Debugf("Failed to read %s with err: %v", devices.metadataFile(info), err) return nil } if err := json.Unmarshal(jsonData, &info); err != nil { - logrus.Debugf("devmapper: Failed to unmarshal devInfo from %s with err: %v", devices.metadataFile(info), err) + logger.Debugf("Failed to unmarshal devInfo from %s with err: %v", devices.metadataFile(info), err) return nil } if info.DeviceID > maxDeviceID { - logrus.Errorf("devmapper: Ignoring Invalid DeviceId=%d", info.DeviceID) + logger.Errorf("Ignoring Invalid DeviceId=%d", info.DeviceID) return nil } @@ -970,7 +976,7 @@ func getDeviceUUID(device string) (string, error) { uuid := strings.TrimSuffix(string(out), "\n") uuid = strings.TrimSpace(uuid) - logrus.Debugf("devmapper: UUID for device: %s is:%s", device, uuid) + logrus.WithField("storage-driver", "devicemapper").Debugf("UUID for device: %s is:%s", device, uuid) return uuid, nil } @@ -1018,7 +1024,7 @@ func (devices *DeviceSet) verifyBaseDeviceUUIDFS(baseInfo *devInfo) error { // file system of base image is not same, warn user that dm.fs // will be ignored. if devices.BaseDeviceFilesystem != devices.filesystem { - logrus.Warnf("devmapper: Base device already exists and has filesystem %s on it. User specified filesystem %s will be ignored.", devices.BaseDeviceFilesystem, devices.filesystem) + logrus.WithField("storage-driver", "devicemapper").Warnf("Base device already exists and has filesystem %s on it. User specified filesystem %s will be ignored.", devices.BaseDeviceFilesystem, devices.filesystem) devices.filesystem = devices.BaseDeviceFilesystem } return nil @@ -1048,7 +1054,7 @@ func (devices *DeviceSet) saveBaseDeviceUUID(baseInfo *devInfo) error { } func (devices *DeviceSet) createBaseImage() error { - logrus.Debug("devmapper: Initializing base device-mapper thin volume") + logrus.WithField("storage-driver", "devicemapper").Debug("Initializing base device-mapper thin volume") // Create initial device info, err := devices.createRegisterDevice("") @@ -1056,7 +1062,7 @@ func (devices *DeviceSet) createBaseImage() error { return err } - logrus.Debug("devmapper: Creating filesystem on base device-mapper thin volume") + logrus.WithField("storage-driver", "devicemapper").Debug("Creating filesystem on base device-mapper thin volume") if err := devices.activateDeviceIfNeeded(info, false); err != nil { return err @@ -1082,7 +1088,7 @@ func (devices *DeviceSet) createBaseImage() error { // Returns if thin pool device exists or not. If device exists, also makes // sure it is a thin pool device and not some other type of device. func (devices *DeviceSet) thinPoolExists(thinPoolDevice string) (bool, error) { - logrus.Debugf("devmapper: Checking for existence of the pool %s", thinPoolDevice) + logrus.WithField("storage-driver", "devicemapper").Debugf("Checking for existence of the pool %s", thinPoolDevice) info, err := devicemapper.GetInfo(thinPoolDevice) if err != nil { @@ -1229,7 +1235,7 @@ func (devices *DeviceSet) setupBaseImage() error { return devices.checkGrowBaseDeviceFS(oldInfo) } - logrus.Debug("devmapper: Removing uninitialized base image") + logrus.WithField("storage-driver", "devicemapper").Debug("Removing uninitialized base image") // If previous base device is in deferred delete state, // that needs to be cleaned up first. So don't try // deferred deletion. @@ -1374,24 +1380,26 @@ func (devices *DeviceSet) removeTransactionMetaData() error { } func (devices *DeviceSet) rollbackTransaction() error { - logrus.Debugf("devmapper: Rolling back open transaction: TransactionID=%d hash=%s device_id=%d", devices.OpenTransactionID, devices.DeviceIDHash, devices.DeviceID) + logger := logrus.WithField("storage-driver", "devicemapper") + + logger.Debugf("Rolling back open transaction: TransactionID=%d hash=%s device_id=%d", devices.OpenTransactionID, devices.DeviceIDHash, devices.DeviceID) // A device id might have already been deleted before transaction // closed. In that case this call will fail. Just leave a message // in case of failure. if err := devicemapper.DeleteDevice(devices.getPoolDevName(), devices.DeviceID); err != nil { - logrus.Errorf("devmapper: Unable to delete device: %s", err) + logger.Errorf("Unable to delete device: %s", err) } dinfo := &devInfo{Hash: devices.DeviceIDHash} if err := devices.removeMetadata(dinfo); err != nil { - logrus.Errorf("devmapper: Unable to remove metadata: %s", err) + logger.Errorf("Unable to remove metadata: %s", err) } else { devices.markDeviceIDFree(devices.DeviceID) } if err := devices.removeTransactionMetaData(); err != nil { - logrus.Errorf("devmapper: Unable to remove transaction meta file %s: %s", devices.transactionMetaFile(), err) + logger.Errorf("Unable to remove transaction meta file %s: %s", devices.transactionMetaFile(), err) } return nil @@ -1411,7 +1419,7 @@ func (devices *DeviceSet) processPendingTransaction() error { // If open transaction ID is less than pool transaction ID, something // is wrong. Bail out. if devices.OpenTransactionID < devices.TransactionID { - logrus.Errorf("devmapper: Open Transaction id %d is less than pool transaction id %d", devices.OpenTransactionID, devices.TransactionID) + logrus.WithField("storage-driver", "devicemapper").Errorf("Open Transaction id %d is less than pool transaction id %d", devices.OpenTransactionID, devices.TransactionID) return nil } @@ -1468,7 +1476,7 @@ func (devices *DeviceSet) refreshTransaction(DeviceID int) error { func (devices *DeviceSet) closeTransaction() error { if err := devices.updatePoolTransactionID(); err != nil { - logrus.Debug("devmapper: Failed to close Transaction") + logrus.WithField("storage-driver", "devicemapper").Debug("Failed to close Transaction") return err } return nil @@ -1477,7 +1485,7 @@ func (devices *DeviceSet) closeTransaction() error { func determineDriverCapabilities(version string) error { // Kernel driver version >= 4.27.0 support deferred removal - logrus.Debugf("devicemapper: kernel dm driver version is %s", version) + logrus.WithField("storage-driver", "devicemapper").Debugf("kernel dm driver version is %s", version) versionSplit := strings.Split(version, ".") major, err := strconv.Atoi(versionSplit[0]) @@ -1523,7 +1531,7 @@ func getDeviceMajorMinor(file *os.File) (uint64, uint64, error) { majorNum := major(dev) minorNum := minor(dev) - logrus.Debugf("devmapper: Major:Minor for device: %s is:%v:%v", file.Name(), majorNum, minorNum) + logrus.WithField("storage-driver", "devicemapper").Debugf("Major:Minor for device: %s is:%v:%v", file.Name(), majorNum, minorNum) return majorNum, minorNum, nil } @@ -1532,7 +1540,7 @@ func getDeviceMajorMinor(file *os.File) (uint64, uint64, error) { func getLoopFileDeviceMajMin(filename string) (string, uint64, uint64, error) { file, err := os.Open(filename) if err != nil { - logrus.Debugf("devmapper: Failed to open file %s", filename) + logrus.WithField("storage-driver", "devicemapper").Debugf("Failed to open file %s", filename) return "", 0, 0, err } @@ -1563,7 +1571,7 @@ func (devices *DeviceSet) getThinPoolDataMetaMajMin() (uint64, uint64, uint64, u return 0, 0, 0, 0, err } - logrus.Debugf("devmapper: poolDataMajMin=%s poolMetaMajMin=%s\n", poolDataMajMin, poolMetadataMajMin) + logrus.WithField("storage-driver", "devicemapper").Debugf("poolDataMajMin=%s poolMetaMajMin=%s\n", poolDataMajMin, poolMetadataMajMin) poolDataMajMinorSplit := strings.Split(poolDataMajMin, ":") poolDataMajor, err := strconv.ParseUint(poolDataMajMinorSplit[0], 10, 32) @@ -1643,7 +1651,7 @@ func (devices *DeviceSet) enableDeferredRemovalDeletion() error { if !devicemapper.LibraryDeferredRemovalSupport { return fmt.Errorf("devmapper: Deferred removal can not be enabled as libdm does not support it") } - logrus.Debug("devmapper: Deferred removal support enabled.") + logrus.WithField("storage-driver", "devicemapper").Debug("Deferred removal support enabled.") devices.deferredRemove = true } @@ -1651,7 +1659,7 @@ func (devices *DeviceSet) enableDeferredRemovalDeletion() error { if !devices.deferredRemove { return fmt.Errorf("devmapper: Deferred deletion can not be enabled as deferred removal is not enabled. Enable deferred removal using --storage-opt dm.use_deferred_removal=true parameter") } - logrus.Debug("devmapper: Deferred deletion support enabled.") + logrus.WithField("storage-driver", "devicemapper").Debug("Deferred deletion support enabled.") devices.deferredDelete = true } return nil @@ -1662,12 +1670,14 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { return err } + logger := logrus.WithField("storage-driver", "devicemapper") + // https://github.com/docker/docker/issues/4036 if supported := devicemapper.UdevSetSyncSupport(true); !supported { if dockerversion.IAmStatic == "true" { - logrus.Error("devmapper: Udev sync is not supported. This will lead to data loss and unexpected behavior. Install a dynamic binary to use devicemapper or select a different storage driver. For more information, see https://docs.docker.com/engine/reference/commandline/dockerd/#storage-driver-options") + logger.Error("Udev sync is not supported. This will lead to data loss and unexpected behavior. Install a dynamic binary to use devicemapper or select a different storage driver. For more information, see https://docs.docker.com/engine/reference/commandline/dockerd/#storage-driver-options") } else { - logrus.Error("devmapper: Udev sync is not supported. This will lead to data loss and unexpected behavior. Install a more recent version of libdevmapper or select a different storage driver. For more information, see https://docs.docker.com/engine/reference/commandline/dockerd/#storage-driver-options") + logger.Error("Udev sync is not supported. This will lead to data loss and unexpected behavior. Install a more recent version of libdevmapper or select a different storage driver. For more information, see https://docs.docker.com/engine/reference/commandline/dockerd/#storage-driver-options") } if !devices.overrideUdevSyncCheck { @@ -1702,7 +1712,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { if !reflect.DeepEqual(prevSetupConfig, directLVMConfig{}) { return errors.New("changing direct-lvm config is not supported") } - logrus.WithField("storage-driver", "devicemapper").WithField("direct-lvm-config", devices.lvmSetupConfig).Debugf("Setting up direct lvm mode") + logger.WithField("direct-lvm-config", devices.lvmSetupConfig).Debugf("Setting up direct lvm mode") if err := verifyBlockDevice(devices.lvmSetupConfig.Device, lvmSetupConfigForce); err != nil { return err } @@ -1714,7 +1724,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { } } devices.thinPoolDevice = "docker-thinpool" - logrus.WithField("storage-driver", "devicemapper").Debugf("Setting dm.thinpooldev to %q", devices.thinPoolDevice) + logger.Debugf("Setting dm.thinpooldev to %q", devices.thinPoolDevice) } // Set the device prefix from the device id and inode of the docker root dir @@ -1729,7 +1739,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { // - The target of this device is at major and minor // - If is defined, use that file inside the device as a loopback image. Otherwise use the device itself. devices.devicePrefix = fmt.Sprintf("docker-%d:%d-%d", major(st.Dev), minor(st.Dev), st.Ino) - logrus.Debugf("devmapper: Generated prefix: %s", devices.devicePrefix) + logger.Debugf("Generated prefix: %s", devices.devicePrefix) // Check for the existence of the thin-pool device poolExists, err := devices.thinPoolExists(devices.getPoolName()) @@ -1749,7 +1759,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { // If the pool doesn't exist, create it if !poolExists && devices.thinPoolDevice == "" { - logrus.Debug("devmapper: Pool doesn't exist. Creating it.") + logger.Debug("Pool doesn't exist. Creating it.") var ( dataFile *os.File @@ -1771,7 +1781,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { data, err := devices.ensureImage("data", devices.dataLoopbackSize) if err != nil { - logrus.Debugf("devmapper: Error device ensureImage (data): %s", err) + logger.Debugf("Error device ensureImage (data): %s", err) return err } @@ -1804,7 +1814,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { metadata, err := devices.ensureImage("metadata", devices.metaDataLoopbackSize) if err != nil { - logrus.Debugf("devmapper: Error device ensureImage (metadata): %s", err) + logger.Debugf("Error device ensureImage (metadata): %s", err) return err } @@ -1829,7 +1839,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { if retErr != nil { err = devices.deactivatePool() if err != nil { - logrus.Warnf("devmapper: Failed to deactivatePool: %v", err) + logger.Warnf("Failed to deactivatePool: %v", err) } } }() @@ -1841,7 +1851,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { // pool, like is it using loop devices. if poolExists && devices.thinPoolDevice == "" { if err := devices.loadThinPoolLoopBackInfo(); err != nil { - logrus.Debugf("devmapper: Failed to load thin pool loopback device information:%v", err) + logger.Debugf("Failed to load thin pool loopback device information:%v", err) return err } } @@ -1856,7 +1866,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { if devices.thinPoolDevice == "" { if devices.metadataLoopFile != "" || devices.dataLoopFile != "" { - logrus.Warn("devmapper: Usage of loopback devices is strongly discouraged for production use. Please use `--storage-opt dm.thinpooldev` or use `man dockerd` to refer to dm.thinpooldev section.") + logger.Warn("Usage of loopback devices is strongly discouraged for production use. Please use `--storage-opt dm.thinpooldev` or use `man dockerd` to refer to dm.thinpooldev section.") } } @@ -1869,7 +1879,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { // Setup the base image if doInit { if err := devices.setupBaseImage(); err != nil { - logrus.Debugf("devmapper: Error device setupBaseImage: %s", err) + logger.Debugf("Error device setupBaseImage: %s", err) return err } } @@ -1879,8 +1889,8 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { // AddDevice adds a device and registers in the hash. func (devices *DeviceSet) AddDevice(hash, baseHash string, storageOpt map[string]string) error { - logrus.Debugf("devmapper: AddDevice START(hash=%s basehash=%s)", hash, baseHash) - defer logrus.Debugf("devmapper: AddDevice END(hash=%s basehash=%s)", hash, baseHash) + logrus.WithField("storage-driver", "devicemapper").Debugf("AddDevice START(hash=%s basehash=%s)", hash, baseHash) + defer logrus.WithField("storage-driver", "devicemapper").Debugf("AddDevice END(hash=%s basehash=%s)", hash, baseHash) // If a deleted device exists, return error. baseInfo, err := devices.lookupDeviceWithLock(baseHash) @@ -1962,7 +1972,7 @@ func (devices *DeviceSet) markForDeferredDeletion(info *devInfo) error { return nil } - logrus.Debugf("devmapper: Marking device %s for deferred deletion.", info.Hash) + logrus.WithField("storage-driver", "devicemapper").Debugf("Marking device %s for deferred deletion.", info.Hash) info.Deleted = true @@ -1979,7 +1989,7 @@ func (devices *DeviceSet) markForDeferredDeletion(info *devInfo) error { // Should be called with devices.Lock() held. func (devices *DeviceSet) deleteTransaction(info *devInfo, syncDelete bool) error { if err := devices.openTransaction(info.Hash, info.DeviceID); err != nil { - logrus.Debugf("devmapper: Error opening transaction hash = %s deviceId = %d", "", info.DeviceID) + logrus.WithField("storage-driver", "devicemapper").Debugf("Error opening transaction hash = %s deviceId = %d", "", info.DeviceID) return err } @@ -1991,7 +2001,7 @@ func (devices *DeviceSet) deleteTransaction(info *devInfo, syncDelete bool) erro // deletion is not enabled, we return an error. If error is // something other then EBUSY, return an error. if syncDelete || !devices.deferredDelete || err != devicemapper.ErrBusy { - logrus.Debugf("devmapper: Error deleting device: %s", err) + logrus.WithField("storage-driver", "devicemapper").Debugf("Error deleting device: %s", err) return err } } @@ -2018,8 +2028,9 @@ func (devices *DeviceSet) deleteTransaction(info *devInfo, syncDelete bool) erro // Issue discard only if device open count is zero. func (devices *DeviceSet) issueDiscard(info *devInfo) error { - logrus.Debugf("devmapper: issueDiscard START(device: %s).", info.Hash) - defer logrus.Debugf("devmapper: issueDiscard END(device: %s).", info.Hash) + logger := logrus.WithField("storage-driver", "devicemapper") + logger.Debugf("issueDiscard START(device: %s).", info.Hash) + defer logger.Debugf("issueDiscard END(device: %s).", info.Hash) // This is a workaround for the kernel not discarding block so // on the thin pool when we remove a thinp device, so we do it // manually. @@ -2035,12 +2046,12 @@ func (devices *DeviceSet) issueDiscard(info *devInfo) error { } if devinfo.OpenCount != 0 { - logrus.Debugf("devmapper: Device: %s is in use. OpenCount=%d. Not issuing discards.", info.Hash, devinfo.OpenCount) + logger.Debugf("Device: %s is in use. OpenCount=%d. Not issuing discards.", info.Hash, devinfo.OpenCount) return nil } if err := devicemapper.BlockDeviceDiscard(info.DevName()); err != nil { - logrus.Debugf("devmapper: Error discarding block on device: %s (ignoring)", err) + logger.Debugf("Error discarding block on device: %s (ignoring)", err) } return nil } @@ -2062,7 +2073,7 @@ func (devices *DeviceSet) deleteDevice(info *devInfo, syncDelete bool) error { } if err := devices.deactivateDeviceMode(info, deferredRemove); err != nil { - logrus.Debugf("devmapper: Error deactivating device: %s", err) + logrus.WithField("storage-driver", "devicemapper").Debugf("Error deactivating device: %s", err) return err } @@ -2073,8 +2084,8 @@ func (devices *DeviceSet) deleteDevice(info *devInfo, syncDelete bool) error { // removal. If one wants to override that and want DeleteDevice() to fail if // device was busy and could not be deleted, set syncDelete=true. func (devices *DeviceSet) DeleteDevice(hash string, syncDelete bool) error { - logrus.Debugf("devmapper: DeleteDevice START(hash=%v syncDelete=%v)", hash, syncDelete) - defer logrus.Debugf("devmapper: DeleteDevice END(hash=%v syncDelete=%v)", hash, syncDelete) + logrus.WithField("storage-driver", "devicemapper").Debugf("DeleteDevice START(hash=%v syncDelete=%v)", hash, syncDelete) + defer logrus.WithField("storage-driver", "devicemapper").Debugf("DeleteDevice END(hash=%v syncDelete=%v)", hash, syncDelete) info, err := devices.lookupDeviceWithLock(hash) if err != nil { return err @@ -2090,8 +2101,8 @@ func (devices *DeviceSet) DeleteDevice(hash string, syncDelete bool) error { } func (devices *DeviceSet) deactivatePool() error { - logrus.Debug("devmapper: deactivatePool() START") - defer logrus.Debug("devmapper: deactivatePool() END") + logrus.WithField("storage-driver", "devicemapper").Debug("deactivatePool() START") + defer logrus.WithField("storage-driver", "devicemapper").Debug("deactivatePool() END") devname := devices.getPoolDevName() devinfo, err := devicemapper.GetInfo(devname) @@ -2107,7 +2118,7 @@ func (devices *DeviceSet) deactivatePool() error { } if d, err := devicemapper.GetDeps(devname); err == nil { - logrus.Warnf("devmapper: device %s still has %d active dependents", devname, d.Count) + logrus.WithField("storage-driver", "devicemapper").Warnf("device %s still has %d active dependents", devname, d.Count) } return nil @@ -2119,8 +2130,8 @@ func (devices *DeviceSet) deactivateDevice(info *devInfo) error { func (devices *DeviceSet) deactivateDeviceMode(info *devInfo, deferredRemove bool) error { var err error - logrus.Debugf("devmapper: deactivateDevice START(%s)", info.Hash) - defer logrus.Debugf("devmapper: deactivateDevice END(%s)", info.Hash) + logrus.WithField("storage-driver", "devicemapper").Debugf("deactivateDevice START(%s)", info.Hash) + defer logrus.WithField("storage-driver", "devicemapper").Debugf("deactivateDevice END(%s)", info.Hash) devinfo, err := devicemapper.GetInfo(info.Name()) if err != nil { @@ -2150,8 +2161,8 @@ func (devices *DeviceSet) deactivateDeviceMode(info *devInfo, deferredRemove boo func (devices *DeviceSet) removeDevice(devname string) error { var err error - logrus.Debugf("devmapper: removeDevice START(%s)", devname) - defer logrus.Debugf("devmapper: removeDevice END(%s)", devname) + logrus.WithField("storage-driver", "devicemapper").Debugf("removeDevice START(%s)", devname) + defer logrus.WithField("storage-driver", "devicemapper").Debugf("removeDevice END(%s)", devname) for i := 0; i < 200; i++ { err = devicemapper.RemoveDevice(devname) @@ -2177,8 +2188,8 @@ func (devices *DeviceSet) cancelDeferredRemovalIfNeeded(info *devInfo) error { return nil } - logrus.Debugf("devmapper: cancelDeferredRemovalIfNeeded START(%s)", info.Name()) - defer logrus.Debugf("devmapper: cancelDeferredRemovalIfNeeded END(%s)", info.Name()) + logrus.WithField("storage-driver", "devicemapper").Debugf("cancelDeferredRemovalIfNeeded START(%s)", info.Name()) + defer logrus.WithField("storage-driver", "devicemapper").Debugf("cancelDeferredRemovalIfNeeded END(%s)", info.Name()) devinfo, err := devicemapper.GetInfoWithDeferred(info.Name()) if err != nil { @@ -2200,8 +2211,8 @@ func (devices *DeviceSet) cancelDeferredRemovalIfNeeded(info *devInfo) error { } func (devices *DeviceSet) cancelDeferredRemoval(info *devInfo) error { - logrus.Debugf("devmapper: cancelDeferredRemoval START(%s)", info.Name()) - defer logrus.Debugf("devmapper: cancelDeferredRemoval END(%s)", info.Name()) + logrus.WithField("storage-driver", "devicemapper").Debugf("cancelDeferredRemoval START(%s)", info.Name()) + defer logrus.WithField("storage-driver", "devicemapper").Debugf("cancelDeferredRemoval END(%s)", info.Name()) var err error @@ -2224,9 +2235,11 @@ func (devices *DeviceSet) cancelDeferredRemoval(info *devInfo) error { } func (devices *DeviceSet) unmountAndDeactivateAll(dir string) { + logger := logrus.WithField("storage-driver", "devicemapper") + files, err := ioutil.ReadDir(dir) if err != nil { - logrus.Warnf("devmapper: unmountAndDeactivate: %s", err) + logger.Warnf("unmountAndDeactivate: %s", err) return } @@ -2242,14 +2255,14 @@ func (devices *DeviceSet) unmountAndDeactivateAll(dir string) { // container. This means it'll go away from the global scope directly, // and the device will be released when that container dies. if err := unix.Unmount(fullname, unix.MNT_DETACH); err != nil && err != unix.EINVAL { - logrus.Warnf("devmapper: Shutdown unmounting %s, error: %s", fullname, err) + logger.Warnf("Shutdown unmounting %s, error: %s", fullname, err) } if devInfo, err := devices.lookupDevice(name); err != nil { - logrus.Debugf("devmapper: Shutdown lookup device %s, error: %s", name, err) + logger.Debugf("Shutdown lookup device %s, error: %s", name, err) } else { if err := devices.deactivateDevice(devInfo); err != nil { - logrus.Debugf("devmapper: Shutdown deactivate %s, error: %s", devInfo.Hash, err) + logger.Debugf("Shutdown deactivate %s, error: %s", devInfo.Hash, err) } } } @@ -2257,9 +2270,11 @@ func (devices *DeviceSet) unmountAndDeactivateAll(dir string) { // Shutdown shuts down the device by unmounting the root. func (devices *DeviceSet) Shutdown(home string) error { - logrus.Debugf("devmapper: [deviceset %s] Shutdown()", devices.devicePrefix) - logrus.Debugf("devmapper: Shutting down DeviceSet: %s", devices.root) - defer logrus.Debugf("devmapper: [deviceset %s] Shutdown() END", devices.devicePrefix) + logger := logrus.WithField("storage-driver", "devicemapper") + + logger.Debugf("[deviceset %s] Shutdown()", devices.devicePrefix) + logger.Debugf("Shutting down DeviceSet: %s", devices.root) + defer logger.Debugf("[deviceset %s] Shutdown() END", devices.devicePrefix) // Stop deletion worker. This should start delivering new events to // ticker channel. That means no new instance of cleanupDeletedDevice() @@ -2284,7 +2299,7 @@ func (devices *DeviceSet) Shutdown(home string) error { info.lock.Lock() devices.Lock() if err := devices.deactivateDevice(info); err != nil { - logrus.Debugf("devmapper: Shutdown deactivate base , error: %s", err) + logger.Debugf("Shutdown deactivate base , error: %s", err) } devices.Unlock() info.lock.Unlock() @@ -2293,7 +2308,7 @@ func (devices *DeviceSet) Shutdown(home string) error { devices.Lock() if devices.thinPoolDevice == "" { if err := devices.deactivatePool(); err != nil { - logrus.Debugf("devmapper: Shutdown deactivate pool , error: %s", err) + logger.Debugf("Shutdown deactivate pool , error: %s", err) } } devices.Unlock() @@ -2382,8 +2397,10 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error { // UnmountDevice unmounts the device and removes it from hash. func (devices *DeviceSet) UnmountDevice(hash, mountPath string) error { - logrus.Debugf("devmapper: UnmountDevice START(hash=%s)", hash) - defer logrus.Debugf("devmapper: UnmountDevice END(hash=%s)", hash) + logger := logrus.WithField("storage-driver", "devicemapper") + + logger.Debugf("UnmountDevice START(hash=%s)", hash) + defer logger.Debugf("UnmountDevice END(hash=%s)", hash) info, err := devices.lookupDeviceWithLock(hash) if err != nil { @@ -2396,11 +2413,11 @@ func (devices *DeviceSet) UnmountDevice(hash, mountPath string) error { devices.Lock() defer devices.Unlock() - logrus.Debugf("devmapper: Unmount(%s)", mountPath) + logger.Debugf("Unmount(%s)", mountPath) if err := unix.Unmount(mountPath, unix.MNT_DETACH); err != nil { return err } - logrus.Debug("devmapper: Unmount done") + logger.Debug("Unmount done") // Remove the mountpoint here. Removing the mountpoint (in newer kernels) // will cause all other instances of this mount in other mount namespaces @@ -2411,7 +2428,7 @@ func (devices *DeviceSet) UnmountDevice(hash, mountPath string) error { // older kernels which don't have // torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied. if err := os.Remove(mountPath); err != nil { - logrus.Debugf("devmapper: error doing a remove on unmounted device %s: %v", mountPath, err) + logger.Debugf("error doing a remove on unmounted device %s: %v", mountPath, err) } return devices.deactivateDevice(info) @@ -2508,7 +2525,7 @@ func (devices *DeviceSet) MetadataDevicePath() string { func (devices *DeviceSet) getUnderlyingAvailableSpace(loopFile string) (uint64, error) { buf := new(unix.Statfs_t) if err := unix.Statfs(loopFile, buf); err != nil { - logrus.Warnf("devmapper: Couldn't stat loopfile filesystem %v: %v", loopFile, err) + logrus.WithField("storage-driver", "devicemapper").Warnf("Couldn't stat loopfile filesystem %v: %v", loopFile, err) return 0, err } return buf.Bfree * uint64(buf.Bsize), nil @@ -2518,7 +2535,7 @@ func (devices *DeviceSet) isRealFile(loopFile string) (bool, error) { if loopFile != "" { fi, err := os.Stat(loopFile) if err != nil { - logrus.Warnf("devmapper: Couldn't stat loopfile %v: %v", loopFile, err) + logrus.WithField("storage-driver", "devicemapper").Warnf("Couldn't stat loopfile %v: %v", loopFile, err) return false, err } return fi.Mode().IsRegular(), nil diff --git a/components/engine/daemon/graphdriver/devmapper/driver.go b/components/engine/daemon/graphdriver/devmapper/driver.go index 6bed6634cf..1384a3a157 100644 --- a/components/engine/daemon/graphdriver/devmapper/driver.go +++ b/components/engine/daemon/graphdriver/devmapper/driver.go @@ -246,7 +246,7 @@ func (d *Driver) Put(id string) error { err := d.DeviceSet.UnmountDevice(id, mp) if err != nil { - logrus.Errorf("devmapper: Error unmounting device %s: %v", id, err) + logrus.WithField("storage-driver", "devicemapper").Errorf("Error unmounting device %s: %v", id, err) } return err diff --git a/components/engine/daemon/graphdriver/overlay/overlay.go b/components/engine/daemon/graphdriver/overlay/overlay.go index 3fb823f57a..2e0bec5bc4 100644 --- a/components/engine/daemon/graphdriver/overlay/overlay.go +++ b/components/engine/daemon/graphdriver/overlay/overlay.go @@ -139,7 +139,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap switch fsMagic { case graphdriver.FsMagicAufs, graphdriver.FsMagicBtrfs, graphdriver.FsMagicEcryptfs, graphdriver.FsMagicNfsFs, graphdriver.FsMagicOverlay, graphdriver.FsMagicZfs: - logrus.Errorf("'overlay' is not supported over %s", backingFs) + logrus.WithField("storage-driver", "overlay").Errorf("'overlay' is not supported over %s", backingFs) return nil, graphdriver.ErrIncompatibleFS } @@ -152,7 +152,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, overlayutils.ErrDTypeNotSupported("overlay", backingFs) } // allow running without d_type only for existing setups (#27443) - logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs)) + logrus.WithField("storage-driver", "overlay").Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs)) } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) @@ -193,7 +193,7 @@ func supportsOverlay() error { return nil } } - logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") + logrus.WithField("storage-driver", "overlay").Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") return graphdriver.ErrNotSupported } @@ -369,11 +369,11 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err erro if err != nil { if c := d.ctr.Decrement(mergedDir); c <= 0 { if mntErr := unix.Unmount(mergedDir, 0); mntErr != nil { - logrus.Debugf("Failed to unmount %s: %v: %v", id, mntErr, err) + logrus.WithField("storage-driver", "overlay").Debugf("Failed to unmount %s: %v: %v", id, mntErr, err) } // Cleanup the created merged directory; see the comment in Put's rmdir if rmErr := unix.Rmdir(mergedDir); rmErr != nil && !os.IsNotExist(rmErr) { - logrus.Warnf("Failed to remove %s: %v: %v", id, rmErr, err) + logrus.WithField("storage-driver", "overlay").Warnf("Failed to remove %s: %v: %v", id, rmErr, err) } } } @@ -417,11 +417,12 @@ func (d *Driver) Put(id string) error { return nil } mountpoint := path.Join(d.dir(id), "merged") + logger := logrus.WithField("storage-driver", "overlay") if count := d.ctr.Decrement(mountpoint); count > 0 { return nil } if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil { - logrus.Debugf("Failed to unmount %s overlay: %v", id, err) + logger.Debugf("Failed to unmount %s overlay: %v", id, err) } // Remove the mountpoint here. Removing the mountpoint (in newer kernels) @@ -432,7 +433,7 @@ func (d *Driver) Put(id string) error { // fail on older kernels which don't have // torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied. if err := unix.Rmdir(mountpoint); err != nil { - logrus.Debugf("Failed to remove %s overlay: %v", id, err) + logger.Debugf("Failed to remove %s overlay: %v", id, err) } return nil } diff --git a/components/engine/daemon/graphdriver/overlay2/check.go b/components/engine/daemon/graphdriver/overlay2/check.go index beae22535b..d6ee42f47f 100644 --- a/components/engine/daemon/graphdriver/overlay2/check.go +++ b/components/engine/daemon/graphdriver/overlay2/check.go @@ -27,7 +27,7 @@ func doesSupportNativeDiff(d string) error { } defer func() { if err := os.RemoveAll(td); err != nil { - logrus.Warnf("Failed to remove check directory %v: %v", td, err) + logrus.WithField("storage-driver", "overlay2").Warnf("Failed to remove check directory %v: %v", td, err) } }() @@ -62,7 +62,7 @@ func doesSupportNativeDiff(d string) error { } defer func() { if err := unix.Unmount(filepath.Join(td, "merged"), 0); err != nil { - logrus.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err) + logrus.WithField("storage-driver", "overlay2").Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err) } }() @@ -113,7 +113,7 @@ func supportsMultipleLowerDir(d string) error { } defer func() { if err := os.RemoveAll(td); err != nil { - logrus.Warnf("Failed to remove check directory %v: %v", td, err) + logrus.WithField("storage-driver", "overlay2").Warnf("Failed to remove check directory %v: %v", td, err) } }() @@ -128,7 +128,7 @@ func supportsMultipleLowerDir(d string) error { return errors.Wrap(err, "failed to mount overlay") } if err := unix.Unmount(filepath.Join(td, "merged"), 0); err != nil { - logrus.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err) + logrus.WithField("storage-driver", "overlay2").Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err) } return nil } diff --git a/components/engine/daemon/graphdriver/overlay2/overlay.go b/components/engine/daemon/graphdriver/overlay2/overlay.go index 30bbba1fc1..39b9a8b389 100644 --- a/components/engine/daemon/graphdriver/overlay2/overlay.go +++ b/components/engine/daemon/graphdriver/overlay2/overlay.go @@ -155,28 +155,30 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap backingFs = fsName } + logger := logrus.WithField("storage-driver", "overlay2") + switch fsMagic { case graphdriver.FsMagicAufs, graphdriver.FsMagicEcryptfs, graphdriver.FsMagicNfsFs, graphdriver.FsMagicOverlay, graphdriver.FsMagicZfs: - logrus.Errorf("'overlay2' is not supported over %s", backingFs) + logger.Errorf("'overlay2' is not supported over %s", backingFs) return nil, graphdriver.ErrIncompatibleFS case graphdriver.FsMagicBtrfs: // Support for OverlayFS on BTRFS was added in kernel 4.7 // See https://btrfs.wiki.kernel.org/index.php/Changelog if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 7, Minor: 0}) < 0 { if !opts.overrideKernelCheck { - logrus.Errorf("'overlay2' requires kernel 4.7 to use on %s", backingFs) + logger.Errorf("'overlay2' requires kernel 4.7 to use on %s", backingFs) return nil, graphdriver.ErrIncompatibleFS } - logrus.Warn("Using pre-4.7.0 kernel for overlay2 on btrfs, may require kernel update") + logger.Warn("Using pre-4.7.0 kernel for overlay2 on btrfs, may require kernel update") } } if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 0, Minor: 0}) < 0 { if opts.overrideKernelCheck { - logrus.Warn("Using pre-4.0.0 kernel for overlay2, mount failures may require kernel update") + logger.Warn("Using pre-4.0.0 kernel for overlay2, mount failures may require kernel update") } else { if err := supportsMultipleLowerDir(testdir); err != nil { - logrus.Debugf("Multiple lower dirs not supported: %v", err) + logger.Debugf("Multiple lower dirs not supported: %v", err) return nil, graphdriver.ErrNotSupported } } @@ -190,7 +192,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, overlayutils.ErrDTypeNotSupported("overlay2", backingFs) } // allow running without d_type only for existing setups (#27443) - logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs)) + logger.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs)) } rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) @@ -226,7 +228,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, fmt.Errorf("Storage Option overlay2.size only supported for backingFS XFS. Found %v", backingFs) } - logrus.Debugf("backingFs=%s, projectQuotaSupported=%v", backingFs, projectQuotaSupported) + logger.Debugf("backingFs=%s, projectQuotaSupported=%v", backingFs, projectQuotaSupported) return d, nil } @@ -275,14 +277,14 @@ func supportsOverlay() error { return nil } } - logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") + logrus.WithField("storage-driver", "overlay2").Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") return graphdriver.ErrNotSupported } func useNaiveDiff(home string) bool { useNaiveDiffLock.Do(func() { if err := doesSupportNativeDiff(home); err != nil { - logrus.Warnf("Not using native diff for overlay2, this may cause degraded performance for building images: %v", err) + logrus.WithField("storage-driver", "overlay2").Warnf("Not using native diff for overlay2, this may cause degraded performance for building images: %v", err) useNaiveDiffOnly = true } }) @@ -517,7 +519,7 @@ func (d *Driver) Remove(id string) error { lid, err := ioutil.ReadFile(path.Join(dir, "link")) if err == nil { if err := os.RemoveAll(path.Join(d.home, linkDir, string(lid))); err != nil { - logrus.Debugf("Failed to remove link: %v", err) + logrus.WithField("storage-driver", "overlay2").Debugf("Failed to remove link: %v", err) } } @@ -554,11 +556,11 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e if retErr != nil { if c := d.ctr.Decrement(mergedDir); c <= 0 { if mntErr := unix.Unmount(mergedDir, 0); mntErr != nil { - logrus.Errorf("error unmounting %v: %v", mergedDir, mntErr) + logrus.WithField("storage-driver", "overlay2").Errorf("error unmounting %v: %v", mergedDir, mntErr) } // Cleanup the created merged directory; see the comment in Put's rmdir if rmErr := unix.Rmdir(mergedDir); rmErr != nil && !os.IsNotExist(rmErr) { - logrus.Debugf("Failed to remove %s: %v: %v", id, rmErr, err) + logrus.WithField("storage-driver", "overlay2").Debugf("Failed to remove %s: %v: %v", id, rmErr, err) } } } @@ -641,11 +643,12 @@ func (d *Driver) Put(id string) error { } mountpoint := path.Join(dir, "merged") + logger := logrus.WithField("storage-driver", "overlay2") if count := d.ctr.Decrement(mountpoint); count > 0 { return nil } if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil { - logrus.Debugf("Failed to unmount %s overlay: %s - %v", id, mountpoint, err) + logger.Debugf("Failed to unmount %s overlay: %s - %v", id, mountpoint, err) } // Remove the mountpoint here. Removing the mountpoint (in newer kernels) // will cause all other instances of this mount in other mount namespaces @@ -655,7 +658,7 @@ func (d *Driver) Put(id string) error { // fail on older kernels which don't have // torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied. if err := unix.Rmdir(mountpoint); err != nil && !os.IsNotExist(err) { - logrus.Debugf("Failed to remove %s overlay: %v", id, err) + logger.Debugf("Failed to remove %s overlay: %v", id, err) } return nil } @@ -696,7 +699,7 @@ func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64 applyDir := d.getDiffPath(id) - logrus.Debugf("Applying tar in %s", applyDir) + logrus.WithField("storage-driver", "overlay2").Debugf("Applying tar in %s", applyDir) // Overlay doesn't need the parent id to apply the diff if err := untar(diff, applyDir, &archive.TarOptions{ UIDMaps: d.uidMaps, @@ -734,7 +737,7 @@ func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) { } diffPath := d.getDiffPath(id) - logrus.Debugf("Tar with options on %s", diffPath) + logrus.WithField("storage-driver", "overlay2").Debugf("Tar with options on %s", diffPath) return archive.TarWithOptions(diffPath, &archive.TarOptions{ Compression: archive.Uncompressed, UIDMaps: d.uidMaps, diff --git a/components/engine/daemon/graphdriver/zfs/zfs.go b/components/engine/daemon/graphdriver/zfs/zfs.go index 7183e69421..913172d877 100644 --- a/components/engine/daemon/graphdriver/zfs/zfs.go +++ b/components/engine/daemon/graphdriver/zfs/zfs.go @@ -37,7 +37,7 @@ type Logger struct{} // Log wraps log message from ZFS driver with a prefix '[zfs]'. func (*Logger) Log(cmd []string) { - logrus.Debugf("[zfs] %s", strings.Join(cmd, " ")) + logrus.WithField("storage-driver", "zfs").Debugf("[zfs] %s", strings.Join(cmd, " ")) } // Init returns a new ZFS driver. @@ -46,14 +46,16 @@ func (*Logger) Log(cmd []string) { func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { var err error + logger := logrus.WithField("storage-driver", "zfs") + if _, err := exec.LookPath("zfs"); err != nil { - logrus.Debugf("[zfs] zfs command is not available: %v", err) + logger.Debugf("zfs command is not available: %v", err) return nil, graphdriver.ErrPrerequisites } file, err := os.OpenFile("/dev/zfs", os.O_RDWR, 600) if err != nil { - logrus.Debugf("[zfs] cannot open /dev/zfs: %v", err) + logger.Debugf("cannot open /dev/zfs: %v", err) return nil, graphdriver.ErrPrerequisites } defer file.Close() @@ -151,7 +153,7 @@ func lookupZfsDataset(rootdir string) (string, error) { } for _, m := range mounts { if err := unix.Stat(m.Mountpoint, &stat); err != nil { - logrus.Debugf("[zfs] failed to stat '%s' while scanning for zfs mount: %v", m.Mountpoint, err) + logrus.WithField("storage-driver", "zfs").Debugf("failed to stat '%s' while scanning for zfs mount: %v", m.Mountpoint, err) continue // may fail on fuse file systems } @@ -364,10 +366,10 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e if retErr != nil { if c := d.ctr.Decrement(mountpoint); c <= 0 { if mntErr := unix.Unmount(mountpoint, 0); mntErr != nil { - logrus.Errorf("Error unmounting %v: %v", mountpoint, mntErr) + logrus.WithField("storage-driver", "zfs").Errorf("Error unmounting %v: %v", mountpoint, mntErr) } if rmErr := unix.Rmdir(mountpoint); rmErr != nil && !os.IsNotExist(rmErr) { - logrus.Debugf("Failed to remove %s: %v", id, rmErr) + logrus.WithField("storage-driver", "zfs").Debugf("Failed to remove %s: %v", id, rmErr) } } @@ -376,7 +378,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e filesystem := d.zfsPath(id) options := label.FormatMountLabel("", mountLabel) - logrus.Debugf(`[zfs] mount("%s", "%s", "%s")`, filesystem, mountpoint, options) + logrus.WithField("storage-driver", "zfs").Debugf(`mount("%s", "%s", "%s")`, filesystem, mountpoint, options) rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) if err != nil { @@ -407,13 +409,15 @@ func (d *Driver) Put(id string) error { return nil } - logrus.Debugf(`[zfs] unmount("%s")`, mountpoint) + logger := logrus.WithField("storage-driver", "zfs") + + logger.Debugf(`unmount("%s")`, mountpoint) if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil { - logrus.Warnf("Failed to unmount %s mount %s: %v", id, mountpoint, err) + logger.Warnf("Failed to unmount %s mount %s: %v", id, mountpoint, err) } if err := unix.Rmdir(mountpoint); err != nil && !os.IsNotExist(err) { - logrus.Debugf("Failed to remove %s mount point %s: %v", id, mountpoint, err) + logger.Debugf("Failed to remove %s mount point %s: %v", id, mountpoint, err) } return nil diff --git a/components/engine/daemon/graphdriver/zfs/zfs_freebsd.go b/components/engine/daemon/graphdriver/zfs/zfs_freebsd.go index e5abf0de6e..f15aae0596 100644 --- a/components/engine/daemon/graphdriver/zfs/zfs_freebsd.go +++ b/components/engine/daemon/graphdriver/zfs/zfs_freebsd.go @@ -17,7 +17,7 @@ func checkRootdirFs(rootdir string) error { // on FreeBSD buf.Fstypename contains ['z', 'f', 's', 0 ... ] if (buf.Fstypename[0] != 122) || (buf.Fstypename[1] != 102) || (buf.Fstypename[2] != 115) || (buf.Fstypename[3] != 0) { - logrus.Debugf("[zfs] no zfs dataset found for rootdir '%s'", rootdir) + logrus.WithField("storage-driver", "zfs").Debugf("no zfs dataset found for rootdir '%s'", rootdir) return graphdriver.ErrPrerequisites } diff --git a/components/engine/daemon/graphdriver/zfs/zfs_linux.go b/components/engine/daemon/graphdriver/zfs/zfs_linux.go index 956bb0e7a5..589ecbd179 100644 --- a/components/engine/daemon/graphdriver/zfs/zfs_linux.go +++ b/components/engine/daemon/graphdriver/zfs/zfs_linux.go @@ -16,7 +16,7 @@ func checkRootdirFs(rootDir string) error { } if fsMagic != graphdriver.FsMagicZfs { - logrus.WithField("root", rootDir).WithField("backingFS", backingFS).WithField("driver", "zfs").Error("No zfs dataset found for root") + logrus.WithField("root", rootDir).WithField("backingFS", backingFS).WithField("storage-driver", "zfs").Error("No zfs dataset found for root") return graphdriver.ErrPrerequisites } diff --git a/components/engine/daemon/info.go b/components/engine/daemon/info.go index 6f5ae69896..7b011fe324 100644 --- a/components/engine/daemon/info.go +++ b/components/engine/daemon/info.go @@ -19,7 +19,6 @@ import ( "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" "github.com/docker/docker/registry" - "github.com/docker/docker/volume/drivers" "github.com/docker/go-connections/sockets" "github.com/sirupsen/logrus" ) @@ -196,7 +195,7 @@ func (daemon *Daemon) SystemVersion() types.Version { func (daemon *Daemon) showPluginsInfo() types.PluginsInfo { var pluginsInfo types.PluginsInfo - pluginsInfo.Volume = volumedrivers.GetDriverList() + pluginsInfo.Volume = daemon.volumes.GetDriverList() pluginsInfo.Network = daemon.GetNetworkDriverList() // The authorization plugins are returned in the order they are // used as they constitute a request/response modification chain. diff --git a/components/engine/daemon/oci_linux.go b/components/engine/daemon/oci_linux.go index a3638ace21..f23f0b9990 100644 --- a/components/engine/daemon/oci_linux.go +++ b/components/engine/daemon/oci_linux.go @@ -18,7 +18,7 @@ import ( "github.com/docker/docker/oci" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/mount" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/devices" @@ -580,7 +580,7 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c if m.Source == "tmpfs" { data := m.Data - parser := volume.NewParser("linux") + parser := volumemounts.NewParser("linux") options := []string{"noexec", "nosuid", "nodev", string(parser.DefaultPropagationMode())} if data != "" { options = append(options, strings.Split(data, ",")...) diff --git a/components/engine/daemon/prune.go b/components/engine/daemon/prune.go index 286e1e4bd2..ba7e3485d0 100644 --- a/components/engine/daemon/prune.go +++ b/components/engine/daemon/prune.go @@ -107,11 +107,20 @@ func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Arg rep := &types.VolumesPruneReport{} - pruneVols := func(v volume.Volume) error { + volumes, err := daemon.volumes.FilterByDriver(volume.DefaultDriverName) + if err != nil { + return nil, err + } + + for _, v := range volumes { select { case <-ctx.Done(): logrus.Debugf("VolumesPrune operation cancelled: %#v", *rep) - return ctx.Err() + err := ctx.Err() + if err == context.Canceled { + return rep, nil + } + return rep, err default: } @@ -122,9 +131,10 @@ func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Arg detailedVolume, ok := v.(volume.DetailedVolume) if ok { if !matchLabels(pruneFilters, detailedVolume.Labels()) { - return nil + continue } } + vSize, err := directory.Size(ctx, v.Path()) if err != nil { logrus.Warnf("could not determine size of volume %s: %v", name, err) @@ -132,21 +142,15 @@ func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Arg err = daemon.volumeRm(v) if err != nil { logrus.Warnf("could not remove volume %s: %v", name, err) - return nil + continue } rep.SpaceReclaimed += uint64(vSize) rep.VolumesDeleted = append(rep.VolumesDeleted, name) } - return nil } - err = daemon.traverseLocalVolumes(pruneVols) - if err == context.Canceled { - return rep, nil - } - - return rep, err + return rep, nil } // localNetworksPrune removes unused local networks diff --git a/components/engine/daemon/volumes.go b/components/engine/daemon/volumes.go index a63a90771a..034924c777 100644 --- a/components/engine/daemon/volumes.go +++ b/components/engine/daemon/volumes.go @@ -1,7 +1,6 @@ package daemon // import "github.com/docker/docker/daemon" import ( - "fmt" "os" "path/filepath" "reflect" @@ -15,7 +14,7 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/errdefs" "github.com/docker/docker/volume" - "github.com/docker/docker/volume/drivers" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -76,8 +75,9 @@ func (m mounts) parts(i int) int { // 4. Cleanup old volumes that are about to be reassigned. func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) { binds := map[string]bool{} - mountPoints := map[string]*volume.MountPoint{} - parser := volume.NewParser(container.OS) + mountPoints := map[string]*volumemounts.MountPoint{} + parser := volumemounts.NewParser(container.OS) + defer func() { // clean up the container mountpoints once return with error if retErr != nil { @@ -117,7 +117,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo } for _, m := range c.MountPoints { - cp := &volume.MountPoint{ + cp := &volumemounts.MountPoint{ Type: m.Type, Name: m.Name, Source: m.Source, @@ -252,7 +252,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo // lazyInitializeVolume initializes a mountpoint's volume if needed. // This happens after a daemon restart. -func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error { +func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volumemounts.MountPoint) error { if len(m.Driver) > 0 && m.Volume == nil { v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID) if err != nil { @@ -272,7 +272,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) { container.Lock() defer container.Unlock() - parser := volume.NewParser(container.OS) + parser := volumemounts.NewParser(container.OS) maybeUpdate := make(map[string]bool) for _, mp := range container.MountPoints { @@ -290,7 +290,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) { mountSpecs[m.Target] = true } - binds := make(map[string]*volume.MountPoint, len(container.HostConfig.Binds)) + binds := make(map[string]*volumemounts.MountPoint, len(container.HostConfig.Binds)) for _, rawSpec := range container.HostConfig.Binds { mp, err := parser.ParseMountRaw(rawSpec, container.HostConfig.VolumeDriver) if err != nil { @@ -300,7 +300,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) { binds[mp.Destination] = mp } - volumesFrom := make(map[string]volume.MountPoint) + volumesFrom := make(map[string]volumemounts.MountPoint) for _, fromSpec := range container.HostConfig.VolumesFrom { from, _, err := parser.ParseVolumesFrom(fromSpec) if err != nil { @@ -323,7 +323,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) { fromC.Unlock() } - needsUpdate := func(containerMount, other *volume.MountPoint) bool { + needsUpdate := func(containerMount, other *volumemounts.MountPoint) bool { if containerMount.Type != other.Type || !reflect.DeepEqual(containerMount.Spec, other.Spec) { return true } @@ -385,32 +385,3 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) { cm.Spec.ReadOnly = !cm.RW } } - -func (daemon *Daemon) traverseLocalVolumes(fn func(volume.Volume) error) error { - localVolumeDriver, err := volumedrivers.GetDriver(volume.DefaultDriverName) - if err != nil { - return fmt.Errorf("can't retrieve local volume driver: %v", err) - } - vols, err := localVolumeDriver.List() - if err != nil { - return fmt.Errorf("can't retrieve local volumes: %v", err) - } - - for _, v := range vols { - name := v.Name() - vol, err := daemon.volumes.Get(name) - if err != nil { - logrus.Warnf("failed to retrieve volume %s from store: %v", name, err) - } else { - // daemon.volumes.Get will return DetailedVolume - v = vol - } - - err = fn(v) - if err != nil { - return err - } - } - - return nil -} diff --git a/components/engine/daemon/volumes_unit_test.go b/components/engine/daemon/volumes_unit_test.go index aa51b4a822..6bdebe467c 100644 --- a/components/engine/daemon/volumes_unit_test.go +++ b/components/engine/daemon/volumes_unit_test.go @@ -4,7 +4,7 @@ import ( "runtime" "testing" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" ) func TestParseVolumesFrom(t *testing.T) { @@ -21,7 +21,7 @@ func TestParseVolumesFrom(t *testing.T) { {"foobar:baz", "", "", true}, } - parser := volume.NewParser(runtime.GOOS) + parser := volumemounts.NewParser(runtime.GOOS) for _, c := range cases { id, mode, err := parser.ParseVolumesFrom(c.spec) diff --git a/components/engine/daemon/volumes_unix.go b/components/engine/daemon/volumes_unix.go index b752dbbb66..efffefa76b 100644 --- a/components/engine/daemon/volumes_unix.go +++ b/components/engine/daemon/volumes_unix.go @@ -3,10 +3,8 @@ package daemon // import "github.com/docker/docker/daemon" import ( - "encoding/json" "fmt" "os" - "path/filepath" "sort" "strconv" "strings" @@ -14,10 +12,7 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/mount" - "github.com/docker/docker/volume" - "github.com/docker/docker/volume/drivers" - "github.com/docker/docker/volume/local" - "github.com/pkg/errors" + volumemounts "github.com/docker/docker/volume/mounts" ) // setupMounts iterates through each of the mount points for a container and @@ -45,7 +40,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er // mount the socket the daemon is listening on. During daemon shutdown, the socket // (/var/run/docker.sock by default) doesn't exist anymore causing the call to m.Setup to // create at directory instead. This in turn will prevent the daemon to restart. - checkfunc := func(m *volume.MountPoint) error { + checkfunc := func(m *volumemounts.MountPoint) error { if _, exist := daemon.hosts[m.Source]; exist && daemon.IsShuttingDown() { return fmt.Errorf("Could not mount %q to container while the daemon is shutting down", m.Source) } @@ -107,86 +102,12 @@ func sortMounts(m []container.Mount) []container.Mount { // setBindModeIfNull is platform specific processing to ensure the // shared mode is set to 'z' if it is null. This is called in the case // of processing a named volume and not a typical bind. -func setBindModeIfNull(bind *volume.MountPoint) { +func setBindModeIfNull(bind *volumemounts.MountPoint) { if bind.Mode == "" { bind.Mode = "z" } } -// migrateVolume links the contents of a volume created pre Docker 1.7 -// into the location expected by the local driver. -// It creates a symlink from DOCKER_ROOT/vfs/dir/VOLUME_ID to DOCKER_ROOT/volumes/VOLUME_ID/_container_data. -// It preserves the volume json configuration generated pre Docker 1.7 to be able to -// downgrade from Docker 1.7 to Docker 1.6 without losing volume compatibility. -func migrateVolume(id, vfs string) error { - l, err := volumedrivers.GetDriver(volume.DefaultDriverName) - if err != nil { - return err - } - - newDataPath := l.(*local.Root).DataPath(id) - fi, err := os.Stat(newDataPath) - if err != nil && !os.IsNotExist(err) { - return err - } - - if fi != nil && fi.IsDir() { - return nil - } - - return os.Symlink(vfs, newDataPath) -} - -// verifyVolumesInfo ports volumes configured for the containers pre docker 1.7. -// It reads the container configuration and creates valid mount points for the old volumes. -func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error { - container.Lock() - defer container.Unlock() - - // Inspect old structures only when we're upgrading from old versions - // to versions >= 1.7 and the MountPoints has not been populated with volumes data. - type volumes struct { - Volumes map[string]string - VolumesRW map[string]bool - } - cfgPath, err := container.ConfigPath() - if err != nil { - return err - } - f, err := os.Open(cfgPath) - if err != nil { - return errors.Wrap(err, "could not open container config") - } - defer f.Close() - var cv volumes - if err := json.NewDecoder(f).Decode(&cv); err != nil { - return errors.Wrap(err, "could not decode container config") - } - - if len(container.MountPoints) == 0 && len(cv.Volumes) > 0 { - for destination, hostPath := range cv.Volumes { - vfsPath := filepath.Join(daemon.root, "vfs", "dir") - rw := cv.VolumesRW != nil && cv.VolumesRW[destination] - - if strings.HasPrefix(hostPath, vfsPath) { - id := filepath.Base(hostPath) - v, err := daemon.volumes.CreateWithRef(id, volume.DefaultDriverName, container.ID, nil, nil) - if err != nil { - return err - } - if err := migrateVolume(id, hostPath); err != nil { - return err - } - container.AddMountPointWithVolume(destination, v, true) - } else { // Bind mount - m := volume.MountPoint{Source: hostPath, Destination: destination, RW: rw} - container.MountPoints[destination] = &m - } - } - } - return nil -} - func (daemon *Daemon) mountVolumes(container *container.Container) error { mounts, err := daemon.setupMounts(container) if err != nil { diff --git a/components/engine/daemon/volumes_unix_test.go b/components/engine/daemon/volumes_unix_test.go index f80ea29fb3..36e19110d1 100644 --- a/components/engine/daemon/volumes_unix_test.go +++ b/components/engine/daemon/volumes_unix_test.go @@ -11,7 +11,7 @@ import ( containertypes "github.com/docker/docker/api/types/container" mounttypes "github.com/docker/docker/api/types/mount" "github.com/docker/docker/container" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" ) func TestBackportMountSpec(t *testing.T) { @@ -19,7 +19,7 @@ func TestBackportMountSpec(t *testing.T) { c := &container.Container{ State: &container.State{}, - MountPoints: map[string]*volume.MountPoint{ + MountPoints: map[string]*volumemounts.MountPoint{ "/apple": {Destination: "/apple", Source: "/var/lib/docker/volumes/12345678", Name: "12345678", RW: true, CopyData: true}, // anonymous volume "/banana": {Destination: "/banana", Source: "/var/lib/docker/volumes/data", Name: "data", RW: true, CopyData: true}, // named volume "/cherry": {Destination: "/cherry", Source: "/var/lib/docker/volumes/data", Name: "data", CopyData: true}, // RO named volume @@ -73,7 +73,7 @@ func TestBackportMountSpec(t *testing.T) { d.containers.Add("1", &container.Container{ State: &container.State{}, ID: "1", - MountPoints: map[string]*volume.MountPoint{ + MountPoints: map[string]*volumemounts.MountPoint{ "/kumquat": {Destination: "/kumquat", Name: "data", RW: false, CopyData: true}, }, HostConfig: &containertypes.HostConfig{ @@ -84,11 +84,11 @@ func TestBackportMountSpec(t *testing.T) { }) type expected struct { - mp *volume.MountPoint + mp *volumemounts.MountPoint comment string } - pretty := func(mp *volume.MountPoint) string { + pretty := func(mp *volumemounts.MountPoint) string { b, err := json.MarshalIndent(mp, "\t", " ") if err != nil { return fmt.Sprintf("%#v", mp) @@ -98,7 +98,7 @@ func TestBackportMountSpec(t *testing.T) { for _, x := range []expected{ { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/apple", RW: true, @@ -114,7 +114,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "anonymous volume", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/banana", RW: true, @@ -130,7 +130,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "named volume", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/cherry", Name: "data", @@ -146,7 +146,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "read-only named volume", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/dates", Name: "data", @@ -162,7 +162,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "named volume with nocopy", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/elderberry", Name: "data", @@ -178,7 +178,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "masks an anonymous volume", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeBind, Destination: "/fig", Source: "/data", @@ -192,7 +192,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "bind mount with read/write", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeBind, Destination: "/guava", Source: "/data", @@ -209,7 +209,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "bind mount with read/write + shared propagation", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/honeydew", Source: "/var/lib/docker/volumes/data", @@ -229,7 +229,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "volume defined in mounts API", }, { - mp: &volume.MountPoint{ + mp: &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Destination: "/kumquat", Source: "/var/lib/docker/volumes/data", diff --git a/components/engine/daemon/volumes_windows.go b/components/engine/daemon/volumes_windows.go index 458f2851da..3d63d02e1c 100644 --- a/components/engine/daemon/volumes_windows.go +++ b/components/engine/daemon/volumes_windows.go @@ -6,7 +6,7 @@ import ( "github.com/docker/docker/api/types/mount" "github.com/docker/docker/container" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/volume" + volumemounts "github.com/docker/docker/volume/mounts" ) // setupMounts configures the mount points for a container by appending each @@ -20,7 +20,7 @@ import ( func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, error) { var mnts []container.Mount - for _, mount := range c.MountPoints { // type is volume.MountPoint + for _, mount := range c.MountPoints { // type is volumemounts.MountPoint if err := daemon.lazyInitializeVolume(c.ID, mount); err != nil { return nil, err } @@ -42,7 +42,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er // setBindModeIfNull is platform specific processing which is a no-op on // Windows. -func setBindModeIfNull(bind *volume.MountPoint) { +func setBindModeIfNull(bind *volumemounts.MountPoint) { return } diff --git a/components/engine/integration-cli/check_test.go b/components/engine/integration-cli/check_test.go index 52d284ac9f..373b8656e1 100644 --- a/components/engine/integration-cli/check_test.go +++ b/components/engine/integration-cli/check_test.go @@ -350,14 +350,7 @@ func (s *DockerSwarmSuite) TearDownTest(c *check.C) { for _, d := range s.daemons { if d != nil { d.Stop(c) - // FIXME(vdemeester) should be handled by SwarmDaemon ? - // raft state file is quite big (64MB) so remove it after every test - walDir := filepath.Join(d.Root, "swarm/raft/wal") - if err := os.RemoveAll(walDir); err != nil { - c.Logf("error removing %v: %v", walDir, err) - } - - d.CleanupExecRoot(c) + d.Cleanup(c) } } s.daemons = nil diff --git a/components/engine/integration-cli/daemon/daemon.go b/components/engine/integration-cli/daemon/daemon.go index 951cbe31f9..fcbbfdfb0c 100644 --- a/components/engine/integration-cli/daemon/daemon.go +++ b/components/engine/integration-cli/daemon/daemon.go @@ -2,7 +2,6 @@ package daemon // import "github.com/docker/docker/integration-cli/daemon" import ( "fmt" - "os/exec" "strings" "time" @@ -88,19 +87,6 @@ func (d *Daemon) inspectFieldWithError(name, field string) (string, error) { return d.inspectFilter(name, fmt.Sprintf(".%s", field)) } -// BuildImageWithOut builds an image with the specified dockerfile and options and returns the output -func (d *Daemon) BuildImageWithOut(name, dockerfile string, useCache bool, buildFlags ...string) (string, int, error) { - buildCmd := BuildImageCmdWithHost(d.dockerBinary, name, dockerfile, d.Sock(), useCache, buildFlags...) - result := icmd.RunCmd(icmd.Cmd{ - Command: buildCmd.Args, - Env: buildCmd.Env, - Dir: buildCmd.Dir, - Stdin: buildCmd.Stdin, - Stdout: buildCmd.Stdout, - }) - return result.Combined(), result.ExitCode, result.Error -} - // CheckActiveContainerCount returns the number of active containers // FIXME(vdemeester) should re-use ActivateContainers in some way func (d *Daemon) CheckActiveContainerCount(c *check.C) (interface{}, check.CommentInterface) { @@ -155,22 +141,3 @@ func WaitInspectWithArgs(dockerBinary, name, expr, expected string, timeout time } return nil } - -// BuildImageCmdWithHost create a build command with the specified arguments. -// Deprecated -// FIXME(vdemeester) move this away -func BuildImageCmdWithHost(dockerBinary, name, dockerfile, host string, useCache bool, buildFlags ...string) *exec.Cmd { - args := []string{} - if host != "" { - args = append(args, "--host", host) - } - args = append(args, "build", "-t", name) - if !useCache { - args = append(args, "--no-cache") - } - args = append(args, buildFlags...) - args = append(args, "-") - buildCmd := exec.Command(dockerBinary, args...) - buildCmd.Stdin = strings.NewReader(dockerfile) - return buildCmd -} diff --git a/components/engine/integration-cli/docker_api_swarm_service_test.go b/components/engine/integration-cli/docker_api_swarm_service_test.go index 25572a0ed8..d56230a6e8 100644 --- a/components/engine/integration-cli/docker_api_swarm_service_test.go +++ b/components/engine/integration-cli/docker_api_swarm_service_test.go @@ -4,20 +4,19 @@ package main import ( "fmt" - "path" "strconv" "strings" "time" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" - "github.com/docker/docker/api/types/swarm/runtime" "github.com/docker/docker/integration-cli/checker" + "github.com/docker/docker/integration-cli/cli" + "github.com/docker/docker/integration-cli/cli/build" "github.com/docker/docker/integration-cli/daemon" testdaemon "github.com/docker/docker/internal/test/daemon" - "github.com/docker/docker/internal/test/fixtures/plugin" - "github.com/docker/docker/internal/test/registry" "github.com/go-check/check" + "github.com/gotestyourself/gotestyourself/icmd" "golang.org/x/net/context" "golang.org/x/sys/unix" ) @@ -209,12 +208,12 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateStartFirst(c *check.C) { image2 := "testhealth:latest" // service started from this image won't pass health check - _, _, err := d.BuildImageWithOut(image2, - `FROM busybox + result := cli.BuildCmd(c, image2, cli.Daemon(d), + build.WithDockerfile(`FROM busybox HEALTHCHECK --interval=1s --timeout=30s --retries=1024 \ - CMD cat /status`, - true) - c.Check(err, check.IsNil) + CMD cat /status`), + ) + result.Assert(c, icmd.Success) // create service instances := 5 @@ -611,78 +610,3 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesStateReporting(c *check.C) { } } } - -// Test plugins deployed via swarm services -func (s *DockerSwarmSuite) TestAPISwarmServicesPlugin(c *check.C) { - testRequires(c, ExperimentalDaemon, DaemonIsLinux, IsAmd64) - - reg := registry.NewV2(c) - defer reg.Close() - - repo := path.Join(privateRegistryURL, "swarm", "test:v1") - repo2 := path.Join(privateRegistryURL, "swarm", "test:v2") - name := "test" - - err := plugin.CreateInRegistry(context.Background(), repo, nil) - c.Assert(err, checker.IsNil, check.Commentf("failed to create plugin")) - err = plugin.CreateInRegistry(context.Background(), repo2, nil) - c.Assert(err, checker.IsNil, check.Commentf("failed to create plugin")) - - d1 := s.AddDaemon(c, true, true) - d2 := s.AddDaemon(c, true, true) - d3 := s.AddDaemon(c, true, false) - - makePlugin := func(repo, name string, constraints []string) func(*swarm.Service) { - return func(s *swarm.Service) { - s.Spec.TaskTemplate.Runtime = "plugin" - s.Spec.TaskTemplate.PluginSpec = &runtime.PluginSpec{ - Name: name, - Remote: repo, - } - if constraints != nil { - s.Spec.TaskTemplate.Placement = &swarm.Placement{ - Constraints: constraints, - } - } - } - } - - id := d1.CreateService(c, makePlugin(repo, name, nil)) - waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(name), checker.True) - waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(name), checker.True) - waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(name), checker.True) - - service := d1.GetService(c, id) - d1.UpdateService(c, service, makePlugin(repo2, name, nil)) - waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginImage(name), checker.Equals, repo2) - waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginImage(name), checker.Equals, repo2) - waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginImage(name), checker.Equals, repo2) - waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(name), checker.True) - waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(name), checker.True) - waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(name), checker.True) - - d1.RemoveService(c, id) - waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(name), checker.False) - waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(name), checker.False) - waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(name), checker.False) - - // constrain to managers only - id = d1.CreateService(c, makePlugin(repo, name, []string{"node.role==manager"})) - waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(name), checker.True) - waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(name), checker.True) - waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(name), checker.False) // Not a manager, not running it - d1.RemoveService(c, id) - waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(name), checker.False) - waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(name), checker.False) - waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(name), checker.False) - - // with no name - id = d1.CreateService(c, makePlugin(repo, "", nil)) - waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(repo), checker.True) - waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(repo), checker.True) - waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(repo), checker.True) - d1.RemoveService(c, id) - waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(repo), checker.False) - waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(repo), checker.False) - waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(repo), checker.False) -} diff --git a/components/engine/integration-cli/docker_api_swarm_test.go b/components/engine/integration-cli/docker_api_swarm_test.go index 86ccfe1cb6..27f00ac07c 100644 --- a/components/engine/integration-cli/docker_api_swarm_test.go +++ b/components/engine/integration-cli/docker_api_swarm_test.go @@ -7,7 +7,6 @@ import ( "io/ioutil" "net" "net/http" - "os" "path/filepath" "strings" "sync" @@ -810,10 +809,6 @@ func (s *DockerSwarmSuite) TestAPISwarmRestartCluster(c *check.C) { if err := daemon.StopWithError(); err != nil { errs <- err } - // FIXME(vdemeester) This is duplicated… - if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" { - daemon.Root = filepath.Dir(daemon.Root) - } }(d) } wg.Wait() diff --git a/components/engine/integration-cli/docker_cli_daemon_plugins_test.go b/components/engine/integration-cli/docker_cli_daemon_plugins_test.go index c527cb1d8e..6d47fb1e03 100644 --- a/components/engine/integration-cli/docker_cli_daemon_plugins_test.go +++ b/components/engine/integration-cli/docker_cli_daemon_plugins_test.go @@ -231,24 +231,6 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) { c.Assert(err, checker.IsNil, check.Commentf(out)) } -func (s *DockerDaemonSuite) TestGraphdriverPlugin(c *check.C) { - testRequires(c, Network, IsAmd64, DaemonIsLinux, overlay2Supported, ExperimentalDaemon) - - s.d.Start(c) - - // install the plugin - plugin := "cpuguy83/docker-overlay2-graphdriver-plugin" - out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", plugin) - c.Assert(err, checker.IsNil, check.Commentf(out)) - - // restart the daemon with the plugin set as the storage driver - s.d.Restart(c, "-s", plugin, "--storage-opt", "overlay2.override_kernel_check=1") - - // run a container - out, err = s.d.Cmd("run", "--rm", "busybox", "true") // this will pull busybox using the plugin - c.Assert(err, checker.IsNil, check.Commentf(out)) -} - func (s *DockerDaemonSuite) TestPluginVolumeRemoveOnRestart(c *check.C) { testRequires(c, DaemonIsLinux, Network, IsAmd64) diff --git a/components/engine/integration-cli/docker_cli_daemon_test.go b/components/engine/integration-cli/docker_cli_daemon_test.go index b9ed41ca96..5015736a31 100644 --- a/components/engine/integration-cli/docker_cli_daemon_test.go +++ b/components/engine/integration-cli/docker_cli_daemon_test.go @@ -31,11 +31,11 @@ import ( moby_daemon "github.com/docker/docker/daemon" "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli" + "github.com/docker/docker/integration-cli/cli/build" "github.com/docker/docker/integration-cli/daemon" testdaemon "github.com/docker/docker/internal/test/daemon" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/mount" - "github.com/docker/docker/pkg/stringid" units "github.com/docker/go-units" "github.com/docker/libnetwork/iptables" "github.com/docker/libtrust" @@ -1156,14 +1156,16 @@ func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneLogsError(c *check.C) { func (s *DockerDaemonSuite) TestDaemonLoggingDriverShouldBeIgnoredForBuild(c *check.C) { s.d.StartWithBusybox(c, "--log-driver=splunk") - out, err := s.d.Cmd("build") - out, code, err := s.d.BuildImageWithOut("busyboxs", ` + result := cli.BuildCmd(c, "busyboxs", cli.Daemon(s.d), + build.WithDockerfile(` FROM busybox - RUN echo foo`, false) - comment := check.Commentf("Failed to build image. output %s, exitCode %d, err %v", out, code, err) - c.Assert(err, check.IsNil, comment) - c.Assert(code, check.Equals, 0, comment) - c.Assert(out, checker.Contains, "foo", comment) + RUN echo foo`), + build.WithoutCache, + ) + comment := check.Commentf("Failed to build image. output %s, exitCode %d, err %v", result.Combined(), result.ExitCode, result.Error) + c.Assert(result.Error, check.IsNil, comment) + c.Assert(result.ExitCode, check.Equals, 0, comment) + c.Assert(result.Combined(), checker.Contains, "foo", comment) } func (s *DockerDaemonSuite) TestDaemonUnixSockCleanedUp(c *check.C) { @@ -1805,23 +1807,18 @@ func (s *DockerDaemonSuite) TestDaemonNoSpaceLeftOnDeviceError(c *check.C) { c.Assert(mount.MakeRShared(testDir), checker.IsNil) defer mount.Unmount(testDir) - // create a 2MiB image and mount it as graph root + // create a 3MiB image (with a 2MiB ext4 fs) and mount it as graph root // Why in a container? Because `mount` sometimes behaves weirdly and often fails outright on this test in debian:jessie (which is what the test suite runs under if run from the Makefile) dockerCmd(c, "run", "--rm", "-v", testDir+":/test", "busybox", "sh", "-c", "dd of=/test/testfs.img bs=1M seek=3 count=0") icmd.RunCommand("mkfs.ext4", "-F", filepath.Join(testDir, "testfs.img")).Assert(c, icmd.Success) - result := icmd.RunCommand("losetup", "-f", "--show", filepath.Join(testDir, "testfs.img")) - result.Assert(c, icmd.Success) - loopname := strings.TrimSpace(string(result.Combined())) - defer exec.Command("losetup", "-d", loopname).Run() - - dockerCmd(c, "run", "--privileged", "--rm", "-v", testDir+":/test:shared", "busybox", "sh", "-c", fmt.Sprintf("mkdir -p /test/test-mount && mount -t ext4 -no loop,rw %v /test/test-mount", loopname)) + dockerCmd(c, "run", "--privileged", "--rm", "-v", testDir+":/test:shared", "busybox", "sh", "-c", "mkdir -p /test/test-mount && mount -n /test/testfs.img /test/test-mount") defer mount.Unmount(filepath.Join(testDir, "test-mount")) s.d.Start(c, "--data-root", filepath.Join(testDir, "test-mount")) defer s.d.Stop(c) - // pull a repository large enough to fill the mount point + // pull a repository large enough to overfill the mounted filesystem pullOut, err := s.d.Cmd("pull", "debian:stretch") c.Assert(err, checker.NotNil, check.Commentf(pullOut)) c.Assert(pullOut, checker.Contains, "no space left on device") @@ -2404,12 +2401,16 @@ func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFileReload(c *chec func (s *DockerDaemonSuite) TestBuildOnDisabledBridgeNetworkDaemon(c *check.C) { s.d.StartWithBusybox(c, "-b=none", "--iptables=false") - out, code, err := s.d.BuildImageWithOut("busyboxs", - `FROM busybox - RUN cat /etc/hosts`, false) - comment := check.Commentf("Failed to build image. output %s, exitCode %d, err %v", out, code, err) - c.Assert(err, check.IsNil, comment) - c.Assert(code, check.Equals, 0, comment) + + result := cli.BuildCmd(c, "busyboxs", cli.Daemon(s.d), + build.WithDockerfile(` + FROM busybox + RUN cat /etc/hosts`), + build.WithoutCache, + ) + comment := check.Commentf("Failed to build image. output %s, exitCode %d, err %v", result.Combined(), result.ExitCode, result.Error) + c.Assert(result.Error, check.IsNil, comment) + c.Assert(result.ExitCode, check.Equals, 0, comment) } // Test case for #21976 @@ -2668,84 +2669,6 @@ func (s *DockerDaemonSuite) TestDaemonRestartSaveContainerExitCode(c *check.C) { c.Assert(out, checker.Equals, errMsg1) } -func (s *DockerDaemonSuite) TestDaemonBackcompatPre17Volumes(c *check.C) { - testRequires(c, SameHostDaemon) - d := s.d - d.StartWithBusybox(c) - - // hack to be able to side-load a container config - out, err := d.Cmd("create", "busybox:latest") - c.Assert(err, checker.IsNil, check.Commentf(out)) - id := strings.TrimSpace(out) - - out, err = d.Cmd("inspect", "--type=image", "--format={{.ID}}", "busybox:latest") - c.Assert(err, checker.IsNil, check.Commentf(out)) - d.Stop(c) - <-d.Wait - - imageID := strings.TrimSpace(out) - volumeID := stringid.GenerateNonCryptoID() - vfsPath := filepath.Join(d.Root, "vfs", "dir", volumeID) - c.Assert(os.MkdirAll(vfsPath, 0755), checker.IsNil) - - config := []byte(` - { - "ID": "` + id + `", - "Name": "hello", - "Driver": "` + d.StorageDriver() + `", - "Image": "` + imageID + `", - "Config": {"Image": "busybox:latest"}, - "NetworkSettings": {}, - "Volumes": { - "/bar":"/foo", - "/foo": "` + vfsPath + `", - "/quux":"/quux" - }, - "VolumesRW": { - "/bar": true, - "/foo": true, - "/quux": false - } - } - `) - - configPath := filepath.Join(d.Root, "containers", id, "config.v2.json") - c.Assert(ioutil.WriteFile(configPath, config, 600), checker.IsNil) - d.Start(c) - - out, err = d.Cmd("inspect", "--type=container", "--format={{ json .Mounts }}", id) - c.Assert(err, checker.IsNil, check.Commentf(out)) - type mount struct { - Name string - Source string - Destination string - Driver string - RW bool - } - - ls := []mount{} - err = json.NewDecoder(strings.NewReader(out)).Decode(&ls) - c.Assert(err, checker.IsNil) - - expected := []mount{ - {Source: "/foo", Destination: "/bar", RW: true}, - {Name: volumeID, Destination: "/foo", RW: true}, - {Source: "/quux", Destination: "/quux", RW: false}, - } - c.Assert(ls, checker.HasLen, len(expected)) - - for _, m := range ls { - var matched bool - for _, x := range expected { - if m.Source == x.Source && m.Destination == x.Destination && m.RW == x.RW || m.Name != x.Name { - matched = true - break - } - } - c.Assert(matched, checker.True, check.Commentf("did find match for %+v", m)) - } -} - func (s *DockerDaemonSuite) TestDaemonWithUserlandProxyPath(c *check.C) { testRequires(c, SameHostDaemon, DaemonIsLinux) diff --git a/components/engine/integration-cli/docker_cli_prune_unix_test.go b/components/engine/integration-cli/docker_cli_prune_unix_test.go index bc5bdc835e..259b486766 100644 --- a/components/engine/integration-cli/docker_cli_prune_unix_test.go +++ b/components/engine/integration-cli/docker_cli_prune_unix_test.go @@ -12,8 +12,10 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli" + "github.com/docker/docker/integration-cli/cli/build" "github.com/docker/docker/integration-cli/daemon" "github.com/go-check/check" + "github.com/gotestyourself/gotestyourself/icmd" ) func pruneNetworkAndVerify(c *check.C, d *daemon.Daemon, kept, pruned []string) { @@ -79,13 +81,15 @@ func (s *DockerSwarmSuite) TestPruneNetwork(c *check.C) { func (s *DockerDaemonSuite) TestPruneImageDangling(c *check.C) { s.d.StartWithBusybox(c) - out, _, err := s.d.BuildImageWithOut("test", - `FROM busybox - LABEL foo=bar`, true, "-q") - c.Assert(err, checker.IsNil) - id := strings.TrimSpace(out) + result := cli.BuildCmd(c, "test", cli.Daemon(s.d), + build.WithDockerfile(`FROM busybox + LABEL foo=bar`), + cli.WithFlags("-q"), + ) + result.Assert(c, icmd.Success) + id := strings.TrimSpace(result.Combined()) - out, err = s.d.Cmd("images", "-q", "--no-trunc") + out, err := s.d.Cmd("images", "-q", "--no-trunc") c.Assert(err, checker.IsNil) c.Assert(strings.TrimSpace(out), checker.Contains, id) @@ -266,20 +270,24 @@ func (s *DockerSuite) TestPruneNetworkLabel(c *check.C) { func (s *DockerDaemonSuite) TestPruneImageLabel(c *check.C) { s.d.StartWithBusybox(c) - out, _, err := s.d.BuildImageWithOut("test1", - `FROM busybox - LABEL foo=bar`, true, "-q") - c.Assert(err, checker.IsNil) - id1 := strings.TrimSpace(out) - out, err = s.d.Cmd("images", "-q", "--no-trunc") + result := cli.BuildCmd(c, "test1", cli.Daemon(s.d), + build.WithDockerfile(`FROM busybox + LABEL foo=bar`), + cli.WithFlags("-q"), + ) + result.Assert(c, icmd.Success) + id1 := strings.TrimSpace(result.Combined()) + out, err := s.d.Cmd("images", "-q", "--no-trunc") c.Assert(err, checker.IsNil) c.Assert(strings.TrimSpace(out), checker.Contains, id1) - out, _, err = s.d.BuildImageWithOut("test2", - `FROM busybox - LABEL bar=foo`, true, "-q") - c.Assert(err, checker.IsNil) - id2 := strings.TrimSpace(out) + result = cli.BuildCmd(c, "test2", cli.Daemon(s.d), + build.WithDockerfile(`FROM busybox + LABEL bar=foo`), + cli.WithFlags("-q"), + ) + result.Assert(c, icmd.Success) + id2 := strings.TrimSpace(result.Combined()) out, err = s.d.Cmd("images", "-q", "--no-trunc") c.Assert(err, checker.IsNil) c.Assert(strings.TrimSpace(out), checker.Contains, id2) diff --git a/components/engine/integration-cli/docker_cli_service_health_test.go b/components/engine/integration-cli/docker_cli_service_health_test.go index 789838545d..ac525d08ea 100644 --- a/components/engine/integration-cli/docker_cli_service_health_test.go +++ b/components/engine/integration-cli/docker_cli_service_health_test.go @@ -9,7 +9,10 @@ import ( "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/daemon/cluster/executor/container" "github.com/docker/docker/integration-cli/checker" + "github.com/docker/docker/integration-cli/cli" + "github.com/docker/docker/integration-cli/cli/build" "github.com/go-check/check" + "github.com/gotestyourself/gotestyourself/icmd" ) // start a service, and then make its task unhealthy during running @@ -20,15 +23,14 @@ func (s *DockerSwarmSuite) TestServiceHealthRun(c *check.C) { d := s.AddDaemon(c, true, true) // build image with health-check - // note: use `daemon.buildImageWithOut` to build, do not use `buildImage` to build imageName := "testhealth" - _, _, err := d.BuildImageWithOut(imageName, - `FROM busybox + result := cli.BuildCmd(c, imageName, cli.Daemon(d), + build.WithDockerfile(`FROM busybox RUN touch /status HEALTHCHECK --interval=1s --timeout=1s --retries=1\ - CMD cat /status`, - true) - c.Check(err, check.IsNil) + CMD cat /status`), + ) + result.Assert(c, icmd.Success) serviceName := "healthServiceRun" out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--name", serviceName, imageName, "top") @@ -84,12 +86,12 @@ func (s *DockerSwarmSuite) TestServiceHealthStart(c *check.C) { // service started from this image won't pass health check imageName := "testhealth" - _, _, err := d.BuildImageWithOut(imageName, - `FROM busybox + result := cli.BuildCmd(c, imageName, cli.Daemon(d), + build.WithDockerfile(`FROM busybox HEALTHCHECK --interval=1s --timeout=1s --retries=1024\ - CMD cat /status`, - true) - c.Check(err, check.IsNil) + CMD cat /status`), + ) + result.Assert(c, icmd.Success) serviceName := "healthServiceStart" out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--name", serviceName, imageName, "top") diff --git a/components/engine/integration/internal/requirement/requirement.go b/components/engine/integration/internal/requirement/requirement.go index f89eb03786..cd498ab87d 100644 --- a/components/engine/integration/internal/requirement/requirement.go +++ b/components/engine/integration/internal/requirement/requirement.go @@ -5,11 +5,15 @@ import ( "strings" "testing" "time" + + "github.com/docker/docker/pkg/parsers/kernel" + "github.com/gotestyourself/gotestyourself/icmd" ) // HasHubConnectivity checks to see if https://hub.docker.com is // accessible from the present environment func HasHubConnectivity(t *testing.T) bool { + t.Helper() // Set a timeout on the GET at 15s var timeout = 15 * time.Second var url = "https://hub.docker.com" @@ -24,3 +28,26 @@ func HasHubConnectivity(t *testing.T) bool { } return err == nil } + +func overlayFSSupported() bool { + result := icmd.RunCommand("/bin/sh", "-c", "cat /proc/filesystems") + if result.Error != nil { + return false + } + return strings.Contains(result.Combined(), "overlay\n") +} + +// Overlay2Supported returns true if the current system supports overlay2 as graphdriver +func Overlay2Supported(kernelVersion string) bool { + if !overlayFSSupported() { + return false + } + + daemonV, err := kernel.ParseRelease(kernelVersion) + if err != nil { + return false + } + requiredV := kernel.VersionInfo{Kernel: 4} + return kernel.CompareKernelVersion(*daemonV, requiredV) > -1 + +} diff --git a/components/engine/integration/internal/swarm/service.go b/components/engine/integration/internal/swarm/service.go index 3f5032976b..506bb778de 100644 --- a/components/engine/integration/internal/swarm/service.go +++ b/components/engine/integration/internal/swarm/service.go @@ -19,10 +19,10 @@ import ( // ServicePoll tweaks the pollSettings for `service` func ServicePoll(config *poll.Settings) { // Override the default pollSettings for `service` resource here ... - + config.Timeout = 30 * time.Second + config.Delay = 100 * time.Millisecond if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" { config.Timeout = 1 * time.Minute - config.Delay = 100 * time.Millisecond } } @@ -49,6 +49,7 @@ func ContainerPoll(config *poll.Settings) { // NewSwarm creates a swarm daemon for testing func NewSwarm(t *testing.T, testEnv *environment.Execution, ops ...func(*daemon.Daemon)) *daemon.Daemon { + t.Helper() skip.IfCondition(t, testEnv.IsRemoteDaemon()) if testEnv.DaemonInfo.ExperimentalBuild { ops = append(ops, daemon.WithExperimental) @@ -63,6 +64,7 @@ type ServiceSpecOpt func(*swarmtypes.ServiceSpec) // CreateService creates a service on the passed in swarm daemon. func CreateService(t *testing.T, d *daemon.Daemon, opts ...ServiceSpecOpt) string { + t.Helper() spec := defaultServiceSpec() for _, o := range opts { o(&spec) @@ -136,6 +138,7 @@ func ServiceWithName(name string) ServiceSpecOpt { // GetRunningTasks gets the list of running tasks for a service func GetRunningTasks(t *testing.T, d *daemon.Daemon, serviceID string) []swarmtypes.Task { + t.Helper() client := d.NewClientT(t) defer client.Close() @@ -153,6 +156,7 @@ func GetRunningTasks(t *testing.T, d *daemon.Daemon, serviceID string) []swarmty // ExecTask runs the passed in exec config on the given task func ExecTask(t *testing.T, d *daemon.Daemon, task swarmtypes.Task, config types.ExecConfig) types.HijackedResponse { + t.Helper() client := d.NewClientT(t) defer client.Close() diff --git a/components/engine/integration-cli/docker_cli_external_graphdriver_unix_test.go b/components/engine/integration/plugin/graphdriver/external_test.go similarity index 51% rename from components/engine/integration-cli/docker_cli_external_graphdriver_unix_test.go rename to components/engine/integration/plugin/graphdriver/external_test.go index a52e58b2d2..d42700beab 100644 --- a/components/engine/integration-cli/docker_cli_external_graphdriver_unix_test.go +++ b/components/engine/integration/plugin/graphdriver/external_test.go @@ -1,8 +1,7 @@ -// +build !windows - -package main +package graphdriver import ( + "context" "encoding/json" "fmt" "io" @@ -10,31 +9,24 @@ import ( "net/http" "net/http/httptest" "os" - "strings" + "runtime" + "testing" + "github.com/docker/docker/api/types" + containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/daemon/graphdriver/vfs" - "github.com/docker/docker/integration-cli/daemon" - testdaemon "github.com/docker/docker/internal/test/daemon" + "github.com/docker/docker/integration/internal/container" + "github.com/docker/docker/integration/internal/requirement" + "github.com/docker/docker/internal/test/daemon" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/plugins" - "github.com/go-check/check" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" + "github.com/gotestyourself/gotestyourself/skip" ) -func init() { - check.Suite(&DockerExternalGraphdriverSuite{ - ds: &DockerSuite{}, - }) -} - -type DockerExternalGraphdriverSuite struct { - server *httptest.Server - jserver *httptest.Server - ds *DockerSuite - d *daemon.Daemon - ec map[string]*graphEventsCounter -} - type graphEventsCounter struct { activations int creations int @@ -52,46 +44,69 @@ type graphEventsCounter struct { diffsize int } -func (s *DockerExternalGraphdriverSuite) SetUpTest(c *check.C) { - s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution)) -} +func TestExternalGraphDriver(t *testing.T) { + skip.If(t, runtime.GOOS == "windows") + skip.If(t, testEnv.IsRemoteDaemon(), "cannot run daemon when remote daemon") + skip.If(t, !requirement.HasHubConnectivity(t)) -func (s *DockerExternalGraphdriverSuite) OnTimeout(c *check.C) { - s.d.DumpStackAndQuit() -} + // Setup plugin(s) + ec := make(map[string]*graphEventsCounter) + sserver := setupPluginViaSpecFile(t, ec) + jserver := setupPluginViaJSONFile(t, ec) + // Create daemon + d := daemon.New(t, daemon.WithExperimental) + c := d.NewClientT(t) -func (s *DockerExternalGraphdriverSuite) TearDownTest(c *check.C) { - if s.d != nil { - s.d.Stop(c) - s.ds.TearDownTest(c) + for _, tc := range []struct { + name string + test func(client.APIClient, *daemon.Daemon) func(*testing.T) + }{ + { + name: "json", + test: testExternalGraphDriver("json", ec), + }, + { + name: "spec", + test: testExternalGraphDriver("spec", ec), + }, + { + name: "pull", + test: testGraphDriverPull, + }, + } { + t.Run(tc.name, tc.test(c, d)) } + + sserver.Close() + jserver.Close() + err := os.RemoveAll("/etc/docker/plugins") + assert.NilError(t, err) } -func (s *DockerExternalGraphdriverSuite) SetUpSuite(c *check.C) { - s.ec = make(map[string]*graphEventsCounter) - s.setUpPluginViaSpecFile(c) - s.setUpPluginViaJSONFile(c) -} - -func (s *DockerExternalGraphdriverSuite) setUpPluginViaSpecFile(c *check.C) { +func setupPluginViaSpecFile(t *testing.T, ec map[string]*graphEventsCounter) *httptest.Server { mux := http.NewServeMux() - s.server = httptest.NewServer(mux) + server := httptest.NewServer(mux) - s.setUpPlugin(c, "test-external-graph-driver", "spec", mux, []byte(s.server.URL)) + setupPlugin(t, ec, "spec", mux, []byte(server.URL)) + + return server } -func (s *DockerExternalGraphdriverSuite) setUpPluginViaJSONFile(c *check.C) { +func setupPluginViaJSONFile(t *testing.T, ec map[string]*graphEventsCounter) *httptest.Server { mux := http.NewServeMux() - s.jserver = httptest.NewServer(mux) + server := httptest.NewServer(mux) - p := plugins.NewLocalPlugin("json-external-graph-driver", s.jserver.URL) + p := plugins.NewLocalPlugin("json-external-graph-driver", server.URL) b, err := json.Marshal(p) - c.Assert(err, check.IsNil) + assert.NilError(t, err) - s.setUpPlugin(c, "json-external-graph-driver", "json", mux, b) + setupPlugin(t, ec, "json", mux, b) + + return server } -func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ext string, mux *http.ServeMux, b []byte) { +func setupPlugin(t *testing.T, ec map[string]*graphEventsCounter, ext string, mux *http.ServeMux, b []byte) { + name := fmt.Sprintf("%s-external-graph-driver", ext) type graphDriverRequest struct { ID string `json:",omitempty"` Parent string `json:",omitempty"` @@ -130,24 +145,24 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex } base, err := ioutil.TempDir("", name) - c.Assert(err, check.IsNil) + assert.NilError(t, err) vfsProto, err := vfs.Init(base, []string{}, nil, nil) - c.Assert(err, check.IsNil, check.Commentf("error initializing graph driver")) + assert.NilError(t, err, "error initializing graph driver") driver := graphdriver.NewNaiveDiffDriver(vfsProto, nil, nil) - s.ec[ext] = &graphEventsCounter{} + ec[ext] = &graphEventsCounter{} mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].activations++ + ec[ext].activations++ respond(w, `{"Implements": ["GraphDriver"]}`) }) mux.HandleFunc("/GraphDriver.Init", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].init++ + ec[ext].init++ respond(w, "{}") }) mux.HandleFunc("/GraphDriver.CreateReadWrite", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].creations++ + ec[ext].creations++ var req graphDriverRequest if err := decReq(r.Body, &req, w); err != nil { @@ -161,7 +176,7 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) mux.HandleFunc("/GraphDriver.Create", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].creations++ + ec[ext].creations++ var req graphDriverRequest if err := decReq(r.Body, &req, w); err != nil { @@ -175,7 +190,7 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) mux.HandleFunc("/GraphDriver.Remove", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].removals++ + ec[ext].removals++ var req graphDriverRequest if err := decReq(r.Body, &req, w); err != nil { @@ -190,7 +205,7 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) mux.HandleFunc("/GraphDriver.Get", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].gets++ + ec[ext].gets++ var req graphDriverRequest if err := decReq(r.Body, &req, w); err != nil { @@ -207,7 +222,7 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) mux.HandleFunc("/GraphDriver.Put", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].puts++ + ec[ext].puts++ var req graphDriverRequest if err := decReq(r.Body, &req, w); err != nil { @@ -222,7 +237,7 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) mux.HandleFunc("/GraphDriver.Exists", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].exists++ + ec[ext].exists++ var req graphDriverRequest if err := decReq(r.Body, &req, w); err != nil { @@ -232,12 +247,12 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) mux.HandleFunc("/GraphDriver.Status", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].stats++ + ec[ext].stats++ respond(w, &graphDriverResponse{Status: driver.Status()}) }) mux.HandleFunc("/GraphDriver.Cleanup", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].cleanups++ + ec[ext].cleanups++ err := driver.Cleanup() if err != nil { respond(w, err) @@ -247,7 +262,7 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) mux.HandleFunc("/GraphDriver.GetMetadata", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].metadata++ + ec[ext].metadata++ var req graphDriverRequest if err := decReq(r.Body, &req, w); err != nil { @@ -263,7 +278,7 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) mux.HandleFunc("/GraphDriver.Diff", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].diff++ + ec[ext].diff++ var req graphDriverRequest if err := decReq(r.Body, &req, w); err != nil { @@ -279,7 +294,7 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) mux.HandleFunc("/GraphDriver.Changes", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].changes++ + ec[ext].changes++ var req graphDriverRequest if err := decReq(r.Body, &req, w); err != nil { return @@ -294,7 +309,7 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) mux.HandleFunc("/GraphDriver.ApplyDiff", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].applydiff++ + ec[ext].applydiff++ diff := r.Body defer r.Body.Close() @@ -314,7 +329,7 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) mux.HandleFunc("/GraphDriver.DiffSize", func(w http.ResponseWriter, r *http.Request) { - s.ec[ext].diffsize++ + ec[ext].diffsize++ var req graphDriverRequest if err := decReq(r.Body, &req, w); err != nil { @@ -330,77 +345,118 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex }) err = os.MkdirAll("/etc/docker/plugins", 0755) - c.Assert(err, check.IsNil, check.Commentf("error creating /etc/docker/plugins")) + assert.NilError(t, err) specFile := "/etc/docker/plugins/" + name + "." + ext err = ioutil.WriteFile(specFile, b, 0644) - c.Assert(err, check.IsNil, check.Commentf("error writing to %s", specFile)) + assert.NilError(t, err) } -func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) { - s.server.Close() - s.jserver.Close() +func testExternalGraphDriver(ext string, ec map[string]*graphEventsCounter) func(client.APIClient, *daemon.Daemon) func(*testing.T) { + return func(c client.APIClient, d *daemon.Daemon) func(*testing.T) { + return func(t *testing.T) { + driverName := fmt.Sprintf("%s-external-graph-driver", ext) + d.StartWithBusybox(t, "-s", driverName) - err := os.RemoveAll("/etc/docker/plugins") - c.Assert(err, check.IsNil, check.Commentf("error removing /etc/docker/plugins")) + ctx := context.Background() + + testGraphDriver(t, c, ctx, driverName, func(t *testing.T) { + d.Restart(t, "-s", driverName) + }) + + _, err := c.Info(ctx) + assert.NilError(t, err) + + d.Stop(t) + + // Don't check ec.exists, because the daemon no longer calls the + // Exists function. + assert.Check(t, is.Equal(ec[ext].activations, 2)) + assert.Check(t, is.Equal(ec[ext].init, 2)) + assert.Check(t, ec[ext].creations >= 1) + assert.Check(t, ec[ext].removals >= 1) + assert.Check(t, ec[ext].gets >= 1) + assert.Check(t, ec[ext].puts >= 1) + assert.Check(t, is.Equal(ec[ext].stats, 5)) + assert.Check(t, is.Equal(ec[ext].cleanups, 2)) + assert.Check(t, ec[ext].applydiff >= 1) + assert.Check(t, is.Equal(ec[ext].changes, 1)) + assert.Check(t, is.Equal(ec[ext].diffsize, 0)) + assert.Check(t, is.Equal(ec[ext].diff, 0)) + assert.Check(t, is.Equal(ec[ext].metadata, 1)) + } + } } -func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) { - testRequires(c, ExperimentalDaemon, SameHostDaemon) +func testGraphDriverPull(c client.APIClient, d *daemon.Daemon) func(*testing.T) { + return func(t *testing.T) { + d.Start(t) + defer d.Stop(t) + ctx := context.Background() - s.testExternalGraphDriver("test-external-graph-driver", "spec", c) - s.testExternalGraphDriver("json-external-graph-driver", "json", c) + r, err := c.ImagePull(ctx, "busybox:latest", types.ImagePullOptions{}) + assert.NilError(t, err) + _, err = io.Copy(ioutil.Discard, r) + assert.NilError(t, err) + + container.Run(t, ctx, c, container.WithImage("busybox:latest")) + } } -func (s *DockerExternalGraphdriverSuite) testExternalGraphDriver(name string, ext string, c *check.C) { - s.d.StartWithBusybox(c, "-s", name) +func TestGraphdriverPluginV2(t *testing.T) { + skip.If(t, runtime.GOOS == "windows") + skip.If(t, testEnv.IsRemoteDaemon(), "cannot run daemon when remote daemon") + skip.If(t, !requirement.HasHubConnectivity(t)) + skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64") + skip.If(t, !requirement.Overlay2Supported(testEnv.DaemonInfo.KernelVersion)) - out, err := s.d.Cmd("run", "--name=graphtest", "busybox", "sh", "-c", "echo hello > /hello") - c.Assert(err, check.IsNil, check.Commentf(out)) + d := daemon.New(t, daemon.WithExperimental) + d.Start(t) + defer d.Stop(t) - s.d.Restart(c, "-s", name) + client := d.NewClientT(t) + defer client.Close() + ctx := context.Background() - out, err = s.d.Cmd("inspect", "--format={{.GraphDriver.Name}}", "graphtest") - c.Assert(err, check.IsNil, check.Commentf(out)) - c.Assert(strings.TrimSpace(out), check.Equals, name) + // install the plugin + plugin := "cpuguy83/docker-overlay2-graphdriver-plugin" + responseReader, err := client.PluginInstall(ctx, plugin, types.PluginInstallOptions{ + RemoteRef: plugin, + AcceptAllPermissions: true, + }) + defer responseReader.Close() + assert.NilError(t, err) + // ensure it's done by waiting for EOF on the response + _, err = io.Copy(ioutil.Discard, responseReader) + assert.NilError(t, err) - out, err = s.d.Cmd("diff", "graphtest") - c.Assert(err, check.IsNil, check.Commentf(out)) - c.Assert(strings.Contains(out, "A /hello"), check.Equals, true, check.Commentf("diff output: %s", out)) + // restart the daemon with the plugin set as the storage driver + d.Stop(t) + d.StartWithBusybox(t, "-s", plugin, "--storage-opt", "overlay2.override_kernel_check=1") - out, err = s.d.Cmd("rm", "-f", "graphtest") - c.Assert(err, check.IsNil, check.Commentf(out)) - - out, err = s.d.Cmd("info") - c.Assert(err, check.IsNil, check.Commentf(out)) - - s.d.Stop(c) - - // Don't check s.ec.exists, because the daemon no longer calls the - // Exists function. - c.Assert(s.ec[ext].activations, check.Equals, 2) - c.Assert(s.ec[ext].init, check.Equals, 2) - c.Assert(s.ec[ext].creations >= 1, check.Equals, true) - c.Assert(s.ec[ext].removals >= 1, check.Equals, true) - c.Assert(s.ec[ext].gets >= 1, check.Equals, true) - c.Assert(s.ec[ext].puts >= 1, check.Equals, true) - c.Assert(s.ec[ext].stats, check.Equals, 5) - c.Assert(s.ec[ext].cleanups, check.Equals, 2) - c.Assert(s.ec[ext].applydiff >= 1, check.Equals, true) - c.Assert(s.ec[ext].changes, check.Equals, 1) - c.Assert(s.ec[ext].diffsize, check.Equals, 0) - c.Assert(s.ec[ext].diff, check.Equals, 0) - c.Assert(s.ec[ext].metadata, check.Equals, 1) + testGraphDriver(t, client, ctx, plugin, nil) } -func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriverPull(c *check.C) { - testRequires(c, Network, ExperimentalDaemon, SameHostDaemon) +func testGraphDriver(t *testing.T, c client.APIClient, ctx context.Context, driverName string, afterContainerRunFn func(*testing.T)) { //nolint: golint + id := container.Run(t, ctx, c, container.WithCmd("sh", "-c", "echo hello > /hello")) - s.d.Start(c) + if afterContainerRunFn != nil { + afterContainerRunFn(t) + } - out, err := s.d.Cmd("pull", "busybox:latest") - c.Assert(err, check.IsNil, check.Commentf(out)) + i, err := c.ContainerInspect(ctx, id) + assert.NilError(t, err) + assert.Check(t, is.Equal(i.GraphDriver.Name, driverName)) - out, err = s.d.Cmd("run", "-d", "busybox", "top") - c.Assert(err, check.IsNil, check.Commentf(out)) + diffs, err := c.ContainerDiff(ctx, id) + assert.NilError(t, err) + assert.Check(t, is.Contains(diffs, containertypes.ContainerChangeResponseItem{ + Kind: archive.ChangeAdd, + Path: "/hello", + }), "diffs: %v", diffs) + + err = c.ContainerRemove(ctx, id, types.ContainerRemoveOptions{ + Force: true, + }) + assert.NilError(t, err) } diff --git a/components/engine/integration/plugin/graphdriver/main_test.go b/components/engine/integration/plugin/graphdriver/main_test.go new file mode 100644 index 0000000000..6b6c1a1232 --- /dev/null +++ b/components/engine/integration/plugin/graphdriver/main_test.go @@ -0,0 +1,36 @@ +package graphdriver // import "github.com/docker/docker/integration/plugin/graphdriver" + +import ( + "fmt" + "os" + "testing" + + "github.com/docker/docker/internal/test/environment" + "github.com/docker/docker/pkg/reexec" +) + +var ( + testEnv *environment.Execution +) + +func init() { + reexec.Init() // This is required for external graphdriver tests +} + +const dockerdBinary = "dockerd" + +func TestMain(m *testing.M) { + var err error + testEnv, err = environment.New() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + err = environment.EnsureFrozenImagesLinux(testEnv) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + testEnv.Print() + os.Exit(m.Run()) +} diff --git a/components/engine/integration/service/plugin_test.go b/components/engine/integration/service/plugin_test.go new file mode 100644 index 0000000000..7617e4d480 --- /dev/null +++ b/components/engine/integration/service/plugin_test.go @@ -0,0 +1,120 @@ +package service + +import ( + "context" + "io" + "io/ioutil" + "os" + "path" + "testing" + + "github.com/docker/docker/api/types" + swarmtypes "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/api/types/swarm/runtime" + "github.com/docker/docker/integration/internal/swarm" + "github.com/docker/docker/internal/test/daemon" + "github.com/docker/docker/internal/test/fixtures/plugin" + "github.com/docker/docker/internal/test/registry" + "github.com/gotestyourself/gotestyourself/assert" + "github.com/gotestyourself/gotestyourself/poll" + "github.com/gotestyourself/gotestyourself/skip" +) + +func TestServicePlugin(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType == "windows") + skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64") + defer setupTest(t)() + + reg := registry.NewV2(t) + defer reg.Close() + + repo := path.Join(registry.DefaultURL, "swarm", "test:v1") + repo2 := path.Join(registry.DefaultURL, "swarm", "test:v2") + name := "test" + + d := daemon.New(t) + d.StartWithBusybox(t) + apiclient := d.NewClientT(t) + err := plugin.Create(context.Background(), apiclient, repo) + assert.NilError(t, err) + r, err := apiclient.PluginPush(context.Background(), repo, "") + assert.NilError(t, err) + _, err = io.Copy(ioutil.Discard, r) + assert.NilError(t, err) + err = apiclient.PluginRemove(context.Background(), repo, types.PluginRemoveOptions{}) + assert.NilError(t, err) + err = plugin.Create(context.Background(), apiclient, repo2) + assert.NilError(t, err) + r, err = apiclient.PluginPush(context.Background(), repo2, "") + assert.NilError(t, err) + _, err = io.Copy(ioutil.Discard, r) + assert.NilError(t, err) + err = apiclient.PluginRemove(context.Background(), repo2, types.PluginRemoveOptions{}) + assert.NilError(t, err) + d.Stop(t) + + d1 := swarm.NewSwarm(t, testEnv, daemon.WithExperimental) + defer d1.Stop(t) + d2 := daemon.New(t, daemon.WithExperimental, daemon.WithSwarmPort(daemon.DefaultSwarmPort+1)) + d2.StartAndSwarmJoin(t, d1, true) + defer d2.Stop(t) + d3 := daemon.New(t, daemon.WithExperimental, daemon.WithSwarmPort(daemon.DefaultSwarmPort+2)) + d3.StartAndSwarmJoin(t, d1, false) + defer d3.Stop(t) + + id := d1.CreateService(t, makePlugin(repo, name, nil)) + poll.WaitOn(t, d1.PluginIsRunning(name), swarm.ServicePoll) + poll.WaitOn(t, d2.PluginIsRunning(name), swarm.ServicePoll) + poll.WaitOn(t, d3.PluginIsRunning(name), swarm.ServicePoll) + + service := d1.GetService(t, id) + d1.UpdateService(t, service, makePlugin(repo2, name, nil)) + poll.WaitOn(t, d1.PluginReferenceIs(name, repo2), swarm.ServicePoll) + poll.WaitOn(t, d2.PluginReferenceIs(name, repo2), swarm.ServicePoll) + poll.WaitOn(t, d3.PluginReferenceIs(name, repo2), swarm.ServicePoll) + poll.WaitOn(t, d1.PluginIsRunning(name), swarm.ServicePoll) + poll.WaitOn(t, d2.PluginIsRunning(name), swarm.ServicePoll) + poll.WaitOn(t, d3.PluginIsRunning(name), swarm.ServicePoll) + + d1.RemoveService(t, id) + poll.WaitOn(t, d1.PluginIsNotPresent(name), swarm.ServicePoll) + poll.WaitOn(t, d2.PluginIsNotPresent(name), swarm.ServicePoll) + poll.WaitOn(t, d3.PluginIsNotPresent(name), swarm.ServicePoll) + + // constrain to managers only + id = d1.CreateService(t, makePlugin(repo, name, []string{"node.role==manager"})) + poll.WaitOn(t, d1.PluginIsRunning(name), swarm.ServicePoll) + poll.WaitOn(t, d2.PluginIsRunning(name), swarm.ServicePoll) + poll.WaitOn(t, d3.PluginIsNotPresent(name), swarm.ServicePoll) + + d1.RemoveService(t, id) + poll.WaitOn(t, d1.PluginIsNotPresent(name), swarm.ServicePoll) + poll.WaitOn(t, d2.PluginIsNotPresent(name), swarm.ServicePoll) + poll.WaitOn(t, d3.PluginIsNotPresent(name), swarm.ServicePoll) + + // with no name + id = d1.CreateService(t, makePlugin(repo, "", nil)) + poll.WaitOn(t, d1.PluginIsRunning(repo), swarm.ServicePoll) + poll.WaitOn(t, d2.PluginIsRunning(repo), swarm.ServicePoll) + poll.WaitOn(t, d3.PluginIsRunning(repo), swarm.ServicePoll) + + d1.RemoveService(t, id) + poll.WaitOn(t, d1.PluginIsNotPresent(repo), swarm.ServicePoll) + poll.WaitOn(t, d2.PluginIsNotPresent(repo), swarm.ServicePoll) + poll.WaitOn(t, d3.PluginIsNotPresent(repo), swarm.ServicePoll) +} + +func makePlugin(repo, name string, constraints []string) func(*swarmtypes.Service) { + return func(s *swarmtypes.Service) { + s.Spec.TaskTemplate.Runtime = "plugin" + s.Spec.TaskTemplate.PluginSpec = &runtime.PluginSpec{ + Name: name, + Remote: repo, + } + if constraints != nil { + s.Spec.TaskTemplate.Placement = &swarmtypes.Placement{ + Constraints: constraints, + } + } + } +} diff --git a/components/engine/internal/test/daemon/config.go b/components/engine/internal/test/daemon/config.go index 4ecc41b514..c57010db90 100644 --- a/components/engine/internal/test/daemon/config.go +++ b/components/engine/internal/test/daemon/config.go @@ -5,6 +5,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/internal/test" "github.com/gotestyourself/gotestyourself/assert" ) @@ -13,6 +14,9 @@ type ConfigConstructor func(*swarm.Config) // CreateConfig creates a config given the specified spec func (d *Daemon) CreateConfig(t assert.TestingT, configSpec swarm.ConfigSpec) string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -23,6 +27,9 @@ func (d *Daemon) CreateConfig(t assert.TestingT, configSpec swarm.ConfigSpec) st // ListConfigs returns the list of the current swarm configs func (d *Daemon) ListConfigs(t assert.TestingT) []swarm.Config { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -33,6 +40,9 @@ func (d *Daemon) ListConfigs(t assert.TestingT) []swarm.Config { // GetConfig returns a swarm config identified by the specified id func (d *Daemon) GetConfig(t assert.TestingT, id string) *swarm.Config { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -43,6 +53,9 @@ func (d *Daemon) GetConfig(t assert.TestingT, id string) *swarm.Config { // DeleteConfig removes the swarm config identified by the specified id func (d *Daemon) DeleteConfig(t assert.TestingT, id string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -53,6 +66,9 @@ func (d *Daemon) DeleteConfig(t assert.TestingT, id string) { // UpdateConfig updates the swarm config identified by the specified id // Currently, only label update is supported. func (d *Daemon) UpdateConfig(t assert.TestingT, id string, f ...ConfigConstructor) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() diff --git a/components/engine/internal/test/daemon/container.go b/components/engine/internal/test/daemon/container.go index d7e11efcdc..6a0ced9447 100644 --- a/components/engine/internal/test/daemon/container.go +++ b/components/engine/internal/test/daemon/container.go @@ -4,11 +4,15 @@ import ( "context" "github.com/docker/docker/api/types" + "github.com/docker/docker/internal/test" "github.com/gotestyourself/gotestyourself/assert" ) // ActiveContainers returns the list of ids of the currently running containers func (d *Daemon) ActiveContainers(t assert.TestingT) []string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -24,6 +28,9 @@ func (d *Daemon) ActiveContainers(t assert.TestingT) []string { // FindContainerIP returns the ip of the specified container func (d *Daemon) FindContainerIP(t assert.TestingT, id string) string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() diff --git a/components/engine/internal/test/daemon/daemon.go b/components/engine/internal/test/daemon/daemon.go index fbe5f5a28f..9ba13edc0a 100644 --- a/components/engine/internal/test/daemon/daemon.go +++ b/components/engine/internal/test/daemon/daemon.go @@ -16,6 +16,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/events" "github.com/docker/docker/client" + "github.com/docker/docker/internal/test" "github.com/docker/docker/internal/test/request" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/ioutils" @@ -80,6 +81,9 @@ type Daemon struct { // This will create a directory such as d123456789 in the folder specified by $DOCKER_INTEGRATION_DAEMON_DEST or $DEST. // The daemon will not automatically start. func New(t testingT, ops ...func(*Daemon)) *Daemon { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } dest := os.Getenv("DOCKER_INTEGRATION_DAEMON_DEST") if dest == "" { dest = os.Getenv("DEST") @@ -113,7 +117,7 @@ func New(t testingT, ops ...func(*Daemon)) *Daemon { execRoot: filepath.Join(os.TempDir(), "docker-execroot", id), dockerdBinary: defaultDockerdBinary, swarmListenAddr: defaultSwarmListenAddr, - SwarmPort: defaultSwarmPort, + SwarmPort: DefaultSwarmPort, log: t, } @@ -169,6 +173,9 @@ func (d *Daemon) NewClient() (*client.Client, error) { // NewClientT creates new client based on daemon's socket path // FIXME(vdemeester): replace NewClient with NewClientT func (d *Daemon) NewClientT(t assert.TestingT) *client.Client { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } c, err := client.NewClientWithOpts( client.FromEnv, client.WithHost(d.Sock())) @@ -176,13 +183,21 @@ func (d *Daemon) NewClientT(t assert.TestingT) *client.Client { return c } -// CleanupExecRoot cleans the daemon exec root (network namespaces, ...) -func (d *Daemon) CleanupExecRoot(t testingT) { - cleanupExecRoot(t, d.execRoot) +// Cleanup cleans the daemon files : exec root (network namespaces, ...), swarmkit files +func (d *Daemon) Cleanup(t testingT) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } + // Cleanup swarmkit wal files if present + cleanupRaftDir(t, d.Root) + cleanupNetworkNamespace(t, d.execRoot) } // Start starts the daemon and return once it is ready to receive requests. func (d *Daemon) Start(t testingT, args ...string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } if err := d.StartWithError(args...); err != nil { t.Fatalf("Error starting daemon with arguments: %v", args) } @@ -201,6 +216,7 @@ func (d *Daemon) StartWithError(args ...string) error { // StartWithLogFile will start the daemon and attach its streams to a given file. func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error { + d.handleUserns() dockerdBinary, err := exec.LookPath(d.dockerdBinary) if err != nil { return errors.Wrapf(err, "[%s] could not find docker binary in $PATH", d.id) @@ -313,6 +329,9 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error { // StartWithBusybox will first start the daemon with Daemon.Start() // then save the busybox image from the main daemon and load it into this Daemon instance. func (d *Daemon) StartWithBusybox(t testingT, arg ...string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } d.Start(t, arg...) d.LoadBusybox(t) } @@ -369,6 +388,9 @@ func (d *Daemon) DumpStackAndQuit() { // instantiate a new one with NewDaemon. // If an error occurs while starting the daemon, the test will fail. func (d *Daemon) Stop(t testingT) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } err := d.StopWithError() if err != nil { if err != errDaemonNotStarted { @@ -445,8 +467,10 @@ out2: // Restart will restart the daemon by first stopping it and the starting it. // If an error occurs while starting the daemon, the test will fail. func (d *Daemon) Restart(t testingT, args ...string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } d.Stop(t) - d.handleUserns() d.Start(t, args...) } @@ -455,7 +479,6 @@ func (d *Daemon) RestartWithError(arg ...string) error { if err := d.StopWithError(); err != nil { return err } - d.handleUserns() return d.StartWithError(arg...) } @@ -520,6 +543,9 @@ func (d *Daemon) ReloadConfig() error { // LoadBusybox image into the daemon func (d *Daemon) LoadBusybox(t assert.TestingT) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } clientHost, err := client.NewEnvClient() assert.NilError(t, err, "failed to create client") defer clientHost.Close() @@ -630,9 +656,22 @@ func (d *Daemon) queryRootDir() (string, error) { // Info returns the info struct for this daemon func (d *Daemon) Info(t assert.TestingT) types.Info { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } apiclient, err := d.NewClient() assert.NilError(t, err) info, err := apiclient.Info(context.Background()) assert.NilError(t, err) return info } + +func cleanupRaftDir(t testingT, rootPath string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } + walDir := filepath.Join(rootPath, "swarm/raft/wal") + if err := os.RemoveAll(walDir); err != nil { + t.Logf("error removing %v: %v", walDir, err) + } +} diff --git a/components/engine/internal/test/daemon/daemon_unix.go b/components/engine/internal/test/daemon/daemon_unix.go index c0aa26c9f8..9dd9e36f0c 100644 --- a/components/engine/internal/test/daemon/daemon_unix.go +++ b/components/engine/internal/test/daemon/daemon_unix.go @@ -6,10 +6,14 @@ import ( "os" "path/filepath" + "github.com/docker/docker/internal/test" "golang.org/x/sys/unix" ) -func cleanupExecRoot(t testingT, execRoot string) { +func cleanupNetworkNamespace(t testingT, execRoot string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } // Cleanup network namespaces in the exec root of this // daemon because this exec root is specific to this // daemon instance and has no chance of getting diff --git a/components/engine/internal/test/daemon/daemon_windows.go b/components/engine/internal/test/daemon/daemon_windows.go index 8ec554fbc4..cb6bb6a4cb 100644 --- a/components/engine/internal/test/daemon/daemon_windows.go +++ b/components/engine/internal/test/daemon/daemon_windows.go @@ -21,5 +21,5 @@ func signalDaemonReload(pid int) error { return fmt.Errorf("daemon reload not supported") } -func cleanupExecRoot(t testingT, execRoot string) { +func cleanupNetworkNamespace(t testingT, execRoot string) { } diff --git a/components/engine/internal/test/daemon/node.go b/components/engine/internal/test/daemon/node.go index 9955208b47..5015c75eb1 100644 --- a/components/engine/internal/test/daemon/node.go +++ b/components/engine/internal/test/daemon/node.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/internal/test" "github.com/gotestyourself/gotestyourself/assert" ) @@ -15,6 +16,9 @@ type NodeConstructor func(*swarm.Node) // GetNode returns a swarm node identified by the specified id func (d *Daemon) GetNode(t assert.TestingT, id string) *swarm.Node { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -26,6 +30,9 @@ func (d *Daemon) GetNode(t assert.TestingT, id string) *swarm.Node { // RemoveNode removes the specified node func (d *Daemon) RemoveNode(t assert.TestingT, id string, force bool) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -38,6 +45,9 @@ func (d *Daemon) RemoveNode(t assert.TestingT, id string, force bool) { // UpdateNode updates a swarm node with the specified node constructor func (d *Daemon) UpdateNode(t assert.TestingT, id string, f ...NodeConstructor) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -59,6 +69,9 @@ func (d *Daemon) UpdateNode(t assert.TestingT, id string, f ...NodeConstructor) // ListNodes returns the list of the current swarm nodes func (d *Daemon) ListNodes(t assert.TestingT) []swarm.Node { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() diff --git a/components/engine/internal/test/daemon/plugin.go b/components/engine/internal/test/daemon/plugin.go new file mode 100644 index 0000000000..fad9727161 --- /dev/null +++ b/components/engine/internal/test/daemon/plugin.go @@ -0,0 +1,77 @@ +package daemon + +import ( + "context" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + "github.com/gotestyourself/gotestyourself/poll" +) + +// PluginIsRunning provides a poller to check if the specified plugin is running +func (d *Daemon) PluginIsRunning(name string) func(poll.LogT) poll.Result { + return withClient(d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result { + if plugin.Enabled { + return poll.Success() + } + return poll.Continue("plugin %q is not enabled", name) + })) +} + +// PluginIsNotRunning provides a poller to check if the specified plugin is not running +func (d *Daemon) PluginIsNotRunning(name string) func(poll.LogT) poll.Result { + return withClient(d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result { + if !plugin.Enabled { + return poll.Success() + } + return poll.Continue("plugin %q is enabled", name) + })) +} + +// PluginIsNotPresent provides a poller to check if the specified plugin is not present +func (d *Daemon) PluginIsNotPresent(name string) func(poll.LogT) poll.Result { + return withClient(d, func(c client.APIClient, t poll.LogT) poll.Result { + _, _, err := c.PluginInspectWithRaw(context.Background(), name) + if client.IsErrNotFound(err) { + return poll.Success() + } + if err != nil { + return poll.Error(err) + } + return poll.Continue("plugin %q exists") + }) +} + +// PluginReferenceIs provides a poller to check if the specified plugin has the specified reference +func (d *Daemon) PluginReferenceIs(name, expectedRef string) func(poll.LogT) poll.Result { + return withClient(d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result { + if plugin.PluginReference == expectedRef { + return poll.Success() + } + return poll.Continue("plugin %q reference is not %q", name, expectedRef) + })) +} + +func withPluginInspect(name string, f func(*types.Plugin, poll.LogT) poll.Result) func(client.APIClient, poll.LogT) poll.Result { + return func(c client.APIClient, t poll.LogT) poll.Result { + plugin, _, err := c.PluginInspectWithRaw(context.Background(), name) + if client.IsErrNotFound(err) { + return poll.Continue("plugin %q not found", name) + } + if err != nil { + return poll.Error(err) + } + return f(plugin, t) + } + +} + +func withClient(d *Daemon, f func(client.APIClient, poll.LogT) poll.Result) func(poll.LogT) poll.Result { + return func(t poll.LogT) poll.Result { + c, err := d.NewClient() + if err != nil { + poll.Error(err) + } + return f(c, t) + } +} diff --git a/components/engine/internal/test/daemon/secret.go b/components/engine/internal/test/daemon/secret.go index 075aedc2e0..615489bfd0 100644 --- a/components/engine/internal/test/daemon/secret.go +++ b/components/engine/internal/test/daemon/secret.go @@ -5,6 +5,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/internal/test" "github.com/gotestyourself/gotestyourself/assert" ) @@ -13,6 +14,9 @@ type SecretConstructor func(*swarm.Secret) // CreateSecret creates a secret given the specified spec func (d *Daemon) CreateSecret(t assert.TestingT, secretSpec swarm.SecretSpec) string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -24,6 +28,9 @@ func (d *Daemon) CreateSecret(t assert.TestingT, secretSpec swarm.SecretSpec) st // ListSecrets returns the list of the current swarm secrets func (d *Daemon) ListSecrets(t assert.TestingT) []swarm.Secret { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -34,6 +41,9 @@ func (d *Daemon) ListSecrets(t assert.TestingT) []swarm.Secret { // GetSecret returns a swarm secret identified by the specified id func (d *Daemon) GetSecret(t assert.TestingT, id string) *swarm.Secret { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -44,6 +54,9 @@ func (d *Daemon) GetSecret(t assert.TestingT, id string) *swarm.Secret { // DeleteSecret removes the swarm secret identified by the specified id func (d *Daemon) DeleteSecret(t assert.TestingT, id string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -54,6 +67,9 @@ func (d *Daemon) DeleteSecret(t assert.TestingT, id string) { // UpdateSecret updates the swarm secret identified by the specified id // Currently, only label update is supported. func (d *Daemon) UpdateSecret(t assert.TestingT, id string, f ...SecretConstructor) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() diff --git a/components/engine/internal/test/daemon/service.go b/components/engine/internal/test/daemon/service.go index a0541e9716..77614d0daa 100644 --- a/components/engine/internal/test/daemon/service.go +++ b/components/engine/internal/test/daemon/service.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/internal/test" "github.com/gotestyourself/gotestyourself/assert" ) @@ -14,6 +15,9 @@ import ( type ServiceConstructor func(*swarm.Service) func (d *Daemon) createServiceWithOptions(t assert.TestingT, opts types.ServiceCreateOptions, f ...ServiceConstructor) string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } var service swarm.Service for _, fn := range f { fn(&service) @@ -32,11 +36,17 @@ func (d *Daemon) createServiceWithOptions(t assert.TestingT, opts types.ServiceC // CreateService creates a swarm service given the specified service constructor func (d *Daemon) CreateService(t assert.TestingT, f ...ServiceConstructor) string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } return d.createServiceWithOptions(t, types.ServiceCreateOptions{}, f...) } // GetService returns the swarm service corresponding to the specified id func (d *Daemon) GetService(t assert.TestingT, id string) *swarm.Service { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -47,6 +57,9 @@ func (d *Daemon) GetService(t assert.TestingT, id string) *swarm.Service { // GetServiceTasks returns the swarm tasks for the specified service func (d *Daemon) GetServiceTasks(t assert.TestingT, service string) []swarm.Task { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -65,6 +78,9 @@ func (d *Daemon) GetServiceTasks(t assert.TestingT, service string) []swarm.Task // UpdateService updates a swarm service with the specified service constructor func (d *Daemon) UpdateService(t assert.TestingT, service *swarm.Service, f ...ServiceConstructor) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -78,6 +94,9 @@ func (d *Daemon) UpdateService(t assert.TestingT, service *swarm.Service, f ...S // RemoveService removes the specified service func (d *Daemon) RemoveService(t assert.TestingT, id string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -87,6 +106,9 @@ func (d *Daemon) RemoveService(t assert.TestingT, id string) { // ListServices returns the list of the current swarm services func (d *Daemon) ListServices(t assert.TestingT) []swarm.Service { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -97,6 +119,9 @@ func (d *Daemon) ListServices(t assert.TestingT) []swarm.Service { // GetTask returns the swarm task identified by the specified id func (d *Daemon) GetTask(t assert.TestingT, id string) swarm.Task { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() diff --git a/components/engine/internal/test/daemon/swarm.go b/components/engine/internal/test/daemon/swarm.go index f66c447ccb..3e803eeeb8 100644 --- a/components/engine/internal/test/daemon/swarm.go +++ b/components/engine/internal/test/daemon/swarm.go @@ -5,17 +5,22 @@ import ( "fmt" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/internal/test" "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" ) const ( - defaultSwarmPort = 2477 + // DefaultSwarmPort is the default port use for swarm in the tests + DefaultSwarmPort = 2477 defaultSwarmListenAddr = "0.0.0.0" ) // StartAndSwarmInit starts the daemon (with busybox) and init the swarm func (d *Daemon) StartAndSwarmInit(t testingT) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } // avoid networking conflicts args := []string{"--iptables=false", "--swarm-default-advertise-addr=lo"} d.StartWithBusybox(t, args...) @@ -25,6 +30,9 @@ func (d *Daemon) StartAndSwarmInit(t testingT) { // StartAndSwarmJoin starts the daemon (with busybox) and join the specified swarm as worker or manager func (d *Daemon) StartAndSwarmJoin(t testingT, leader *Daemon, manager bool) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } // avoid networking conflicts args := []string{"--iptables=false", "--swarm-default-advertise-addr=lo"} d.StartWithBusybox(t, args...) @@ -55,6 +63,9 @@ func (d *Daemon) NodeID() string { // SwarmInit initializes a new swarm cluster. func (d *Daemon) SwarmInit(t assert.TestingT, req swarm.InitRequest) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } if req.ListenAddr == "" { req.ListenAddr = fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort) } @@ -67,6 +78,9 @@ func (d *Daemon) SwarmInit(t assert.TestingT, req swarm.InitRequest) { // SwarmJoin joins a daemon to an existing cluster. func (d *Daemon) SwarmJoin(t assert.TestingT, req swarm.JoinRequest) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } if req.ListenAddr == "" { req.ListenAddr = fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort) } @@ -93,6 +107,9 @@ func (d *Daemon) SwarmLeave(force bool) error { // SwarmInfo returns the swarm information of the daemon func (d *Daemon) SwarmInfo(t assert.TestingT) swarm.Info { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) info, err := cli.Info(context.Background()) assert.NilError(t, err, "get swarm info") @@ -115,6 +132,9 @@ func (d *Daemon) SwarmUnlock(req swarm.UnlockRequest) error { // GetSwarm returns the current swarm object func (d *Daemon) GetSwarm(t assert.TestingT) swarm.Swarm { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -125,6 +145,9 @@ func (d *Daemon) GetSwarm(t assert.TestingT) swarm.Swarm { // UpdateSwarm updates the current swarm object with the specified spec constructors func (d *Daemon) UpdateSwarm(t assert.TestingT, f ...SpecConstructor) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -139,6 +162,9 @@ func (d *Daemon) UpdateSwarm(t assert.TestingT, f ...SpecConstructor) { // RotateTokens update the swarm to rotate tokens func (d *Daemon) RotateTokens(t assert.TestingT) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() @@ -156,6 +182,9 @@ func (d *Daemon) RotateTokens(t assert.TestingT) { // JoinTokens returns the current swarm join tokens func (d *Daemon) JoinTokens(t assert.TestingT) swarm.JoinTokens { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } cli := d.NewClientT(t) defer cli.Close() diff --git a/components/engine/internal/test/environment/clean.go b/components/engine/internal/test/environment/clean.go index d83175c845..fab8e7f6fb 100644 --- a/components/engine/internal/test/environment/clean.go +++ b/components/engine/internal/test/environment/clean.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/client" + "github.com/docker/docker/internal/test" "github.com/gotestyourself/gotestyourself/assert" "golang.org/x/net/context" ) @@ -25,6 +26,9 @@ type logT interface { // and removing everything else. It's meant to run after any tests so that they don't // depend on each others. func (e *Execution) Clean(t testingT) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } client := e.APIClient() platform := e.OSType @@ -41,6 +45,9 @@ func (e *Execution) Clean(t testingT) { } func unpauseAllContainers(t assert.TestingT, client client.ContainerAPIClient) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } ctx := context.Background() containers := getPausedContainers(ctx, t, client) if len(containers) > 0 { @@ -52,6 +59,9 @@ func unpauseAllContainers(t assert.TestingT, client client.ContainerAPIClient) { } func getPausedContainers(ctx context.Context, t assert.TestingT, client client.ContainerAPIClient) []types.Container { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } filter := filters.NewArgs() filter.Add("status", "paused") containers, err := client.ContainerList(ctx, types.ContainerListOptions{ @@ -66,6 +76,9 @@ func getPausedContainers(ctx context.Context, t assert.TestingT, client client.C var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`) func deleteAllContainers(t assert.TestingT, apiclient client.ContainerAPIClient, protectedContainers map[string]struct{}) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } ctx := context.Background() containers := getAllContainers(ctx, t, apiclient) if len(containers) == 0 { @@ -88,6 +101,9 @@ func deleteAllContainers(t assert.TestingT, apiclient client.ContainerAPIClient, } func getAllContainers(ctx context.Context, t assert.TestingT, client client.ContainerAPIClient) []types.Container { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } containers, err := client.ContainerList(ctx, types.ContainerListOptions{ Quiet: true, All: true, @@ -97,6 +113,9 @@ func getAllContainers(ctx context.Context, t assert.TestingT, client client.Cont } func deleteAllImages(t testingT, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } images, err := apiclient.ImageList(context.Background(), types.ImageListOptions{}) assert.Check(t, err, "failed to list images") @@ -119,6 +138,9 @@ func deleteAllImages(t testingT, apiclient client.ImageAPIClient, protectedImage } func removeImage(ctx context.Context, t assert.TestingT, apiclient client.ImageAPIClient, ref string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } _, err := apiclient.ImageRemove(ctx, ref, types.ImageRemoveOptions{ Force: true, }) @@ -129,6 +151,9 @@ func removeImage(ctx context.Context, t assert.TestingT, apiclient client.ImageA } func deleteAllVolumes(t assert.TestingT, c client.VolumeAPIClient, protectedVolumes map[string]struct{}) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } volumes, err := c.VolumeList(context.Background(), filters.Args{}) assert.Check(t, err, "failed to list volumes") @@ -146,6 +171,9 @@ func deleteAllVolumes(t assert.TestingT, c client.VolumeAPIClient, protectedVolu } func deleteAllNetworks(t assert.TestingT, c client.NetworkAPIClient, daemonPlatform string, protectedNetworks map[string]struct{}) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{}) assert.Check(t, err, "failed to list networks") @@ -166,6 +194,9 @@ func deleteAllNetworks(t assert.TestingT, c client.NetworkAPIClient, daemonPlatf } func deleteAllPlugins(t assert.TestingT, c client.PluginAPIClient, protectedPlugins map[string]struct{}) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } plugins, err := c.PluginList(context.Background(), filters.Args{}) // Docker EE does not allow cluster-wide plugin management. if client.IsErrNotImplemented(err) { diff --git a/components/engine/internal/test/environment/protect.go b/components/engine/internal/test/environment/protect.go index 3dfe606cea..59acdf418e 100644 --- a/components/engine/internal/test/environment/protect.go +++ b/components/engine/internal/test/environment/protect.go @@ -6,6 +6,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" dclient "github.com/docker/docker/client" + "github.com/docker/docker/internal/test" "github.com/gotestyourself/gotestyourself/assert" ) @@ -33,6 +34,9 @@ func newProtectedElements() protectedElements { // volumes, and, on Linux, plugins) from being cleaned up at the end of test // runs func ProtectAll(t testingT, testEnv *Execution) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } ProtectContainers(t, testEnv) ProtectImages(t, testEnv) ProtectNetworks(t, testEnv) @@ -45,6 +49,9 @@ func ProtectAll(t testingT, testEnv *Execution) { // ProtectContainer adds the specified container(s) to be protected in case of // clean func (e *Execution) ProtectContainer(t testingT, containers ...string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } for _, container := range containers { e.protectedElements.containers[container] = struct{}{} } @@ -53,11 +60,17 @@ func (e *Execution) ProtectContainer(t testingT, containers ...string) { // ProtectContainers protects existing containers from being cleaned up at the // end of test runs func ProtectContainers(t testingT, testEnv *Execution) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } containers := getExistingContainers(t, testEnv) testEnv.ProtectContainer(t, containers...) } func getExistingContainers(t assert.TestingT, testEnv *Execution) []string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } client := testEnv.APIClient() containerList, err := client.ContainerList(context.Background(), types.ContainerListOptions{ All: true, @@ -73,6 +86,9 @@ func getExistingContainers(t assert.TestingT, testEnv *Execution) []string { // ProtectImage adds the specified image(s) to be protected in case of clean func (e *Execution) ProtectImage(t testingT, images ...string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } for _, image := range images { e.protectedElements.images[image] = struct{}{} } @@ -81,6 +97,9 @@ func (e *Execution) ProtectImage(t testingT, images ...string) { // ProtectImages protects existing images and on linux frozen images from being // cleaned up at the end of test runs func ProtectImages(t testingT, testEnv *Execution) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } images := getExistingImages(t, testEnv) if testEnv.OSType == "linux" { @@ -90,6 +109,9 @@ func ProtectImages(t testingT, testEnv *Execution) { } func getExistingImages(t assert.TestingT, testEnv *Execution) []string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } client := testEnv.APIClient() filter := filters.NewArgs() filter.Add("dangling", "false") @@ -124,6 +146,9 @@ func tagsFromImageSummary(image types.ImageSummary) []string { // ProtectNetwork adds the specified network(s) to be protected in case of // clean func (e *Execution) ProtectNetwork(t testingT, networks ...string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } for _, network := range networks { e.protectedElements.networks[network] = struct{}{} } @@ -132,11 +157,17 @@ func (e *Execution) ProtectNetwork(t testingT, networks ...string) { // ProtectNetworks protects existing networks from being cleaned up at the end // of test runs func ProtectNetworks(t testingT, testEnv *Execution) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } networks := getExistingNetworks(t, testEnv) testEnv.ProtectNetwork(t, networks...) } func getExistingNetworks(t assert.TestingT, testEnv *Execution) []string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } client := testEnv.APIClient() networkList, err := client.NetworkList(context.Background(), types.NetworkListOptions{}) assert.NilError(t, err, "failed to list networks") @@ -150,6 +181,9 @@ func getExistingNetworks(t assert.TestingT, testEnv *Execution) []string { // ProtectPlugin adds the specified plugin(s) to be protected in case of clean func (e *Execution) ProtectPlugin(t testingT, plugins ...string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } for _, plugin := range plugins { e.protectedElements.plugins[plugin] = struct{}{} } @@ -158,11 +192,17 @@ func (e *Execution) ProtectPlugin(t testingT, plugins ...string) { // ProtectPlugins protects existing plugins from being cleaned up at the end of // test runs func ProtectPlugins(t testingT, testEnv *Execution) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } plugins := getExistingPlugins(t, testEnv) testEnv.ProtectPlugin(t, plugins...) } func getExistingPlugins(t assert.TestingT, testEnv *Execution) []string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } client := testEnv.APIClient() pluginList, err := client.PluginList(context.Background(), filters.Args{}) // Docker EE does not allow cluster-wide plugin management. @@ -180,6 +220,9 @@ func getExistingPlugins(t assert.TestingT, testEnv *Execution) []string { // ProtectVolume adds the specified volume(s) to be protected in case of clean func (e *Execution) ProtectVolume(t testingT, volumes ...string) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } for _, volume := range volumes { e.protectedElements.volumes[volume] = struct{}{} } @@ -188,11 +231,17 @@ func (e *Execution) ProtectVolume(t testingT, volumes ...string) { // ProtectVolumes protects existing volumes from being cleaned up at the end of // test runs func ProtectVolumes(t testingT, testEnv *Execution) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } volumes := getExistingVolumes(t, testEnv) testEnv.ProtectVolume(t, volumes...) } func getExistingVolumes(t assert.TestingT, testEnv *Execution) []string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } client := testEnv.APIClient() volumeList, err := client.VolumeList(context.Background(), filters.Args{}) assert.NilError(t, err, "failed to list volumes") diff --git a/components/engine/internal/test/fakecontext/context.go b/components/engine/internal/test/fakecontext/context.go index 196c694bd9..8b11da207e 100644 --- a/components/engine/internal/test/fakecontext/context.go +++ b/components/engine/internal/test/fakecontext/context.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" + "github.com/docker/docker/internal/test" "github.com/docker/docker/pkg/archive" ) @@ -17,6 +18,9 @@ type testingT interface { // New creates a fake build context func New(t testingT, dir string, modifiers ...func(*Fake) error) *Fake { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } fakeContext := &Fake{Dir: dir} if dir == "" { if err := newDir(fakeContext); err != nil { @@ -116,6 +120,9 @@ func (f *Fake) Close() error { // AsTarReader returns a ReadCloser with the contents of Dir as a tar archive. func (f *Fake) AsTarReader(t testingT) io.ReadCloser { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } reader, err := archive.TarWithOptions(f.Dir, &archive.TarOptions{}) if err != nil { t.Fatalf("Failed to create tar from %s: %s", f.Dir, err) diff --git a/components/engine/internal/test/fakegit/fakegit.go b/components/engine/internal/test/fakegit/fakegit.go index 45b99608f3..59f4bcb056 100644 --- a/components/engine/internal/test/fakegit/fakegit.go +++ b/components/engine/internal/test/fakegit/fakegit.go @@ -9,6 +9,7 @@ import ( "os/exec" "path/filepath" + "github.com/docker/docker/internal/test" "github.com/docker/docker/internal/test/fakecontext" "github.com/docker/docker/internal/test/fakestorage" "github.com/gotestyourself/gotestyourself/assert" @@ -63,6 +64,9 @@ func (g *FakeGit) Close() { // New create a fake git server that can be used for git related tests func New(c testingT, name string, files map[string]string, enforceLocalServer bool) *FakeGit { + if ht, ok := c.(test.HelperT); ok { + ht.Helper() + } ctx := fakecontext.New(c, "", fakecontext.WithFiles(files)) defer ctx.Close() curdir, err := os.Getwd() diff --git a/components/engine/internal/test/fakestorage/fixtures.go b/components/engine/internal/test/fakestorage/fixtures.go index f8e80527f0..a694834f7d 100644 --- a/components/engine/internal/test/fakestorage/fixtures.go +++ b/components/engine/internal/test/fakestorage/fixtures.go @@ -10,6 +10,7 @@ import ( "sync" "github.com/docker/docker/api/types" + "github.com/docker/docker/internal/test" "github.com/docker/docker/pkg/archive" "github.com/gotestyourself/gotestyourself/assert" ) @@ -17,6 +18,9 @@ import ( var ensureHTTPServerOnce sync.Once func ensureHTTPServerImage(t testingT) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } var doIt bool ensureHTTPServerOnce.Do(func() { doIt = true diff --git a/components/engine/internal/test/fakestorage/storage.go b/components/engine/internal/test/fakestorage/storage.go index ee001386e6..adce3512c1 100644 --- a/components/engine/internal/test/fakestorage/storage.go +++ b/components/engine/internal/test/fakestorage/storage.go @@ -14,6 +14,7 @@ import ( "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" + "github.com/docker/docker/internal/test" "github.com/docker/docker/internal/test/environment" "github.com/docker/docker/internal/test/fakecontext" "github.com/docker/docker/internal/test/request" @@ -56,6 +57,9 @@ func SetTestEnvironment(env *environment.Execution) { // New returns a static file server that will be use as build context. func New(t testingT, dir string, modifiers ...func(*fakecontext.Fake) error) Fake { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } if testEnv == nil { t.Fatal("fakstorage package requires SetTestEnvironment() to be called before use.") } diff --git a/components/engine/internal/test/helper.go b/components/engine/internal/test/helper.go new file mode 100644 index 0000000000..1b9fd75090 --- /dev/null +++ b/components/engine/internal/test/helper.go @@ -0,0 +1,6 @@ +package test + +// HelperT is a subset of testing.T that implements the Helper function +type HelperT interface { + Helper() +} diff --git a/components/engine/internal/test/registry/registry.go b/components/engine/internal/test/registry/registry.go index a801beea29..2e89c32e57 100644 --- a/components/engine/internal/test/registry/registry.go +++ b/components/engine/internal/test/registry/registry.go @@ -9,6 +9,7 @@ import ( "path/filepath" "time" + "github.com/docker/docker/internal/test" "github.com/gotestyourself/gotestyourself/assert" "github.com/opencontainers/go-digest" ) @@ -54,6 +55,9 @@ type Config struct { // NewV2 creates a v2 registry server func NewV2(t testingT, ops ...func(*Config)) *V2 { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } c := &Config{ registryURL: DefaultURL, } @@ -135,6 +139,9 @@ http: // WaitReady waits for the registry to be ready to serve requests (or fail after a while) func (r *V2) WaitReady(t testingT) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } var err error for i := 0; i != 50; i++ { if err = r.Ping(); err == nil { @@ -183,6 +190,9 @@ func (r *V2) getBlobFilename(blobDigest digest.Digest) string { // ReadBlobContents read the file corresponding to the specified digest func (r *V2) ReadBlobContents(t assert.TestingT, blobDigest digest.Digest) []byte { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } // Load the target manifest blob. manifestBlob, err := ioutil.ReadFile(r.getBlobFilename(blobDigest)) assert.NilError(t, err, "unable to read blob") @@ -191,6 +201,9 @@ func (r *V2) ReadBlobContents(t assert.TestingT, blobDigest digest.Digest) []byt // WriteBlobContents write the file corresponding to the specified digest with the given content func (r *V2) WriteBlobContents(t assert.TestingT, blobDigest digest.Digest, data []byte) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } err := ioutil.WriteFile(r.getBlobFilename(blobDigest), data, os.FileMode(0644)) assert.NilError(t, err, "unable to write malicious data blob") } @@ -198,6 +211,9 @@ func (r *V2) WriteBlobContents(t assert.TestingT, blobDigest digest.Digest, data // TempMoveBlobData moves the existing data file aside, so that we can replace it with a // malicious blob of data for example. func (r *V2) TempMoveBlobData(t testingT, blobDigest digest.Digest) (undo func()) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } tempFile, err := ioutil.TempFile("", "registry-temp-blob-") assert.NilError(t, err, "unable to get temporary blob file") tempFile.Close() diff --git a/components/engine/internal/test/registry/registry_mock.go b/components/engine/internal/test/registry/registry_mock.go index 77c4ac1bd4..d139401a62 100644 --- a/components/engine/internal/test/registry/registry_mock.go +++ b/components/engine/internal/test/registry/registry_mock.go @@ -6,6 +6,8 @@ import ( "regexp" "strings" "sync" + + "github.com/docker/docker/internal/test" ) type handlerFunc func(w http.ResponseWriter, r *http.Request) @@ -27,6 +29,9 @@ func (tr *Mock) RegisterHandler(path string, h handlerFunc) { // NewMock creates a registry mock func NewMock(t testingT) (*Mock, error) { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } testReg := &Mock{handlers: make(map[string]handlerFunc)} ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/components/engine/internal/test/request/request.go b/components/engine/internal/test/request/request.go index 5b3fa4efd5..00450d94a2 100644 --- a/components/engine/internal/test/request/request.go +++ b/components/engine/internal/test/request/request.go @@ -14,6 +14,7 @@ import ( "time" "github.com/docker/docker/client" + "github.com/docker/docker/internal/test" "github.com/docker/docker/internal/test/environment" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/ioutils" @@ -25,6 +26,9 @@ import ( // NewAPIClient returns a docker API client configured from environment variables func NewAPIClient(t assert.TestingT, ops ...func(*client.Client) error) client.APIClient { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } ops = append([]func(*client.Client) error{client.FromEnv}, ops...) clt, err := client.NewClientWithOpts(ops...) assert.NilError(t, err) @@ -33,6 +37,9 @@ func NewAPIClient(t assert.TestingT, ops ...func(*client.Client) error) client.A // DaemonTime provides the current time on the daemon host func DaemonTime(ctx context.Context, t assert.TestingT, client client.APIClient, testEnv *environment.Execution) time.Time { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } if testEnv.IsLocalDaemon() { return time.Now() } @@ -48,6 +55,9 @@ func DaemonTime(ctx context.Context, t assert.TestingT, client client.APIClient, // DaemonUnixTime returns the current time on the daemon host with nanoseconds precision. // It return the time formatted how the client sends timestamps to the server. func DaemonUnixTime(ctx context.Context, t assert.TestingT, client client.APIClient, testEnv *environment.Execution) string { + if ht, ok := t.(test.HelperT); ok { + ht.Helper() + } dt := DaemonTime(ctx, t, client, testEnv) return fmt.Sprintf("%d.%09d", dt.Unix(), int64(dt.Nanosecond())) } diff --git a/components/engine/libcontainerd/client_daemon.go b/components/engine/libcontainerd/client_daemon.go index 348aeb9d9f..72f9692260 100644 --- a/components/engine/libcontainerd/client_daemon.go +++ b/components/engine/libcontainerd/client_daemon.go @@ -34,7 +34,7 @@ import ( "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/ioutils" "github.com/opencontainers/image-spec/specs-go/v1" - specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -114,6 +114,13 @@ type client struct { containers map[string]*container } +func (c *client) reconnect() error { + c.Lock() + err := c.remote.Reconnect() + c.Unlock() + return err +} + func (c *client) setRemote(remote *containerd.Client) { c.Lock() c.remote = remote @@ -131,9 +138,30 @@ func (c *client) Version(ctx context.Context) (containerd.Version, error) { return c.getRemote().Version(ctx) } +// Restore loads the containerd container. +// It should not be called concurrently with any other operation for the given ID. func (c *client) Restore(ctx context.Context, id string, attachStdio StdioCallback) (alive bool, pid int, err error) { c.Lock() - defer c.Unlock() + _, ok := c.containers[id] + if ok { + c.Unlock() + return false, 0, errors.WithStack(newConflictError("id already in use")) + } + + cntr := &container{} + c.containers[id] = cntr + cntr.mu.Lock() + defer cntr.mu.Unlock() + + c.Unlock() + + defer func() { + if err != nil { + c.Lock() + delete(c.containers, id) + c.Unlock() + } + }() var dio *cio.DirectIO defer func() { @@ -144,9 +172,9 @@ func (c *client) Restore(ctx context.Context, id string, attachStdio StdioCallba err = wrapError(err) }() - ctr, err := c.remote.LoadContainer(ctx, id) + ctr, err := c.getRemote().LoadContainer(ctx, id) if err != nil { - return false, -1, errors.WithStack(err) + return false, -1, errors.WithStack(wrapError(err)) } attachIO := func(fifos *cio.FIFOSet) (cio.IO, error) { @@ -160,24 +188,23 @@ func (c *client) Restore(ctx context.Context, id string, attachStdio StdioCallba } t, err := ctr.Task(ctx, attachIO) if err != nil && !containerderrors.IsNotFound(err) { - return false, -1, err + return false, -1, errors.Wrap(wrapError(err), "error getting containerd task for container") } if t != nil { s, err := t.Status(ctx) if err != nil { - return false, -1, err + return false, -1, errors.Wrap(wrapError(err), "error getting task status") } alive = s.Status != containerd.Stopped pid = int(t.Pid()) } - c.containers[id] = &container{ - bundleDir: filepath.Join(c.stateDir, id), - ctr: ctr, - task: t, - // TODO(mlaventure): load execs - } + + cntr.bundleDir = filepath.Join(c.stateDir, id) + cntr.ctr = ctr + cntr.task = t + // TODO(mlaventure): load execs c.logger.WithFields(logrus.Fields{ "container": id, diff --git a/components/engine/libcontainerd/remote_daemon.go b/components/engine/libcontainerd/remote_daemon.go index cc98456ba5..d5ff83be88 100644 --- a/components/engine/libcontainerd/remote_daemon.go +++ b/components/engine/libcontainerd/remote_daemon.go @@ -311,20 +311,17 @@ func (r *remote) monitorConnection(monitor *containerd.Client) { <-r.daemonWaitCh } - monitor.Close() os.Remove(r.GRPC.Address) if err := r.startContainerd(); err != nil { r.logger.WithError(err).Error("failed restarting containerd") continue } - newMonitor, err := containerd.New(r.GRPC.Address) - if err != nil { + if err := monitor.Reconnect(); err != nil { r.logger.WithError(err).Error("failed connect to containerd") continue } - monitor = newMonitor var wg sync.WaitGroup for _, c := range r.clients { @@ -333,18 +330,12 @@ func (r *remote) monitorConnection(monitor *containerd.Client) { go func(c *client) { defer wg.Done() c.logger.WithField("namespace", c.namespace).Debug("creating new containerd remote client") - c.remote.Close() - - remote, err := containerd.New(r.GRPC.Address, containerd.WithDefaultNamespace(c.namespace)) - if err != nil { + if err := c.reconnect(); err != nil { r.logger.WithError(err).Error("failed to connect to containerd") // TODO: Better way to handle this? // This *shouldn't* happen, but this could wind up where the daemon // is not able to communicate with an eventually up containerd - return } - - c.setRemote(remote) }(c) wg.Wait() diff --git a/components/engine/libcontainerd/remote_daemon_options_linux.go b/components/engine/libcontainerd/remote_daemon_options_linux.go index 24ef5a5a4b..a820fb3894 100644 --- a/components/engine/libcontainerd/remote_daemon_options_linux.go +++ b/components/engine/libcontainerd/remote_daemon_options_linux.go @@ -16,19 +16,3 @@ func (o oomScore) Apply(r Remote) error { } return fmt.Errorf("WithOOMScore option not supported for this remote") } - -// WithSubreaper sets whether containerd should register itself as a -// subreaper -func WithSubreaper(reap bool) RemoteOption { - return subreaper(reap) -} - -type subreaper bool - -func (s subreaper) Apply(r Remote) error { - if remote, ok := r.(*remote); ok { - remote.NoSubreaper = !bool(s) - return nil - } - return fmt.Errorf("WithSubreaper option not supported for this remote") -} diff --git a/components/engine/pkg/term/termios_bsd.go b/components/engine/pkg/term/termios_bsd.go index 48f25ce7eb..48b16f5203 100644 --- a/components/engine/pkg/term/termios_bsd.go +++ b/components/engine/pkg/term/termios_bsd.go @@ -1,4 +1,4 @@ -// +build darwin freebsd openbsd +// +build darwin freebsd openbsd netbsd package term // import "github.com/docker/docker/pkg/term" diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index 8a1554bad2..91cde6db28 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -13,7 +13,7 @@ github.com/mattn/go-shellwords v1.0.3 github.com/sirupsen/logrus v1.0.3 github.com/tchap/go-patricia v2.2.6 github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 -golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 +golang.org/x/net 5561cd9b4330353950f399814f427425c0a26fd2 golang.org/x/sys 37707fdb30a5b38865cfb95e5aab41707daec7fd github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1 github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6 @@ -108,7 +108,7 @@ github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 # containerd -github.com/containerd/containerd 3fa104f843ec92328912e042b767d26825f202aa +github.com/containerd/containerd 4ac4fd0b6a268fe6f38b2b2e32e40daa7e424fac github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6 github.com/containerd/continuity d8fb8589b0e8e85b8c8bbaa8840226d0dfeb7371 github.com/containerd/cgroups fe281dd265766145e943a034aa41086474ea6130 diff --git a/components/engine/vendor/github.com/containerd/containerd/LICENSE.code b/components/engine/vendor/github.com/containerd/containerd/LICENSE similarity index 100% rename from components/engine/vendor/github.com/containerd/containerd/LICENSE.code rename to components/engine/vendor/github.com/containerd/containerd/LICENSE diff --git a/components/engine/vendor/github.com/containerd/containerd/README.md b/components/engine/vendor/github.com/containerd/containerd/README.md index 84d1eec8ae..d76ce1b2c1 100644 --- a/components/engine/vendor/github.com/containerd/containerd/README.md +++ b/components/engine/vendor/github.com/containerd/containerd/README.md @@ -4,6 +4,7 @@ [![Build Status](https://travis-ci.org/containerd/containerd.svg?branch=master)](https://travis-ci.org/containerd/containerd) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd?ref=badge_shield) [![Go Report Card](https://goreportcard.com/badge/github.com/containerd/containerd)](https://goreportcard.com/report/github.com/containerd/containerd) +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1271/badge)](https://bestpractices.coreinfrastructure.org/projects/1271) containerd is an industry-standard container runtime with an emphasis on simplicity, robustness and portability. It is available as a daemon for Linux and Windows, which can manage the complete container lifecycle of its host system: image transfer and storage, container execution and supervision, low-level storage and network attachments, etc. @@ -13,7 +14,7 @@ containerd is designed to be embedded into a larger system, rather than being us ## Getting Started -See our documentation on [containerd.io](containerd.io): +See our documentation on [containerd.io](https://containerd.io): * [for ops and admins](docs/ops.md) * [namespaces](docs/namespaces.md) * [client options](docs/client-opts.md) diff --git a/components/engine/vendor/github.com/containerd/containerd/api/events/container.pb.go b/components/engine/vendor/github.com/containerd/containerd/api/events/container.pb.go index b05a402bb0..5b715fc1ef 100644 --- a/components/engine/vendor/github.com/containerd/containerd/api/events/container.pb.go +++ b/components/engine/vendor/github.com/containerd/containerd/api/events/container.pb.go @@ -158,9 +158,7 @@ func (m *ContainerCreate_Runtime) Field(fieldpath []string) (string, bool) { return "", false } - adaptor, ok := decoded.(interface { - Field([]string) (string, bool) - }) + adaptor, ok := decoded.(interface{ Field([]string) (string, bool) }) if !ok { return "", false } diff --git a/components/engine/vendor/github.com/containerd/containerd/api/events/doc.go b/components/engine/vendor/github.com/containerd/containerd/api/events/doc.go index ac1e83fb75..354bef79fd 100644 --- a/components/engine/vendor/github.com/containerd/containerd/api/events/doc.go +++ b/components/engine/vendor/github.com/containerd/containerd/api/events/doc.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + // Package events has protobuf types for various events that are used in // containerd. package events diff --git a/components/engine/vendor/github.com/containerd/containerd/api/services/events/v1/doc.go b/components/engine/vendor/github.com/containerd/containerd/api/services/events/v1/doc.go index 070604bb8f..b7f86da869 100644 --- a/components/engine/vendor/github.com/containerd/containerd/api/services/events/v1/doc.go +++ b/components/engine/vendor/github.com/containerd/containerd/api/services/events/v1/doc.go @@ -1,2 +1,18 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + // Package events defines the event pushing and subscription service. package events diff --git a/components/engine/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go b/components/engine/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go index e2ad455a43..52cca0acd7 100644 --- a/components/engine/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go +++ b/components/engine/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go @@ -115,9 +115,7 @@ func (m *Envelope) Field(fieldpath []string) (string, bool) { return "", false } - adaptor, ok := decoded.(interface { - Field([]string) (string, bool) - }) + adaptor, ok := decoded.(interface{ Field([]string) (string, bool) }) if !ok { return "", false } diff --git a/components/engine/vendor/github.com/containerd/containerd/api/services/images/v1/docs.go b/components/engine/vendor/github.com/containerd/containerd/api/services/images/v1/docs.go index a8d61a31e4..4170f38aff 100644 --- a/components/engine/vendor/github.com/containerd/containerd/api/services/images/v1/docs.go +++ b/components/engine/vendor/github.com/containerd/containerd/api/services/images/v1/docs.go @@ -1 +1,17 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package images diff --git a/components/engine/vendor/github.com/containerd/containerd/api/services/introspection/v1/doc.go b/components/engine/vendor/github.com/containerd/containerd/api/services/introspection/v1/doc.go index 3b9d7947c9..f6f65eadfd 100644 --- a/components/engine/vendor/github.com/containerd/containerd/api/services/introspection/v1/doc.go +++ b/components/engine/vendor/github.com/containerd/containerd/api/services/introspection/v1/doc.go @@ -1 +1,17 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package introspection diff --git a/components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/doc.go b/components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/doc.go index 3685b64558..db2422a8bb 100644 --- a/components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/doc.go +++ b/components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/doc.go @@ -1 +1,17 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package leases diff --git a/components/engine/vendor/github.com/containerd/containerd/api/types/doc.go b/components/engine/vendor/github.com/containerd/containerd/api/types/doc.go index ab1254f4c2..475b465ed4 100644 --- a/components/engine/vendor/github.com/containerd/containerd/api/types/doc.go +++ b/components/engine/vendor/github.com/containerd/containerd/api/types/doc.go @@ -1 +1,17 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package types diff --git a/components/engine/vendor/github.com/containerd/containerd/archive/strconv.go b/components/engine/vendor/github.com/containerd/containerd/archive/strconv.go new file mode 100644 index 0000000000..d262e90598 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/archive/strconv.go @@ -0,0 +1,68 @@ +// +build windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package archive + +import ( + "strconv" + "strings" + "time" + + "github.com/dmcgowan/go-tar" +) + +// Forked from https://github.com/golang/go/blob/master/src/archive/tar/strconv.go +// as archive/tar doesn't support CreationTime, but does handle PAX time parsing, +// and there's no need to re-invent the wheel. + +// parsePAXTime takes a string of the form %d.%d as described in the PAX +// specification. Note that this implementation allows for negative timestamps, +// which is allowed for by the PAX specification, but not always portable. +func parsePAXTime(s string) (time.Time, error) { + const maxNanoSecondDigits = 9 + + // Split string into seconds and sub-seconds parts. + ss, sn := s, "" + if pos := strings.IndexByte(s, '.'); pos >= 0 { + ss, sn = s[:pos], s[pos+1:] + } + + // Parse the seconds. + secs, err := strconv.ParseInt(ss, 10, 64) + if err != nil { + return time.Time{}, tar.ErrHeader + } + if len(sn) == 0 { + return time.Unix(secs, 0), nil // No sub-second values + } + + // Parse the nanoseconds. + if strings.Trim(sn, "0123456789") != "" { + return time.Time{}, tar.ErrHeader + } + if len(sn) < maxNanoSecondDigits { + sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad + } else { + sn = sn[:maxNanoSecondDigits] // Right truncate + } + nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed + if len(ss) > 0 && ss[0] == '-' { + return time.Unix(secs, -nsecs), nil // Negative correction + } + return time.Unix(secs, nsecs), nil +} diff --git a/components/engine/vendor/github.com/containerd/containerd/archive/tar.go b/components/engine/vendor/github.com/containerd/containerd/archive/tar.go index a649c5b458..7e9182c8f5 100644 --- a/components/engine/vendor/github.com/containerd/containerd/archive/tar.go +++ b/components/engine/vendor/github.com/containerd/containerd/archive/tar.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package archive import ( @@ -13,19 +29,21 @@ import ( "syscall" "time" - "github.com/containerd/containerd/fs" "github.com/containerd/containerd/log" + "github.com/containerd/continuity/fs" "github.com/dmcgowan/go-tar" "github.com/pkg/errors" ) -var bufferPool = &sync.Pool{ +var bufPool = &sync.Pool{ New: func() interface{} { buffer := make([]byte, 32*1024) return &buffer }, } +var errInvalidArchive = errors.New("invalid archive") + // Diff returns a tar stream of the computed filesystem // difference between the provided directories. // @@ -87,12 +105,23 @@ const ( // Apply applies a tar stream of an OCI style diff tar. // See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets -func Apply(ctx context.Context, root string, r io.Reader) (int64, error) { +func Apply(ctx context.Context, root string, r io.Reader, opts ...ApplyOpt) (int64, error) { root = filepath.Clean(root) + var options ApplyOptions + for _, opt := range opts { + if err := opt(&options); err != nil { + return 0, errors.Wrap(err, "failed to apply option") + } + } + + return apply(ctx, root, tar.NewReader(r), options) +} + +// applyNaive applies a tar stream of an OCI style diff tar. +// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets +func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { var ( - tr = tar.NewReader(r) - size int64 dirs []*tar.Header // Used for handling opaque directory markers which @@ -220,6 +249,15 @@ func Apply(ctx context.Context, root string, r io.Reader) (int64, error) { originalBase := base[len(whiteoutPrefix):] originalPath := filepath.Join(dir, originalBase) + + // Ensure originalPath is under dir + if dir[len(dir)-1] != filepath.Separator { + dir += string(filepath.Separator) + } + if !strings.HasPrefix(originalPath, dir) { + return 0, errors.Wrapf(errInvalidArchive, "invalid whiteout name: %v", base) + } + if err := os.RemoveAll(originalPath); err != nil { return 0, err } @@ -285,163 +323,6 @@ func Apply(ctx context.Context, root string, r io.Reader) (int64, error) { return size, nil } -type changeWriter struct { - tw *tar.Writer - source string - whiteoutT time.Time - inodeSrc map[uint64]string - inodeRefs map[uint64][]string -} - -func newChangeWriter(w io.Writer, source string) *changeWriter { - return &changeWriter{ - tw: tar.NewWriter(w), - source: source, - whiteoutT: time.Now(), - inodeSrc: map[uint64]string{}, - inodeRefs: map[uint64][]string{}, - } -} - -func (cw *changeWriter) HandleChange(k fs.ChangeKind, p string, f os.FileInfo, err error) error { - if err != nil { - return err - } - if k == fs.ChangeKindDelete { - whiteOutDir := filepath.Dir(p) - whiteOutBase := filepath.Base(p) - whiteOut := filepath.Join(whiteOutDir, whiteoutPrefix+whiteOutBase) - hdr := &tar.Header{ - Name: whiteOut[1:], - Size: 0, - ModTime: cw.whiteoutT, - AccessTime: cw.whiteoutT, - ChangeTime: cw.whiteoutT, - } - if err := cw.tw.WriteHeader(hdr); err != nil { - return errors.Wrap(err, "failed to write whiteout header") - } - } else { - var ( - link string - err error - source = filepath.Join(cw.source, p) - ) - - if f.Mode()&os.ModeSymlink != 0 { - if link, err = os.Readlink(source); err != nil { - return err - } - } - - hdr, err := tar.FileInfoHeader(f, link) - if err != nil { - return err - } - - hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) - - name := p - if strings.HasPrefix(name, string(filepath.Separator)) { - name, err = filepath.Rel(string(filepath.Separator), name) - if err != nil { - return errors.Wrap(err, "failed to make path relative") - } - } - name, err = tarName(name) - if err != nil { - return errors.Wrap(err, "cannot canonicalize path") - } - // suffix with '/' for directories - if f.IsDir() && !strings.HasSuffix(name, "/") { - name += "/" - } - hdr.Name = name - - if err := setHeaderForSpecialDevice(hdr, name, f); err != nil { - return errors.Wrap(err, "failed to set device headers") - } - - // additionalLinks stores file names which must be linked to - // this file when this file is added - var additionalLinks []string - inode, isHardlink := fs.GetLinkInfo(f) - if isHardlink { - // If the inode has a source, always link to it - if source, ok := cw.inodeSrc[inode]; ok { - hdr.Typeflag = tar.TypeLink - hdr.Linkname = source - hdr.Size = 0 - } else { - if k == fs.ChangeKindUnmodified { - cw.inodeRefs[inode] = append(cw.inodeRefs[inode], name) - return nil - } - cw.inodeSrc[inode] = name - additionalLinks = cw.inodeRefs[inode] - delete(cw.inodeRefs, inode) - } - } else if k == fs.ChangeKindUnmodified && !f.IsDir() { - // Nothing to write to diff - // Unmodified directories should still be written to keep - // directory permissions correct on direct unpack - return nil - } - - if capability, err := getxattr(source, "security.capability"); err != nil { - return errors.Wrap(err, "failed to get capabilities xattr") - } else if capability != nil { - if hdr.PAXRecords == nil { - hdr.PAXRecords = map[string]string{} - } - hdr.PAXRecords[paxSchilyXattr+"security.capability"] = string(capability) - } - - if err := cw.tw.WriteHeader(hdr); err != nil { - return errors.Wrap(err, "failed to write file header") - } - - if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 { - file, err := open(source) - if err != nil { - return errors.Wrapf(err, "failed to open path: %v", source) - } - defer file.Close() - - buf := bufferPool.Get().(*[]byte) - n, err := io.CopyBuffer(cw.tw, file, *buf) - bufferPool.Put(buf) - if err != nil { - return errors.Wrap(err, "failed to copy") - } - if n != hdr.Size { - return errors.New("short write copying file") - } - } - - if additionalLinks != nil { - source = hdr.Name - for _, extra := range additionalLinks { - hdr.Name = extra - hdr.Typeflag = tar.TypeLink - hdr.Linkname = source - hdr.Size = 0 - if err := cw.tw.WriteHeader(hdr); err != nil { - return errors.Wrap(err, "failed to write file header") - } - } - } - } - return nil -} - -func (cw *changeWriter) Close() error { - if err := cw.tw.Close(); err != nil { - return errors.Wrap(err, "failed to close tar writer") - } - return nil -} - func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header, reader io.Reader) error { // hdr.Mode is in linux format, which we can use for syscalls, // but for os.Foo() calls we need the mode converted to os.FileMode, @@ -535,9 +416,201 @@ func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header return chtimes(path, boundTime(latestTime(hdr.AccessTime, hdr.ModTime)), boundTime(hdr.ModTime)) } +type changeWriter struct { + tw *tar.Writer + source string + whiteoutT time.Time + inodeSrc map[uint64]string + inodeRefs map[uint64][]string + addedDirs map[string]struct{} +} + +func newChangeWriter(w io.Writer, source string) *changeWriter { + return &changeWriter{ + tw: tar.NewWriter(w), + source: source, + whiteoutT: time.Now(), + inodeSrc: map[uint64]string{}, + inodeRefs: map[uint64][]string{}, + addedDirs: map[string]struct{}{}, + } +} + +func (cw *changeWriter) HandleChange(k fs.ChangeKind, p string, f os.FileInfo, err error) error { + if err != nil { + return err + } + if k == fs.ChangeKindDelete { + whiteOutDir := filepath.Dir(p) + whiteOutBase := filepath.Base(p) + whiteOut := filepath.Join(whiteOutDir, whiteoutPrefix+whiteOutBase) + hdr := &tar.Header{ + Typeflag: tar.TypeReg, + Name: whiteOut[1:], + Size: 0, + ModTime: cw.whiteoutT, + AccessTime: cw.whiteoutT, + ChangeTime: cw.whiteoutT, + } + if err := cw.includeParents(hdr); err != nil { + return err + } + if err := cw.tw.WriteHeader(hdr); err != nil { + return errors.Wrap(err, "failed to write whiteout header") + } + } else { + var ( + link string + err error + source = filepath.Join(cw.source, p) + ) + + if f.Mode()&os.ModeSymlink != 0 { + if link, err = os.Readlink(source); err != nil { + return err + } + } + + hdr, err := tar.FileInfoHeader(f, link) + if err != nil { + return err + } + + hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) + + name := p + if strings.HasPrefix(name, string(filepath.Separator)) { + name, err = filepath.Rel(string(filepath.Separator), name) + if err != nil { + return errors.Wrap(err, "failed to make path relative") + } + } + name, err = tarName(name) + if err != nil { + return errors.Wrap(err, "cannot canonicalize path") + } + // suffix with '/' for directories + if f.IsDir() && !strings.HasSuffix(name, "/") { + name += "/" + } + hdr.Name = name + + if err := setHeaderForSpecialDevice(hdr, name, f); err != nil { + return errors.Wrap(err, "failed to set device headers") + } + + // additionalLinks stores file names which must be linked to + // this file when this file is added + var additionalLinks []string + inode, isHardlink := fs.GetLinkInfo(f) + if isHardlink { + // If the inode has a source, always link to it + if source, ok := cw.inodeSrc[inode]; ok { + hdr.Typeflag = tar.TypeLink + hdr.Linkname = source + hdr.Size = 0 + } else { + if k == fs.ChangeKindUnmodified { + cw.inodeRefs[inode] = append(cw.inodeRefs[inode], name) + return nil + } + cw.inodeSrc[inode] = name + additionalLinks = cw.inodeRefs[inode] + delete(cw.inodeRefs, inode) + } + } else if k == fs.ChangeKindUnmodified { + // Nothing to write to diff + return nil + } + + if capability, err := getxattr(source, "security.capability"); err != nil { + return errors.Wrap(err, "failed to get capabilities xattr") + } else if capability != nil { + if hdr.PAXRecords == nil { + hdr.PAXRecords = map[string]string{} + } + hdr.PAXRecords[paxSchilyXattr+"security.capability"] = string(capability) + } + + if err := cw.includeParents(hdr); err != nil { + return err + } + if err := cw.tw.WriteHeader(hdr); err != nil { + return errors.Wrap(err, "failed to write file header") + } + + if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 { + file, err := open(source) + if err != nil { + return errors.Wrapf(err, "failed to open path: %v", source) + } + defer file.Close() + + n, err := copyBuffered(context.TODO(), cw.tw, file) + if err != nil { + return errors.Wrap(err, "failed to copy") + } + if n != hdr.Size { + return errors.New("short write copying file") + } + } + + if additionalLinks != nil { + source = hdr.Name + for _, extra := range additionalLinks { + hdr.Name = extra + hdr.Typeflag = tar.TypeLink + hdr.Linkname = source + hdr.Size = 0 + + if err := cw.includeParents(hdr); err != nil { + return err + } + if err := cw.tw.WriteHeader(hdr); err != nil { + return errors.Wrap(err, "failed to write file header") + } + } + } + } + return nil +} + +func (cw *changeWriter) Close() error { + if err := cw.tw.Close(); err != nil { + return errors.Wrap(err, "failed to close tar writer") + } + return nil +} + +func (cw *changeWriter) includeParents(hdr *tar.Header) error { + name := strings.TrimRight(hdr.Name, "/") + fname := filepath.Join(cw.source, name) + parent := filepath.Dir(name) + pname := filepath.Join(cw.source, parent) + + // Do not include root directory as parent + if fname != cw.source && pname != cw.source { + _, ok := cw.addedDirs[parent] + if !ok { + cw.addedDirs[parent] = struct{}{} + fi, err := os.Stat(pname) + if err != nil { + return err + } + if err := cw.HandleChange(fs.ChangeKindModify, parent, fi, nil); err != nil { + return err + } + } + } + if hdr.Typeflag == tar.TypeDir { + cw.addedDirs[name] = struct{}{} + } + return nil +} + func copyBuffered(ctx context.Context, dst io.Writer, src io.Reader) (written int64, err error) { - buf := bufferPool.Get().(*[]byte) - defer bufferPool.Put(buf) + buf := bufPool.Get().(*[]byte) + defer bufPool.Put(buf) for { select { diff --git a/components/engine/vendor/github.com/containerd/containerd/archive/tar_opts.go b/components/engine/vendor/github.com/containerd/containerd/archive/tar_opts.go new file mode 100644 index 0000000000..b0f86abdff --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/archive/tar_opts.go @@ -0,0 +1,20 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package archive + +// ApplyOpt allows setting mutable archive apply properties on creation +type ApplyOpt func(options *ApplyOptions) error diff --git a/components/engine/vendor/github.com/containerd/containerd/archive/tar_opts_unix.go b/components/engine/vendor/github.com/containerd/containerd/archive/tar_opts_unix.go new file mode 100644 index 0000000000..e19afddd25 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/archive/tar_opts_unix.go @@ -0,0 +1,23 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package archive + +// ApplyOptions provides additional options for an Apply operation +type ApplyOptions struct { +} diff --git a/components/engine/vendor/github.com/containerd/containerd/archive/tar_opts_windows.go b/components/engine/vendor/github.com/containerd/containerd/archive/tar_opts_windows.go new file mode 100644 index 0000000000..0991ab0945 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/archive/tar_opts_windows.go @@ -0,0 +1,44 @@ +// +build windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package archive + +// ApplyOptions provides additional options for an Apply operation +type ApplyOptions struct { + ParentLayerPaths []string // Parent layer paths used for Windows layer apply + IsWindowsContainerLayer bool // True if the tar stream to be applied is a Windows Container Layer +} + +// WithParentLayers adds parent layers to the apply process this is required +// for all Windows layers except the base layer. +func WithParentLayers(parentPaths []string) ApplyOpt { + return func(options *ApplyOptions) error { + options.ParentLayerPaths = parentPaths + return nil + } +} + +// AsWindowsContainerLayer indicates that the tar stream to apply is that of +// a Windows Container Layer. The caller must be holding SeBackupPrivilege and +// SeRestorePrivilege. +func AsWindowsContainerLayer() ApplyOpt { + return func(options *ApplyOptions) error { + options.IsWindowsContainerLayer = true + return nil + } +} diff --git a/components/engine/vendor/github.com/containerd/containerd/archive/tar_unix.go b/components/engine/vendor/github.com/containerd/containerd/archive/tar_unix.go index 44b1069432..f577996ee0 100644 --- a/components/engine/vendor/github.com/containerd/containerd/archive/tar_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/archive/tar_unix.go @@ -1,8 +1,25 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package archive import ( + "context" "os" "sync" "syscall" @@ -28,11 +45,14 @@ func setHeaderForSpecialDevice(hdr *tar.Header, name string, fi os.FileInfo) err return errors.New("unsupported stat type") } + // Rdev is int32 on darwin/bsd, int64 on linux/solaris + rdev := uint64(s.Rdev) // nolint: unconvert + // Currently go does not fill in the major/minors if s.Mode&syscall.S_IFBLK != 0 || s.Mode&syscall.S_IFCHR != 0 { - hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) - hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) + hdr.Devmajor = int64(unix.Major(rdev)) + hdr.Devminor = int64(unix.Minor(rdev)) } return nil @@ -128,3 +148,9 @@ func getxattr(path, attr string) ([]byte, error) { func setxattr(path, key, value string) error { return sysx.LSetxattr(path, key, []byte(value), 0) } + +// apply applies a tar stream of an OCI style diff tar. +// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets +func apply(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { + return applyNaive(ctx, root, tr, options) +} diff --git a/components/engine/vendor/github.com/containerd/containerd/archive/tar_windows.go b/components/engine/vendor/github.com/containerd/containerd/archive/tar_windows.go index cb3b6c59a9..7172e735c0 100644 --- a/components/engine/vendor/github.com/containerd/containerd/archive/tar_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/archive/tar_windows.go @@ -1,15 +1,71 @@ +// +build windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package archive import ( + "bufio" + "context" + "encoding/base64" "errors" "fmt" + "io" "os" + "path" + "path/filepath" + "strconv" "strings" + "syscall" + "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim" + "github.com/containerd/containerd/log" "github.com/containerd/containerd/sys" "github.com/dmcgowan/go-tar" ) +const ( + // MSWINDOWS pax vendor extensions + hdrMSWindowsPrefix = "MSWINDOWS." + + hdrFileAttributes = hdrMSWindowsPrefix + "fileattr" + hdrSecurityDescriptor = hdrMSWindowsPrefix + "sd" + hdrRawSecurityDescriptor = hdrMSWindowsPrefix + "rawsd" + hdrMountPoint = hdrMSWindowsPrefix + "mountpoint" + hdrEaPrefix = hdrMSWindowsPrefix + "xattr." + + // LIBARCHIVE pax vendor extensions + hdrLibArchivePrefix = "LIBARCHIVE." + + hdrCreateTime = hdrLibArchivePrefix + "creationtime" +) + +var ( + // mutatedFiles is a list of files that are mutated by the import process + // and must be backed up and restored. + mutatedFiles = map[string]string{ + "UtilityVM/Files/EFI/Microsoft/Boot/BCD": "bcd.bak", + "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG": "bcd.log.bak", + "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak", + "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak", + } +) + // tarName returns platform-specific filepath // to canonical posix-style path for tar archival. p is relative // path. @@ -101,3 +157,290 @@ func setxattr(path, key, value string) error { // since xattrs should not exist in windows diff archives return errors.New("xattrs not supported on Windows") } + +// apply applies a tar stream of an OCI style diff tar of a Windows layer. +// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets +func apply(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { + if options.IsWindowsContainerLayer { + return applyWindowsLayer(ctx, root, tr, options) + } + return applyNaive(ctx, root, tr, options) +} + +// applyWindowsLayer applies a tar stream of an OCI style diff tar of a Windows layer. +// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets +func applyWindowsLayer(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { + home, id := filepath.Split(root) + info := hcsshim.DriverInfo{ + HomeDir: home, + } + + w, err := hcsshim.NewLayerWriter(info, id, options.ParentLayerPaths) + if err != nil { + return 0, err + } + defer func() { + if err := w.Close(); err != nil { + log.G(ctx).Errorf("failed to close layer writer: %v", err) + } + }() + + buf := bufio.NewWriter(nil) + hdr, nextErr := tr.Next() + // Iterate through the files in the archive. + for { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + } + + if nextErr == io.EOF { + // end of tar archive + break + } + if nextErr != nil { + return 0, nextErr + } + + // Note: path is used instead of filepath to prevent OS specific handling + // of the tar path + base := path.Base(hdr.Name) + if strings.HasPrefix(base, whiteoutPrefix) { + dir := path.Dir(hdr.Name) + originalBase := base[len(whiteoutPrefix):] + originalPath := path.Join(dir, originalBase) + if err := w.Remove(filepath.FromSlash(originalPath)); err != nil { + return 0, err + } + hdr, nextErr = tr.Next() + } else if hdr.Typeflag == tar.TypeLink { + err := w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname)) + if err != nil { + return 0, err + } + hdr, nextErr = tr.Next() + } else { + name, fileSize, fileInfo, err := fileInfoFromHeader(hdr) + if err != nil { + return 0, err + } + if err := w.Add(filepath.FromSlash(name), fileInfo); err != nil { + return 0, err + } + size += fileSize + hdr, nextErr = tarToBackupStreamWithMutatedFiles(buf, w, tr, hdr, root) + } + } + + return +} + +// fileInfoFromHeader retrieves basic Win32 file information from a tar header, using the additional metadata written by +// WriteTarFileFromBackupStream. +func fileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *winio.FileBasicInfo, err error) { + name = hdr.Name + if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA { + size = hdr.Size + } + fileInfo = &winio.FileBasicInfo{ + LastAccessTime: syscall.NsecToFiletime(hdr.AccessTime.UnixNano()), + LastWriteTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()), + ChangeTime: syscall.NsecToFiletime(hdr.ChangeTime.UnixNano()), + + // Default CreationTime to ModTime, updated below if MSWINDOWS.createtime exists + CreationTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()), + } + if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok { + attr, err := strconv.ParseUint(attrStr, 10, 32) + if err != nil { + return "", 0, nil, err + } + fileInfo.FileAttributes = uintptr(attr) + } else { + if hdr.Typeflag == tar.TypeDir { + fileInfo.FileAttributes |= syscall.FILE_ATTRIBUTE_DIRECTORY + } + } + if createStr, ok := hdr.PAXRecords[hdrCreateTime]; ok { + createTime, err := parsePAXTime(createStr) + if err != nil { + return "", 0, nil, err + } + fileInfo.CreationTime = syscall.NsecToFiletime(createTime.UnixNano()) + } + return +} + +// tarToBackupStreamWithMutatedFiles reads data from a tar stream and +// writes it to a backup stream, and also saves any files that will be mutated +// by the import layer process to a backup location. +func tarToBackupStreamWithMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Reader, hdr *tar.Header, root string) (nextHdr *tar.Header, err error) { + var ( + bcdBackup *os.File + bcdBackupWriter *winio.BackupFileWriter + ) + if backupPath, ok := mutatedFiles[hdr.Name]; ok { + bcdBackup, err = os.Create(filepath.Join(root, backupPath)) + if err != nil { + return nil, err + } + defer func() { + cerr := bcdBackup.Close() + if err == nil { + err = cerr + } + }() + + bcdBackupWriter = winio.NewBackupFileWriter(bcdBackup, false) + defer func() { + cerr := bcdBackupWriter.Close() + if err == nil { + err = cerr + } + }() + + buf.Reset(io.MultiWriter(w, bcdBackupWriter)) + } else { + buf.Reset(w) + } + + defer func() { + ferr := buf.Flush() + if err == nil { + err = ferr + } + }() + + return writeBackupStreamFromTarFile(buf, t, hdr) +} + +// writeBackupStreamFromTarFile writes a Win32 backup stream from the current tar file. Since this function may process multiple +// tar file entries in order to collect all the alternate data streams for the file, it returns the next +// tar file that was not processed, or io.EOF is there are no more. +func writeBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (*tar.Header, error) { + bw := winio.NewBackupStreamWriter(w) + var sd []byte + var err error + // Maintaining old SDDL-based behavior for backward compatibility. All new tar headers written + // by this library will have raw binary for the security descriptor. + if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok { + sd, err = winio.SddlToSecurityDescriptor(sddl) + if err != nil { + return nil, err + } + } + if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok { + sd, err = base64.StdEncoding.DecodeString(sdraw) + if err != nil { + return nil, err + } + } + if len(sd) != 0 { + bhdr := winio.BackupHeader{ + Id: winio.BackupSecurity, + Size: int64(len(sd)), + } + err := bw.WriteHeader(&bhdr) + if err != nil { + return nil, err + } + _, err = bw.Write(sd) + if err != nil { + return nil, err + } + } + var eas []winio.ExtendedAttribute + for k, v := range hdr.PAXRecords { + if !strings.HasPrefix(k, hdrEaPrefix) { + continue + } + data, err := base64.StdEncoding.DecodeString(v) + if err != nil { + return nil, err + } + eas = append(eas, winio.ExtendedAttribute{ + Name: k[len(hdrEaPrefix):], + Value: data, + }) + } + if len(eas) != 0 { + eadata, err := winio.EncodeExtendedAttributes(eas) + if err != nil { + return nil, err + } + bhdr := winio.BackupHeader{ + Id: winio.BackupEaData, + Size: int64(len(eadata)), + } + err = bw.WriteHeader(&bhdr) + if err != nil { + return nil, err + } + _, err = bw.Write(eadata) + if err != nil { + return nil, err + } + } + if hdr.Typeflag == tar.TypeSymlink { + _, isMountPoint := hdr.PAXRecords[hdrMountPoint] + rp := winio.ReparsePoint{ + Target: filepath.FromSlash(hdr.Linkname), + IsMountPoint: isMountPoint, + } + reparse := winio.EncodeReparsePoint(&rp) + bhdr := winio.BackupHeader{ + Id: winio.BackupReparseData, + Size: int64(len(reparse)), + } + err := bw.WriteHeader(&bhdr) + if err != nil { + return nil, err + } + _, err = bw.Write(reparse) + if err != nil { + return nil, err + } + } + + buf := bufPool.Get().(*[]byte) + defer bufPool.Put(buf) + + if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA { + bhdr := winio.BackupHeader{ + Id: winio.BackupData, + Size: hdr.Size, + } + err := bw.WriteHeader(&bhdr) + if err != nil { + return nil, err + } + _, err = io.CopyBuffer(bw, t, *buf) + if err != nil { + return nil, err + } + } + // Copy all the alternate data streams and return the next non-ADS header. + for { + ahdr, err := t.Next() + if err != nil { + return nil, err + } + if ahdr.Typeflag != tar.TypeReg || !strings.HasPrefix(ahdr.Name, hdr.Name+":") { + return ahdr, nil + } + bhdr := winio.BackupHeader{ + Id: winio.BackupAlternateData, + Size: ahdr.Size, + Name: ahdr.Name[len(hdr.Name):] + ":$DATA", + } + err = bw.WriteHeader(&bhdr) + if err != nil { + return nil, err + } + _, err = io.CopyBuffer(bw, t, *buf) + if err != nil { + return nil, err + } + } +} diff --git a/components/engine/vendor/github.com/containerd/containerd/archive/time.go b/components/engine/vendor/github.com/containerd/containerd/archive/time.go index 4e9ae95084..16651a4d05 100644 --- a/components/engine/vendor/github.com/containerd/containerd/archive/time.go +++ b/components/engine/vendor/github.com/containerd/containerd/archive/time.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package archive import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/archive/time_darwin.go b/components/engine/vendor/github.com/containerd/containerd/archive/time_darwin.go index 2ac517a91c..9c2b656b04 100644 --- a/components/engine/vendor/github.com/containerd/containerd/archive/time_darwin.go +++ b/components/engine/vendor/github.com/containerd/containerd/archive/time_darwin.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package archive import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/archive/time_unix.go b/components/engine/vendor/github.com/containerd/containerd/archive/time_unix.go index 054f06eba5..4a69cb7d0e 100644 --- a/components/engine/vendor/github.com/containerd/containerd/archive/time_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/archive/time_unix.go @@ -1,5 +1,21 @@ // +build linux freebsd solaris +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package archive import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/archive/time_windows.go b/components/engine/vendor/github.com/containerd/containerd/archive/time_windows.go index 0b18c348d2..71f397821a 100644 --- a/components/engine/vendor/github.com/containerd/containerd/archive/time_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/archive/time_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package archive import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/cio/io.go b/components/engine/vendor/github.com/containerd/containerd/cio/io.go index 1b4a4dc251..a49c11735b 100644 --- a/components/engine/vendor/github.com/containerd/containerd/cio/io.go +++ b/components/engine/vendor/github.com/containerd/containerd/cio/io.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package cio import ( @@ -6,8 +22,17 @@ import ( "io" "os" "sync" + + "github.com/containerd/containerd/defaults" ) +var bufPool = sync.Pool{ + New: func() interface{} { + buffer := make([]byte, 32<<10) + return &buffer + }, +} + // Config holds the IO configurations. type Config struct { // Terminal is true if one has been allocated @@ -68,6 +93,7 @@ type Streams struct { Stdout io.Writer Stderr io.Writer Terminal bool + FIFODir string } // Opt customize options for creating a Creator or Attach @@ -92,16 +118,25 @@ func WithStreams(stdin io.Reader, stdout, stderr io.Writer) Opt { } } +// WithFIFODir sets the fifo directory. +// e.g. "/run/containerd/fifo", "/run/users/1001/containerd/fifo" +func WithFIFODir(dir string) Opt { + return func(opt *Streams) { + opt.FIFODir = dir + } +} + // NewCreator returns an IO creator from the options func NewCreator(opts ...Opt) Creator { streams := &Streams{} for _, opt := range opts { opt(streams) } + if streams.FIFODir == "" { + streams.FIFODir = defaults.DefaultFIFODir + } return func(id string) (IO, error) { - // TODO: accept root as a param - root := "/run/containerd/fifo" - fifos, err := NewFIFOSetInDir(root, id, streams.Terminal) + fifos, err := NewFIFOSetInDir(streams.FIFODir, id, streams.Terminal) if err != nil { return nil, err } diff --git a/components/engine/vendor/github.com/containerd/containerd/cio/io_unix.go b/components/engine/vendor/github.com/containerd/containerd/cio/io_unix.go index 005fb0ce98..3ab2a30b0c 100644 --- a/components/engine/vendor/github.com/containerd/containerd/cio/io_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/cio/io_unix.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package cio import ( @@ -47,7 +63,10 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { if fifos.Stdin != "" { go func() { - io.Copy(pipes.Stdin, ioset.Stdin) + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + + io.CopyBuffer(pipes.Stdin, ioset.Stdin, *p) pipes.Stdin.Close() }() } @@ -55,7 +74,10 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { var wg = &sync.WaitGroup{} wg.Add(1) go func() { - io.Copy(ioset.Stdout, pipes.Stdout) + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + + io.CopyBuffer(ioset.Stdout, pipes.Stdout, *p) pipes.Stdout.Close() wg.Done() }() @@ -63,7 +85,10 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { if !fifos.Terminal { wg.Add(1) go func() { - io.Copy(ioset.Stderr, pipes.Stderr) + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + + io.CopyBuffer(ioset.Stderr, pipes.Stderr, *p) pipes.Stderr.Close() wg.Done() }() @@ -89,17 +114,24 @@ func openFifos(ctx context.Context, fifos *FIFOSet) (pipes, error) { if f.Stdin, err = fifo.OpenFifo(ctx, fifos.Stdin, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { return f, errors.Wrapf(err, "failed to open stdin fifo") } + defer func() { + if err != nil && f.Stdin != nil { + f.Stdin.Close() + } + }() } if fifos.Stdout != "" { if f.Stdout, err = fifo.OpenFifo(ctx, fifos.Stdout, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { - f.Stdin.Close() return f, errors.Wrapf(err, "failed to open stdout fifo") } + defer func() { + if err != nil && f.Stdout != nil { + f.Stdout.Close() + } + }() } if fifos.Stderr != "" { if f.Stderr, err = fifo.OpenFifo(ctx, fifos.Stderr, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { - f.Stdin.Close() - f.Stdout.Close() return f, errors.Wrapf(err, "failed to open stderr fifo") } } diff --git a/components/engine/vendor/github.com/containerd/containerd/cio/io_windows.go b/components/engine/vendor/github.com/containerd/containerd/cio/io_windows.go index 017c9a11f6..fa9532a3bd 100644 --- a/components/engine/vendor/github.com/containerd/containerd/cio/io_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/cio/io_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package cio import ( @@ -47,7 +63,11 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { log.L.WithError(err).Errorf("failed to accept stdin connection on %s", fifos.Stdin) return } - io.Copy(c, ioset.Stdin) + + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + + io.CopyBuffer(c, ioset.Stdin, *p) c.Close() l.Close() }() @@ -73,7 +93,11 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { log.L.WithError(err).Errorf("failed to accept stdout connection on %s", fifos.Stdout) return } - io.Copy(ioset.Stdout, c) + + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + + io.CopyBuffer(ioset.Stdout, c, *p) c.Close() l.Close() }() @@ -99,7 +123,11 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { log.L.WithError(err).Errorf("failed to accept stderr connection on %s", fifos.Stderr) return } - io.Copy(ioset.Stderr, c) + + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + + io.CopyBuffer(ioset.Stderr, c, *p) c.Close() l.Close() }() diff --git a/components/engine/vendor/github.com/containerd/containerd/client.go b/components/engine/vendor/github.com/containerd/containerd/client.go index 39547f589a..2ac256dd9d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/client.go +++ b/components/engine/vendor/github.com/containerd/containerd/client.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( @@ -7,8 +23,6 @@ import ( "net/http" "runtime" "strconv" - "strings" - "sync" "time" containersapi "github.com/containerd/containerd/api/services/containers/v1" @@ -24,7 +38,6 @@ import ( "github.com/containerd/containerd/containers" "github.com/containerd/containerd/content" "github.com/containerd/containerd/dialer" - "github.com/containerd/containerd/diff" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/namespaces" @@ -80,11 +93,22 @@ func New(address string, opts ...ClientOpt) (*Client, error) { grpc.WithStreamInterceptor(stream), ) } - conn, err := grpc.Dial(dialer.DialAddress(address), gopts...) - if err != nil { - return nil, errors.Wrapf(err, "failed to dial %q", address) + connector := func() (*grpc.ClientConn, error) { + conn, err := grpc.Dial(dialer.DialAddress(address), gopts...) + if err != nil { + return nil, errors.Wrapf(err, "failed to dial %q", address) + } + return conn, nil } - return NewWithConn(conn, opts...) + conn, err := connector() + if err != nil { + return nil, err + } + return &Client{ + conn: conn, + connector: connector, + runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS), + }, nil } // NewWithConn returns a new containerd client that is connected to the containerd @@ -99,8 +123,23 @@ func NewWithConn(conn *grpc.ClientConn, opts ...ClientOpt) (*Client, error) { // Client is the client to interact with containerd and its various services // using a uniform interface type Client struct { - conn *grpc.ClientConn - runtime string + conn *grpc.ClientConn + runtime string + connector func() (*grpc.ClientConn, error) +} + +// Reconnect re-establishes the GRPC connection to the containerd daemon +func (c *Client) Reconnect() error { + if c.connector == nil { + return errors.New("unable to reconnect to containerd, no connector available") + } + c.conn.Close() + conn, err := c.connector() + if err != nil { + return err + } + c.conn = conn + return nil } // IsServing returns true if the client can successfully connect to the @@ -222,11 +261,11 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image name, desc, err := pullCtx.Resolver.Resolve(ctx, ref) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "failed to resolve reference %q", ref) } fetcher, err := pullCtx.Resolver.Fetcher(ctx, name) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "failed to get fetcher for %q", name) } var ( @@ -237,10 +276,17 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image schema1Converter = schema1.NewConverter(store, fetcher) handler = images.Handlers(append(pullCtx.BaseHandlers, schema1Converter)...) } else { + // Get all the children for a descriptor + childrenHandler := images.ChildrenHandler(store) + // Set any children labels for that content + childrenHandler = images.SetChildrenLabels(store, childrenHandler) + // Filter the childen by the platform + childrenHandler = images.FilterPlatform(platforms.Default(), childrenHandler) + handler = images.Handlers(append(pullCtx.BaseHandlers, remotes.FetchHandler(store, fetcher), - images.ChildrenHandler(store, platforms.Default()))..., - ) + childrenHandler, + )...) } if err := images.Dispatch(ctx, handler, desc); err != nil { @@ -281,7 +327,7 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image } if pullCtx.Unpack { if err := img.Unpack(ctx, pullCtx.Snapshotter); err != nil { - return nil, err + errors.Wrapf(err, "failed to unpack image on snapshotter %s", pullCtx.Snapshotter) } } return img, nil @@ -301,51 +347,7 @@ func (c *Client) Push(ctx context.Context, ref string, desc ocispec.Descriptor, return err } - var m sync.Mutex - manifestStack := []ocispec.Descriptor{} - - filterHandler := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { - switch desc.MediaType { - case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest, - images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex: - m.Lock() - manifestStack = append(manifestStack, desc) - m.Unlock() - return nil, images.ErrStopHandler - default: - return nil, nil - } - }) - - cs := c.ContentStore() - pushHandler := remotes.PushHandler(cs, pusher) - - handlers := append(pushCtx.BaseHandlers, - images.ChildrenHandler(cs, platforms.Default()), - filterHandler, - pushHandler, - ) - - if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil { - return err - } - - // Iterate in reverse order as seen, parent always uploaded after child - for i := len(manifestStack) - 1; i >= 0; i-- { - _, err := pushHandler(ctx, manifestStack[i]) - if err != nil { - // TODO(estesp): until we have a more complete method for index push, we need to report - // missing dependencies in an index/manifest list by sensing the "400 Bad Request" - // as a marker for this problem - if (manifestStack[i].MediaType == ocispec.MediaTypeImageIndex || - manifestStack[i].MediaType == images.MediaTypeDockerSchema2ManifestList) && - errors.Cause(err) != nil && strings.Contains(errors.Cause(err).Error(), "400 Bad Request") { - return errors.Wrap(err, "manifest list/index references to blobs and/or manifests are missing in your target registry") - } - return err - } - } - return nil + return remotes.PushContent(ctx, pusher, desc, c.ContentStore(), pushCtx.BaseHandlers...) } // GetImage returns an existing image @@ -378,11 +380,11 @@ func (c *Client) ListImages(ctx context.Context, filters ...string) ([]Image, er // Subscribe to events that match one or more of the provided filters. // -// Callers should listen on both the envelope channel and errs channel. If the -// errs channel returns nil or an error, the subscriber should terminate. +// Callers should listen on both the envelope and errs channels. If the errs +// channel returns nil or an error, the subscriber should terminate. // -// To cancel shutdown reciept of events, cancel the provided context. The errs -// channel will be closed and return a nil error. +// The subscriber can stop receiving events by canceling the provided context. +// The errs channel will be closed and return a nil error. func (c *Client) Subscribe(ctx context.Context, filters ...string) (ch <-chan *eventsapi.Envelope, errs <-chan error) { var ( evq = make(chan *eventsapi.Envelope) @@ -458,7 +460,7 @@ func (c *Client) ImageService() images.Store { } // DiffService returns the underlying Differ -func (c *Client) DiffService() diff.Differ { +func (c *Client) DiffService() DiffService { return NewDiffServiceFromClient(diffapi.NewDiffClient(c.conn)) } diff --git a/components/engine/vendor/github.com/containerd/containerd/client_opts.go b/components/engine/vendor/github.com/containerd/containerd/client_opts.go index c1e93bae92..dfa02ee7be 100644 --- a/components/engine/vendor/github.com/containerd/containerd/client_opts.go +++ b/components/engine/vendor/github.com/containerd/containerd/client_opts.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/container.go b/components/engine/vendor/github.com/containerd/containerd/container.go index ad60c69eac..895e793ae7 100644 --- a/components/engine/vendor/github.com/containerd/containerd/container.go +++ b/components/engine/vendor/github.com/containerd/containerd/container.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/container_opts.go b/components/engine/vendor/github.com/containerd/containerd/container_opts.go index fb22a90963..4f586a4ecd 100644 --- a/components/engine/vendor/github.com/containerd/containerd/container_opts.go +++ b/components/engine/vendor/github.com/containerd/containerd/container_opts.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/container_opts_unix.go b/components/engine/vendor/github.com/containerd/containerd/container_opts_unix.go index b678033b7e..8ae9551132 100644 --- a/components/engine/vendor/github.com/containerd/containerd/container_opts_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/container_opts_unix.go @@ -1,12 +1,27 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( "context" "encoding/json" "fmt" - "io/ioutil" "os" "path/filepath" "syscall" @@ -16,6 +31,7 @@ import ( "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" + "github.com/containerd/containerd/linux/runctypes" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/platforms" "github.com/gogo/protobuf/proto" @@ -115,7 +131,7 @@ func WithTaskCheckpoint(im Image) NewTaskOpts { } } -func decodeIndex(ctx context.Context, store content.Store, id digest.Digest) (*v1.Index, error) { +func decodeIndex(ctx context.Context, store content.Provider, id digest.Digest) (*v1.Index, error) { var index v1.Index p, err := content.ReadBlob(ctx, store, id) if err != nil { @@ -166,7 +182,7 @@ func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool if err != nil { return err } - if err := remapRootFS(mounts, uid, gid); err != nil { + if err := remapRootFS(ctx, mounts, uid, gid); err != nil { snapshotter.Remove(ctx, usernsID) return err } @@ -187,22 +203,10 @@ func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool } } -func remapRootFS(mounts []mount.Mount, uid, gid uint32) error { - root, err := ioutil.TempDir("", "ctd-remap") - if err != nil { - return err - } - defer os.Remove(root) - for _, m := range mounts { - if err := m.Mount(root); err != nil { - return err - } - } - err = filepath.Walk(root, incrementFS(root, uid, gid)) - if uerr := mount.Unmount(root, 0); err == nil { - err = uerr - } - return err +func remapRootFS(ctx context.Context, mounts []mount.Mount, uid, gid uint32) error { + return mount.WithTempMount(ctx, mounts, func(root string) error { + return filepath.Walk(root, incrementFS(root, uid, gid)) + }) } func incrementFS(root string, uidInc, gidInc uint32) filepath.WalkFunc { @@ -218,3 +222,19 @@ func incrementFS(root string, uidInc, gidInc uint32) filepath.WalkFunc { return os.Lchown(path, u, g) } } + +// WithNoPivotRoot instructs the runtime not to you pivot_root +func WithNoPivotRoot(_ context.Context, _ *Client, info *TaskInfo) error { + if info.Options == nil { + info.Options = &runctypes.CreateOptions{ + NoPivotRoot: true, + } + return nil + } + copts, ok := info.Options.(*runctypes.CreateOptions) + if !ok { + return errors.New("invalid options type, expected runctypes.CreateOptions") + } + copts.NoPivotRoot = true + return nil +} diff --git a/components/engine/vendor/github.com/containerd/containerd/containers/containers.go b/components/engine/vendor/github.com/containerd/containerd/containers/containers.go index df4ad83c9e..c624164e80 100644 --- a/components/engine/vendor/github.com/containerd/containerd/containers/containers.go +++ b/components/engine/vendor/github.com/containerd/containerd/containers/containers.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containers import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/containerstore.go b/components/engine/vendor/github.com/containerd/containerd/containerstore.go index 4db2350b08..ee3d8c7278 100644 --- a/components/engine/vendor/github.com/containerd/containerd/containerstore.go +++ b/components/engine/vendor/github.com/containerd/containerd/containerstore.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/content/content.go b/components/engine/vendor/github.com/containerd/containerd/content/content.go index 05fd4aebe1..de9dd48f55 100644 --- a/components/engine/vendor/github.com/containerd/containerd/content/content.go +++ b/components/engine/vendor/github.com/containerd/containerd/content/content.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package content import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/content/helpers.go b/components/engine/vendor/github.com/containerd/containerd/content/helpers.go index 83c31d917e..ac0b5a3dbe 100644 --- a/components/engine/vendor/github.com/containerd/containerd/content/helpers.go +++ b/components/engine/vendor/github.com/containerd/containerd/content/helpers.go @@ -1,8 +1,25 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package content import ( "context" "io" + "io/ioutil" "sync" "github.com/containerd/containerd/errdefs" @@ -76,14 +93,7 @@ func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected dige if ws.Offset > 0 { r, err = seekReader(r, ws.Offset, size) if err != nil { - if !isUnseekable(err) { - return errors.Wrapf(err, "unable to resume write to %v", ws.Ref) - } - - // reader is unseekable, try to move the writer back to the start. - if err := cw.Truncate(0); err != nil { - return errors.Wrapf(err, "content writer truncate failed") - } + return errors.Wrapf(err, "unable to resume write to %v", ws.Ref) } } @@ -103,14 +113,9 @@ func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected dige return nil } -var errUnseekable = errors.New("seek not supported") - -func isUnseekable(err error) bool { - return errors.Cause(err) == errUnseekable -} - // seekReader attempts to seek the reader to the given offset, either by -// resolving `io.Seeker` or by detecting `io.ReaderAt`. +// resolving `io.Seeker`, by detecting `io.ReaderAt`, or discarding +// up to the given offset. func seekReader(r io.Reader, offset, size int64) (io.Reader, error) { // attempt to resolve r as a seeker and setup the offset. seeker, ok := r.(io.Seeker) @@ -134,5 +139,17 @@ func seekReader(r io.Reader, offset, size int64) (io.Reader, error) { return sr, nil } - return r, errors.Wrapf(errUnseekable, "seek to offset %v failed", offset) + // well then, let's just discard up to the offset + buf := bufPool.Get().(*[]byte) + defer bufPool.Put(buf) + + n, err := io.CopyBuffer(ioutil.Discard, io.LimitReader(r, offset), *buf) + if err != nil { + return nil, errors.Wrap(err, "failed to discard to offset") + } + if n != offset { + return nil, errors.Errorf("unable to discard to offset") + } + + return r, nil } diff --git a/components/engine/vendor/github.com/containerd/containerd/content/local/locks.go b/components/engine/vendor/github.com/containerd/containerd/content/local/locks.go index 9a6c62fd85..411c29a9d9 100644 --- a/components/engine/vendor/github.com/containerd/containerd/content/local/locks.go +++ b/components/engine/vendor/github.com/containerd/containerd/content/local/locks.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package local import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/content/local/readerat.go b/components/engine/vendor/github.com/containerd/containerd/content/local/readerat.go index ae1af5d8d4..42b99dc42b 100644 --- a/components/engine/vendor/github.com/containerd/containerd/content/local/readerat.go +++ b/components/engine/vendor/github.com/containerd/containerd/content/local/readerat.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package local import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/content/local/store.go b/components/engine/vendor/github.com/containerd/containerd/content/local/store.go index 9ff95de457..69437dfd19 100644 --- a/components/engine/vendor/github.com/containerd/containerd/content/local/store.go +++ b/components/engine/vendor/github.com/containerd/containerd/content/local/store.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package local import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/content/local/store_unix.go b/components/engine/vendor/github.com/containerd/containerd/content/local/store_unix.go index c0587e1b23..f5f34fd0cd 100644 --- a/components/engine/vendor/github.com/containerd/containerd/content/local/store_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/content/local/store_unix.go @@ -1,5 +1,21 @@ // +build linux solaris darwin freebsd +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package local import ( @@ -12,8 +28,7 @@ import ( func getATime(fi os.FileInfo) time.Time { if st, ok := fi.Sys().(*syscall.Stat_t); ok { - return time.Unix(int64(sys.StatAtime(st).Sec), - int64(sys.StatAtime(st).Nsec)) + return sys.StatATimeAsTime(st) } return fi.ModTime() diff --git a/components/engine/vendor/github.com/containerd/containerd/content/local/store_windows.go b/components/engine/vendor/github.com/containerd/containerd/content/local/store_windows.go index f745aafdbd..bce8499790 100644 --- a/components/engine/vendor/github.com/containerd/containerd/content/local/store_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/content/local/store_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package local import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/content/local/writer.go b/components/engine/vendor/github.com/containerd/containerd/content/local/writer.go index e6b4276b42..a6579a9d21 100644 --- a/components/engine/vendor/github.com/containerd/containerd/content/local/writer.go +++ b/components/engine/vendor/github.com/containerd/containerd/content/local/writer.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package local import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/content_reader.go b/components/engine/vendor/github.com/containerd/containerd/content_reader.go index 7acd2d30e8..72628e6ca3 100644 --- a/components/engine/vendor/github.com/containerd/containerd/content_reader.go +++ b/components/engine/vendor/github.com/containerd/containerd/content_reader.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/content_store.go b/components/engine/vendor/github.com/containerd/containerd/content_store.go index 1b539694d1..790249c2bf 100644 --- a/components/engine/vendor/github.com/containerd/containerd/content_store.go +++ b/components/engine/vendor/github.com/containerd/containerd/content_store.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/content_writer.go b/components/engine/vendor/github.com/containerd/containerd/content_writer.go index b18f3512a2..a4247daa09 100644 --- a/components/engine/vendor/github.com/containerd/containerd/content_writer.go +++ b/components/engine/vendor/github.com/containerd/containerd/content_writer.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/defaults/defaults_unix.go b/components/engine/vendor/github.com/containerd/containerd/defaults/defaults_unix.go new file mode 100644 index 0000000000..30ed42235e --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/defaults/defaults_unix.go @@ -0,0 +1,35 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package defaults + +const ( + // DefaultRootDir is the default location used by containerd to store + // persistent data + DefaultRootDir = "/var/lib/containerd" + // DefaultStateDir is the default location used by containerd to store + // transient data + DefaultStateDir = "/run/containerd" + // DefaultAddress is the default unix socket address + DefaultAddress = "/run/containerd/containerd.sock" + // DefaultDebugAddress is the default unix socket address for pprof data + DefaultDebugAddress = "/run/containerd/debug.sock" + // DefaultFIFODir is the default location used by client-side cio library + // to store FIFOs. + DefaultFIFODir = "/run/containerd/fifo" +) diff --git a/components/engine/vendor/github.com/containerd/containerd/defaults/defaults_windows.go b/components/engine/vendor/github.com/containerd/containerd/defaults/defaults_windows.go new file mode 100644 index 0000000000..983bf762f7 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/defaults/defaults_windows.go @@ -0,0 +1,43 @@ +// +build windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package defaults + +import ( + "os" + "path/filepath" +) + +var ( + // DefaultRootDir is the default location used by containerd to store + // persistent data + DefaultRootDir = filepath.Join(os.Getenv("programfiles"), "containerd", "root") + // DefaultStateDir is the default location used by containerd to store + // transient data + DefaultStateDir = filepath.Join(os.Getenv("programfiles"), "containerd", "state") +) + +const ( + // DefaultAddress is the default winpipe address + DefaultAddress = `\\.\pipe\containerd-containerd` + // DefaultDebugAddress is the default winpipe address for pprof data + DefaultDebugAddress = `\\.\pipe\containerd-debug` + // DefaultFIFODir is the default location used by client-side cio library + // to store FIFOs. Unused on Windows. + DefaultFIFODir = "" +) diff --git a/components/engine/vendor/github.com/containerd/containerd/defaults/doc.go b/components/engine/vendor/github.com/containerd/containerd/defaults/doc.go new file mode 100644 index 0000000000..274d504a38 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/defaults/doc.go @@ -0,0 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package defaults provides several common defaults for interacting wtih +// containerd. These can be used on the client-side or server-side. +package defaults diff --git a/components/engine/vendor/github.com/containerd/containerd/dialer/dialer.go b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer.go index 65af69f9bc..766d344934 100644 --- a/components/engine/vendor/github.com/containerd/containerd/dialer/dialer.go +++ b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package dialer import ( @@ -42,7 +58,7 @@ func Dialer(address string, timeout time.Duration) (net.Conn, error) { close(stopC) go func() { dr := <-synC - if dr != nil { + if dr != nil && dr.c != nil { dr.c.Close() } }() diff --git a/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_unix.go b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_unix.go index 7f8d43b031..e7d1958339 100644 --- a/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_unix.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package dialer import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_windows.go b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_windows.go index 2aac03898a..64d30dea0c 100644 --- a/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package dialer import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/diff.go b/components/engine/vendor/github.com/containerd/containerd/diff.go index 4e47efafc4..af95e03e7e 100644 --- a/components/engine/vendor/github.com/containerd/containerd/diff.go +++ b/components/engine/vendor/github.com/containerd/containerd/diff.go @@ -1,17 +1,40 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( + "context" + diffapi "github.com/containerd/containerd/api/services/diff/v1" "github.com/containerd/containerd/api/types" "github.com/containerd/containerd/diff" "github.com/containerd/containerd/mount" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "golang.org/x/net/context" ) +// DiffService handles the computation and application of diffs +type DiffService interface { + diff.Comparer + diff.Applier +} + // NewDiffServiceFromClient returns a new diff service which communicates // over a GRPC connection. -func NewDiffServiceFromClient(client diffapi.DiffClient) diff.Differ { +func NewDiffServiceFromClient(client diffapi.DiffClient) DiffService { return &diffRemote{ client: client, } @@ -33,7 +56,7 @@ func (r *diffRemote) Apply(ctx context.Context, diff ocispec.Descriptor, mounts return toDescriptor(resp.Applied), nil } -func (r *diffRemote) DiffMounts(ctx context.Context, a, b []mount.Mount, opts ...diff.Opt) (ocispec.Descriptor, error) { +func (r *diffRemote) Compare(ctx context.Context, a, b []mount.Mount, opts ...diff.Opt) (ocispec.Descriptor, error) { var config diff.Config for _, opt := range opts { if err := opt(&config); err != nil { diff --git a/components/engine/vendor/github.com/containerd/containerd/diff/diff.go b/components/engine/vendor/github.com/containerd/containerd/diff/diff.go index 85cef35835..2b6f01c74e 100644 --- a/components/engine/vendor/github.com/containerd/containerd/diff/diff.go +++ b/components/engine/vendor/github.com/containerd/containerd/diff/diff.go @@ -1,9 +1,26 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package diff import ( + "context" + "github.com/containerd/containerd/mount" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "golang.org/x/net/context" ) // Config is used to hold parameters needed for a diff operation @@ -24,21 +41,24 @@ type Config struct { // Opt is used to configure a diff operation type Opt func(*Config) error -// Differ allows the apply and creation of filesystem diffs between mounts -type Differ interface { +// Comparer allows creation of filesystem diffs between mounts +type Comparer interface { + // Compare computes the difference between two mounts and returns a + // descriptor for the computed diff. The options can provide + // a ref which can be used to track the content creation of the diff. + // The media type which is used to determine the format of the created + // content can also be provided as an option. + Compare(ctx context.Context, lower, upper []mount.Mount, opts ...Opt) (ocispec.Descriptor, error) +} + +// Applier allows applying diffs between mounts +type Applier interface { // Apply applies the content referred to by the given descriptor to // the provided mount. The method of applying is based on the // implementation and content descriptor. For example, in the common // case the descriptor is a file system difference in tar format, // that tar would be applied on top of the mounts. Apply(ctx context.Context, desc ocispec.Descriptor, mount []mount.Mount) (ocispec.Descriptor, error) - - // DiffMounts computes the difference between two mounts and returns a - // descriptor for the computed diff. The options can provide - // a ref which can be used to track the content creation of the diff. - // The media type which is used to determine the format of the created - // content can also be provided as an option. - DiffMounts(ctx context.Context, lower, upper []mount.Mount, opts ...Opt) (ocispec.Descriptor, error) } // WithMediaType sets the media type to use for creating the diff, without diff --git a/components/engine/vendor/github.com/containerd/containerd/errdefs/errors.go b/components/engine/vendor/github.com/containerd/containerd/errdefs/errors.go index b4d6ea860b..40427fc5a5 100644 --- a/components/engine/vendor/github.com/containerd/containerd/errdefs/errors.go +++ b/components/engine/vendor/github.com/containerd/containerd/errdefs/errors.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + // Package errdefs defines the common errors used throughout containerd // packages. // diff --git a/components/engine/vendor/github.com/containerd/containerd/errdefs/grpc.go b/components/engine/vendor/github.com/containerd/containerd/errdefs/grpc.go index 6a3bbcaa14..4eab03ab86 100644 --- a/components/engine/vendor/github.com/containerd/containerd/errdefs/grpc.go +++ b/components/engine/vendor/github.com/containerd/containerd/errdefs/grpc.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package errdefs import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/events/events.go b/components/engine/vendor/github.com/containerd/containerd/events/events.go index 2bfedb1e83..b7eb86f1eb 100644 --- a/components/engine/vendor/github.com/containerd/containerd/events/events.go +++ b/components/engine/vendor/github.com/containerd/containerd/events/events.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package events import ( @@ -26,9 +42,9 @@ func (e *Envelope) Field(fieldpath []string) (string, bool) { switch fieldpath[0] { // unhandled: timestamp case "namespace": - return string(e.Namespace), len(e.Namespace) > 0 + return e.Namespace, len(e.Namespace) > 0 case "topic": - return string(e.Topic), len(e.Topic) > 0 + return e.Topic, len(e.Topic) > 0 case "event": decoded, err := typeurl.UnmarshalAny(e.Event) if err != nil { diff --git a/components/engine/vendor/github.com/containerd/containerd/events/exchange/exchange.go b/components/engine/vendor/github.com/containerd/containerd/events/exchange/exchange.go index 3178fc407a..51c760f045 100644 --- a/components/engine/vendor/github.com/containerd/containerd/events/exchange/exchange.go +++ b/components/engine/vendor/github.com/containerd/containerd/events/exchange/exchange.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package exchange import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/filters/adaptor.go b/components/engine/vendor/github.com/containerd/containerd/filters/adaptor.go index 5a5ac7ec13..5a9c559c1e 100644 --- a/components/engine/vendor/github.com/containerd/containerd/filters/adaptor.go +++ b/components/engine/vendor/github.com/containerd/containerd/filters/adaptor.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package filters // Adaptor specifies the mapping of fieldpaths to a type. For the given field diff --git a/components/engine/vendor/github.com/containerd/containerd/filters/filter.go b/components/engine/vendor/github.com/containerd/containerd/filters/filter.go index 621755762d..30debadc96 100644 --- a/components/engine/vendor/github.com/containerd/containerd/filters/filter.go +++ b/components/engine/vendor/github.com/containerd/containerd/filters/filter.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + // Package filters defines a syntax and parser that can be used for the // filtration of items across the containerd API. The core is built on the // concept of protobuf field paths, with quoting. Several operators allow the diff --git a/components/engine/vendor/github.com/containerd/containerd/filters/parser.go b/components/engine/vendor/github.com/containerd/containerd/filters/parser.go index c765ea00c8..9dced523b1 100644 --- a/components/engine/vendor/github.com/containerd/containerd/filters/parser.go +++ b/components/engine/vendor/github.com/containerd/containerd/filters/parser.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package filters import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/filters/quote.go b/components/engine/vendor/github.com/containerd/containerd/filters/quote.go index 08698e1ba5..2d64e23a30 100644 --- a/components/engine/vendor/github.com/containerd/containerd/filters/quote.go +++ b/components/engine/vendor/github.com/containerd/containerd/filters/quote.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package filters import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/filters/scanner.go b/components/engine/vendor/github.com/containerd/containerd/filters/scanner.go index 3a8e72395c..c3961962cc 100644 --- a/components/engine/vendor/github.com/containerd/containerd/filters/scanner.go +++ b/components/engine/vendor/github.com/containerd/containerd/filters/scanner.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package filters import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/copy.go b/components/engine/vendor/github.com/containerd/containerd/fs/copy.go deleted file mode 100644 index e8f452819b..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/copy.go +++ /dev/null @@ -1,119 +0,0 @@ -package fs - -import ( - "io/ioutil" - "os" - "path/filepath" - "sync" - - "github.com/pkg/errors" -) - -var bufferPool = &sync.Pool{ - New: func() interface{} { - buffer := make([]byte, 32*1024) - return &buffer - }, -} - -// CopyDir copies the directory from src to dst. -// Most efficient copy of files is attempted. -func CopyDir(dst, src string) error { - inodes := map[uint64]string{} - return copyDirectory(dst, src, inodes) -} - -func copyDirectory(dst, src string, inodes map[uint64]string) error { - stat, err := os.Stat(src) - if err != nil { - return errors.Wrapf(err, "failed to stat %s", src) - } - if !stat.IsDir() { - return errors.Errorf("source is not directory") - } - - if st, err := os.Stat(dst); err != nil { - if err := os.Mkdir(dst, stat.Mode()); err != nil { - return errors.Wrapf(err, "failed to mkdir %s", dst) - } - } else if !st.IsDir() { - return errors.Errorf("cannot copy to non-directory: %s", dst) - } else { - if err := os.Chmod(dst, stat.Mode()); err != nil { - return errors.Wrapf(err, "failed to chmod on %s", dst) - } - } - - fis, err := ioutil.ReadDir(src) - if err != nil { - return errors.Wrapf(err, "failed to read %s", src) - } - - if err := copyFileInfo(stat, dst); err != nil { - return errors.Wrapf(err, "failed to copy file info for %s", dst) - } - - for _, fi := range fis { - source := filepath.Join(src, fi.Name()) - target := filepath.Join(dst, fi.Name()) - - switch { - case fi.IsDir(): - if err := copyDirectory(target, source, inodes); err != nil { - return err - } - continue - case (fi.Mode() & os.ModeType) == 0: - link, err := getLinkSource(target, fi, inodes) - if err != nil { - return errors.Wrap(err, "failed to get hardlink") - } - if link != "" { - if err := os.Link(link, target); err != nil { - return errors.Wrap(err, "failed to create hard link") - } - } else if err := copyFile(source, target); err != nil { - return errors.Wrap(err, "failed to copy files") - } - case (fi.Mode() & os.ModeSymlink) == os.ModeSymlink: - link, err := os.Readlink(source) - if err != nil { - return errors.Wrapf(err, "failed to read link: %s", source) - } - if err := os.Symlink(link, target); err != nil { - return errors.Wrapf(err, "failed to create symlink: %s", target) - } - case (fi.Mode() & os.ModeDevice) == os.ModeDevice: - if err := copyDevice(target, fi); err != nil { - return errors.Wrapf(err, "failed to create device") - } - default: - // TODO: Support pipes and sockets - return errors.Wrapf(err, "unsupported mode %s", fi.Mode()) - } - if err := copyFileInfo(fi, target); err != nil { - return errors.Wrap(err, "failed to copy file info") - } - - if err := copyXAttrs(target, source); err != nil { - return errors.Wrap(err, "failed to copy xattrs") - } - } - - return nil -} - -func copyFile(source, target string) error { - src, err := os.Open(source) - if err != nil { - return errors.Wrapf(err, "failed to open source %s", source) - } - defer src.Close() - tgt, err := os.Create(target) - if err != nil { - return errors.Wrapf(err, "failed to open target %s", target) - } - defer tgt.Close() - - return copyFileContent(tgt, src) -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/copy_linux.go b/components/engine/vendor/github.com/containerd/containerd/fs/copy_linux.go deleted file mode 100644 index c1fb2d1c40..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/copy_linux.go +++ /dev/null @@ -1,83 +0,0 @@ -package fs - -import ( - "io" - "os" - "syscall" - - "github.com/containerd/containerd/sys" - "github.com/containerd/continuity/sysx" - "github.com/pkg/errors" - "golang.org/x/sys/unix" -) - -func copyFileInfo(fi os.FileInfo, name string) error { - st := fi.Sys().(*syscall.Stat_t) - if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil { - return errors.Wrapf(err, "failed to chown %s", name) - } - - if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink { - if err := os.Chmod(name, fi.Mode()); err != nil { - return errors.Wrapf(err, "failed to chmod %s", name) - } - } - - timespec := []unix.Timespec{unix.Timespec(sys.StatAtime(st)), unix.Timespec(sys.StatMtime(st))} - if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil { - return errors.Wrapf(err, "failed to utime %s", name) - } - - return nil -} - -func copyFileContent(dst, src *os.File) error { - st, err := src.Stat() - if err != nil { - return errors.Wrap(err, "unable to stat source") - } - - n, err := unix.CopyFileRange(int(src.Fd()), nil, int(dst.Fd()), nil, int(st.Size()), 0) - if err != nil { - if err != unix.ENOSYS && err != unix.EXDEV { - return errors.Wrap(err, "copy file range failed") - } - - buf := bufferPool.Get().(*[]byte) - _, err = io.CopyBuffer(dst, src, *buf) - bufferPool.Put(buf) - return err - } - - if int64(n) != st.Size() { - return errors.Wrapf(err, "short copy: %d of %d", int64(n), st.Size()) - } - - return nil -} - -func copyXAttrs(dst, src string) error { - xattrKeys, err := sysx.LListxattr(src) - if err != nil { - return errors.Wrapf(err, "failed to list xattrs on %s", src) - } - for _, xattr := range xattrKeys { - data, err := sysx.LGetxattr(src, xattr) - if err != nil { - return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src) - } - if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil { - return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst) - } - } - - return nil -} - -func copyDevice(dst string, fi os.FileInfo) error { - st, ok := fi.Sys().(*syscall.Stat_t) - if !ok { - return errors.New("unsupported stat type") - } - return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev)) -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/copy_unix.go b/components/engine/vendor/github.com/containerd/containerd/fs/copy_unix.go deleted file mode 100644 index b31a14fcdb..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/copy_unix.go +++ /dev/null @@ -1,68 +0,0 @@ -// +build solaris darwin freebsd - -package fs - -import ( - "io" - "os" - "syscall" - - "github.com/containerd/containerd/sys" - "github.com/containerd/continuity/sysx" - "github.com/pkg/errors" - "golang.org/x/sys/unix" -) - -func copyFileInfo(fi os.FileInfo, name string) error { - st := fi.Sys().(*syscall.Stat_t) - if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil { - return errors.Wrapf(err, "failed to chown %s", name) - } - - if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink { - if err := os.Chmod(name, fi.Mode()); err != nil { - return errors.Wrapf(err, "failed to chmod %s", name) - } - } - - timespec := []syscall.Timespec{sys.StatAtime(st), sys.StatMtime(st)} - if err := syscall.UtimesNano(name, timespec); err != nil { - return errors.Wrapf(err, "failed to utime %s", name) - } - - return nil -} - -func copyFileContent(dst, src *os.File) error { - buf := bufferPool.Get().(*[]byte) - _, err := io.CopyBuffer(dst, src, *buf) - bufferPool.Put(buf) - - return err -} - -func copyXAttrs(dst, src string) error { - xattrKeys, err := sysx.LListxattr(src) - if err != nil { - return errors.Wrapf(err, "failed to list xattrs on %s", src) - } - for _, xattr := range xattrKeys { - data, err := sysx.LGetxattr(src, xattr) - if err != nil { - return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src) - } - if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil { - return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst) - } - } - - return nil -} - -func copyDevice(dst string, fi os.FileInfo) error { - st, ok := fi.Sys().(*syscall.Stat_t) - if !ok { - return errors.New("unsupported stat type") - } - return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev)) -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/copy_windows.go b/components/engine/vendor/github.com/containerd/containerd/fs/copy_windows.go deleted file mode 100644 index 6fb3de5710..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/copy_windows.go +++ /dev/null @@ -1,33 +0,0 @@ -package fs - -import ( - "io" - "os" - - "github.com/pkg/errors" -) - -func copyFileInfo(fi os.FileInfo, name string) error { - if err := os.Chmod(name, fi.Mode()); err != nil { - return errors.Wrapf(err, "failed to chmod %s", name) - } - - // TODO: copy windows specific metadata - - return nil -} - -func copyFileContent(dst, src *os.File) error { - buf := bufferPool.Get().(*[]byte) - _, err := io.CopyBuffer(dst, src, *buf) - bufferPool.Put(buf) - return err -} - -func copyXAttrs(dst, src string) error { - return nil -} - -func copyDevice(dst string, fi os.FileInfo) error { - return errors.New("device copy not supported") -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/diff.go b/components/engine/vendor/github.com/containerd/containerd/fs/diff.go deleted file mode 100644 index 3a53f42150..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/diff.go +++ /dev/null @@ -1,381 +0,0 @@ -package fs - -import ( - "context" - "os" - "path/filepath" - "strings" - - "golang.org/x/sync/errgroup" - - "github.com/sirupsen/logrus" -) - -// ChangeKind is the type of modification that -// a change is making. -type ChangeKind int - -const ( - // ChangeKindUnmodified represents an unmodified - // file - ChangeKindUnmodified = iota - - // ChangeKindAdd represents an addition of - // a file - ChangeKindAdd - - // ChangeKindModify represents a change to - // an existing file - ChangeKindModify - - // ChangeKindDelete represents a delete of - // a file - ChangeKindDelete -) - -func (k ChangeKind) String() string { - switch k { - case ChangeKindUnmodified: - return "unmodified" - case ChangeKindAdd: - return "add" - case ChangeKindModify: - return "modify" - case ChangeKindDelete: - return "delete" - default: - return "" - } -} - -// Change represents single change between a diff and its parent. -type Change struct { - Kind ChangeKind - Path string -} - -// ChangeFunc is the type of function called for each change -// computed during a directory changes calculation. -type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error - -// Changes computes changes between two directories calling the -// given change function for each computed change. The first -// directory is intended to the base directory and second -// directory the changed directory. -// -// The change callback is called by the order of path names and -// should be appliable in that order. -// Due to this apply ordering, the following is true -// - Removed directory trees only create a single change for the root -// directory removed. Remaining changes are implied. -// - A directory which is modified to become a file will not have -// delete entries for sub-path items, their removal is implied -// by the removal of the parent directory. -// -// Opaque directories will not be treated specially and each file -// removed from the base directory will show up as a removal. -// -// File content comparisons will be done on files which have timestamps -// which may have been truncated. If either of the files being compared -// has a zero value nanosecond value, each byte will be compared for -// differences. If 2 files have the same seconds value but different -// nanosecond values where one of those values is zero, the files will -// be considered unchanged if the content is the same. This behavior -// is to account for timestamp truncation during archiving. -func Changes(ctx context.Context, a, b string, changeFn ChangeFunc) error { - if a == "" { - logrus.Debugf("Using single walk diff for %s", b) - return addDirChanges(ctx, changeFn, b) - } else if diffOptions := detectDirDiff(b, a); diffOptions != nil { - logrus.Debugf("Using single walk diff for %s from %s", diffOptions.diffDir, a) - return diffDirChanges(ctx, changeFn, a, diffOptions) - } - - logrus.Debugf("Using double walk diff for %s from %s", b, a) - return doubleWalkDiff(ctx, changeFn, a, b) -} - -func addDirChanges(ctx context.Context, changeFn ChangeFunc, root string) error { - return filepath.Walk(root, func(path string, f os.FileInfo, err error) error { - if err != nil { - return err - } - - // Rebase path - path, err = filepath.Rel(root, path) - if err != nil { - return err - } - - path = filepath.Join(string(os.PathSeparator), path) - - // Skip root - if path == string(os.PathSeparator) { - return nil - } - - return changeFn(ChangeKindAdd, path, f, nil) - }) -} - -// diffDirOptions is used when the diff can be directly calculated from -// a diff directory to its base, without walking both trees. -type diffDirOptions struct { - diffDir string - skipChange func(string) (bool, error) - deleteChange func(string, string, os.FileInfo) (string, error) -} - -// diffDirChanges walks the diff directory and compares changes against the base. -func diffDirChanges(ctx context.Context, changeFn ChangeFunc, base string, o *diffDirOptions) error { - changedDirs := make(map[string]struct{}) - return filepath.Walk(o.diffDir, func(path string, f os.FileInfo, err error) error { - if err != nil { - return err - } - - // Rebase path - path, err = filepath.Rel(o.diffDir, path) - if err != nil { - return err - } - - path = filepath.Join(string(os.PathSeparator), path) - - // Skip root - if path == string(os.PathSeparator) { - return nil - } - - // TODO: handle opaqueness, start new double walker at this - // location to get deletes, and skip tree in single walker - - if o.skipChange != nil { - if skip, err := o.skipChange(path); skip { - return err - } - } - - var kind ChangeKind - - deletedFile, err := o.deleteChange(o.diffDir, path, f) - if err != nil { - return err - } - - // Find out what kind of modification happened - if deletedFile != "" { - path = deletedFile - kind = ChangeKindDelete - f = nil - } else { - // Otherwise, the file was added - kind = ChangeKindAdd - - // ...Unless it already existed in a base, in which case, it's a modification - stat, err := os.Stat(filepath.Join(base, path)) - if err != nil && !os.IsNotExist(err) { - return err - } - if err == nil { - // The file existed in the base, so that's a modification - - // However, if it's a directory, maybe it wasn't actually modified. - // If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar - if stat.IsDir() && f.IsDir() { - if f.Size() == stat.Size() && f.Mode() == stat.Mode() && sameFsTime(f.ModTime(), stat.ModTime()) { - // Both directories are the same, don't record the change - return nil - } - } - kind = ChangeKindModify - } - } - - // If /foo/bar/file.txt is modified, then /foo/bar must be part of the changed files. - // This block is here to ensure the change is recorded even if the - // modify time, mode and size of the parent directory in the rw and ro layers are all equal. - // Check https://github.com/docker/docker/pull/13590 for details. - if f.IsDir() { - changedDirs[path] = struct{}{} - } - if kind == ChangeKindAdd || kind == ChangeKindDelete { - parent := filepath.Dir(path) - if _, ok := changedDirs[parent]; !ok && parent != "/" { - pi, err := os.Stat(filepath.Join(o.diffDir, parent)) - if err := changeFn(ChangeKindModify, parent, pi, err); err != nil { - return err - } - changedDirs[parent] = struct{}{} - } - } - - return changeFn(kind, path, f, nil) - }) -} - -// doubleWalkDiff walks both directories to create a diff -func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err error) { - g, ctx := errgroup.WithContext(ctx) - - var ( - c1 = make(chan *currentPath) - c2 = make(chan *currentPath) - - f1, f2 *currentPath - rmdir string - lastEmittedDir = string(filepath.Separator) - parents []os.FileInfo - ) - g.Go(func() error { - defer close(c1) - return pathWalk(ctx, a, c1) - }) - g.Go(func() error { - defer close(c2) - return pathWalk(ctx, b, c2) - }) - g.Go(func() error { - for c1 != nil || c2 != nil { - if f1 == nil && c1 != nil { - f1, err = nextPath(ctx, c1) - if err != nil { - return err - } - if f1 == nil { - c1 = nil - } - } - - if f2 == nil && c2 != nil { - f2, err = nextPath(ctx, c2) - if err != nil { - return err - } - if f2 == nil { - c2 = nil - } - } - if f1 == nil && f2 == nil { - continue - } - - var ( - f os.FileInfo - emit = true - ) - k, p := pathChange(f1, f2) - switch k { - case ChangeKindAdd: - if rmdir != "" { - rmdir = "" - } - f = f2.f - f2 = nil - case ChangeKindDelete: - // Check if this file is already removed by being - // under of a removed directory - if rmdir != "" && strings.HasPrefix(f1.path, rmdir) { - f1 = nil - continue - } else if rmdir == "" && f1.f.IsDir() { - rmdir = f1.path + string(os.PathSeparator) - } else if rmdir != "" { - rmdir = "" - } - f1 = nil - case ChangeKindModify: - same, err := sameFile(f1, f2) - if err != nil { - return err - } - if f1.f.IsDir() && !f2.f.IsDir() { - rmdir = f1.path + string(os.PathSeparator) - } else if rmdir != "" { - rmdir = "" - } - f = f2.f - f1 = nil - f2 = nil - if same { - if !isLinked(f) { - emit = false - } - k = ChangeKindUnmodified - } - } - if emit { - emittedDir, emitParents := commonParents(lastEmittedDir, p, parents) - for _, pf := range emitParents { - p := filepath.Join(emittedDir, pf.Name()) - if err := changeFn(ChangeKindUnmodified, p, pf, nil); err != nil { - return err - } - emittedDir = p - } - - if err := changeFn(k, p, f, nil); err != nil { - return err - } - - if f != nil && f.IsDir() { - lastEmittedDir = p - } else { - lastEmittedDir = emittedDir - } - - parents = parents[:0] - } else if f.IsDir() { - lastEmittedDir, parents = commonParents(lastEmittedDir, p, parents) - parents = append(parents, f) - } - } - return nil - }) - - return g.Wait() -} - -func commonParents(base, updated string, dirs []os.FileInfo) (string, []os.FileInfo) { - if basePrefix := makePrefix(base); strings.HasPrefix(updated, basePrefix) { - var ( - parents []os.FileInfo - last = base - ) - for _, d := range dirs { - next := filepath.Join(last, d.Name()) - if strings.HasPrefix(updated, makePrefix(last)) { - parents = append(parents, d) - last = next - } else { - break - } - } - return base, parents - } - - baseS := strings.Split(base, string(filepath.Separator)) - updatedS := strings.Split(updated, string(filepath.Separator)) - commonS := []string{string(filepath.Separator)} - - min := len(baseS) - if len(updatedS) < min { - min = len(updatedS) - } - for i := 0; i < min; i++ { - if baseS[i] == updatedS[i] { - commonS = append(commonS, baseS[i]) - } else { - break - } - } - - return filepath.Join(commonS...), []os.FileInfo{} -} - -func makePrefix(d string) string { - if d == "" || d[len(d)-1] != filepath.Separator { - return d + string(filepath.Separator) - } - return d -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/diff_unix.go b/components/engine/vendor/github.com/containerd/containerd/fs/diff_unix.go deleted file mode 100644 index 3751814443..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/diff_unix.go +++ /dev/null @@ -1,58 +0,0 @@ -// +build !windows - -package fs - -import ( - "bytes" - "os" - "syscall" - - "github.com/containerd/continuity/sysx" - "github.com/pkg/errors" -) - -// detectDirDiff returns diff dir options if a directory could -// be found in the mount info for upper which is the direct -// diff with the provided lower directory -func detectDirDiff(upper, lower string) *diffDirOptions { - // TODO: get mount options for upper - // TODO: detect AUFS - // TODO: detect overlay - return nil -} - -// compareSysStat returns whether the stats are equivalent, -// whether the files are considered the same file, and -// an error -func compareSysStat(s1, s2 interface{}) (bool, error) { - ls1, ok := s1.(*syscall.Stat_t) - if !ok { - return false, nil - } - ls2, ok := s2.(*syscall.Stat_t) - if !ok { - return false, nil - } - - return ls1.Mode == ls2.Mode && ls1.Uid == ls2.Uid && ls1.Gid == ls2.Gid && ls1.Rdev == ls2.Rdev, nil -} - -func compareCapabilities(p1, p2 string) (bool, error) { - c1, err := sysx.LGetxattr(p1, "security.capability") - if err != nil && err != sysx.ENODATA { - return false, errors.Wrapf(err, "failed to get xattr for %s", p1) - } - c2, err := sysx.LGetxattr(p2, "security.capability") - if err != nil && err != sysx.ENODATA { - return false, errors.Wrapf(err, "failed to get xattr for %s", p2) - } - return bytes.Equal(c1, c2), nil -} - -func isLinked(f os.FileInfo) bool { - s, ok := f.Sys().(*syscall.Stat_t) - if !ok { - return false - } - return !f.IsDir() && s.Nlink > 1 -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/diff_windows.go b/components/engine/vendor/github.com/containerd/containerd/fs/diff_windows.go deleted file mode 100644 index 8eed36507e..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/diff_windows.go +++ /dev/null @@ -1,32 +0,0 @@ -package fs - -import ( - "os" - - "golang.org/x/sys/windows" -) - -func detectDirDiff(upper, lower string) *diffDirOptions { - return nil -} - -func compareSysStat(s1, s2 interface{}) (bool, error) { - f1, ok := s1.(windows.Win32FileAttributeData) - if !ok { - return false, nil - } - f2, ok := s2.(windows.Win32FileAttributeData) - if !ok { - return false, nil - } - return f1.FileAttributes == f2.FileAttributes, nil -} - -func compareCapabilities(p1, p2 string) (bool, error) { - // TODO: Use windows equivalent - return true, nil -} - -func isLinked(os.FileInfo) bool { - return false -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/dtype_linux.go b/components/engine/vendor/github.com/containerd/containerd/fs/dtype_linux.go deleted file mode 100644 index cc06573f1b..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/dtype_linux.go +++ /dev/null @@ -1,87 +0,0 @@ -// +build linux - -package fs - -import ( - "fmt" - "io/ioutil" - "os" - "syscall" - "unsafe" -) - -func locateDummyIfEmpty(path string) (string, error) { - children, err := ioutil.ReadDir(path) - if err != nil { - return "", err - } - if len(children) != 0 { - return "", nil - } - dummyFile, err := ioutil.TempFile(path, "fsutils-dummy") - if err != nil { - return "", err - } - name := dummyFile.Name() - err = dummyFile.Close() - return name, err -} - -// SupportsDType returns whether the filesystem mounted on path supports d_type -func SupportsDType(path string) (bool, error) { - // locate dummy so that we have at least one dirent - dummy, err := locateDummyIfEmpty(path) - if err != nil { - return false, err - } - if dummy != "" { - defer os.Remove(dummy) - } - - visited := 0 - supportsDType := true - fn := func(ent *syscall.Dirent) bool { - visited++ - if ent.Type == syscall.DT_UNKNOWN { - supportsDType = false - // stop iteration - return true - } - // continue iteration - return false - } - if err = iterateReadDir(path, fn); err != nil { - return false, err - } - if visited == 0 { - return false, fmt.Errorf("did not hit any dirent during iteration %s", path) - } - return supportsDType, nil -} - -func iterateReadDir(path string, fn func(*syscall.Dirent) bool) error { - d, err := os.Open(path) - if err != nil { - return err - } - defer d.Close() - fd := int(d.Fd()) - buf := make([]byte, 4096) - for { - nbytes, err := syscall.ReadDirent(fd, buf) - if err != nil { - return err - } - if nbytes == 0 { - break - } - for off := 0; off < nbytes; { - ent := (*syscall.Dirent)(unsafe.Pointer(&buf[off])) - if stop := fn(ent); stop { - return nil - } - off += int(ent.Reclen) - } - } - return nil -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/du.go b/components/engine/vendor/github.com/containerd/containerd/fs/du.go deleted file mode 100644 index 26f5333154..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/du.go +++ /dev/null @@ -1,22 +0,0 @@ -package fs - -import "context" - -// Usage of disk information -type Usage struct { - Inodes int64 - Size int64 -} - -// DiskUsage counts the number of inodes and disk usage for the resources under -// path. -func DiskUsage(roots ...string) (Usage, error) { - return diskUsage(roots...) -} - -// DiffUsage counts the numbers of inodes and disk usage in the -// diff between the 2 directories. The first path is intended -// as the base directory and the second as the changed directory. -func DiffUsage(ctx context.Context, a, b string) (Usage, error) { - return diffUsage(ctx, a, b) -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/du_unix.go b/components/engine/vendor/github.com/containerd/containerd/fs/du_unix.go deleted file mode 100644 index 6328e80f3a..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/du_unix.go +++ /dev/null @@ -1,83 +0,0 @@ -// +build !windows - -package fs - -import ( - "context" - "os" - "path/filepath" - "syscall" -) - -type inode struct { - // TODO(stevvooe): Can probably reduce memory usage by not tracking - // device, but we can leave this right for now. - dev, ino uint64 -} - -func diskUsage(roots ...string) (Usage, error) { - - var ( - size int64 - inodes = map[inode]struct{}{} // expensive! - ) - - for _, root := range roots { - if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { - if err != nil { - return err - } - - stat := fi.Sys().(*syscall.Stat_t) - - inoKey := inode{dev: uint64(stat.Dev), ino: uint64(stat.Ino)} - if _, ok := inodes[inoKey]; !ok { - inodes[inoKey] = struct{}{} - size += fi.Size() - } - - return nil - }); err != nil { - return Usage{}, err - } - } - - return Usage{ - Inodes: int64(len(inodes)), - Size: size, - }, nil -} - -func diffUsage(ctx context.Context, a, b string) (Usage, error) { - var ( - size int64 - inodes = map[inode]struct{}{} // expensive! - ) - - if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error { - if err != nil { - return err - } - - if kind == ChangeKindAdd || kind == ChangeKindModify { - stat := fi.Sys().(*syscall.Stat_t) - - inoKey := inode{dev: uint64(stat.Dev), ino: uint64(stat.Ino)} - if _, ok := inodes[inoKey]; !ok { - inodes[inoKey] = struct{}{} - size += fi.Size() - } - - return nil - - } - return nil - }); err != nil { - return Usage{}, err - } - - return Usage{ - Inodes: int64(len(inodes)), - Size: size, - }, nil -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/du_windows.go b/components/engine/vendor/github.com/containerd/containerd/fs/du_windows.go deleted file mode 100644 index 3f852fc15e..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/du_windows.go +++ /dev/null @@ -1,60 +0,0 @@ -// +build windows - -package fs - -import ( - "context" - "os" - "path/filepath" -) - -func diskUsage(roots ...string) (Usage, error) { - var ( - size int64 - ) - - // TODO(stevvooe): Support inodes (or equivalent) for windows. - - for _, root := range roots { - if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { - if err != nil { - return err - } - - size += fi.Size() - return nil - }); err != nil { - return Usage{}, err - } - } - - return Usage{ - Size: size, - }, nil -} - -func diffUsage(ctx context.Context, a, b string) (Usage, error) { - var ( - size int64 - ) - - if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error { - if err != nil { - return err - } - - if kind == ChangeKindAdd || kind == ChangeKindModify { - size += fi.Size() - - return nil - - } - return nil - }); err != nil { - return Usage{}, err - } - - return Usage{ - Size: size, - }, nil -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/hardlink.go b/components/engine/vendor/github.com/containerd/containerd/fs/hardlink.go deleted file mode 100644 index 38da93813c..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/hardlink.go +++ /dev/null @@ -1,27 +0,0 @@ -package fs - -import "os" - -// GetLinkInfo returns an identifier representing the node a hardlink is pointing -// to. If the file is not hard linked then 0 will be returned. -func GetLinkInfo(fi os.FileInfo) (uint64, bool) { - return getLinkInfo(fi) -} - -// getLinkSource returns a path for the given name and -// file info to its link source in the provided inode -// map. If the given file name is not in the map and -// has other links, it is added to the inode map -// to be a source for other link locations. -func getLinkSource(name string, fi os.FileInfo, inodes map[uint64]string) (string, error) { - inode, isHardlink := getLinkInfo(fi) - if !isHardlink { - return "", nil - } - - path, ok := inodes[inode] - if !ok { - inodes[inode] = name - } - return path, nil -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/hardlink_unix.go b/components/engine/vendor/github.com/containerd/containerd/fs/hardlink_unix.go deleted file mode 100644 index 3b825c940b..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/hardlink_unix.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build !windows - -package fs - -import ( - "os" - "syscall" -) - -func getLinkInfo(fi os.FileInfo) (uint64, bool) { - s, ok := fi.Sys().(*syscall.Stat_t) - if !ok { - return 0, false - } - - return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/hardlink_windows.go b/components/engine/vendor/github.com/containerd/containerd/fs/hardlink_windows.go deleted file mode 100644 index ad8845a7fb..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/hardlink_windows.go +++ /dev/null @@ -1,7 +0,0 @@ -package fs - -import "os" - -func getLinkInfo(fi os.FileInfo) (uint64, bool) { - return 0, false -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/path.go b/components/engine/vendor/github.com/containerd/containerd/fs/path.go deleted file mode 100644 index 412da67115..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/path.go +++ /dev/null @@ -1,276 +0,0 @@ -package fs - -import ( - "bytes" - "context" - "io" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" -) - -var ( - errTooManyLinks = errors.New("too many links") -) - -type currentPath struct { - path string - f os.FileInfo - fullPath string -} - -func pathChange(lower, upper *currentPath) (ChangeKind, string) { - if lower == nil { - if upper == nil { - panic("cannot compare nil paths") - } - return ChangeKindAdd, upper.path - } - if upper == nil { - return ChangeKindDelete, lower.path - } - // TODO: compare by directory - - switch i := strings.Compare(lower.path, upper.path); { - case i < 0: - // File in lower that is not in upper - return ChangeKindDelete, lower.path - case i > 0: - // File in upper that is not in lower - return ChangeKindAdd, upper.path - default: - return ChangeKindModify, upper.path - } -} - -func sameFile(f1, f2 *currentPath) (bool, error) { - if os.SameFile(f1.f, f2.f) { - return true, nil - } - - equalStat, err := compareSysStat(f1.f.Sys(), f2.f.Sys()) - if err != nil || !equalStat { - return equalStat, err - } - - if eq, err := compareCapabilities(f1.fullPath, f2.fullPath); err != nil || !eq { - return eq, err - } - - // If not a directory also check size, modtime, and content - if !f1.f.IsDir() { - if f1.f.Size() != f2.f.Size() { - return false, nil - } - t1 := f1.f.ModTime() - t2 := f2.f.ModTime() - - if t1.Unix() != t2.Unix() { - return false, nil - } - - // If the timestamp may have been truncated in one of the - // files, check content of file to determine difference - if t1.Nanosecond() == 0 || t2.Nanosecond() == 0 { - var eq bool - if (f1.f.Mode() & os.ModeSymlink) == os.ModeSymlink { - eq, err = compareSymlinkTarget(f1.fullPath, f2.fullPath) - } else if f1.f.Size() > 0 { - eq, err = compareFileContent(f1.fullPath, f2.fullPath) - } - if err != nil || !eq { - return eq, err - } - } else if t1.Nanosecond() != t2.Nanosecond() { - return false, nil - } - } - - return true, nil -} - -func compareSymlinkTarget(p1, p2 string) (bool, error) { - t1, err := os.Readlink(p1) - if err != nil { - return false, err - } - t2, err := os.Readlink(p2) - if err != nil { - return false, err - } - return t1 == t2, nil -} - -const compareChuckSize = 32 * 1024 - -// compareFileContent compares the content of 2 same sized files -// by comparing each byte. -func compareFileContent(p1, p2 string) (bool, error) { - f1, err := os.Open(p1) - if err != nil { - return false, err - } - defer f1.Close() - f2, err := os.Open(p2) - if err != nil { - return false, err - } - defer f2.Close() - - b1 := make([]byte, compareChuckSize) - b2 := make([]byte, compareChuckSize) - for { - n1, err1 := f1.Read(b1) - if err1 != nil && err1 != io.EOF { - return false, err1 - } - n2, err2 := f2.Read(b2) - if err2 != nil && err2 != io.EOF { - return false, err2 - } - if n1 != n2 || !bytes.Equal(b1[:n1], b2[:n2]) { - return false, nil - } - if err1 == io.EOF && err2 == io.EOF { - return true, nil - } - } -} - -func pathWalk(ctx context.Context, root string, pathC chan<- *currentPath) error { - return filepath.Walk(root, func(path string, f os.FileInfo, err error) error { - if err != nil { - return err - } - - // Rebase path - path, err = filepath.Rel(root, path) - if err != nil { - return err - } - - path = filepath.Join(string(os.PathSeparator), path) - - // Skip root - if path == string(os.PathSeparator) { - return nil - } - - p := ¤tPath{ - path: path, - f: f, - fullPath: filepath.Join(root, path), - } - - select { - case <-ctx.Done(): - return ctx.Err() - case pathC <- p: - return nil - } - }) -} - -func nextPath(ctx context.Context, pathC <-chan *currentPath) (*currentPath, error) { - select { - case <-ctx.Done(): - return nil, ctx.Err() - case p := <-pathC: - return p, nil - } -} - -// RootPath joins a path with a root, evaluating and bounding any -// symlink to the root directory. -func RootPath(root, path string) (string, error) { - if path == "" { - return root, nil - } - var linksWalked int // to protect against cycles - for { - i := linksWalked - newpath, err := walkLinks(root, path, &linksWalked) - if err != nil { - return "", err - } - path = newpath - if i == linksWalked { - newpath = filepath.Join("/", newpath) - if path == newpath { - return filepath.Join(root, newpath), nil - } - path = newpath - } - } -} - -func walkLink(root, path string, linksWalked *int) (newpath string, islink bool, err error) { - if *linksWalked > 255 { - return "", false, errTooManyLinks - } - - path = filepath.Join("/", path) - if path == "/" { - return path, false, nil - } - realPath := filepath.Join(root, path) - - fi, err := os.Lstat(realPath) - if err != nil { - // If path does not yet exist, treat as non-symlink - if os.IsNotExist(err) { - return path, false, nil - } - return "", false, err - } - if fi.Mode()&os.ModeSymlink == 0 { - return path, false, nil - } - newpath, err = os.Readlink(realPath) - if err != nil { - return "", false, err - } - if filepath.IsAbs(newpath) && strings.HasPrefix(newpath, root) { - newpath = newpath[:len(root)] - if !strings.HasPrefix(newpath, "/") { - newpath = "/" + newpath - } - } - *linksWalked++ - return newpath, true, nil -} - -func walkLinks(root, path string, linksWalked *int) (string, error) { - switch dir, file := filepath.Split(path); { - case dir == "": - newpath, _, err := walkLink(root, file, linksWalked) - return newpath, err - case file == "": - if os.IsPathSeparator(dir[len(dir)-1]) { - if dir == "/" { - return dir, nil - } - return walkLinks(root, dir[:len(dir)-1], linksWalked) - } - newpath, _, err := walkLink(root, dir, linksWalked) - return newpath, err - default: - newdir, err := walkLinks(root, dir, linksWalked) - if err != nil { - return "", err - } - newpath, islink, err := walkLink(root, filepath.Join(newdir, file), linksWalked) - if err != nil { - return "", err - } - if !islink { - return newpath, nil - } - if filepath.IsAbs(newpath) { - return newpath, nil - } - return filepath.Join(newdir, newpath), nil - } -} diff --git a/components/engine/vendor/github.com/containerd/containerd/fs/time.go b/components/engine/vendor/github.com/containerd/containerd/fs/time.go deleted file mode 100644 index c336f4d881..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/fs/time.go +++ /dev/null @@ -1,13 +0,0 @@ -package fs - -import "time" - -// Gnu tar and the go tar writer don't have sub-second mtime -// precision, which is problematic when we apply changes via tar -// files, we handle this by comparing for exact times, *or* same -// second count and either a or b having exactly 0 nanoseconds -func sameFsTime(a, b time.Time) bool { - return a == b || - (a.Unix() == b.Unix() && - (a.Nanosecond() == 0 || b.Nanosecond() == 0)) -} diff --git a/components/engine/vendor/github.com/containerd/containerd/gc/gc.go b/components/engine/vendor/github.com/containerd/containerd/gc/gc.go index 66898c5deb..35a1712cb3 100644 --- a/components/engine/vendor/github.com/containerd/containerd/gc/gc.go +++ b/components/engine/vendor/github.com/containerd/containerd/gc/gc.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + // Package gc experiments with providing central gc tooling to ensure // deterministic resource removal within containerd. // @@ -8,6 +24,7 @@ package gc import ( "context" "sync" + "time" ) // ResourceType represents type of resource at a node @@ -21,6 +38,11 @@ type Node struct { Key string } +// Stats about a garbage collection run +type Stats interface { + Elapsed() time.Duration +} + // Tricolor implements basic, single-thread tri-color GC. Given the roots, the // complete set and a refs function, this function returns a map of all // reachable objects. diff --git a/components/engine/vendor/github.com/containerd/containerd/grpc.go b/components/engine/vendor/github.com/containerd/containerd/grpc.go index c56eeada34..05fd5cca24 100644 --- a/components/engine/vendor/github.com/containerd/containerd/grpc.go +++ b/components/engine/vendor/github.com/containerd/containerd/grpc.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/identifiers/validate.go b/components/engine/vendor/github.com/containerd/containerd/identifiers/validate.go index 37f9b72ffa..c58513c025 100644 --- a/components/engine/vendor/github.com/containerd/containerd/identifiers/validate.go +++ b/components/engine/vendor/github.com/containerd/containerd/identifiers/validate.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + // Package identifiers provides common validation for identifiers and keys // across containerd. // diff --git a/components/engine/vendor/github.com/containerd/containerd/image.go b/components/engine/vendor/github.com/containerd/containerd/image.go index 6e9f4bd19d..5ae6db2323 100644 --- a/components/engine/vendor/github.com/containerd/containerd/image.go +++ b/components/engine/vendor/github.com/containerd/containerd/image.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/image_store.go b/components/engine/vendor/github.com/containerd/containerd/image_store.go index 9a3aafc84f..3676bdadad 100644 --- a/components/engine/vendor/github.com/containerd/containerd/image_store.go +++ b/components/engine/vendor/github.com/containerd/containerd/image_store.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/images/handlers.go b/components/engine/vendor/github.com/containerd/containerd/images/handlers.go index 63acdb7220..b95251c576 100644 --- a/components/engine/vendor/github.com/containerd/containerd/images/handlers.go +++ b/components/engine/vendor/github.com/containerd/containerd/images/handlers.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package images import ( @@ -5,6 +21,7 @@ import ( "fmt" "github.com/containerd/containerd/content" + "github.com/containerd/containerd/platforms" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "golang.org/x/sync/errgroup" @@ -128,8 +145,78 @@ func Dispatch(ctx context.Context, handler Handler, descs ...ocispec.Descriptor) // // One can also replace this with another implementation to allow descending of // arbitrary types. -func ChildrenHandler(provider content.Provider, platform string) HandlerFunc { +func ChildrenHandler(provider content.Provider) HandlerFunc { return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { - return Children(ctx, provider, desc, platform) + return Children(ctx, provider, desc) + } +} + +// SetChildrenLabels is a handler wrapper which sets labels for the content on +// the children returned by the handler and passes through the children. +// Must follow a handler that returns the children to be labeled. +func SetChildrenLabels(manager content.Manager, f HandlerFunc) HandlerFunc { + return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + children, err := f(ctx, desc) + if err != nil { + return children, err + } + + if len(children) > 0 { + info := content.Info{ + Digest: desc.Digest, + Labels: map[string]string{}, + } + fields := []string{} + for i, ch := range children { + info.Labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = ch.Digest.String() + fields = append(fields, fmt.Sprintf("labels.containerd.io/gc.ref.content.%d", i)) + } + + _, err := manager.Update(ctx, info, fields...) + if err != nil { + return nil, err + } + } + + return children, err + } +} + +// FilterPlatform is a handler wrapper which limits the descriptors returned +// by a handler to a single platform. +func FilterPlatform(platform string, f HandlerFunc) HandlerFunc { + return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + children, err := f(ctx, desc) + if err != nil { + return children, err + } + + var descs []ocispec.Descriptor + if platform != "" && isMultiPlatform(desc.MediaType) { + matcher, err := platforms.Parse(platform) + if err != nil { + return nil, err + } + + for _, d := range children { + if d.Platform == nil || matcher.Match(*d.Platform) { + descs = append(descs, d) + } + } + } else { + descs = children + } + + return descs, nil + } + +} + +func isMultiPlatform(mediaType string) bool { + switch mediaType { + case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex: + return true + default: + return false } } diff --git a/components/engine/vendor/github.com/containerd/containerd/images/image.go b/components/engine/vendor/github.com/containerd/containerd/images/image.go index 7b4215faf6..cdcf0af341 100644 --- a/components/engine/vendor/github.com/containerd/containerd/images/image.go +++ b/components/engine/vendor/github.com/containerd/containerd/images/image.go @@ -1,8 +1,25 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package images import ( "context" "encoding/json" + "strings" "time" "github.com/containerd/containerd/content" @@ -101,7 +118,7 @@ func (image *Image) Size(ctx context.Context, provider content.Provider, platfor } size += desc.Size return nil, nil - }), ChildrenHandler(provider, platform)), image.Target) + }), FilterPlatform(platform, ChildrenHandler(provider))), image.Target) } // Manifest resolves a manifest from the image for the given platform. @@ -237,7 +254,7 @@ func Platforms(ctx context.Context, provider content.Provider, image ocispec.Des platforms.Normalize(ocispec.Platform{OS: image.OS, Architecture: image.Architecture})) } return nil, nil - }), ChildrenHandler(provider, "")), image) + }), ChildrenHandler(provider)), image) } // Check returns nil if the all components of an image are available in the @@ -284,7 +301,7 @@ func Check(ctx context.Context, provider content.Provider, image ocispec.Descrip } // Children returns the immediate children of content described by the descriptor. -func Children(ctx context.Context, provider content.Provider, desc ocispec.Descriptor, platform string) ([]ocispec.Descriptor, error) { +func Children(ctx context.Context, provider content.Provider, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { var descs []ocispec.Descriptor switch desc.MediaType { case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest: @@ -313,21 +330,7 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr return nil, err } - if platform != "" { - matcher, err := platforms.Parse(platform) - if err != nil { - return nil, err - } - - for _, d := range index.Manifests { - if d.Platform == nil || matcher.Match(*d.Platform) { - descs = append(descs, d) - } - } - } else { - descs = append(descs, index.Manifests...) - } - + descs = append(descs, index.Manifests...) case MediaTypeDockerSchema2Layer, MediaTypeDockerSchema2LayerGzip, MediaTypeDockerSchema2LayerForeign, MediaTypeDockerSchema2LayerForeignGzip, MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig, @@ -357,13 +360,24 @@ func RootFS(ctx context.Context, provider content.Provider, configDesc ocispec.D if err := json.Unmarshal(p, &config); err != nil { return nil, err } + return config.RootFS.DiffIDs, nil +} - // TODO(stevvooe): Remove this bit when OCI structure uses correct type for - // rootfs.DiffIDs. - var diffIDs []digest.Digest - for _, diffID := range config.RootFS.DiffIDs { - diffIDs = append(diffIDs, digest.Digest(diffID)) +// IsCompressedDiff returns true if mediaType is a known compressed diff media type. +// It returns false if the media type is a diff, but not compressed. If the media type +// is not a known diff type, it returns errdefs.ErrNotImplemented +func IsCompressedDiff(ctx context.Context, mediaType string) (bool, error) { + switch mediaType { + case ocispec.MediaTypeImageLayer, MediaTypeDockerSchema2Layer: + case ocispec.MediaTypeImageLayerGzip, MediaTypeDockerSchema2LayerGzip: + return true, nil + default: + // Still apply all generic media types *.tar[.+]gzip and *.tar + if strings.HasSuffix(mediaType, ".tar.gzip") || strings.HasSuffix(mediaType, ".tar+gzip") { + return true, nil + } else if !strings.HasSuffix(mediaType, ".tar") { + return false, errdefs.ErrNotImplemented + } } - - return diffIDs, nil + return false, nil } diff --git a/components/engine/vendor/github.com/containerd/containerd/images/importexport.go b/components/engine/vendor/github.com/containerd/containerd/images/importexport.go index f8cf742bad..04a55fd383 100644 --- a/components/engine/vendor/github.com/containerd/containerd/images/importexport.go +++ b/components/engine/vendor/github.com/containerd/containerd/images/importexport.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package images import ( @@ -17,5 +33,5 @@ type Importer interface { // Exporter is the interface for image exporter. type Exporter interface { // Export exports an image to a tar stream. - Export(ctx context.Context, store content.Store, desc ocispec.Descriptor, writer io.Writer) error + Export(ctx context.Context, store content.Provider, desc ocispec.Descriptor, writer io.Writer) error } diff --git a/components/engine/vendor/github.com/containerd/containerd/images/mediatypes.go b/components/engine/vendor/github.com/containerd/containerd/images/mediatypes.go index f01f615c4b..ca4ca071b3 100644 --- a/components/engine/vendor/github.com/containerd/containerd/images/mediatypes.go +++ b/components/engine/vendor/github.com/containerd/containerd/images/mediatypes.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package images // mediatype definitions for image components handled in containerd. diff --git a/components/engine/vendor/github.com/containerd/containerd/labels/validate.go b/components/engine/vendor/github.com/containerd/containerd/labels/validate.go index b05fe1857c..0de461663a 100644 --- a/components/engine/vendor/github.com/containerd/containerd/labels/validate.go +++ b/components/engine/vendor/github.com/containerd/containerd/labels/validate.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package labels import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/lease.go b/components/engine/vendor/github.com/containerd/containerd/lease.go index 8eb3bc0fe1..6187c1df77 100644 --- a/components/engine/vendor/github.com/containerd/containerd/lease.go +++ b/components/engine/vendor/github.com/containerd/containerd/lease.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/leases/context.go b/components/engine/vendor/github.com/containerd/containerd/leases/context.go index cfd7e4a46e..b66b154ce4 100644 --- a/components/engine/vendor/github.com/containerd/containerd/leases/context.go +++ b/components/engine/vendor/github.com/containerd/containerd/leases/context.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package leases import "context" diff --git a/components/engine/vendor/github.com/containerd/containerd/leases/grpc.go b/components/engine/vendor/github.com/containerd/containerd/leases/grpc.go index cea5b25feb..284924e7d7 100644 --- a/components/engine/vendor/github.com/containerd/containerd/leases/grpc.go +++ b/components/engine/vendor/github.com/containerd/containerd/leases/grpc.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package leases import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/bundle.go b/components/engine/vendor/github.com/containerd/containerd/linux/bundle.go index 629d7f5bfb..735547c5c7 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/bundle.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/bundle.go @@ -1,11 +1,26 @@ // +build linux +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package linux import ( - "bytes" "context" - "io" + "io/ioutil" "os" "path/filepath" @@ -52,12 +67,7 @@ func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) { if err := os.Mkdir(filepath.Join(path, "rootfs"), 0711); err != nil { return nil, err } - f, err := os.Create(filepath.Join(path, configFilename)) - if err != nil { - return nil, err - } - defer f.Close() - _, err = io.Copy(f, bytes.NewReader(spec)) + err = ioutil.WriteFile(filepath.Join(path, configFilename), spec, 0666) return &bundle{ id: id, path: path, @@ -90,9 +100,9 @@ func ShimLocal(exchange *exchange.Exchange) ShimOpt { } // ShimConnect is a ShimOpt for connecting to an existing remote shim -func ShimConnect() ShimOpt { +func ShimConnect(onClose func()) ShimOpt { return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) { - return b.shimConfig(ns, ropts), client.WithConnect(b.shimAddress(ns)) + return b.shimConfig(ns, ropts), client.WithConnect(b.shimAddress(ns), onClose) } } diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/proc/deleted_state.go b/components/engine/vendor/github.com/containerd/containerd/linux/proc/deleted_state.go index fb25878047..87a3fd0c06 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/proc/deleted_state.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/proc/deleted_state.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package proc import ( @@ -48,3 +64,7 @@ func (s *deletedState) Kill(ctx context.Context, sig uint32, all bool) error { func (s *deletedState) SetExited(status int) { // no op } + +func (s *deletedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + return nil, errors.Errorf("cannot exec in a deleted state") +} diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/proc/exec.go b/components/engine/vendor/github.com/containerd/containerd/linux/proc/exec.go index 00a8547b87..636879da19 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/proc/exec.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/proc/exec.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package proc import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/proc/exec_state.go b/components/engine/vendor/github.com/containerd/containerd/linux/proc/exec_state.go index 3c3c265829..617ec0d978 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/proc/exec_state.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/proc/exec_state.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package proc import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/proc/init.go b/components/engine/vendor/github.com/containerd/containerd/linux/proc/init.go index f24f92f7da..82f9ebdf2f 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/proc/init.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/proc/init.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package proc import ( @@ -346,8 +362,8 @@ func (p *Init) Runtime() *runc.Runc { return p.runtime } -// Exec returns a new exec'd process -func (p *Init) Exec(context context.Context, path string, r *ExecConfig) (Process, error) { +// exec returns a new exec'd process +func (p *Init) exec(context context.Context, path string, r *ExecConfig) (Process, error) { // process exec request var spec specs.Process if err := json.Unmarshal(r.Spec.Value, &spec); err != nil { diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/proc/init_state.go b/components/engine/vendor/github.com/containerd/containerd/linux/proc/init_state.go index b5b398ec39..8944d6192e 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/proc/init_state.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/proc/init_state.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package proc import ( @@ -22,6 +38,7 @@ type initState interface { Resume(context.Context) error Update(context.Context, *google_protobuf.Any) error Checkpoint(context.Context, *CheckpointConfig) error + Exec(context.Context, string, *ExecConfig) (Process, error) } type createdState struct { @@ -113,6 +130,12 @@ func (s *createdState) SetExited(status int) { } } +func (s *createdState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + s.p.mu.Lock() + defer s.p.mu.Unlock() + return s.p.exec(ctx, path, r) +} + type createdCheckpointState struct { p *Init opts *runc.RestoreOpts @@ -227,6 +250,13 @@ func (s *createdCheckpointState) SetExited(status int) { } } +func (s *createdCheckpointState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + s.p.mu.Lock() + defer s.p.mu.Unlock() + + return nil, errors.Errorf("cannot exec in a created state") +} + type runningState struct { p *Init } @@ -312,6 +342,12 @@ func (s *runningState) SetExited(status int) { } } +func (s *runningState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + s.p.mu.Lock() + defer s.p.mu.Unlock() + return s.p.exec(ctx, path, r) +} + type pausedState struct { p *Init } @@ -396,7 +432,13 @@ func (s *pausedState) SetExited(status int) { if err := s.transition("stopped"); err != nil { panic(err) } +} +func (s *pausedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + s.p.mu.Lock() + defer s.p.mu.Unlock() + + return nil, errors.Errorf("cannot exec in a paused state") } type stoppedState struct { @@ -471,3 +513,10 @@ func (s *stoppedState) Kill(ctx context.Context, sig uint32, all bool) error { func (s *stoppedState) SetExited(status int) { // no op } + +func (s *stoppedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + s.p.mu.Lock() + defer s.p.mu.Unlock() + + return nil, errors.Errorf("cannot exec in a stopped state") +} diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/proc/io.go b/components/engine/vendor/github.com/containerd/containerd/linux/proc/io.go index e78b383008..335b8b89ca 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/proc/io.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/proc/io.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package proc import ( @@ -13,6 +29,13 @@ import ( runc "github.com/containerd/go-runc" ) +var bufPool = sync.Pool{ + New: func() interface{} { + buffer := make([]byte, 32<<10) + return &buffer + }, +} + func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, wg, cwg *sync.WaitGroup) error { for name, dest := range map[string]func(wc io.WriteCloser, rc io.Closer){ stdout: func(wc io.WriteCloser, rc io.Closer) { @@ -20,7 +43,9 @@ func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, w cwg.Add(1) go func() { cwg.Done() - io.Copy(wc, rio.Stdout()) + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + io.CopyBuffer(wc, rio.Stdout(), *p) wg.Done() wc.Close() rc.Close() @@ -31,7 +56,10 @@ func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, w cwg.Add(1) go func() { cwg.Done() - io.Copy(wc, rio.Stderr()) + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + + io.CopyBuffer(wc, rio.Stderr(), *p) wg.Done() wc.Close() rc.Close() @@ -59,7 +87,10 @@ func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, w cwg.Add(1) go func() { cwg.Done() - io.Copy(rio.Stdin(), f) + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + + io.CopyBuffer(rio.Stdin(), f, *p) rio.Stdin().Close() f.Close() }() diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/proc/process.go b/components/engine/vendor/github.com/containerd/containerd/linux/proc/process.go index c0d33adbce..135f9962b1 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/proc/process.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/proc/process.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package proc import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/proc/types.go b/components/engine/vendor/github.com/containerd/containerd/linux/proc/types.go index 9055c25d10..0cc123fd81 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/proc/types.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/proc/types.go @@ -1,16 +1,39 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package proc import ( - containerd_types "github.com/containerd/containerd/api/types" google_protobuf "github.com/gogo/protobuf/types" ) +// Mount holds filesystem mount configuration +type Mount struct { + Type string + Source string + Target string + Options []string +} + // CreateConfig hold task creation configuration type CreateConfig struct { ID string Bundle string Runtime string - Rootfs []*containerd_types.Mount + Rootfs []Mount Terminal bool Stdin string Stdout string diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/proc/utils.go b/components/engine/vendor/github.com/containerd/containerd/linux/proc/utils.go index 1197957b57..3d0334c450 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/proc/utils.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/proc/utils.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package proc import ( @@ -66,7 +82,10 @@ func copyFile(to, from string) error { return err } defer tt.Close() - _, err = io.Copy(tt, ff) + + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + _, err = io.CopyBuffer(tt, ff, *p) return err } diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/process.go b/components/engine/vendor/github.com/containerd/containerd/linux/process.go index 10acc69d5b..0790d8a52d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/process.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/process.go @@ -1,5 +1,21 @@ // +build linux +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package linux import ( @@ -10,6 +26,8 @@ import ( "github.com/containerd/containerd/errdefs" shim "github.com/containerd/containerd/linux/shim/v1" "github.com/containerd/containerd/runtime" + "github.com/pkg/errors" + "github.com/stevvooe/ttrpc" ) // Process implements a linux process @@ -44,7 +62,14 @@ func (p *Process) State(ctx context.Context) (runtime.State, error) { ID: p.id, }) if err != nil { - return runtime.State{}, errdefs.FromGRPC(err) + if errors.Cause(err) != ttrpc.ErrClosed { + return runtime.State{}, errdefs.FromGRPC(err) + } + + // We treat ttrpc.ErrClosed as the shim being closed, but really this + // likely means that the process no longer exists. We'll have to plumb + // the connection differently if this causes problems. + return runtime.State{}, errdefs.ErrNotFound } var status runtime.Status switch response.Status { diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/runtime.go b/components/engine/vendor/github.com/containerd/containerd/linux/runtime.go index 82ed4f4ea6..1e1b77da7e 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/runtime.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/runtime.go @@ -1,5 +1,21 @@ // +build linux +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package linux import ( @@ -26,9 +42,7 @@ import ( "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/plugin" - "github.com/containerd/containerd/reaper" "github.com/containerd/containerd/runtime" - "github.com/containerd/containerd/sys" runc "github.com/containerd/go-runc" "github.com/containerd/typeurl" ptypes "github.com/gogo/protobuf/types" @@ -159,9 +173,6 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts return nil, err } - ec := reaper.Default.Subscribe() - defer reaper.Default.Unsubscribe(ec) - bundle, err := newBundle(id, filepath.Join(r.state, namespace), filepath.Join(r.root, namespace), @@ -206,10 +217,7 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts "id": id, "namespace": namespace, }).Warn("cleaning up after killed shim") - err = r.cleanupAfterDeadShim(context.Background(), bundle, namespace, id, lc.pid, ec) - if err == nil { - r.tasks.Delete(ctx, lc) - } else { + if err = r.cleanupAfterDeadShim(context.Background(), bundle, namespace, id, lc.pid); err != nil { log.G(ctx).WithError(err).WithFields(logrus.Fields{ "id": id, "namespace": namespace, @@ -313,12 +321,12 @@ func (r *Runtime) Delete(ctx context.Context, c runtime.Task) (*runtime.Exit, er rsp, err := lc.shim.Delete(ctx, empty) if err != nil { - if cerr := r.cleanupAfterDeadShim(ctx, bundle, namespace, c.ID(), lc.pid, nil); cerr != nil { + if cerr := r.cleanupAfterDeadShim(ctx, bundle, namespace, c.ID(), lc.pid); cerr != nil { log.G(ctx).WithError(err).Error("unable to cleanup task") } return nil, errdefs.FromGRPC(err) } - r.tasks.Delete(ctx, lc) + r.tasks.Delete(ctx, lc.id) if err := lc.shim.KillShim(ctx); err != nil { log.G(ctx).WithError(err).Error("failed to kill shim") } @@ -388,13 +396,19 @@ func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) { ) ctx = namespaces.WithNamespace(ctx, ns) pid, _ := runc.ReadPidFile(filepath.Join(bundle.path, proc.InitPidFile)) - s, err := bundle.NewShimClient(ctx, ns, ShimConnect(), nil) + s, err := bundle.NewShimClient(ctx, ns, ShimConnect(func() { + err := r.cleanupAfterDeadShim(ctx, bundle, ns, id, pid) + if err != nil { + log.G(ctx).WithError(err).WithField("bundle", bundle.path). + Error("cleaning up after dead shim") + } + }), nil) if err != nil { log.G(ctx).WithError(err).WithFields(logrus.Fields{ "id": id, "namespace": ns, }).Error("connecting to shim") - err := r.cleanupAfterDeadShim(ctx, bundle, ns, id, pid, nil) + err := r.cleanupAfterDeadShim(ctx, bundle, ns, id, pid) if err != nil { log.G(ctx).WithError(err).WithField("bundle", bundle.path). Error("cleaning up after dead shim") @@ -419,7 +433,7 @@ func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) { return o, nil } -func (r *Runtime) cleanupAfterDeadShim(ctx context.Context, bundle *bundle, ns, id string, pid int, ec chan runc.Exit) error { +func (r *Runtime) cleanupAfterDeadShim(ctx context.Context, bundle *bundle, ns, id string, pid int) error { ctx = namespaces.WithNamespace(ctx, ns) if err := r.terminate(ctx, bundle, ns, id); err != nil { if r.config.ShimDebug { @@ -428,17 +442,6 @@ func (r *Runtime) cleanupAfterDeadShim(ctx context.Context, bundle *bundle, ns, log.G(ctx).WithError(err).Warn("failed to terminate task") } - if ec != nil { - // if sub-reaper is set, reap our new child - if v, err := sys.GetSubreaper(); err == nil && v == 1 { - for e := range ec { - if e.Pid == pid { - break - } - } - } - } - // Notify Client exitedAt := time.Now().UTC() r.events.Publish(ctx, runtime.TaskExitEventTopic, &eventstypes.TaskExit{ @@ -449,6 +452,7 @@ func (r *Runtime) cleanupAfterDeadShim(ctx context.Context, bundle *bundle, ns, ExitedAt: exitedAt, }) + r.tasks.Delete(ctx, id) if err := bundle.Delete(); err != nil { log.G(ctx).WithError(err).Error("delete bundle") } @@ -464,12 +468,10 @@ func (r *Runtime) cleanupAfterDeadShim(ctx context.Context, bundle *bundle, ns, } func (r *Runtime) terminate(ctx context.Context, bundle *bundle, ns, id string) error { - ctx = namespaces.WithNamespace(ctx, ns) rt, err := r.getRuntime(ctx, ns, id) if err != nil { return err } - if err := rt.Delete(ctx, id, &runc.DeleteOpts{ Force: true, }); err != nil { diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client.go b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client.go index 6e78d8bea3..37881a36f2 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package client import ( @@ -23,7 +39,6 @@ import ( "github.com/containerd/containerd/linux/shim" shimapi "github.com/containerd/containerd/linux/shim/v1" "github.com/containerd/containerd/log" - "github.com/containerd/containerd/reaper" "github.com/containerd/containerd/sys" ptypes "github.com/gogo/protobuf/types" ) @@ -51,8 +66,7 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa if err != nil { return nil, nil, err } - ec, err := reaper.Default.Start(cmd) - if err != nil { + if err := cmd.Start(); err != nil { return nil, nil, errors.Wrapf(err, "failed to start shim") } defer func() { @@ -61,7 +75,7 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa } }() go func() { - reaper.Default.Wait(cmd, ec) + cmd.Wait() exitHandler() }() log.G(ctx).WithFields(logrus.Fields{ @@ -82,7 +96,7 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa if err = sys.SetOOMScore(cmd.Process.Pid, sys.OOMScoreMaxKillable); err != nil { return nil, nil, errors.Wrap(err, "failed to set OOM Score on shim") } - c, clo, err := WithConnect(address)(ctx, config) + c, clo, err := WithConnect(address, func() {})(ctx, config) if err != nil { return nil, nil, errors.Wrap(err, "failed to connect") } @@ -151,13 +165,15 @@ func annonDialer(address string, timeout time.Duration) (net.Conn, error) { } // WithConnect connects to an existing shim -func WithConnect(address string) Opt { +func WithConnect(address string, onClose func()) Opt { return func(ctx context.Context, config shim.Config) (shimapi.ShimService, io.Closer, error) { conn, err := connect(address, annonDialer) if err != nil { return nil, nil, err } - return shimapi.NewShimClient(ttrpc.NewClient(conn)), conn, nil + client := ttrpc.NewClient(conn) + client.OnClose(onClose) + return shimapi.NewShimClient(client), conn, nil } } diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_linux.go b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_linux.go index 3125541eda..2519380f5d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_linux.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_linux.go @@ -1,5 +1,21 @@ // +build linux +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package client import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_unix.go b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_unix.go index 0a24ce45fe..8a5b22fb78 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_unix.go @@ -1,5 +1,21 @@ // +build !linux,!windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package client import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/shim/local.go b/components/engine/vendor/github.com/containerd/containerd/linux/shim/local.go index 6e2192693f..1600ef6f1e 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/shim/local.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/shim/local.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package shim import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/shim/service.go b/components/engine/vendor/github.com/containerd/containerd/linux/shim/service.go index 129b1790fc..49d847e87d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/shim/service.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/shim/service.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package shim import ( @@ -29,7 +45,15 @@ import ( "google.golang.org/grpc/status" ) -var empty = &ptypes.Empty{} +var ( + empty = &ptypes.Empty{} + bufPool = sync.Pool{ + New: func() interface{} { + buffer := make([]byte, 32<<10) + return &buffer + }, + } +) // Config contains shim specific configuration type Config struct { @@ -87,6 +111,16 @@ type Service struct { func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (*shimapi.CreateTaskResponse, error) { s.mu.Lock() defer s.mu.Unlock() + + var mounts []proc.Mount + for _, m := range r.Rootfs { + mounts = append(mounts, proc.Mount{ + Type: m.Type, + Source: m.Source, + Target: m.Target, + Options: m.Options, + }) + } process, err := proc.New( ctx, s.config.Path, @@ -100,7 +134,7 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (*sh ID: r.ID, Bundle: r.Bundle, Runtime: r.Runtime, - Rootfs: r.Rootfs, + Rootfs: mounts, Terminal: r.Terminal, Stdin: r.Stdin, Stdout: r.Stdout, @@ -129,7 +163,7 @@ func (s *Service) Start(ctx context.Context, r *shimapi.StartRequest) (*shimapi. defer s.mu.Unlock() p := s.processes[r.ID] if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process %s not found", r.ID) + return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process %s", r.ID) } if err := p.Start(ctx); err != nil { return nil, err @@ -235,10 +269,10 @@ func (s *Service) ResizePty(ctx context.Context, r *shimapi.ResizePtyRequest) (* // State returns runtime state information for a process func (s *Service) State(ctx context.Context, r *shimapi.StateRequest) (*shimapi.StateResponse, error) { s.mu.Lock() + defer s.mu.Unlock() p := s.processes[r.ID] - s.mu.Unlock() if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process id %s not found", r.ID) + return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process id %s", r.ID) } st, err := p.Status(ctx) if err != nil { diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/shim/service_linux.go b/components/engine/vendor/github.com/containerd/containerd/linux/shim/service_linux.go index bbe9d188a0..18ae6503b4 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/shim/service_linux.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/shim/service_linux.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package shim import ( @@ -33,7 +49,9 @@ func (p *linuxPlatform) CopyConsole(ctx context.Context, console console.Console cwg.Add(1) go func() { cwg.Done() - io.Copy(epollConsole, in) + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + io.CopyBuffer(epollConsole, in, *p) }() } @@ -49,7 +67,9 @@ func (p *linuxPlatform) CopyConsole(ctx context.Context, console console.Console cwg.Add(1) go func() { cwg.Done() - io.Copy(outw, epollConsole) + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + io.CopyBuffer(outw, epollConsole, *p) epollConsole.Close() outr.Close() outw.Close() diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/shim/service_unix.go b/components/engine/vendor/github.com/containerd/containerd/linux/shim/service_unix.go index d4419e56ae..708e45c296 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/shim/service_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/shim/service_unix.go @@ -1,5 +1,21 @@ // +build !windows,!linux +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package shim import ( @@ -24,7 +40,10 @@ func (p *unixPlatform) CopyConsole(ctx context.Context, console console.Console, cwg.Add(1) go func() { cwg.Done() - io.Copy(console, in) + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + + io.CopyBuffer(console, in, *p) }() } outw, err := fifo.OpenFifo(ctx, stdout, syscall.O_WRONLY, 0) @@ -39,7 +58,10 @@ func (p *unixPlatform) CopyConsole(ctx context.Context, console console.Console, cwg.Add(1) go func() { cwg.Done() - io.Copy(outw, console) + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + + io.CopyBuffer(outw, console, *p) console.Close() outr.Close() outw.Close() diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/task.go b/components/engine/vendor/github.com/containerd/containerd/linux/task.go index 4d1e93fb1f..eca82fbbd0 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/task.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/task.go @@ -1,14 +1,27 @@ // +build linux +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package linux import ( "context" "sync" - "github.com/pkg/errors" - "google.golang.org/grpc" - "github.com/containerd/cgroups" eventstypes "github.com/containerd/containerd/api/events" "github.com/containerd/containerd/api/types/task" @@ -20,6 +33,8 @@ import ( "github.com/containerd/containerd/runtime" runc "github.com/containerd/go-runc" "github.com/gogo/protobuf/types" + "github.com/pkg/errors" + "github.com/stevvooe/ttrpc" ) // Task on a linux based system @@ -109,7 +124,7 @@ func (t *Task) State(ctx context.Context) (runtime.State, error) { ID: t.id, }) if err != nil { - if err != grpc.ErrServerStopped { + if errors.Cause(err) != ttrpc.ErrClosed { return runtime.State{}, errdefs.FromGRPC(err) } return runtime.State{}, errdefs.ErrNotFound diff --git a/components/engine/vendor/github.com/containerd/containerd/log/context.go b/components/engine/vendor/github.com/containerd/containerd/log/context.go index 1081719c11..f40603b17b 100644 --- a/components/engine/vendor/github.com/containerd/containerd/log/context.go +++ b/components/engine/vendor/github.com/containerd/containerd/log/context.go @@ -1,8 +1,24 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package log import ( "context" - "path" + "sync/atomic" "github.com/sirupsen/logrus" ) @@ -20,9 +36,21 @@ var ( type ( loggerKey struct{} - moduleKey struct{} ) +// TraceLevel is the log level for tracing. Trace level is lower than debug level, +// and is usually used to trace detailed behavior of the program. +const TraceLevel = logrus.Level(uint32(logrus.DebugLevel + 1)) + +// ParseLevel takes a string level and returns the Logrus log level constant. +// It supports trace level. +func ParseLevel(lvl string) (logrus.Level, error) { + if lvl == "trace" { + return TraceLevel, nil + } + return logrus.ParseLevel(lvl) +} + // WithLogger returns a new context with the provided logger. Use in // combination with logger.WithField(s) for great effect. func WithLogger(ctx context.Context, logger *logrus.Entry) context.Context { @@ -41,41 +69,18 @@ func GetLogger(ctx context.Context) *logrus.Entry { return logger.(*logrus.Entry) } -// WithModule adds the module to the context, appending it with a slash if a -// module already exists. A module is just an roughly correlated defined by the -// call tree for a given context. -// -// As an example, we might have a "node" module already part of a context. If -// this function is called with "tls", the new value of module will be -// "node/tls". -// -// Modules represent the call path. If the new module and last module are the -// same, a new module entry will not be created. If the new module and old -// older module are the same but separated by other modules, the cycle will be -// represented by the module path. -func WithModule(ctx context.Context, module string) context.Context { - parent := GetModulePath(ctx) - - if parent != "" { - // don't re-append module when module is the same. - if path.Base(parent) == module { - return ctx - } - - module = path.Join(parent, module) +// Trace logs a message at level Trace with the log entry passed-in. +func Trace(e *logrus.Entry, args ...interface{}) { + level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level))) + if level >= TraceLevel { + e.Debug(args...) } - - ctx = WithLogger(ctx, GetLogger(ctx).WithField("module", module)) - return context.WithValue(ctx, moduleKey{}, module) } -// GetModulePath returns the module path for the provided context. If no module -// is set, an empty string is returned. -func GetModulePath(ctx context.Context) string { - module := ctx.Value(moduleKey{}) - if module == nil { - return "" +// Tracef logs a message at level Trace with the log entry passed-in. +func Tracef(e *logrus.Entry, format string, args ...interface{}) { + level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level))) + if level >= TraceLevel { + e.Debugf(format, args...) } - - return module.(string) } diff --git a/components/engine/vendor/github.com/containerd/containerd/log/grpc.go b/components/engine/vendor/github.com/containerd/containerd/log/grpc.go deleted file mode 100644 index cb2c92182f..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/log/grpc.go +++ /dev/null @@ -1,12 +0,0 @@ -package log - -import ( - "io/ioutil" - "log" - - "google.golang.org/grpc/grpclog" -) - -func init() { - grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) -} diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/adaptors.go b/components/engine/vendor/github.com/containerd/containerd/metadata/adaptors.go index 4e7ef673dd..8145f9acee 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/adaptors.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/adaptors.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/bolt.go b/components/engine/vendor/github.com/containerd/containerd/metadata/bolt.go index 2e4c352705..156a33593f 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/bolt.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/bolt.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/boltutil/helpers.go b/components/engine/vendor/github.com/containerd/containerd/metadata/boltutil/helpers.go index b713132dc0..4b2ede8763 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/boltutil/helpers.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/boltutil/helpers.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package boltutil import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/buckets.go b/components/engine/vendor/github.com/containerd/containerd/metadata/buckets.go index 9325f1698a..cade7eea34 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/buckets.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/buckets.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import ( @@ -106,13 +122,8 @@ func imagesBucketPath(namespace string) [][]byte { return [][]byte{bucketKeyVersion, []byte(namespace), bucketKeyObjectImages} } -func withImagesBucket(tx *bolt.Tx, namespace string, fn func(bkt *bolt.Bucket) error) error { - bkt, err := createBucketIfNotExists(tx, imagesBucketPath(namespace)...) - if err != nil { - return err - } - - return fn(bkt) +func createImagesBucket(tx *bolt.Tx, namespace string) (*bolt.Bucket, error) { + return createBucketIfNotExists(tx, imagesBucketPath(namespace)...) } func getImagesBucket(tx *bolt.Tx, namespace string) *bolt.Bucket { @@ -143,6 +154,10 @@ func createSnapshotterBucket(tx *bolt.Tx, namespace, snapshotter string) (*bolt. return bkt, nil } +func getSnapshottersBucket(tx *bolt.Tx, namespace string) *bolt.Bucket { + return getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectSnapshots) +} + func getSnapshotterBucket(tx *bolt.Tx, namespace, snapshotter string) *bolt.Bucket { return getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectSnapshots, []byte(snapshotter)) } diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/containers.go b/components/engine/vendor/github.com/containerd/containerd/metadata/containers.go index 32f339afef..2ba45c72ff 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/containers.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/containers.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/content.go b/components/engine/vendor/github.com/containerd/containerd/metadata/content.go index c13f7867ee..3a746c0167 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/content.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/content.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import ( @@ -318,12 +334,23 @@ func (cs *contentStore) Writer(ctx context.Context, ref string, size int64, expe cs.l.RLock() defer cs.l.RUnlock() - var w content.Writer + var ( + w content.Writer + exists bool + ) if err := update(ctx, cs.db, func(tx *bolt.Tx) error { if expected != "" { cbkt := getBlobBucket(tx, ns, expected) if cbkt != nil { - return errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", expected) + // Add content to lease to prevent other reference removals + // from effecting this object during a provided lease + if err := addContentLease(ctx, tx, expected); err != nil { + return errors.Wrap(err, "unable to lease content") + } + // Return error outside of transaction to ensure + // commit succeeds with the lease. + exists = true + return nil } } @@ -363,6 +390,9 @@ func (cs *contentStore) Writer(ctx context.Context, ref string, size int64, expe }); err != nil { return nil, err } + if exists { + return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", expected) + } // TODO: keep the expected in the writer to use on commit // when no expected is provided there. diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/db.go b/components/engine/vendor/github.com/containerd/containerd/metadata/db.go index 8be62a95c3..f08e1d46c3 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/db.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/db.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import ( @@ -204,7 +220,7 @@ func (m *DB) Update(fn func(*bolt.Tx) error) error { // RegisterMutationCallback registers a function to be called after a metadata // mutations has been performed. // -// The callback function in an argument for whether a deletion has occurred +// The callback function is an argument for whether a deletion has occurred // since the last garbage collection. func (m *DB) RegisterMutationCallback(fn func(bool)) { m.dirtyL.Lock() @@ -219,15 +235,20 @@ type GCStats struct { SnapshotD map[string]time.Duration } +// Elapsed returns the duration which elapsed during a collection +func (s GCStats) Elapsed() time.Duration { + return s.MetaD +} + // GarbageCollect starts garbage collection -func (m *DB) GarbageCollect(ctx context.Context) (stats GCStats, err error) { +func (m *DB) GarbageCollect(ctx context.Context) (gc.Stats, error) { m.wlock.Lock() t1 := time.Now() marked, err := m.getMarked(ctx) if err != nil { m.wlock.Unlock() - return GCStats{}, err + return nil, err } m.dirtyL.Lock() @@ -259,9 +280,10 @@ func (m *DB) GarbageCollect(ctx context.Context) (stats GCStats, err error) { }); err != nil { m.dirtyL.Unlock() m.wlock.Unlock() - return GCStats{}, err + return nil, err } + var stats GCStats var wg sync.WaitGroup if len(m.dirtySS) > 0 { @@ -303,7 +325,7 @@ func (m *DB) GarbageCollect(ctx context.Context) (stats GCStats, err error) { wg.Wait() - return + return stats, err } func (m *DB) getMarked(ctx context.Context) (map[gc.Node]struct{}, error) { diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/gc.go b/components/engine/vendor/github.com/containerd/containerd/metadata/gc.go index 186f350ee3..d13047eb1c 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/gc.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/gc.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/images.go b/components/engine/vendor/github.com/containerd/containerd/metadata/images.go index 070439a8ca..62b1179cd3 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/images.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/images.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import ( @@ -20,12 +36,12 @@ import ( ) type imageStore struct { - tx *bolt.Tx + db *DB } // NewImageStore returns a store backed by a bolt DB -func NewImageStore(tx *bolt.Tx) images.Store { - return &imageStore{tx: tx} +func NewImageStore(db *DB) images.Store { + return &imageStore{db: db} } func (s *imageStore) Get(ctx context.Context, name string) (images.Image, error) { @@ -36,19 +52,25 @@ func (s *imageStore) Get(ctx context.Context, name string) (images.Image, error) return images.Image{}, err } - bkt := getImagesBucket(s.tx, namespace) - if bkt == nil { - return images.Image{}, errors.Wrapf(errdefs.ErrNotFound, "image %q", name) - } + if err := view(ctx, s.db, func(tx *bolt.Tx) error { + bkt := getImagesBucket(tx, namespace) + if bkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "image %q", name) + } - ibkt := bkt.Bucket([]byte(name)) - if ibkt == nil { - return images.Image{}, errors.Wrapf(errdefs.ErrNotFound, "image %q", name) - } + ibkt := bkt.Bucket([]byte(name)) + if ibkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "image %q", name) + } - image.Name = name - if err := readImage(&image, ibkt); err != nil { - return images.Image{}, errors.Wrapf(err, "image %q", name) + image.Name = name + if err := readImage(&image, ibkt); err != nil { + return errors.Wrapf(err, "image %q", name) + } + + return nil + }); err != nil { + return images.Image{}, err } return image, nil @@ -65,28 +87,30 @@ func (s *imageStore) List(ctx context.Context, fs ...string) ([]images.Image, er return nil, errors.Wrapf(errdefs.ErrInvalidArgument, err.Error()) } - bkt := getImagesBucket(s.tx, namespace) - if bkt == nil { - return nil, nil // empty store - } - var m []images.Image - if err := bkt.ForEach(func(k, v []byte) error { - var ( - image = images.Image{ - Name: string(k), + if err := view(ctx, s.db, func(tx *bolt.Tx) error { + bkt := getImagesBucket(tx, namespace) + if bkt == nil { + return nil // empty store + } + + return bkt.ForEach(func(k, v []byte) error { + var ( + image = images.Image{ + Name: string(k), + } + kbkt = bkt.Bucket(k) + ) + + if err := readImage(&image, kbkt); err != nil { + return err } - kbkt = bkt.Bucket(k) - ) - if err := readImage(&image, kbkt); err != nil { - return err - } - - if filter.Match(adaptImage(image)) { - m = append(m, image) - } - return nil + if filter.Match(adaptImage(image)) { + m = append(m, image) + } + return nil + }) }); err != nil { return nil, err } @@ -100,11 +124,16 @@ func (s *imageStore) Create(ctx context.Context, image images.Image) (images.Ima return images.Image{}, err } - if err := validateImage(&image); err != nil { - return images.Image{}, err - } + if err := update(ctx, s.db, func(tx *bolt.Tx) error { + if err := validateImage(&image); err != nil { + return err + } + + bkt, err := createImagesBucket(tx, namespace) + if err != nil { + return err + } - return image, withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error { ibkt, err := bkt.CreateBucket([]byte(image.Name)) if err != nil { if err != bolt.ErrBucketExists { @@ -117,7 +146,11 @@ func (s *imageStore) Create(ctx context.Context, image images.Image) (images.Ima image.CreatedAt = time.Now().UTC() image.UpdatedAt = image.CreatedAt return writeImage(ibkt, &image) - }) + }); err != nil { + return images.Image{}, err + } + + return image, nil } func (s *imageStore) Update(ctx context.Context, image images.Image, fieldpaths ...string) (images.Image, error) { @@ -131,7 +164,13 @@ func (s *imageStore) Update(ctx context.Context, image images.Image, fieldpaths } var updated images.Image - return updated, withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error { + + if err := update(ctx, s.db, func(tx *bolt.Tx) error { + bkt, err := createImagesBucket(tx, namespace) + if err != nil { + return err + } + ibkt := bkt.Bucket([]byte(image.Name)) if ibkt == nil { return errors.Wrapf(errdefs.ErrNotFound, "image %q", image.Name) @@ -180,7 +219,12 @@ func (s *imageStore) Update(ctx context.Context, image images.Image, fieldpaths updated.CreatedAt = createdat updated.UpdatedAt = time.Now().UTC() return writeImage(ibkt, &updated) - }) + }); err != nil { + return images.Image{}, err + } + + return updated, nil + } func (s *imageStore) Delete(ctx context.Context, name string, opts ...images.DeleteOpt) error { @@ -189,11 +233,24 @@ func (s *imageStore) Delete(ctx context.Context, name string, opts ...images.Del return err } - return withImagesBucket(s.tx, namespace, func(bkt *bolt.Bucket) error { - err := bkt.DeleteBucket([]byte(name)) + return update(ctx, s.db, func(tx *bolt.Tx) error { + bkt := getImagesBucket(tx, namespace) + if bkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "image %q", name) + } + + err = bkt.DeleteBucket([]byte(name)) if err == bolt.ErrBucketNotFound { return errors.Wrapf(errdefs.ErrNotFound, "image %q", name) } + + // A reference to a piece of content has been removed, + // mark content store as dirty for triggering garbage + // collection + s.db.dirtyL.Lock() + s.db.dirtyCS = true + s.db.dirtyL.Unlock() + return err }) } @@ -275,7 +332,7 @@ func writeImage(bkt *bolt.Bucket, image *images.Image) error { } // write the target bucket - tbkt, err := bkt.CreateBucketIfNotExists([]byte(bucketKeyTarget)) + tbkt, err := bkt.CreateBucketIfNotExists(bucketKeyTarget) if err != nil { return err } diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/leases.go b/components/engine/vendor/github.com/containerd/containerd/metadata/leases.go index eff0b20987..317f000ce1 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/leases.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/leases.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/migrations.go b/components/engine/vendor/github.com/containerd/containerd/metadata/migrations.go index 997aee298d..5be5f83011 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/migrations.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/migrations.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import "github.com/boltdb/bolt" diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/namespaces.go b/components/engine/vendor/github.com/containerd/containerd/metadata/namespaces.go index 4b4c4e5fe7..000bef6c9c 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/namespaces.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/namespaces.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import ( @@ -133,31 +149,38 @@ func (s *namespaceStore) Delete(ctx context.Context, namespace string) error { } func (s *namespaceStore) namespaceEmpty(ctx context.Context, namespace string) (bool, error) { - ctx = namespaces.WithNamespace(ctx, namespace) - - // need to check the various object stores. - - imageStore := NewImageStore(s.tx) - images, err := imageStore.List(ctx) - if err != nil { - return false, err + // Get all data buckets + buckets := []*bolt.Bucket{ + getImagesBucket(s.tx, namespace), + getBlobsBucket(s.tx, namespace), + getContainersBucket(s.tx, namespace), } - if len(images) > 0 { - return false, nil + if snbkt := getSnapshottersBucket(s.tx, namespace); snbkt != nil { + if err := snbkt.ForEach(func(k, v []byte) error { + if v == nil { + buckets = append(buckets, snbkt.Bucket(k)) + } + return nil + }); err != nil { + return false, err + } } - containerStore := NewContainerStore(s.tx) - containers, err := containerStore.List(ctx) - if err != nil { - return false, err + // Ensure data buckets are empty + for _, bkt := range buckets { + if !isBucketEmpty(bkt) { + return false, nil + } } - if len(containers) > 0 { - return false, nil - } - - // TODO(stevvooe): Need to add check for content store, as well. Still need - // to make content store namespace aware. - return true, nil } + +func isBucketEmpty(bkt *bolt.Bucket) bool { + if bkt == nil { + return true + } + + k, _ := bkt.Cursor().First() + return k == nil +} diff --git a/components/engine/vendor/github.com/containerd/containerd/metadata/snapshot.go b/components/engine/vendor/github.com/containerd/containerd/metadata/snapshot.go index 6c34e49c22..b126bc9841 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/snapshot.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/snapshot.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package metadata import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/lookup_unix.go b/components/engine/vendor/github.com/containerd/containerd/mount/lookup_unix.go index df9625a968..e8b0a0b483 100644 --- a/components/engine/vendor/github.com/containerd/containerd/mount/lookup_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/mount/lookup_unix.go @@ -1,24 +1,34 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package mount import ( - "fmt" "path/filepath" "sort" "strings" - "syscall" "github.com/pkg/errors" ) // Lookup returns the mount info corresponds to the path. func Lookup(dir string) (Info, error) { - var dirStat syscall.Stat_t dir = filepath.Clean(dir) - if err := syscall.Stat(dir, &dirStat); err != nil { - return Info{}, errors.Wrapf(err, "failed to access %q", dir) - } mounts, err := Self() if err != nil { @@ -26,21 +36,18 @@ func Lookup(dir string) (Info, error) { } // Sort descending order by Info.Mountpoint - sort.Slice(mounts, func(i, j int) bool { + sort.SliceStable(mounts, func(i, j int) bool { return mounts[j].Mountpoint < mounts[i].Mountpoint }) for _, m := range mounts { // Note that m.{Major, Minor} are generally unreliable for our purpose here // https://www.spinics.net/lists/linux-btrfs/msg58908.html - var st syscall.Stat_t - if err := syscall.Stat(m.Mountpoint, &st); err != nil { - // may fail; ignore err - continue - } - if st.Dev == dirStat.Dev && strings.HasPrefix(dir, m.Mountpoint) { + // Note that device number is not checked here, because for overlayfs files + // may have different device number with the mountpoint. + if strings.HasPrefix(dir, m.Mountpoint) { return m, nil } } - return Info{}, fmt.Errorf("failed to find the mount info for %q", dir) + return Info{}, errors.Errorf("failed to find the mount info for %q", dir) } diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/lookup_unsupported.go b/components/engine/vendor/github.com/containerd/containerd/mount/lookup_unsupported.go index e5f84e7f28..46ec66a904 100644 --- a/components/engine/vendor/github.com/containerd/containerd/mount/lookup_unsupported.go +++ b/components/engine/vendor/github.com/containerd/containerd/mount/lookup_unsupported.go @@ -1,5 +1,21 @@ // +build windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package mount import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/mount.go b/components/engine/vendor/github.com/containerd/containerd/mount/mount.go index 94c2c9f927..b25556b2e0 100644 --- a/components/engine/vendor/github.com/containerd/containerd/mount/mount.go +++ b/components/engine/vendor/github.com/containerd/containerd/mount/mount.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package mount // Mount is the lingua franca of containerd. A mount represents a diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/mount_linux.go b/components/engine/vendor/github.com/containerd/containerd/mount/mount_linux.go index de2e8bb7d2..82fc0b279e 100644 --- a/components/engine/vendor/github.com/containerd/containerd/mount/mount_linux.go +++ b/components/engine/vendor/github.com/containerd/containerd/mount/mount_linux.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package mount import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/mount_unix.go b/components/engine/vendor/github.com/containerd/containerd/mount/mount_unix.go index edb0e8dd19..6741293f89 100644 --- a/components/engine/vendor/github.com/containerd/containerd/mount/mount_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/mount/mount_unix.go @@ -1,5 +1,21 @@ // +build darwin freebsd +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package mount import "github.com/pkg/errors" diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/mount_windows.go b/components/engine/vendor/github.com/containerd/containerd/mount/mount_windows.go index 8ad7eab129..f7c97894b4 100644 --- a/components/engine/vendor/github.com/containerd/containerd/mount/mount_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/mount/mount_windows.go @@ -1,6 +1,29 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package mount -import "github.com/pkg/errors" +import ( + "encoding/json" + "path/filepath" + "strings" + + "github.com/Microsoft/hcsshim" + "github.com/pkg/errors" +) var ( // ErrNotImplementOnWindows is returned when an action is not implemented for windows @@ -9,15 +32,70 @@ var ( // Mount to the provided target func (m *Mount) Mount(target string) error { - return ErrNotImplementOnWindows + home, layerID := filepath.Split(m.Source) + + parentLayerPaths, err := m.GetParentPaths() + if err != nil { + return err + } + + var di = hcsshim.DriverInfo{ + HomeDir: home, + } + + if err = hcsshim.ActivateLayer(di, layerID); err != nil { + return errors.Wrapf(err, "failed to activate layer %s", m.Source) + } + defer func() { + if err != nil { + hcsshim.DeactivateLayer(di, layerID) + } + }() + + if err = hcsshim.PrepareLayer(di, layerID, parentLayerPaths); err != nil { + return errors.Wrapf(err, "failed to prepare layer %s", m.Source) + } + return nil +} + +// ParentLayerPathsFlag is the options flag used to represent the JSON encoded +// list of parent layers required to use the layer +const ParentLayerPathsFlag = "parentLayerPaths=" + +// GetParentPaths of the mount +func (m *Mount) GetParentPaths() ([]string, error) { + var parentLayerPaths []string + for _, option := range m.Options { + if strings.HasPrefix(option, ParentLayerPathsFlag) { + err := json.Unmarshal([]byte(option[len(ParentLayerPathsFlag):]), &parentLayerPaths) + if err != nil { + return nil, errors.Wrap(err, "failed to unmarshal parent layer paths from mount") + } + } + } + return parentLayerPaths, nil } // Unmount the mount at the provided path func Unmount(mount string, flags int) error { - return ErrNotImplementOnWindows + var ( + home, layerID = filepath.Split(mount) + di = hcsshim.DriverInfo{ + HomeDir: home, + } + ) + + if err := hcsshim.UnprepareLayer(di, layerID); err != nil { + return errors.Wrapf(err, "failed to unprepare layer %s", mount) + } + if err := hcsshim.DeactivateLayer(di, layerID); err != nil { + return errors.Wrapf(err, "failed to deactivate layer %s", mount) + } + + return nil } -// UnmountAll mounts at the provided path +// UnmountAll unmounts from the provided path func UnmountAll(mount string, flags int) error { - return ErrNotImplementOnWindows + return Unmount(mount, flags) } diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo.go b/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo.go index 2192cce20f..e7a68402f5 100644 --- a/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo.go +++ b/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package mount // Info reveals information about a particular mounted filesystem. This diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_freebsd.go b/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_freebsd.go index d2bc075a6b..bbe79767e3 100644 --- a/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_freebsd.go +++ b/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_freebsd.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package mount /* diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_linux.go b/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_linux.go index 023772264a..9c442c8ed0 100644 --- a/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_linux.go +++ b/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_linux.go @@ -1,5 +1,21 @@ // +build linux +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package mount import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go b/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go index aa659d899a..eba602f1a6 100644 --- a/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go +++ b/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go @@ -1,5 +1,21 @@ // +build !linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package mount import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/temp.go b/components/engine/vendor/github.com/containerd/containerd/mount/temp.go new file mode 100644 index 0000000000..ec7a06bcbe --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/mount/temp.go @@ -0,0 +1,50 @@ +package mount + +import ( + "context" + "io/ioutil" + "os" + + "github.com/containerd/containerd/log" + "github.com/pkg/errors" +) + +var tempMountLocation = os.TempDir() + +// WithTempMount mounts the provided mounts to a temp dir, and pass the temp dir to f. +// The mounts are valid during the call to the f. +// Finally we will unmount and remove the temp dir regardless of the result of f. +func WithTempMount(ctx context.Context, mounts []Mount, f func(root string) error) (err error) { + root, uerr := ioutil.TempDir(tempMountLocation, "containerd-mount") + if uerr != nil { + return errors.Wrapf(uerr, "failed to create temp dir") + } + // We use Remove here instead of RemoveAll. + // The RemoveAll will delete the temp dir and all children it contains. + // When the Unmount fails, RemoveAll will incorrectly delete data from + // the mounted dir. However, if we use Remove, even though we won't + // successfully delete the temp dir and it may leak, we won't loss data + // from the mounted dir. + // For details, please refer to #1868 #1785. + defer func() { + if uerr = os.Remove(root); uerr != nil { + log.G(ctx).WithError(uerr).WithField("dir", root).Errorf("failed to remove mount temp dir") + } + }() + + // We should do defer first, if not we will not do Unmount when only a part of Mounts are failed. + defer func() { + if uerr = UnmountAll(root, 0); uerr != nil { + uerr = errors.Wrapf(uerr, "failed to unmount %s", root) + if err == nil { + err = uerr + } else { + err = errors.Wrap(err, uerr.Error()) + } + } + }() + if uerr = All(mounts, root); uerr != nil { + return errors.Wrapf(uerr, "failed to mount %s", root) + } + return errors.Wrapf(f(root), "mount callback failed on %s", root) +} diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/temp_unix.go b/components/engine/vendor/github.com/containerd/containerd/mount/temp_unix.go new file mode 100644 index 0000000000..770631362a --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/mount/temp_unix.go @@ -0,0 +1,47 @@ +// +build !windows + +package mount + +import ( + "os" + "path/filepath" + "sort" + "strings" +) + +// SetTempMountLocation sets the temporary mount location +func SetTempMountLocation(root string) error { + root, err := filepath.Abs(root) + if err != nil { + return err + } + if err := os.MkdirAll(root, 0700); err != nil { + return err + } + tempMountLocation = root + return nil +} + +// CleanupTempMounts all temp mounts and remove the directories +func CleanupTempMounts(flags int) error { + mounts, err := Self() + if err != nil { + return err + } + var toUnmount []string + for _, m := range mounts { + if strings.HasPrefix(m.Mountpoint, tempMountLocation) { + toUnmount = append(toUnmount, m.Mountpoint) + } + } + sort.Sort(sort.Reverse(sort.StringSlice(toUnmount))) + for _, path := range toUnmount { + if err := UnmountAll(path, flags); err != nil { + return err + } + if err := os.Remove(path); err != nil { + return err + } + } + return nil +} diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/temp_unsupported.go b/components/engine/vendor/github.com/containerd/containerd/mount/temp_unsupported.go new file mode 100644 index 0000000000..d3a188fc06 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/mount/temp_unsupported.go @@ -0,0 +1,13 @@ +// +build windows + +package mount + +// SetTempMountLocation sets the temporary mount location +func SetTempMountLocation(root string) error { + return nil +} + +// CleanupTempMounts all temp mounts and remove the directories +func CleanupTempMounts(flags int) error { + return nil +} diff --git a/components/engine/vendor/github.com/containerd/containerd/namespaces.go b/components/engine/vendor/github.com/containerd/containerd/namespaces.go index 36fc50cabf..eea70ca33a 100644 --- a/components/engine/vendor/github.com/containerd/containerd/namespaces.go +++ b/components/engine/vendor/github.com/containerd/containerd/namespaces.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/namespaces/context.go b/components/engine/vendor/github.com/containerd/containerd/namespaces/context.go index 7087114873..afcd9d1b0d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/namespaces/context.go +++ b/components/engine/vendor/github.com/containerd/containerd/namespaces/context.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package namespaces import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/namespaces/grpc.go b/components/engine/vendor/github.com/containerd/containerd/namespaces/grpc.go index c18fc933d1..fe1ca1d7cb 100644 --- a/components/engine/vendor/github.com/containerd/containerd/namespaces/grpc.go +++ b/components/engine/vendor/github.com/containerd/containerd/namespaces/grpc.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package namespaces import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/namespaces/store.go b/components/engine/vendor/github.com/containerd/containerd/namespaces/store.go index 68ff714bb7..0b5c985691 100644 --- a/components/engine/vendor/github.com/containerd/containerd/namespaces/store.go +++ b/components/engine/vendor/github.com/containerd/containerd/namespaces/store.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package namespaces import "context" diff --git a/components/engine/vendor/github.com/containerd/containerd/namespaces/validate.go b/components/engine/vendor/github.com/containerd/containerd/namespaces/validate.go index ff97a8cc8b..222da3ea43 100644 --- a/components/engine/vendor/github.com/containerd/containerd/namespaces/validate.go +++ b/components/engine/vendor/github.com/containerd/containerd/namespaces/validate.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + // Package namespaces provides tools for working with namespaces across // containerd. // diff --git a/components/engine/vendor/github.com/containerd/containerd/oci/client.go b/components/engine/vendor/github.com/containerd/containerd/oci/client.go index d2cd355c56..9923101bfa 100644 --- a/components/engine/vendor/github.com/containerd/containerd/oci/client.go +++ b/components/engine/vendor/github.com/containerd/containerd/oci/client.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package oci import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/oci/spec.go b/components/engine/vendor/github.com/containerd/containerd/oci/spec.go index 558a3570f1..78284187a5 100644 --- a/components/engine/vendor/github.com/containerd/containerd/oci/spec.go +++ b/components/engine/vendor/github.com/containerd/containerd/oci/spec.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package oci import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts.go b/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts.go index c940c7aabd..53741ea80b 100644 --- a/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts.go +++ b/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts.go @@ -1,7 +1,24 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package oci import ( "context" + "strings" "github.com/containerd/containerd/containers" specs "github.com/opencontainers/runtime-spec/specs-go" @@ -33,3 +50,59 @@ func WithHostname(name string) SpecOpts { return nil } } + +// WithEnv appends environment variables +func WithEnv(environmentVariables []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error { + if len(environmentVariables) > 0 { + s.Process.Env = replaceOrAppendEnvValues(s.Process.Env, environmentVariables) + } + return nil + } +} + +// WithMounts appends mounts +func WithMounts(mounts []specs.Mount) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error { + s.Mounts = append(s.Mounts, mounts...) + return nil + } +} + +// replaceOrAppendEnvValues returns the defaults with the overrides either +// replaced by env key or appended to the list +func replaceOrAppendEnvValues(defaults, overrides []string) []string { + cache := make(map[string]int, len(defaults)) + for i, e := range defaults { + parts := strings.SplitN(e, "=", 2) + cache[parts[0]] = i + } + + for _, value := range overrides { + // Values w/o = means they want this env to be removed/unset. + if !strings.Contains(value, "=") { + if i, exists := cache[value]; exists { + defaults[i] = "" // Used to indicate it should be removed + } + continue + } + + // Just do a normal set/update + parts := strings.SplitN(value, "=", 2) + if i, exists := cache[parts[0]]; exists { + defaults[i] = value + } else { + defaults = append(defaults, value) + } + } + + // Now remove all entries that we want to "unset" + for i := 0; i < len(defaults); i++ { + if defaults[i] == "" { + defaults = append(defaults[:i], defaults[i+1:]...) + i-- + } + } + + return defaults +} diff --git a/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go b/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go index 865aff29a3..87d313addb 100644 --- a/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go @@ -1,12 +1,27 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package oci import ( "context" "encoding/json" "fmt" - "io/ioutil" "os" "path/filepath" "strconv" @@ -14,14 +29,15 @@ import ( "github.com/containerd/containerd/containers" "github.com/containerd/containerd/content" - "github.com/containerd/containerd/fs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/namespaces" + "github.com/containerd/continuity/fs" "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/runc/libcontainer/user" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "github.com/syndtr/gocapability/capability" ) // WithTTY sets the information on the spec as well as the environment variables for @@ -96,22 +112,25 @@ func WithImageConfig(image Image) SpecOpts { s.Process.Env = append(s.Process.Env, config.Env...) cmd := config.Cmd s.Process.Args = append(config.Entrypoint, cmd...) + cwd := config.WorkingDir + if cwd == "" { + cwd = "/" + } + s.Process.Cwd = cwd if config.User != "" { + // According to OCI Image Spec v1.0.0, the following are valid for Linux: + // user, uid, user:group, uid:gid, uid:group, user:gid parts := strings.Split(config.User, ":") switch len(parts) { case 1: v, err := strconv.Atoi(parts[0]) if err != nil { // if we cannot parse as a uint they try to see if it is a username - if err := WithUsername(config.User)(ctx, client, c, s); err != nil { - return err - } - return err - } - if err := WithUserID(uint32(v))(ctx, client, c, s); err != nil { - return err + return WithUsername(config.User)(ctx, client, c, s) } + return WithUserID(uint32(v))(ctx, client, c, s) case 2: + // TODO: support username and groupname v, err := strconv.Atoi(parts[0]) if err != nil { return errors.Wrapf(err, "parse uid %s", parts[0]) @@ -126,11 +145,6 @@ func WithImageConfig(image Image) SpecOpts { return fmt.Errorf("invalid USER value %s", config.User) } } - cwd := config.WorkingDir - if cwd == "" { - cwd = "/" - } - s.Process.Cwd = cwd return nil } } @@ -260,6 +274,24 @@ func WithUIDGID(uid, gid uint32) SpecOpts { // uid, and not returns error. func WithUserID(uid uint32) SpecOpts { return func(ctx context.Context, client Client, c *containers.Container, s *specs.Spec) (err error) { + if c.Snapshotter == "" && c.SnapshotKey == "" { + if !isRootfsAbs(s.Root.Path) { + return errors.Errorf("rootfs absolute path is required") + } + uuid, ugid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool { + return u.Uid == int(uid) + }) + if err != nil { + if os.IsNotExist(err) || err == errNoUsersFound { + s.Process.User.UID, s.Process.User.GID = uid, uid + return nil + } + return err + } + s.Process.User.UID, s.Process.User.GID = uuid, ugid + return nil + + } if c.Snapshotter == "" { return errors.Errorf("no snapshotter set for container") } @@ -271,49 +303,20 @@ func WithUserID(uid uint32) SpecOpts { if err != nil { return err } - root, err := ioutil.TempDir("", "ctd-username") - if err != nil { - return err - } - defer os.Remove(root) - for _, m := range mounts { - if err := m.Mount(root); err != nil { + return mount.WithTempMount(ctx, mounts, func(root string) error { + uuid, ugid, err := getUIDGIDFromPath(root, func(u user.User) bool { + return u.Uid == int(uid) + }) + if err != nil { + if os.IsNotExist(err) || err == errNoUsersFound { + s.Process.User.UID, s.Process.User.GID = uid, uid + return nil + } return err } - } - defer func() { - if uerr := mount.Unmount(root, 0); uerr != nil { - if err == nil { - err = uerr - } - } - }() - ppath, err := fs.RootPath(root, "/etc/passwd") - if err != nil { - return err - } - f, err := os.Open(ppath) - if err != nil { - if os.IsNotExist(err) { - s.Process.User.UID, s.Process.User.GID = uid, uid - return nil - } - return err - } - defer f.Close() - users, err := user.ParsePasswdFilter(f, func(u user.User) bool { - return u.Uid == int(uid) - }) - if err != nil { - return err - } - if len(users) == 0 { - s.Process.User.UID, s.Process.User.GID = uid, uid + s.Process.User.UID, s.Process.User.GID = uuid, ugid return nil - } - u := users[0] - s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid) - return nil + }) } } @@ -323,6 +326,19 @@ func WithUserID(uid uint32) SpecOpts { // it returns error. func WithUsername(username string) SpecOpts { return func(ctx context.Context, client Client, c *containers.Container, s *specs.Spec) (err error) { + if c.Snapshotter == "" && c.SnapshotKey == "" { + if !isRootfsAbs(s.Root.Path) { + return errors.Errorf("rootfs absolute path is required") + } + uid, gid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool { + return u.Name == username + }) + if err != nil { + return err + } + s.Process.User.UID, s.Process.User.GID = uid, gid + return nil + } if c.Snapshotter == "" { return errors.Errorf("no snapshotter set for container") } @@ -334,43 +350,70 @@ func WithUsername(username string) SpecOpts { if err != nil { return err } - root, err := ioutil.TempDir("", "ctd-username") - if err != nil { - return err - } - defer os.Remove(root) - for _, m := range mounts { - if err := m.Mount(root); err != nil { + return mount.WithTempMount(ctx, mounts, func(root string) error { + uid, gid, err := getUIDGIDFromPath(root, func(u user.User) bool { + return u.Name == username + }) + if err != nil { return err } - } - defer func() { - if uerr := mount.Unmount(root, 0); uerr != nil { - if err == nil { - err = uerr - } - } - }() - ppath, err := fs.RootPath(root, "/etc/passwd") - if err != nil { - return err - } - f, err := os.Open(ppath) - if err != nil { - return err - } - defer f.Close() - users, err := user.ParsePasswdFilter(f, func(u user.User) bool { - return u.Name == username + s.Process.User.UID, s.Process.User.GID = uid, gid + return nil }) - if err != nil { - return err - } - if len(users) == 0 { - return errors.Errorf("no users found for %s", username) - } - u := users[0] - s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid) - return nil } } + +// WithAllCapabilities set all linux capabilities for the process +func WithAllCapabilities(_ context.Context, _ Client, _ *containers.Container, s *specs.Spec) error { + caps := getAllCapabilities() + + s.Process.Capabilities.Bounding = caps + s.Process.Capabilities.Effective = caps + s.Process.Capabilities.Permitted = caps + s.Process.Capabilities.Inheritable = caps + + return nil +} + +func getAllCapabilities() []string { + last := capability.CAP_LAST_CAP + // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap + if last == capability.Cap(63) { + last = capability.CAP_BLOCK_SUSPEND + } + var caps []string + for _, cap := range capability.List() { + if cap > last { + continue + } + caps = append(caps, "CAP_"+strings.ToUpper(cap.String())) + } + return caps +} + +var errNoUsersFound = errors.New("no users found") + +func getUIDGIDFromPath(root string, filter func(user.User) bool) (uid, gid uint32, err error) { + ppath, err := fs.RootPath(root, "/etc/passwd") + if err != nil { + return 0, 0, err + } + f, err := os.Open(ppath) + if err != nil { + return 0, 0, err + } + defer f.Close() + users, err := user.ParsePasswdFilter(f, filter) + if err != nil { + return 0, 0, err + } + if len(users) == 0 { + return 0, 0, errNoUsersFound + } + u := users[0] + return uint32(u.Uid), uint32(u.Gid), nil +} + +func isRootfsAbs(root string) bool { + return filepath.IsAbs(root) +} diff --git a/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go b/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go index 796ad55981..5b8ebba130 100644 --- a/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go @@ -1,5 +1,21 @@ // +build windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package oci import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/oci/spec_unix.go b/components/engine/vendor/github.com/containerd/containerd/oci/spec_unix.go index c8f3b37afb..e52e422281 100644 --- a/components/engine/vendor/github.com/containerd/containerd/oci/spec_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/oci/spec_unix.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package oci import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/oci/spec_windows.go b/components/engine/vendor/github.com/containerd/containerd/oci/spec_windows.go index 64c228883d..f8cdb8a9b4 100644 --- a/components/engine/vendor/github.com/containerd/containerd/oci/spec_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/oci/spec_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package oci import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/platforms/cpuinfo.go b/components/engine/vendor/github.com/containerd/containerd/platforms/cpuinfo.go new file mode 100644 index 0000000000..a5c5ab42b9 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/platforms/cpuinfo.go @@ -0,0 +1,101 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package platforms + +import ( + "bufio" + "os" + "runtime" + "strings" + + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/log" + "github.com/pkg/errors" +) + +// Present the ARM instruction set architecture, eg: v7, v8 +var cpuVariant string + +func init() { + if isArmArch(runtime.GOARCH) { + cpuVariant = getCPUVariant() + } else { + cpuVariant = "" + } +} + +// For Linux, the kernel has already detected the ABI, ISA and Features. +// So we don't need to access the ARM registers to detect platform information +// by ourselves. We can just parse these information from /proc/cpuinfo +func getCPUInfo(pattern string) (info string, err error) { + if !isLinuxOS(runtime.GOOS) { + return "", errors.Wrapf(errdefs.ErrNotImplemented, "getCPUInfo for OS %s", runtime.GOOS) + } + + cpuinfo, err := os.Open("/proc/cpuinfo") + if err != nil { + return "", err + } + defer cpuinfo.Close() + + // Start to Parse the Cpuinfo line by line. For SMP SoC, we parse + // the first core is enough. + scanner := bufio.NewScanner(cpuinfo) + for scanner.Scan() { + newline := scanner.Text() + list := strings.Split(newline, ":") + + if len(list) > 1 && strings.EqualFold(strings.TrimSpace(list[0]), pattern) { + return strings.TrimSpace(list[1]), nil + } + } + + // Check whether the scanner encountered errors + err = scanner.Err() + if err != nil { + return "", err + } + + return "", errors.Wrapf(errdefs.ErrNotFound, "getCPUInfo for pattern: %s", pattern) +} + +func getCPUVariant() string { + variant, err := getCPUInfo("Cpu architecture") + if err != nil { + log.L.WithError(err).Error("failure getting variant") + return "" + } + + switch variant { + case "8": + variant = "v8" + case "7", "7M", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)": + variant = "v7" + case "6", "6TEJ": + variant = "v6" + case "5", "5T", "5TE", "5TEJ": + variant = "v5" + case "4", "4T": + variant = "v4" + case "3": + variant = "v3" + default: + variant = "unknown" + } + + return variant +} diff --git a/components/engine/vendor/github.com/containerd/containerd/platforms/database.go b/components/engine/vendor/github.com/containerd/containerd/platforms/database.go index bd66e2517f..df9cc2392a 100644 --- a/components/engine/vendor/github.com/containerd/containerd/platforms/database.go +++ b/components/engine/vendor/github.com/containerd/containerd/platforms/database.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package platforms import ( @@ -5,6 +21,13 @@ import ( "strings" ) +// isLinuxOS returns true if the operating system is Linux. +// +// The OS value should be normalized before calling this function. +func isLinuxOS(os string) bool { + return os == "linux" +} + // These function are generated from from https://golang.org/src/go/build/syslist.go. // // We use switch statements because they are slightly faster than map lookups @@ -21,6 +44,17 @@ func isKnownOS(os string) bool { return false } +// isArmArch returns true if the architecture is ARM. +// +// The arch value should be normalized before being passed to this function. +func isArmArch(arch string) bool { + switch arch { + case "arm", "arm64": + return true + } + return false +} + // isKnownArch returns true if we know about the architecture. // // The arch value should be normalized before being passed to this function. diff --git a/components/engine/vendor/github.com/containerd/containerd/platforms/defaults.go b/components/engine/vendor/github.com/containerd/containerd/platforms/defaults.go index 2b57b49795..dee59abadc 100644 --- a/components/engine/vendor/github.com/containerd/containerd/platforms/defaults.go +++ b/components/engine/vendor/github.com/containerd/containerd/platforms/defaults.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package platforms import ( @@ -16,6 +32,7 @@ func DefaultSpec() specs.Platform { return specs.Platform{ OS: runtime.GOOS, Architecture: runtime.GOARCH, - // TODO(stevvooe): Need to resolve GOARM for arm hosts. + // The Variant field will be empty if arch != ARM. + Variant: cpuVariant, } } diff --git a/components/engine/vendor/github.com/containerd/containerd/platforms/platforms.go b/components/engine/vendor/github.com/containerd/containerd/platforms/platforms.go index 56c6ddc511..77b6d8410d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/platforms/platforms.go +++ b/components/engine/vendor/github.com/containerd/containerd/platforms/platforms.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + // Package platforms provides a toolkit for normalizing, matching and // specifying container platforms. // diff --git a/components/engine/vendor/github.com/containerd/containerd/plugin/context.go b/components/engine/vendor/github.com/containerd/containerd/plugin/context.go index 87e53b84f2..1211c907ef 100644 --- a/components/engine/vendor/github.com/containerd/containerd/plugin/context.go +++ b/components/engine/vendor/github.com/containerd/containerd/plugin/context.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package plugin import ( @@ -6,7 +22,6 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/events/exchange" - "github.com/containerd/containerd/log" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -28,7 +43,7 @@ type InitContext struct { // NewContext returns a new plugin InitContext func NewContext(ctx context.Context, r *Registration, plugins *Set, root, state string) *InitContext { return &InitContext{ - Context: log.WithModule(ctx, r.URI()), + Context: ctx, Root: filepath.Join(root, r.URI()), State: filepath.Join(state, r.URI()), Meta: &Meta{ diff --git a/components/engine/vendor/github.com/containerd/containerd/plugin/plugin.go b/components/engine/vendor/github.com/containerd/containerd/plugin/plugin.go index 5746bf72d7..470429a0c7 100644 --- a/components/engine/vendor/github.com/containerd/containerd/plugin/plugin.go +++ b/components/engine/vendor/github.com/containerd/containerd/plugin/plugin.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package plugin import ( @@ -140,10 +156,19 @@ func Register(r *Registration) { register.r = append(register.r, r) } -// Graph returns an ordered list of registered plugins for initialization -func Graph() (ordered []*Registration) { +// Graph returns an ordered list of registered plugins for initialization. +// Plugins in disableList specified by id will be disabled. +func Graph(disableList []string) (ordered []*Registration) { register.RLock() defer register.RUnlock() + for _, d := range disableList { + for i, r := range register.r { + if r.ID == d { + register.r = append(register.r[:i], register.r[i+1:]...) + break + } + } + } added := map[*Registration]bool{} for _, r := range register.r { diff --git a/components/engine/vendor/github.com/containerd/containerd/plugin/plugin_go18.go b/components/engine/vendor/github.com/containerd/containerd/plugin/plugin_go18.go index eee0e3fdb7..5b82db8685 100644 --- a/components/engine/vendor/github.com/containerd/containerd/plugin/plugin_go18.go +++ b/components/engine/vendor/github.com/containerd/containerd/plugin/plugin_go18.go @@ -1,5 +1,21 @@ // +build go1.8,!windows,amd64,!static_build +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package plugin import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/plugin/plugin_other.go b/components/engine/vendor/github.com/containerd/containerd/plugin/plugin_other.go index 180917af80..2978f60fd3 100644 --- a/components/engine/vendor/github.com/containerd/containerd/plugin/plugin_other.go +++ b/components/engine/vendor/github.com/containerd/containerd/plugin/plugin_other.go @@ -1,5 +1,21 @@ // +build !go1.8 windows !amd64 static_build +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package plugin func loadPlugins(path string) error { diff --git a/components/engine/vendor/github.com/containerd/containerd/process.go b/components/engine/vendor/github.com/containerd/containerd/process.go index 32049cf075..6b11203373 100644 --- a/components/engine/vendor/github.com/containerd/containerd/process.go +++ b/components/engine/vendor/github.com/containerd/containerd/process.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/protobuf/google/rpc/doc.go b/components/engine/vendor/github.com/containerd/containerd/protobuf/google/rpc/doc.go index 9ab1e3e8e7..c76291766b 100644 --- a/components/engine/vendor/github.com/containerd/containerd/protobuf/google/rpc/doc.go +++ b/components/engine/vendor/github.com/containerd/containerd/protobuf/google/rpc/doc.go @@ -1 +1,17 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package rpc diff --git a/components/engine/vendor/github.com/containerd/containerd/reaper/reaper.go b/components/engine/vendor/github.com/containerd/containerd/reaper/reaper.go index 9127fc5a18..bdcb14c267 100644 --- a/components/engine/vendor/github.com/containerd/containerd/reaper/reaper.go +++ b/components/engine/vendor/github.com/containerd/containerd/reaper/reaper.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package reaper import ( @@ -15,7 +31,7 @@ import ( // ErrNoSuchProcess is returned when the process no longer exists var ErrNoSuchProcess = errors.New("no such process") -const bufferSize = 1024 +const bufferSize = 32 // Reap should be called when the process receives an SIGCHLD. Reap will reap // all exited processes and close their wait channels diff --git a/components/engine/vendor/github.com/containerd/containerd/reference/reference.go b/components/engine/vendor/github.com/containerd/containerd/reference/reference.go index 55c43b881c..79f165de02 100644 --- a/components/engine/vendor/github.com/containerd/containerd/reference/reference.go +++ b/components/engine/vendor/github.com/containerd/containerd/reference/reference.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package reference import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/auth.go b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/auth.go index aa33752a62..80bcb9dcf7 100644 --- a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/auth.go +++ b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/auth.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package docker import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go index 222cf83c0c..51e605e12d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go +++ b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package docker import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go index f6de60a274..5a77789537 100644 --- a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go +++ b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package docker import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/pusher.go b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/pusher.go index 405480b541..e2caf70286 100644 --- a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/pusher.go +++ b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/pusher.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package docker import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/resolver.go b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/resolver.go index 57a18b664a..06b1724b44 100644 --- a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/resolver.go +++ b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/resolver.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package docker import ( @@ -136,6 +152,9 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp log.G(ctx).Debug("resolving") resp, err := fetcher.doRequestWithRetries(ctx, req, nil) if err != nil { + if errors.Cause(err) == ErrInvalidAuthorization { + err = errors.Wrapf(err, "pull access denied, repository does not exist or may require authorization") + } return "", ocispec.Descriptor{}, err } resp.Body.Close() // don't care about body contents. diff --git a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go index 6b74cd67ee..1cf4dd7a1b 100644 --- a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go +++ b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package schema1 import ( @@ -103,8 +119,41 @@ func (c *Converter) Handle(ctx context.Context, desc ocispec.Descriptor) ([]ocis } } +// ConvertOptions provides options on converting a docker schema1 manifest. +type ConvertOptions struct { + // ManifestMediaType specifies the media type of the manifest OCI descriptor. + ManifestMediaType string + + // ConfigMediaType specifies the media type of the manifest config OCI + // descriptor. + ConfigMediaType string +} + +// ConvertOpt allows configuring a convert operation. +type ConvertOpt func(context.Context, *ConvertOptions) error + +// UseDockerSchema2 is used to indicate that a schema1 manifest should be +// converted into the media types for a docker schema2 manifest. +func UseDockerSchema2() ConvertOpt { + return func(ctx context.Context, o *ConvertOptions) error { + o.ManifestMediaType = images.MediaTypeDockerSchema2Manifest + o.ConfigMediaType = images.MediaTypeDockerSchema2Config + return nil + } +} + // Convert a docker manifest to an OCI descriptor -func (c *Converter) Convert(ctx context.Context) (ocispec.Descriptor, error) { +func (c *Converter) Convert(ctx context.Context, opts ...ConvertOpt) (ocispec.Descriptor, error) { + co := ConvertOptions{ + ManifestMediaType: ocispec.MediaTypeImageManifest, + ConfigMediaType: ocispec.MediaTypeImageConfig, + } + for _, opt := range opts { + if err := opt(ctx, &co); err != nil { + return ocispec.Descriptor{}, err + } + } + history, diffIDs, err := c.schema1ManifestHistory() if err != nil { return ocispec.Descriptor{}, errors.Wrap(err, "schema 1 conversion failed") @@ -121,13 +170,13 @@ func (c *Converter) Convert(ctx context.Context) (ocispec.Descriptor, error) { DiffIDs: diffIDs, } - b, err := json.Marshal(img) + b, err := json.MarshalIndent(img, "", " ") if err != nil { return ocispec.Descriptor{}, errors.Wrap(err, "failed to marshal image") } config := ocispec.Descriptor{ - MediaType: ocispec.MediaTypeImageConfig, + MediaType: co.ConfigMediaType, Digest: digest.Canonical.FromBytes(b), Size: int64(len(b)), } @@ -145,13 +194,13 @@ func (c *Converter) Convert(ctx context.Context) (ocispec.Descriptor, error) { Layers: layers, } - mb, err := json.Marshal(manifest) + mb, err := json.MarshalIndent(manifest, "", " ") if err != nil { return ocispec.Descriptor{}, errors.Wrap(err, "failed to marshal image") } desc := ocispec.Descriptor{ - MediaType: ocispec.MediaTypeImageManifest, + MediaType: co.ManifestMediaType, Digest: digest.Canonical.FromBytes(mb), Size: int64(len(mb)), } diff --git a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/scope.go b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/scope.go index 9cf0997dc2..52c2443118 100644 --- a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/scope.go +++ b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/scope.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package docker import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/status.go b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/status.go index 4b8dbbc5c4..8069d67671 100644 --- a/components/engine/vendor/github.com/containerd/containerd/remotes/docker/status.go +++ b/components/engine/vendor/github.com/containerd/containerd/remotes/docker/status.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package docker import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/remotes/handlers.go b/components/engine/vendor/github.com/containerd/containerd/remotes/handlers.go index ad4cd9f312..3353848100 100644 --- a/components/engine/vendor/github.com/containerd/containerd/remotes/handlers.go +++ b/components/engine/vendor/github.com/containerd/containerd/remotes/handlers.go @@ -1,17 +1,35 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package remotes import ( "context" - "encoding/json" "fmt" "io" "math/rand" + "strings" + "sync" "time" "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/log" + "github.com/containerd/containerd/platforms" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -99,88 +117,32 @@ func fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc break } + ws, err := cw.Status() + if err != nil { + return err + } + + if ws.Offset == desc.Size { + // If writer is already complete, commit and return + err := cw.Commit(ctx, desc.Size, desc.Digest) + if err != nil && !errdefs.IsAlreadyExists(err) { + return errors.Wrapf(err, "failed commit on ref %q", ws.Ref) + } + return nil + } + rc, err := fetcher.Fetch(ctx, desc) if err != nil { return err } defer rc.Close() - r, opts := commitOpts(desc, rc) - return content.Copy(ctx, cw, r, desc.Size, desc.Digest, opts...) -} - -// commitOpts gets the appropriate content options to alter -// the content info on commit based on media type. -func commitOpts(desc ocispec.Descriptor, r io.Reader) (io.Reader, []content.Opt) { - var childrenF func(r io.Reader) ([]ocispec.Descriptor, error) - - // TODO(AkihiroSuda): use images/oci.GetChildrenDescriptors? - switch desc.MediaType { - case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest: - childrenF = func(r io.Reader) ([]ocispec.Descriptor, error) { - var ( - manifest ocispec.Manifest - decoder = json.NewDecoder(r) - ) - if err := decoder.Decode(&manifest); err != nil { - return nil, err - } - - return append([]ocispec.Descriptor{manifest.Config}, manifest.Layers...), nil - } - case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex: - childrenF = func(r io.Reader) ([]ocispec.Descriptor, error) { - var ( - index ocispec.Index - decoder = json.NewDecoder(r) - ) - if err := decoder.Decode(&index); err != nil { - return nil, err - } - - return index.Manifests, nil - } - default: - return r, nil - } - - pr, pw := io.Pipe() - - var children []ocispec.Descriptor - errC := make(chan error) - - go func() { - defer close(errC) - ch, err := childrenF(pr) - if err != nil { - errC <- err - } - children = ch - }() - - opt := func(info *content.Info) error { - err := <-errC - if err != nil { - return errors.Wrap(err, "unable to get commit labels") - } - - if len(children) > 0 { - if info.Labels == nil { - info.Labels = map[string]string{} - } - for i, ch := range children { - info.Labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = ch.Digest.String() - } - } - return nil - } - - return io.TeeReader(r, pw), []content.Opt{opt} + return content.Copy(ctx, cw, rc, desc.Size, desc.Digest) } // PushHandler returns a handler that will push all content from the provider // using a writer from the pusher. -func PushHandler(provider content.Provider, pusher Pusher) images.HandlerFunc { +func PushHandler(pusher Pusher, provider content.Provider) images.HandlerFunc { return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { ctx = log.WithLogger(ctx, log.G(ctx).WithFields(logrus.Fields{ "digest": desc.Digest, @@ -215,3 +177,55 @@ func push(ctx context.Context, provider content.Provider, pusher Pusher, desc oc rd := io.NewSectionReader(ra, 0, desc.Size) return content.Copy(ctx, cw, rd, desc.Size, desc.Digest) } + +// PushContent pushes content specified by the descriptor from the provider. +// +// Base handlers can be provided which will be called before any push specific +// handlers. +func PushContent(ctx context.Context, pusher Pusher, desc ocispec.Descriptor, provider content.Provider, baseHandlers ...images.Handler) error { + var m sync.Mutex + manifestStack := []ocispec.Descriptor{} + + filterHandler := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + switch desc.MediaType { + case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest, + images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex: + m.Lock() + manifestStack = append(manifestStack, desc) + m.Unlock() + return nil, images.ErrStopHandler + default: + return nil, nil + } + }) + + pushHandler := PushHandler(pusher, provider) + + handlers := append(baseHandlers, + images.FilterPlatform(platforms.Default(), images.ChildrenHandler(provider)), + filterHandler, + pushHandler, + ) + + if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil { + return err + } + + // Iterate in reverse order as seen, parent always uploaded after child + for i := len(manifestStack) - 1; i >= 0; i-- { + _, err := pushHandler(ctx, manifestStack[i]) + if err != nil { + // TODO(estesp): until we have a more complete method for index push, we need to report + // missing dependencies in an index/manifest list by sensing the "400 Bad Request" + // as a marker for this problem + if (manifestStack[i].MediaType == ocispec.MediaTypeImageIndex || + manifestStack[i].MediaType == images.MediaTypeDockerSchema2ManifestList) && + errors.Cause(err) != nil && strings.Contains(errors.Cause(err).Error(), "400 Bad Request") { + return errors.Wrap(err, "manifest list/index references to blobs and/or manifests are missing in your target registry") + } + return err + } + } + + return nil +} diff --git a/components/engine/vendor/github.com/containerd/containerd/remotes/resolver.go b/components/engine/vendor/github.com/containerd/containerd/remotes/resolver.go index caf4c97ce1..a9b2b78aa8 100644 --- a/components/engine/vendor/github.com/containerd/containerd/remotes/resolver.go +++ b/components/engine/vendor/github.com/containerd/containerd/remotes/resolver.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package remotes import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/rootfs/apply.go b/components/engine/vendor/github.com/containerd/containerd/rootfs/apply.go index 405129564a..73613337df 100644 --- a/components/engine/vendor/github.com/containerd/containerd/rootfs/apply.go +++ b/components/engine/vendor/github.com/containerd/containerd/rootfs/apply.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package rootfs import ( @@ -30,7 +46,7 @@ type Layer struct { // The returned result is a chain id digest representing all the applied layers. // Layers are applied in order they are given, making the first layer the // bottom-most layer in the layer chain. -func ApplyLayers(ctx context.Context, layers []Layer, sn snapshots.Snapshotter, a diff.Differ) (digest.Digest, error) { +func ApplyLayers(ctx context.Context, layers []Layer, sn snapshots.Snapshotter, a diff.Applier) (digest.Digest, error) { var chain []digest.Digest for _, layer := range layers { if _, err := ApplyLayer(ctx, layer, chain, sn, a); err != nil { @@ -46,7 +62,7 @@ func ApplyLayers(ctx context.Context, layers []Layer, sn snapshots.Snapshotter, // ApplyLayer applies a single layer on top of the given provided layer chain, // using the provided snapshotter and applier. If the layer was unpacked true // is returned, if the layer already exists false is returned. -func ApplyLayer(ctx context.Context, layer Layer, chain []digest.Digest, sn snapshots.Snapshotter, a diff.Differ, opts ...snapshots.Opt) (bool, error) { +func ApplyLayer(ctx context.Context, layer Layer, chain []digest.Digest, sn snapshots.Snapshotter, a diff.Applier, opts ...snapshots.Opt) (bool, error) { var ( parent = identity.ChainID(chain) chainID = identity.ChainID(append(chain, layer.Diff.Digest)) diff --git a/components/engine/vendor/github.com/containerd/containerd/rootfs/diff.go b/components/engine/vendor/github.com/containerd/containerd/rootfs/diff.go index bab7a3cca1..c7f954e9ff 100644 --- a/components/engine/vendor/github.com/containerd/containerd/rootfs/diff.go +++ b/components/engine/vendor/github.com/containerd/containerd/rootfs/diff.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package rootfs import ( @@ -10,11 +26,11 @@ import ( "golang.org/x/net/context" ) -// Diff creates a layer diff for the given snapshot identifier from the parent -// of the snapshot. A content ref is provided to track the progress of the -// content creation and the provided snapshotter and mount differ are used +// CreateDiff creates a layer diff for the given snapshot identifier from the +// parent of the snapshot. A content ref is provided to track the progress of +// the content creation and the provided snapshotter and mount differ are used // for calculating the diff. The descriptor for the layer diff is returned. -func Diff(ctx context.Context, snapshotID string, sn snapshots.Snapshotter, d diff.Differ, opts ...diff.Opt) (ocispec.Descriptor, error) { +func CreateDiff(ctx context.Context, snapshotID string, sn snapshots.Snapshotter, d diff.Comparer, opts ...diff.Opt) (ocispec.Descriptor, error) { info, err := sn.Stat(ctx, snapshotID) if err != nil { return ocispec.Descriptor{}, err @@ -39,8 +55,8 @@ func Diff(ctx context.Context, snapshotID string, sn snapshots.Snapshotter, d di if err != nil { return ocispec.Descriptor{}, err } - defer sn.Remove(ctx, lowerKey) + defer sn.Remove(ctx, upperKey) } - return d.DiffMounts(ctx, lower, upper, opts...) + return d.Compare(ctx, lower, upper, opts...) } diff --git a/components/engine/vendor/github.com/containerd/containerd/rootfs/init.go b/components/engine/vendor/github.com/containerd/containerd/rootfs/init.go index 4f32f11ae7..326f30f709 100644 --- a/components/engine/vendor/github.com/containerd/containerd/rootfs/init.go +++ b/components/engine/vendor/github.com/containerd/containerd/rootfs/init.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package rootfs import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/rootfs/init_linux.go b/components/engine/vendor/github.com/containerd/containerd/rootfs/init_linux.go index cabc4577e0..84dc56522d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/rootfs/init_linux.go +++ b/components/engine/vendor/github.com/containerd/containerd/rootfs/init_linux.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package rootfs import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/rootfs/init_other.go b/components/engine/vendor/github.com/containerd/containerd/rootfs/init_other.go index b5e04e2e60..261121085d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/rootfs/init_other.go +++ b/components/engine/vendor/github.com/containerd/containerd/rootfs/init_other.go @@ -1,5 +1,21 @@ // +build !linux +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package rootfs const ( diff --git a/components/engine/vendor/github.com/containerd/containerd/runtime/events.go b/components/engine/vendor/github.com/containerd/containerd/runtime/events.go index 36b701dd3c..4a064c8854 100644 --- a/components/engine/vendor/github.com/containerd/containerd/runtime/events.go +++ b/components/engine/vendor/github.com/containerd/containerd/runtime/events.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package runtime const ( diff --git a/components/engine/vendor/github.com/containerd/containerd/runtime/monitor.go b/components/engine/vendor/github.com/containerd/containerd/runtime/monitor.go index f5f8f1c75f..eb07ebdb4a 100644 --- a/components/engine/vendor/github.com/containerd/containerd/runtime/monitor.go +++ b/components/engine/vendor/github.com/containerd/containerd/runtime/monitor.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package runtime // TaskMonitor provides an interface for monitoring of containers within containerd diff --git a/components/engine/vendor/github.com/containerd/containerd/runtime/runtime.go b/components/engine/vendor/github.com/containerd/containerd/runtime/runtime.go index 39ffe86b9f..000af4ad34 100644 --- a/components/engine/vendor/github.com/containerd/containerd/runtime/runtime.go +++ b/components/engine/vendor/github.com/containerd/containerd/runtime/runtime.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package runtime import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/runtime/task.go b/components/engine/vendor/github.com/containerd/containerd/runtime/task.go index 4c02455dcf..f8a67cfaee 100644 --- a/components/engine/vendor/github.com/containerd/containerd/runtime/task.go +++ b/components/engine/vendor/github.com/containerd/containerd/runtime/task.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package runtime import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/runtime/task_list.go b/components/engine/vendor/github.com/containerd/containerd/runtime/task_list.go index 05f34c3230..92f25715c8 100644 --- a/components/engine/vendor/github.com/containerd/containerd/runtime/task_list.go +++ b/components/engine/vendor/github.com/containerd/containerd/runtime/task_list.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package runtime import ( @@ -92,7 +108,7 @@ func (l *TaskList) AddWithNamespace(namespace string, t Task) error { } // Delete a task -func (l *TaskList) Delete(ctx context.Context, t Task) { +func (l *TaskList) Delete(ctx context.Context, id string) { l.mu.Lock() defer l.mu.Unlock() namespace, err := namespaces.NamespaceRequired(ctx) @@ -101,6 +117,6 @@ func (l *TaskList) Delete(ctx context.Context, t Task) { } tasks, ok := l.tasks[namespace] if ok { - delete(tasks, t.ID()) + delete(tasks, id) } } diff --git a/components/engine/vendor/github.com/containerd/containerd/runtime/typeurl.go b/components/engine/vendor/github.com/containerd/containerd/runtime/typeurl.go index 8ba2b43a61..eb54e250f3 100644 --- a/components/engine/vendor/github.com/containerd/containerd/runtime/typeurl.go +++ b/components/engine/vendor/github.com/containerd/containerd/runtime/typeurl.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package runtime import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/server/config.go b/components/engine/vendor/github.com/containerd/containerd/server/config.go index f056c7b837..67d1f4bcb5 100644 --- a/components/engine/vendor/github.com/containerd/containerd/server/config.go +++ b/components/engine/vendor/github.com/containerd/containerd/server/config.go @@ -1,9 +1,22 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package server import ( - "bytes" - "io" - "github.com/BurntSushi/toml" "github.com/containerd/containerd/errdefs" "github.com/pkg/errors" @@ -21,10 +34,11 @@ type Config struct { Debug Debug `toml:"debug"` // Metrics and monitoring settings Metrics MetricsConfig `toml:"metrics"` + // DisabledPlugins are IDs of plugins to disable. Disabled plugins won't be + // initialized and started. + DisabledPlugins []string `toml:"disabled_plugins"` // Plugins provides plugin specific configuration for the initialization of a plugin Plugins map[string]toml.Primitive `toml:"plugins"` - // NoSubreaper disables containerd as a subreaper - NoSubreaper bool `toml:"no_subreaper"` // OOMScore adjust the containerd's oom score OOMScore int `toml:"oom_score"` // Cgroup specifies cgroup information for the containerd daemon process @@ -50,7 +64,8 @@ type Debug struct { // MetricsConfig provides metrics configuration type MetricsConfig struct { - Address string `toml:"address"` + Address string `toml:"address"` + GRPCHistogram bool `toml:"grpc_histogram"` } // CgroupConfig provides cgroup configuration @@ -70,16 +85,6 @@ func (c *Config) Decode(id string, v interface{}) (interface{}, error) { return v, nil } -// WriteTo marshals the config to the provided writer -func (c *Config) WriteTo(w io.Writer) (int64, error) { - buf := bytes.NewBuffer(nil) - e := toml.NewEncoder(buf) - if err := e.Encode(c); err != nil { - return 0, err - } - return io.Copy(w, buf) -} - // LoadConfig loads the containerd server config from the provided path func LoadConfig(path string, v *Config) error { if v == nil { diff --git a/components/engine/vendor/github.com/containerd/containerd/server/server.go b/components/engine/vendor/github.com/containerd/containerd/server/server.go index 6af6df073a..f5b24f6364 100644 --- a/components/engine/vendor/github.com/containerd/containerd/server/server.go +++ b/components/engine/vendor/github.com/containerd/containerd/server/server.go @@ -1,7 +1,24 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package server import ( "expvar" + "io" "net" "net/http" "net/http/pprof" @@ -10,17 +27,6 @@ import ( "strings" "github.com/boltdb/bolt" - containers "github.com/containerd/containerd/api/services/containers/v1" - contentapi "github.com/containerd/containerd/api/services/content/v1" - diff "github.com/containerd/containerd/api/services/diff/v1" - eventsapi "github.com/containerd/containerd/api/services/events/v1" - images "github.com/containerd/containerd/api/services/images/v1" - introspection "github.com/containerd/containerd/api/services/introspection/v1" - leasesapi "github.com/containerd/containerd/api/services/leases/v1" - namespaces "github.com/containerd/containerd/api/services/namespaces/v1" - snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1" - tasks "github.com/containerd/containerd/api/services/tasks/v1" - version "github.com/containerd/containerd/api/services/version/v1" "github.com/containerd/containerd/content" "github.com/containerd/containerd/content/local" "github.com/containerd/containerd/events/exchange" @@ -34,7 +40,6 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" - "google.golang.org/grpc/health/grpc_health_v1" ) // New creates and initializes a new containerd server @@ -57,12 +62,12 @@ func New(ctx context.Context, config *Config) (*Server, error) { if err := apply(ctx, config); err != nil { return nil, err } - plugins, err := loadPlugins(config) + plugins, err := LoadPlugins(config) if err != nil { return nil, err } rpc := grpc.NewServer( - grpc.UnaryInterceptor(interceptor), + grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor), grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor), ) var ( @@ -70,6 +75,7 @@ func New(ctx context.Context, config *Config) (*Server, error) { s = &Server{ rpc: rpc, events: exchange.NewExchange(), + config: config, } initialized = plugin.NewPluginSet() ) @@ -113,6 +119,7 @@ func New(ctx context.Context, config *Config) (*Server, error) { if service, ok := instance.(plugin.Service); ok { services = append(services, service) } + s.plugins = append(s.plugins, result) } // register services after all plugins have been initialized for _, service := range services { @@ -125,12 +132,18 @@ func New(ctx context.Context, config *Config) (*Server, error) { // Server is the containerd main daemon type Server struct { - rpc *grpc.Server - events *exchange.Exchange + rpc *grpc.Server + events *exchange.Exchange + config *Config + plugins []*plugin.Plugin } // ServeGRPC provides the containerd grpc APIs on the provided listener func (s *Server) ServeGRPC(l net.Listener) error { + if s.config.Metrics.GRPCHistogram { + // enable grpc time histograms to measure rpc latencies + grpc_prometheus.EnableHandlingTimeHistogram() + } // before we start serving the grpc API regster the grpc_prometheus metrics // handler. This needs to be the last service registered so that it can collect // metrics for every other service @@ -162,9 +175,28 @@ func (s *Server) ServeDebug(l net.Listener) error { // Stop the containerd server canceling any open connections func (s *Server) Stop() { s.rpc.Stop() + for i := len(s.plugins) - 1; i >= 0; i-- { + p := s.plugins[i] + instance, err := p.Instance() + if err != nil { + log.L.WithError(err).WithField("id", p.Registration.ID). + Errorf("could not get plugin instance") + continue + } + closer, ok := instance.(io.Closer) + if !ok { + continue + } + if err := closer.Close(); err != nil { + log.L.WithError(err).WithField("id", p.Registration.ID). + Errorf("failed to close plugin") + } + } } -func loadPlugins(config *Config) ([]*plugin.Registration, error) { +// LoadPlugins loads all plugins into containerd and generates an ordered graph +// of all plugins. +func LoadPlugins(config *Config) ([]*plugin.Registration, error) { // load all plugins into containerd if err := plugin.Load(filepath.Join(config.Root, "plugins")); err != nil { return nil, err @@ -226,45 +258,7 @@ func loadPlugins(config *Config) ([]*plugin.Registration, error) { }) // return the ordered graph for plugins - return plugin.Graph(), nil -} - -func interceptor( - ctx context.Context, - req interface{}, - info *grpc.UnaryServerInfo, - handler grpc.UnaryHandler, -) (interface{}, error) { - ctx = log.WithModule(ctx, "containerd") - switch info.Server.(type) { - case tasks.TasksServer: - ctx = log.WithModule(ctx, "tasks") - case containers.ContainersServer: - ctx = log.WithModule(ctx, "containers") - case contentapi.ContentServer: - ctx = log.WithModule(ctx, "content") - case images.ImagesServer: - ctx = log.WithModule(ctx, "images") - case grpc_health_v1.HealthServer: - // No need to change the context - case version.VersionServer: - ctx = log.WithModule(ctx, "version") - case snapshotsapi.SnapshotsServer: - ctx = log.WithModule(ctx, "snapshot") - case diff.DiffServer: - ctx = log.WithModule(ctx, "diff") - case namespaces.NamespacesServer: - ctx = log.WithModule(ctx, "namespaces") - case eventsapi.EventsServer: - ctx = log.WithModule(ctx, "events") - case introspection.IntrospectionServer: - ctx = log.WithModule(ctx, "introspection") - case leasesapi.LeasesServer: - ctx = log.WithModule(ctx, "leases") - default: - log.G(ctx).Warnf("unknown GRPC server type: %#v\n", info.Server) - } - return grpc_prometheus.UnaryServerInterceptor(ctx, req, info, handler) + return plugin.Graph(config.DisabledPlugins), nil } func trapClosedConnErr(err error) error { diff --git a/components/engine/vendor/github.com/containerd/containerd/server/server_linux.go b/components/engine/vendor/github.com/containerd/containerd/server/server_linux.go index 98bfbd7259..c45ccd3d51 100644 --- a/components/engine/vendor/github.com/containerd/containerd/server/server_linux.go +++ b/components/engine/vendor/github.com/containerd/containerd/server/server_linux.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package server import ( @@ -12,12 +28,6 @@ import ( // apply sets config settings on the server process func apply(ctx context.Context, config *Config) error { - if !config.NoSubreaper { - log.G(ctx).Info("setting subreaper...") - if err := sys.SetSubreaper(1); err != nil { - return err - } - } if config.OOMScore != 0 { log.G(ctx).Debugf("changing OOM score to %d", config.OOMScore) if err := sys.SetOOMScore(os.Getpid(), config.OOMScore); err != nil { diff --git a/components/engine/vendor/github.com/containerd/containerd/server/server_solaris.go b/components/engine/vendor/github.com/containerd/containerd/server/server_solaris.go index 3c39816be2..0dbbb9feac 100644 --- a/components/engine/vendor/github.com/containerd/containerd/server/server_solaris.go +++ b/components/engine/vendor/github.com/containerd/containerd/server/server_solaris.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package server import "context" diff --git a/components/engine/vendor/github.com/containerd/containerd/server/server_unsupported.go b/components/engine/vendor/github.com/containerd/containerd/server/server_unsupported.go index 4df599e114..c6211dbb2a 100644 --- a/components/engine/vendor/github.com/containerd/containerd/server/server_unsupported.go +++ b/components/engine/vendor/github.com/containerd/containerd/server/server_unsupported.go @@ -1,5 +1,21 @@ // +build !linux,!windows,!solaris +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package server import "context" diff --git a/components/engine/vendor/github.com/containerd/containerd/server/server_windows.go b/components/engine/vendor/github.com/containerd/containerd/server/server_windows.go index 37b71dfa1a..ac0b8481c2 100644 --- a/components/engine/vendor/github.com/containerd/containerd/server/server_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/server/server_windows.go @@ -1,5 +1,21 @@ // +build windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package server import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/snapshot.go b/components/engine/vendor/github.com/containerd/containerd/snapshot.go index 85bdba1b68..155ec718f0 100644 --- a/components/engine/vendor/github.com/containerd/containerd/snapshot.go +++ b/components/engine/vendor/github.com/containerd/containerd/snapshot.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/snapshots/snapshotter.go b/components/engine/vendor/github.com/containerd/containerd/snapshots/snapshotter.go index cde4c72618..d11252d1e3 100644 --- a/components/engine/vendor/github.com/containerd/containerd/snapshots/snapshotter.go +++ b/components/engine/vendor/github.com/containerd/containerd/snapshots/snapshotter.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package snapshots import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_linux.go b/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_linux.go index c432a2d552..d925d4ef94 100644 --- a/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_linux.go +++ b/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_linux.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd const ( diff --git a/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_unix.go b/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_unix.go index cb8b08ac19..abd8f86947 100644 --- a/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_unix.go @@ -1,5 +1,21 @@ // +build darwin freebsd solaris +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd const ( diff --git a/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_windows.go b/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_windows.go index 3b73582187..320211a4a5 100644 --- a/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/snapshotter_default_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd const ( diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/epoll.go b/components/engine/vendor/github.com/containerd/containerd/sys/epoll.go index 3a4d97cfa8..683f38eea8 100644 --- a/components/engine/vendor/github.com/containerd/containerd/sys/epoll.go +++ b/components/engine/vendor/github.com/containerd/containerd/sys/epoll.go @@ -1,5 +1,21 @@ // +build linux +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package sys import "golang.org/x/sys/unix" diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/fds.go b/components/engine/vendor/github.com/containerd/containerd/sys/fds.go index 3c1ec67e5a..db3cf702f4 100644 --- a/components/engine/vendor/github.com/containerd/containerd/sys/fds.go +++ b/components/engine/vendor/github.com/containerd/containerd/sys/fds.go @@ -1,5 +1,21 @@ // +build !windows,!darwin +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package sys import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/filesys_unix.go b/components/engine/vendor/github.com/containerd/containerd/sys/filesys_unix.go new file mode 100644 index 0000000000..700f44efa9 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/sys/filesys_unix.go @@ -0,0 +1,26 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package sys + +import "os" + +// ForceRemoveAll on unix is just a wrapper for os.RemoveAll +func ForceRemoveAll(path string) error { + return os.RemoveAll(path) +} diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/filesys_windows.go b/components/engine/vendor/github.com/containerd/containerd/sys/filesys_windows.go index b5ce13579d..dc880c3427 100644 --- a/components/engine/vendor/github.com/containerd/containerd/sys/filesys_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/sys/filesys_windows.go @@ -1,5 +1,21 @@ // +build windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package sys import ( @@ -11,6 +27,7 @@ import ( "unsafe" winio "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim" ) // MkdirAllWithACL is a wrapper for MkdirAll that creates a directory @@ -234,3 +251,13 @@ func syscallOpenSequential(path string, mode int, _ uint32) (fd syscall.Handle, h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0) return h, e } + +// ForceRemoveAll is the same as os.RemoveAll, but uses hcsshim.DestroyLayer in order +// to delete container layers. +func ForceRemoveAll(path string) error { + info := hcsshim.DriverInfo{ + HomeDir: filepath.Dir(path), + } + + return hcsshim.DestroyLayer(info, filepath.Base(path)) +} diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/oom_unix.go b/components/engine/vendor/github.com/containerd/containerd/sys/oom_unix.go index 23fcc94371..1abe7485b6 100644 --- a/components/engine/vendor/github.com/containerd/containerd/sys/oom_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/sys/oom_unix.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package sys import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/oom_windows.go b/components/engine/vendor/github.com/containerd/containerd/sys/oom_windows.go index 6e42ddce8e..f44bcebd1e 100644 --- a/components/engine/vendor/github.com/containerd/containerd/sys/oom_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/sys/oom_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package sys // SetOOMScore sets the oom score for the process diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/prctl.go b/components/engine/vendor/github.com/containerd/containerd/sys/prctl.go deleted file mode 100644 index aa1a4ad38a..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/sys/prctl.go +++ /dev/null @@ -1,41 +0,0 @@ -// +build linux - -// Package sys provides access to the Get Child and Set Child prctl flags. -// See http://man7.org/linux/man-pages/man2/prctl.2.html -package sys - -import ( - "unsafe" - - "golang.org/x/sys/unix" -) - -// GetSubreaper returns the subreaper setting for the calling process -func GetSubreaper() (int, error) { - var i uintptr - // PR_GET_CHILD_SUBREAPER allows retrieving the current child - // subreaper. - // Returns the "child subreaper" setting of the caller, in the - // location pointed to by (int *) arg2. - if err := unix.Prctl(unix.PR_GET_CHILD_SUBREAPER, uintptr(unsafe.Pointer(&i)), 0, 0, 0); err != nil { - return -1, err - } - return int(i), nil -} - -// SetSubreaper sets the value i as the subreaper setting for the calling process -func SetSubreaper(i int) error { - // PR_SET_CHILD_SUBREAPER allows setting the child subreaper. - // If arg2 is nonzero, set the "child subreaper" attribute of the - // calling process; if arg2 is zero, unset the attribute. When a - // process is marked as a child subreaper, all of the children - // that it creates, and their descendants, will be marked as - // having a subreaper. In effect, a subreaper fulfills the role - // of init(1) for its descendant processes. Upon termination of - // a process that is orphaned (i.e., its immediate parent has - // already terminated) and marked as having a subreaper, the - // nearest still living ancestor subreaper will receive a SIGCHLD - // signal and be able to wait(2) on the process to discover its - // termination status. - return unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0) -} diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/proc.go b/components/engine/vendor/github.com/containerd/containerd/sys/proc.go index fbe7b51905..496eb1fea1 100644 --- a/components/engine/vendor/github.com/containerd/containerd/sys/proc.go +++ b/components/engine/vendor/github.com/containerd/containerd/sys/proc.go @@ -1,5 +1,21 @@ // +build linux +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package sys import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/reaper.go b/components/engine/vendor/github.com/containerd/containerd/sys/reaper.go index bbc5a1e868..23cb040b80 100644 --- a/components/engine/vendor/github.com/containerd/containerd/sys/reaper.go +++ b/components/engine/vendor/github.com/containerd/containerd/sys/reaper.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package sys import "golang.org/x/sys/unix" diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/socket_unix.go b/components/engine/vendor/github.com/containerd/containerd/sys/socket_unix.go index 0d5f049aa8..4d71c709ad 100644 --- a/components/engine/vendor/github.com/containerd/containerd/sys/socket_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/sys/socket_unix.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package sys import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/socket_windows.go b/components/engine/vendor/github.com/containerd/containerd/sys/socket_windows.go index de25c08601..3ee7679b49 100644 --- a/components/engine/vendor/github.com/containerd/containerd/sys/socket_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/sys/socket_windows.go @@ -1,5 +1,21 @@ // +build windows +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package sys import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/stat_bsd.go b/components/engine/vendor/github.com/containerd/containerd/sys/stat_bsd.go index e043ae52bf..b9c95d90d7 100644 --- a/components/engine/vendor/github.com/containerd/containerd/sys/stat_bsd.go +++ b/components/engine/vendor/github.com/containerd/containerd/sys/stat_bsd.go @@ -1,9 +1,26 @@ // +build darwin freebsd +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package sys import ( "syscall" + "time" ) // StatAtime returns the access time from a stat struct @@ -20,3 +37,8 @@ func StatCtime(st *syscall.Stat_t) syscall.Timespec { func StatMtime(st *syscall.Stat_t) syscall.Timespec { return st.Mtimespec } + +// StatATimeAsTime returns the access time as a time.Time +func StatATimeAsTime(st *syscall.Stat_t) time.Time { + return time.Unix(int64(st.Atimespec.Sec), int64(st.Atimespec.Nsec)) // nolint: unconvert +} diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/stat_unix.go b/components/engine/vendor/github.com/containerd/containerd/sys/stat_unix.go index 1f983a98db..21a666dff8 100644 --- a/components/engine/vendor/github.com/containerd/containerd/sys/stat_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/sys/stat_unix.go @@ -1,9 +1,26 @@ // +build linux solaris +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package sys import ( "syscall" + "time" ) // StatAtime returns the Atim @@ -20,3 +37,8 @@ func StatCtime(st *syscall.Stat_t) syscall.Timespec { func StatMtime(st *syscall.Stat_t) syscall.Timespec { return st.Mtim } + +// StatATimeAsTime returns st.Atim as a time.Time +func StatATimeAsTime(st *syscall.Stat_t) time.Time { + return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) // nolint: unconvert +} diff --git a/components/engine/vendor/github.com/containerd/containerd/task.go b/components/engine/vendor/github.com/containerd/containerd/task.go index 121da9af5b..f801d493d6 100644 --- a/components/engine/vendor/github.com/containerd/containerd/task.go +++ b/components/engine/vendor/github.com/containerd/containerd/task.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( @@ -267,7 +283,6 @@ func (t *task) Delete(ctx context.Context, opts ...ProcessDeleteOpts) (*ExitStat if t.io != nil { t.io.Cancel() t.io.Wait() - t.io.Close() } r, err := t.client.TaskService().Delete(ctx, &tasks.DeleteTaskRequest{ ContainerID: t.id, @@ -275,6 +290,10 @@ func (t *task) Delete(ctx context.Context, opts ...ProcessDeleteOpts) (*ExitStat if err != nil { return nil, errdefs.FromGRPC(err) } + // Only cleanup the IO after a successful Delete + if t.io != nil { + t.io.Close() + } return &ExitStatus{code: r.ExitStatus, exitedAt: r.ExitedAt}, nil } @@ -536,7 +555,7 @@ func (t *task) checkpointRWSnapshot(ctx context.Context, index *v1.Index, snapsh opts := []diff.Opt{ diff.WithReference(fmt.Sprintf("checkpoint-rw-%s", id)), } - rw, err := rootfs.Diff(ctx, id, t.client.SnapshotService(snapshotterName), t.client.DiffService(), opts...) + rw, err := rootfs.CreateDiff(ctx, id, t.client.SnapshotService(snapshotterName), t.client.DiffService(), opts...) if err != nil { return err } @@ -572,7 +591,7 @@ func (t *task) writeIndex(ctx context.Context, index *v1.Index) (d v1.Descriptor return writeContent(ctx, t.client.ContentStore(), v1.MediaTypeImageIndex, t.id, buf, content.WithLabels(labels)) } -func writeContent(ctx context.Context, store content.Store, mediaType, ref string, r io.Reader, opts ...content.Opt) (d v1.Descriptor, err error) { +func writeContent(ctx context.Context, store content.Ingester, mediaType, ref string, r io.Reader, opts ...content.Opt) (d v1.Descriptor, err error) { writer, err := store.Writer(ctx, ref, 0, "") if err != nil { return d, err diff --git a/components/engine/vendor/github.com/containerd/containerd/task_opts.go b/components/engine/vendor/github.com/containerd/containerd/task_opts.go index a387adb6ed..495d4225b9 100644 --- a/components/engine/vendor/github.com/containerd/containerd/task_opts.go +++ b/components/engine/vendor/github.com/containerd/containerd/task_opts.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/task_opts_linux.go b/components/engine/vendor/github.com/containerd/containerd/task_opts_linux.go index 5b91cb5485..63136fd6ae 100644 --- a/components/engine/vendor/github.com/containerd/containerd/task_opts_linux.go +++ b/components/engine/vendor/github.com/containerd/containerd/task_opts_linux.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/task_opts_windows.go b/components/engine/vendor/github.com/containerd/containerd/task_opts_windows.go index d77402c675..60836bc8fa 100644 --- a/components/engine/vendor/github.com/containerd/containerd/task_opts_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/task_opts_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package containerd import ( diff --git a/components/engine/vendor/github.com/containerd/containerd/vendor.conf b/components/engine/vendor/github.com/containerd/containerd/vendor.conf index 030c77349b..0639c6399c 100644 --- a/components/engine/vendor/github.com/containerd/containerd/vendor.conf +++ b/components/engine/vendor/github.com/containerd/containerd/vendor.conf @@ -1,26 +1,24 @@ github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6 -github.com/containerd/go-runc ed1cbe1fc31f5fb2359d3a54b6330d1a097858b7 +github.com/containerd/go-runc 4f6e87ae043f859a38255247b49c9abc262d002f github.com/containerd/console 84eeaae905fa414d03e07bcd6c8d3f19e7cf180e -github.com/containerd/cgroups 29da22c6171a4316169f9205ab6c49f59b5b852f +github.com/containerd/cgroups c0710c92e8b3a44681d1321dcfd1360fc5c6c089 github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788 -github.com/docker/go-metrics 8fd5772bf1584597834c6f7961a530f06cbfbb87 +github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 github.com/godbus/dbus c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f -github.com/prometheus/client_golang v0.8.0 -github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6 -github.com/prometheus/common 195bde7883f7c39ea62b0d92ab7359b5327065cb -github.com/prometheus/procfs fcdb11ccb4389efb1b210b7ffb623ab71c5fdd60 +github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823 +github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c +github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563 +github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 github.com/matttproud/golang_protobuf_extensions v1.0.0 github.com/docker/go-units v0.3.1 github.com/gogo/protobuf v0.5 github.com/golang/protobuf 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9 github.com/opencontainers/runtime-spec v1.0.1 -github.com/opencontainers/runc 7f24b40cc5423969b4554ef04ba0b00e2b4ba010 +github.com/opencontainers/runc a618ab5a0186905949ee463dbb762c3d23e12a80 github.com/sirupsen/logrus v1.0.0 github.com/containerd/btrfs cc52c4dea2ce11a44e6639e561bb5c2af9ada9e3 -github.com/stretchr/testify v1.1.4 -github.com/davecgh/go-spew v1.1.0 github.com/pmezard/go-difflib v1.0.0 github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6 github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c @@ -30,14 +28,49 @@ github.com/pkg/errors v0.8.0 github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448 golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993 https://github.com/golang/sys github.com/opencontainers/image-spec v1.0.1 -github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e +github.com/containerd/continuity d8fb8589b0e8e85b8c8bbaa8840226d0dfeb7371 golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c -github.com/BurntSushi/toml v0.2.0-21-g9906417 +github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 -github.com/Microsoft/go-winio v0.4.4 +github.com/Microsoft/go-winio v0.4.5 github.com/Microsoft/hcsshim v0.6.7 github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 github.com/dmcgowan/go-tar go1.10 -github.com/stevvooe/ttrpc 76e68349ad9ab4d03d764c713826d31216715e4f +github.com/stevvooe/ttrpc d4528379866b0ce7e9d71f3eb96f0582fc374577 +github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 +github.com/gotestyourself/gotestyourself 44dbf532bbf5767611f6f2a61bded572e337010a +github.com/google/go-cmp v0.1.0 +# cri dependencies +github.com/containerd/cri-containerd c9081b2ec0eefc799f0f1caabbea29d516c72c44 +github.com/blang/semver v3.1.0 +github.com/containernetworking/cni v0.6.0 +github.com/containernetworking/plugins v0.6.0 +github.com/cri-o/ocicni 9b451e26eb7c694d564991fbf44f77d0afb9b03c +github.com/davecgh/go-spew v1.1.0 +github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621 +github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00 +github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528 +github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46 +github.com/fsnotify/fsnotify 7d7316ed6e1ed2de075aab8dfc76de5d158d66e1 +github.com/ghodss/yaml 73d445a93680fa1a78ae23a5839bad48f32ba1ee +github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed +github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c +github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55 +github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f +github.com/json-iterator/go 1.0.4 +github.com/opencontainers/runtime-tools 6073aff4ac61897f75895123f7e24135204a404d +github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206 +github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 +github.com/spf13/pflag v1.0.0 +github.com/tchap/go-patricia 5ad6cdb7538b0097d5598c7e57f0a24072adf7dc +golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 +gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 +gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77 +k8s.io/api a1d6dce6736a6c75929bb75111e89077e35a5856 +k8s.io/apimachinery 8259d997cf059cd83dc47e5f8074b7a7d7967c09 +k8s.io/apiserver 8e45eac9dff86447a5c2effe6a3d2cba70121ebf +k8s.io/client-go 33bd23f75b6de861994706a322b0afab824b2171 +k8s.io/kubernetes 05944b1d2ca7f60b09762a330425108f48f6b603 +k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e diff --git a/components/engine/vendor/github.com/containerd/containerd/windows/hcsshimtypes/doc.go b/components/engine/vendor/github.com/containerd/containerd/windows/hcsshimtypes/doc.go index 4b1b4b3414..9fe5cd0a4d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/windows/hcsshimtypes/doc.go +++ b/components/engine/vendor/github.com/containerd/containerd/windows/hcsshimtypes/doc.go @@ -1,2 +1,18 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + // Package hcsshimtypes holds the windows runtime specific types package hcsshimtypes diff --git a/components/engine/vendor/golang.org/x/net/README b/components/engine/vendor/golang.org/x/net/README deleted file mode 100644 index 6b13d8e505..0000000000 --- a/components/engine/vendor/golang.org/x/net/README +++ /dev/null @@ -1,3 +0,0 @@ -This repository holds supplementary Go networking libraries. - -To submit changes to this repository, see http://golang.org/doc/contribute.html. diff --git a/components/engine/vendor/golang.org/x/net/README.md b/components/engine/vendor/golang.org/x/net/README.md new file mode 100644 index 0000000000..00a9b6eb25 --- /dev/null +++ b/components/engine/vendor/golang.org/x/net/README.md @@ -0,0 +1,16 @@ +# Go Networking + +This repository holds supplementary Go networking libraries. + +## Download/Install + +The easiest way to install is to run `go get -u golang.org/x/net`. You can +also manually git clone the repository to `$GOPATH/src/golang.org/x/net`. + +## Report Issues / Send Patches + +This repository uses Gerrit for code changes. To learn how to submit +changes to this repository, see https://golang.org/doc/contribute.html. +The main issue tracker for the net repository is located at +https://github.com/golang/go/issues. Prefix your issue with "x/net:" in the +subject line, so it is easy to find. diff --git a/components/engine/vendor/golang.org/x/net/context/context.go b/components/engine/vendor/golang.org/x/net/context/context.go index f143ed6a1e..a3c021d3f8 100644 --- a/components/engine/vendor/golang.org/x/net/context/context.go +++ b/components/engine/vendor/golang.org/x/net/context/context.go @@ -5,6 +5,8 @@ // Package context defines the Context type, which carries deadlines, // cancelation signals, and other request-scoped values across API boundaries // and between processes. +// As of Go 1.7 this package is available in the standard library under the +// name context. https://golang.org/pkg/context. // // Incoming requests to a server should create a Context, and outgoing calls to // servers should accept a Context. The chain of function calls between must @@ -36,103 +38,6 @@ // Contexts. package context // import "golang.org/x/net/context" -import "time" - -// A Context carries a deadline, a cancelation signal, and other values across -// API boundaries. -// -// Context's methods may be called by multiple goroutines simultaneously. -type Context interface { - // Deadline returns the time when work done on behalf of this context - // should be canceled. Deadline returns ok==false when no deadline is - // set. Successive calls to Deadline return the same results. - Deadline() (deadline time.Time, ok bool) - - // Done returns a channel that's closed when work done on behalf of this - // context should be canceled. Done may return nil if this context can - // never be canceled. Successive calls to Done return the same value. - // - // WithCancel arranges for Done to be closed when cancel is called; - // WithDeadline arranges for Done to be closed when the deadline - // expires; WithTimeout arranges for Done to be closed when the timeout - // elapses. - // - // Done is provided for use in select statements: - // - // // Stream generates values with DoSomething and sends them to out - // // until DoSomething returns an error or ctx.Done is closed. - // func Stream(ctx context.Context, out chan<- Value) error { - // for { - // v, err := DoSomething(ctx) - // if err != nil { - // return err - // } - // select { - // case <-ctx.Done(): - // return ctx.Err() - // case out <- v: - // } - // } - // } - // - // See http://blog.golang.org/pipelines for more examples of how to use - // a Done channel for cancelation. - Done() <-chan struct{} - - // Err returns a non-nil error value after Done is closed. Err returns - // Canceled if the context was canceled or DeadlineExceeded if the - // context's deadline passed. No other values for Err are defined. - // After Done is closed, successive calls to Err return the same value. - Err() error - - // Value returns the value associated with this context for key, or nil - // if no value is associated with key. Successive calls to Value with - // the same key returns the same result. - // - // Use context values only for request-scoped data that transits - // processes and API boundaries, not for passing optional parameters to - // functions. - // - // A key identifies a specific value in a Context. Functions that wish - // to store values in Context typically allocate a key in a global - // variable then use that key as the argument to context.WithValue and - // Context.Value. A key can be any type that supports equality; - // packages should define keys as an unexported type to avoid - // collisions. - // - // Packages that define a Context key should provide type-safe accessors - // for the values stores using that key: - // - // // Package user defines a User type that's stored in Contexts. - // package user - // - // import "golang.org/x/net/context" - // - // // User is the type of value stored in the Contexts. - // type User struct {...} - // - // // key is an unexported type for keys defined in this package. - // // This prevents collisions with keys defined in other packages. - // type key int - // - // // userKey is the key for user.User values in Contexts. It is - // // unexported; clients use user.NewContext and user.FromContext - // // instead of using this key directly. - // var userKey key = 0 - // - // // NewContext returns a new Context that carries value u. - // func NewContext(ctx context.Context, u *User) context.Context { - // return context.WithValue(ctx, userKey, u) - // } - // - // // FromContext returns the User value stored in ctx, if any. - // func FromContext(ctx context.Context) (*User, bool) { - // u, ok := ctx.Value(userKey).(*User) - // return u, ok - // } - Value(key interface{}) interface{} -} - // Background returns a non-nil, empty Context. It is never canceled, has no // values, and has no deadline. It is typically used by the main function, // initialization, and tests, and as the top-level Context for incoming @@ -149,8 +54,3 @@ func Background() Context { func TODO() Context { return todo } - -// A CancelFunc tells an operation to abandon its work. -// A CancelFunc does not wait for the work to stop. -// After the first call, subsequent calls to a CancelFunc do nothing. -type CancelFunc func() diff --git a/components/engine/vendor/golang.org/x/net/context/go19.go b/components/engine/vendor/golang.org/x/net/context/go19.go new file mode 100644 index 0000000000..d88bd1db12 --- /dev/null +++ b/components/engine/vendor/golang.org/x/net/context/go19.go @@ -0,0 +1,20 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package context + +import "context" // standard library's context, as of Go 1.7 + +// A Context carries a deadline, a cancelation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context = context.Context + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc = context.CancelFunc diff --git a/components/engine/vendor/golang.org/x/net/context/pre_go19.go b/components/engine/vendor/golang.org/x/net/context/pre_go19.go new file mode 100644 index 0000000000..b105f80be4 --- /dev/null +++ b/components/engine/vendor/golang.org/x/net/context/pre_go19.go @@ -0,0 +1,109 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +package context + +import "time" + +// A Context carries a deadline, a cancelation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context interface { + // Deadline returns the time when work done on behalf of this context + // should be canceled. Deadline returns ok==false when no deadline is + // set. Successive calls to Deadline return the same results. + Deadline() (deadline time.Time, ok bool) + + // Done returns a channel that's closed when work done on behalf of this + // context should be canceled. Done may return nil if this context can + // never be canceled. Successive calls to Done return the same value. + // + // WithCancel arranges for Done to be closed when cancel is called; + // WithDeadline arranges for Done to be closed when the deadline + // expires; WithTimeout arranges for Done to be closed when the timeout + // elapses. + // + // Done is provided for use in select statements: + // + // // Stream generates values with DoSomething and sends them to out + // // until DoSomething returns an error or ctx.Done is closed. + // func Stream(ctx context.Context, out chan<- Value) error { + // for { + // v, err := DoSomething(ctx) + // if err != nil { + // return err + // } + // select { + // case <-ctx.Done(): + // return ctx.Err() + // case out <- v: + // } + // } + // } + // + // See http://blog.golang.org/pipelines for more examples of how to use + // a Done channel for cancelation. + Done() <-chan struct{} + + // Err returns a non-nil error value after Done is closed. Err returns + // Canceled if the context was canceled or DeadlineExceeded if the + // context's deadline passed. No other values for Err are defined. + // After Done is closed, successive calls to Err return the same value. + Err() error + + // Value returns the value associated with this context for key, or nil + // if no value is associated with key. Successive calls to Value with + // the same key returns the same result. + // + // Use context values only for request-scoped data that transits + // processes and API boundaries, not for passing optional parameters to + // functions. + // + // A key identifies a specific value in a Context. Functions that wish + // to store values in Context typically allocate a key in a global + // variable then use that key as the argument to context.WithValue and + // Context.Value. A key can be any type that supports equality; + // packages should define keys as an unexported type to avoid + // collisions. + // + // Packages that define a Context key should provide type-safe accessors + // for the values stores using that key: + // + // // Package user defines a User type that's stored in Contexts. + // package user + // + // import "golang.org/x/net/context" + // + // // User is the type of value stored in the Contexts. + // type User struct {...} + // + // // key is an unexported type for keys defined in this package. + // // This prevents collisions with keys defined in other packages. + // type key int + // + // // userKey is the key for user.User values in Contexts. It is + // // unexported; clients use user.NewContext and user.FromContext + // // instead of using this key directly. + // var userKey key = 0 + // + // // NewContext returns a new Context that carries value u. + // func NewContext(ctx context.Context, u *User) context.Context { + // return context.WithValue(ctx, userKey, u) + // } + // + // // FromContext returns the User value stored in ctx, if any. + // func FromContext(ctx context.Context) (*User, bool) { + // u, ok := ctx.Value(userKey).(*User) + // return u, ok + // } + Value(key interface{}) interface{} +} + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc func() diff --git a/components/engine/vendor/golang.org/x/net/http2/configure_transport.go b/components/engine/vendor/golang.org/x/net/http2/configure_transport.go index 4f720f530b..b65fc6d423 100644 --- a/components/engine/vendor/golang.org/x/net/http2/configure_transport.go +++ b/components/engine/vendor/golang.org/x/net/http2/configure_transport.go @@ -56,7 +56,7 @@ func configureTransport(t1 *http.Transport) (*Transport, error) { } // registerHTTPSProtocol calls Transport.RegisterProtocol but -// convering panics into errors. +// converting panics into errors. func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error) { defer func() { if e := recover(); e != nil { diff --git a/components/engine/vendor/golang.org/x/net/http2/errors.go b/components/engine/vendor/golang.org/x/net/http2/errors.go index 20fd7626a5..71f2c46317 100644 --- a/components/engine/vendor/golang.org/x/net/http2/errors.go +++ b/components/engine/vendor/golang.org/x/net/http2/errors.go @@ -87,13 +87,16 @@ type goAwayFlowError struct{} func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" } -// connErrorReason wraps a ConnectionError with an informative error about why it occurs. - +// connError represents an HTTP/2 ConnectionError error code, along +// with a string (for debugging) explaining why. +// // Errors of this type are only returned by the frame parser functions -// and converted into ConnectionError(ErrCodeProtocol). +// and converted into ConnectionError(Code), after stashing away +// the Reason into the Framer's errDetail field, accessible via +// the (*Framer).ErrorDetail method. type connError struct { - Code ErrCode - Reason string + Code ErrCode // the ConnectionError error code + Reason string // additional reason } func (e connError) Error() string { diff --git a/components/engine/vendor/golang.org/x/net/http2/go18.go b/components/engine/vendor/golang.org/x/net/http2/go18.go index 73cc2381f3..4f30d228a8 100644 --- a/components/engine/vendor/golang.org/x/net/http2/go18.go +++ b/components/engine/vendor/golang.org/x/net/http2/go18.go @@ -52,3 +52,5 @@ func reqGetBody(req *http.Request) func() (io.ReadCloser, error) { func reqBodyIsNoBody(body io.ReadCloser) bool { return body == http.NoBody } + +func go18httpNoBody() io.ReadCloser { return http.NoBody } // for tests only diff --git a/components/engine/vendor/golang.org/x/net/http2/http2.go b/components/engine/vendor/golang.org/x/net/http2/http2.go index b6b0f9ad15..d565f40e0c 100644 --- a/components/engine/vendor/golang.org/x/net/http2/http2.go +++ b/components/engine/vendor/golang.org/x/net/http2/http2.go @@ -376,12 +376,16 @@ func (s *sorter) SortStrings(ss []string) { // validPseudoPath reports whether v is a valid :path pseudo-header // value. It must be either: // -// *) a non-empty string starting with '/', but not with with "//", +// *) a non-empty string starting with '/' // *) the string '*', for OPTIONS requests. // // For now this is only used a quick check for deciding when to clean // up Opaque URLs before sending requests from the Transport. // See golang.org/issue/16847 +// +// We used to enforce that the path also didn't start with "//", but +// Google's GFE accepts such paths and Chrome sends them, so ignore +// that part of the spec. See golang.org/issue/19103. func validPseudoPath(v string) bool { - return (len(v) > 0 && v[0] == '/' && (len(v) == 1 || v[1] != '/')) || v == "*" + return (len(v) > 0 && v[0] == '/') || v == "*" } diff --git a/components/engine/vendor/golang.org/x/net/http2/not_go18.go b/components/engine/vendor/golang.org/x/net/http2/not_go18.go index efbf83c32c..6f8d3f86fa 100644 --- a/components/engine/vendor/golang.org/x/net/http2/not_go18.go +++ b/components/engine/vendor/golang.org/x/net/http2/not_go18.go @@ -25,3 +25,5 @@ func reqGetBody(req *http.Request) func() (io.ReadCloser, error) { } func reqBodyIsNoBody(io.ReadCloser) bool { return false } + +func go18httpNoBody() io.ReadCloser { return nil } // for tests only diff --git a/components/engine/vendor/golang.org/x/net/http2/pipe.go b/components/engine/vendor/golang.org/x/net/http2/pipe.go index 0b9848be8c..a6140099cb 100644 --- a/components/engine/vendor/golang.org/x/net/http2/pipe.go +++ b/components/engine/vendor/golang.org/x/net/http2/pipe.go @@ -50,7 +50,7 @@ func (p *pipe) Read(d []byte) (n int, err error) { if p.breakErr != nil { return 0, p.breakErr } - if p.b.Len() > 0 { + if p.b != nil && p.b.Len() > 0 { return p.b.Read(d) } if p.err != nil { diff --git a/components/engine/vendor/golang.org/x/net/http2/server.go b/components/engine/vendor/golang.org/x/net/http2/server.go index 7367b31c57..d790c3b3d5 100644 --- a/components/engine/vendor/golang.org/x/net/http2/server.go +++ b/components/engine/vendor/golang.org/x/net/http2/server.go @@ -853,8 +853,13 @@ func (sc *serverConn) serve() { } } - if sc.inGoAway && sc.curOpenStreams() == 0 && !sc.needToSendGoAway && !sc.writingFrame { - return + // Start the shutdown timer after sending a GOAWAY. When sending GOAWAY + // with no error code (graceful shutdown), don't start the timer until + // all open streams have been completed. + sentGoAway := sc.inGoAway && !sc.needToSendGoAway && !sc.writingFrame + gracefulShutdownComplete := sc.goAwayCode == ErrCodeNo && sc.curOpenStreams() == 0 + if sentGoAway && sc.shutdownTimer == nil && (sc.goAwayCode != ErrCodeNo || gracefulShutdownComplete) { + sc.shutDownIn(goAwayTimeout) } } } @@ -1218,30 +1223,31 @@ func (sc *serverConn) startGracefulShutdown() { sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) }) } +// After sending GOAWAY, the connection will close after goAwayTimeout. +// If we close the connection immediately after sending GOAWAY, there may +// be unsent data in our kernel receive buffer, which will cause the kernel +// to send a TCP RST on close() instead of a FIN. This RST will abort the +// connection immediately, whether or not the client had received the GOAWAY. +// +// Ideally we should delay for at least 1 RTT + epsilon so the client has +// a chance to read the GOAWAY and stop sending messages. Measuring RTT +// is hard, so we approximate with 1 second. See golang.org/issue/18701. +// +// This is a var so it can be shorter in tests, where all requests uses the +// loopback interface making the expected RTT very small. +// +// TODO: configurable? +var goAwayTimeout = 1 * time.Second + func (sc *serverConn) startGracefulShutdownInternal() { - sc.goAwayIn(ErrCodeNo, 0) + sc.goAway(ErrCodeNo) } func (sc *serverConn) goAway(code ErrCode) { - sc.serveG.check() - var forceCloseIn time.Duration - if code != ErrCodeNo { - forceCloseIn = 250 * time.Millisecond - } else { - // TODO: configurable - forceCloseIn = 1 * time.Second - } - sc.goAwayIn(code, forceCloseIn) -} - -func (sc *serverConn) goAwayIn(code ErrCode, forceCloseIn time.Duration) { sc.serveG.check() if sc.inGoAway { return } - if forceCloseIn != 0 { - sc.shutDownIn(forceCloseIn) - } sc.inGoAway = true sc.needToSendGoAway = true sc.goAwayCode = code @@ -2252,6 +2258,7 @@ type responseWriterState struct { wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet. sentHeader bool // have we sent the header frame? handlerDone bool // handler has finished + dirty bool // a Write failed; don't reuse this responseWriterState sentContentLen int64 // non-zero if handler set a Content-Length header wroteBytes int64 @@ -2333,6 +2340,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { date: date, }) if err != nil { + rws.dirty = true return 0, err } if endStream { @@ -2354,6 +2362,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { if len(p) > 0 || endStream { // only send a 0 byte DATA frame if we're ending the stream. if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil { + rws.dirty = true return 0, err } } @@ -2365,6 +2374,9 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { trailers: rws.trailers, endStream: true, }) + if err != nil { + rws.dirty = true + } return len(p), err } return len(p), nil @@ -2504,7 +2516,7 @@ func cloneHeader(h http.Header) http.Header { // // * Handler calls w.Write or w.WriteString -> // * -> rws.bw (*bufio.Writer) -> -// * (Handler migth call Flush) +// * (Handler might call Flush) // * -> chunkWriter{rws} // * -> responseWriterState.writeChunk(p []byte) // * -> responseWriterState.writeChunk (most of the magic; see comment there) @@ -2543,10 +2555,19 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int, func (w *responseWriter) handlerDone() { rws := w.rws + dirty := rws.dirty rws.handlerDone = true w.Flush() w.rws = nil - responseWriterStatePool.Put(rws) + if !dirty { + // Only recycle the pool if all prior Write calls to + // the serverConn goroutine completed successfully. If + // they returned earlier due to resets from the peer + // there might still be write goroutines outstanding + // from the serverConn referencing the rws memory. See + // issue 20704. + responseWriterStatePool.Put(rws) + } } // Push errors. diff --git a/components/engine/vendor/golang.org/x/net/http2/transport.go b/components/engine/vendor/golang.org/x/net/http2/transport.go index 3a85f25a20..c112d22170 100644 --- a/components/engine/vendor/golang.org/x/net/http2/transport.go +++ b/components/engine/vendor/golang.org/x/net/http2/transport.go @@ -18,6 +18,7 @@ import ( "io/ioutil" "log" "math" + mathrand "math/rand" "net" "net/http" "sort" @@ -86,7 +87,7 @@ type Transport struct { // MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to // send in the initial settings frame. It is how many bytes - // of response headers are allow. Unlike the http2 spec, zero here + // of response headers are allowed. Unlike the http2 spec, zero here // means to use a default limit (currently 10MB). If you actually // want to advertise an ulimited value to the peer, Transport // interprets the highest possible value here (0xffffffff or 1<<32-1) @@ -164,15 +165,17 @@ type ClientConn struct { goAwayDebug string // goAway frame's debug data, retained as a string streams map[uint32]*clientStream // client-initiated nextStreamID uint32 + pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams pings map[[8]byte]chan struct{} // in flight ping data to notification channel bw *bufio.Writer br *bufio.Reader fr *Framer lastActive time.Time // Settings from peer: (also guarded by mu) - maxFrameSize uint32 - maxConcurrentStreams uint32 - initialWindowSize uint32 + maxFrameSize uint32 + maxConcurrentStreams uint32 + peerMaxHeaderListSize uint64 + initialWindowSize uint32 hbuf bytes.Buffer // HPACK encoder writes into this henc *hpack.Encoder @@ -216,35 +219,45 @@ type clientStream struct { resTrailer *http.Header // client's Response.Trailer } -// awaitRequestCancel runs in its own goroutine and waits for the user -// to cancel a RoundTrip request, its context to expire, or for the -// request to be done (any way it might be removed from the cc.streams -// map: peer reset, successful completion, TCP connection breakage, -// etc) -func (cs *clientStream) awaitRequestCancel(req *http.Request) { +// awaitRequestCancel waits for the user to cancel a request or for the done +// channel to be signaled. A non-nil error is returned only if the request was +// canceled. +func awaitRequestCancel(req *http.Request, done <-chan struct{}) error { ctx := reqContext(req) if req.Cancel == nil && ctx.Done() == nil { - return + return nil } select { case <-req.Cancel: - cs.cancelStream() - cs.bufPipe.CloseWithError(errRequestCanceled) + return errRequestCanceled case <-ctx.Done(): + return ctx.Err() + case <-done: + return nil + } +} + +// awaitRequestCancel waits for the user to cancel a request, its context to +// expire, or for the request to be done (any way it might be removed from the +// cc.streams map: peer reset, successful completion, TCP connection breakage, +// etc). If the request is canceled, then cs will be canceled and closed. +func (cs *clientStream) awaitRequestCancel(req *http.Request) { + if err := awaitRequestCancel(req, cs.done); err != nil { cs.cancelStream() - cs.bufPipe.CloseWithError(ctx.Err()) - case <-cs.done: + cs.bufPipe.CloseWithError(err) } } func (cs *clientStream) cancelStream() { - cs.cc.mu.Lock() + cc := cs.cc + cc.mu.Lock() didReset := cs.didReset cs.didReset = true - cs.cc.mu.Unlock() + cc.mu.Unlock() if !didReset { - cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) + cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) + cc.forgetStreamID(cs.ID) } } @@ -329,7 +342,7 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res } addr := authorityAddr(req.URL.Scheme, req.URL.Host) - for { + for retry := 0; ; retry++ { cc, err := t.connPool().GetClientConn(req, addr) if err != nil { t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err) @@ -337,9 +350,25 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res } traceGotConn(req, cc) res, err := cc.RoundTrip(req) - if err != nil { - if req, err = shouldRetryRequest(req, err); err == nil { - continue + if err != nil && retry <= 6 { + afterBodyWrite := false + if e, ok := err.(afterReqBodyWriteError); ok { + err = e + afterBodyWrite = true + } + if req, err = shouldRetryRequest(req, err, afterBodyWrite); err == nil { + // After the first retry, do exponential backoff with 10% jitter. + if retry == 0 { + continue + } + backoff := float64(uint(1) << (uint(retry) - 1)) + backoff += backoff * (0.1 * mathrand.Float64()) + select { + case <-time.After(time.Second * time.Duration(backoff)): + continue + case <-reqContext(req).Done(): + return nil, reqContext(req).Err() + } } } if err != nil { @@ -360,43 +389,60 @@ func (t *Transport) CloseIdleConnections() { } var ( - errClientConnClosed = errors.New("http2: client conn is closed") - errClientConnUnusable = errors.New("http2: client conn not usable") - - errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") - errClientConnGotGoAwayAfterSomeReqBody = errors.New("http2: Transport received Server's graceful shutdown GOAWAY; some request body already written") + errClientConnClosed = errors.New("http2: client conn is closed") + errClientConnUnusable = errors.New("http2: client conn not usable") + errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") ) +// afterReqBodyWriteError is a wrapper around errors returned by ClientConn.RoundTrip. +// It is used to signal that err happened after part of Request.Body was sent to the server. +type afterReqBodyWriteError struct { + err error +} + +func (e afterReqBodyWriteError) Error() string { + return e.err.Error() + "; some request body already written" +} + // shouldRetryRequest is called by RoundTrip when a request fails to get // response headers. It is always called with a non-nil error. // It returns either a request to retry (either the same request, or a // modified clone), or an error if the request can't be replayed. -func shouldRetryRequest(req *http.Request, err error) (*http.Request, error) { - switch err { - default: +func shouldRetryRequest(req *http.Request, err error, afterBodyWrite bool) (*http.Request, error) { + if !canRetryError(err) { return nil, err - case errClientConnUnusable, errClientConnGotGoAway: - return req, nil - case errClientConnGotGoAwayAfterSomeReqBody: - // If the Body is nil (or http.NoBody), it's safe to reuse - // this request and its Body. - if req.Body == nil || reqBodyIsNoBody(req.Body) { - return req, nil - } - // Otherwise we depend on the Request having its GetBody - // func defined. - getBody := reqGetBody(req) // Go 1.8: getBody = req.GetBody - if getBody == nil { - return nil, errors.New("http2: Transport: peer server initiated graceful shutdown after some of Request.Body was written; define Request.GetBody to avoid this error") - } - body, err := getBody() - if err != nil { - return nil, err - } - newReq := *req - newReq.Body = body - return &newReq, nil } + if !afterBodyWrite { + return req, nil + } + // If the Body is nil (or http.NoBody), it's safe to reuse + // this request and its Body. + if req.Body == nil || reqBodyIsNoBody(req.Body) { + return req, nil + } + // Otherwise we depend on the Request having its GetBody + // func defined. + getBody := reqGetBody(req) // Go 1.8: getBody = req.GetBody + if getBody == nil { + return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err) + } + body, err := getBody() + if err != nil { + return nil, err + } + newReq := *req + newReq.Body = body + return &newReq, nil +} + +func canRetryError(err error) bool { + if err == errClientConnUnusable || err == errClientConnGotGoAway { + return true + } + if se, ok := err.(StreamError); ok { + return se.Code == ErrCodeRefusedStream + } + return false } func (t *Transport) dialClientConn(addr string, singleUse bool) (*ClientConn, error) { @@ -474,17 +520,18 @@ func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) { func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) { cc := &ClientConn{ - t: t, - tconn: c, - readerDone: make(chan struct{}), - nextStreamID: 1, - maxFrameSize: 16 << 10, // spec default - initialWindowSize: 65535, // spec default - maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough. - streams: make(map[uint32]*clientStream), - singleUse: singleUse, - wantSettingsAck: true, - pings: make(map[[8]byte]chan struct{}), + t: t, + tconn: c, + readerDone: make(chan struct{}), + nextStreamID: 1, + maxFrameSize: 16 << 10, // spec default + initialWindowSize: 65535, // spec default + maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough. + peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. + streams: make(map[uint32]*clientStream), + singleUse: singleUse, + wantSettingsAck: true, + pings: make(map[[8]byte]chan struct{}), } if d := t.idleConnTimeout(); d != 0 { cc.idleTimeout = d @@ -560,6 +607,8 @@ func (cc *ClientConn) setGoAway(f *GoAwayFrame) { } } +// CanTakeNewRequest reports whether the connection can take a new request, +// meaning it has not been closed or received or sent a GOAWAY. func (cc *ClientConn) CanTakeNewRequest() bool { cc.mu.Lock() defer cc.mu.Unlock() @@ -571,8 +620,7 @@ func (cc *ClientConn) canTakeNewRequestLocked() bool { return false } return cc.goAway == nil && !cc.closed && - int64(len(cc.streams)+1) < int64(cc.maxConcurrentStreams) && - cc.nextStreamID < math.MaxInt32 + int64(cc.nextStreamID)+int64(cc.pendingRequests) < math.MaxInt32 } // onIdleTimeout is called from a time.AfterFunc goroutine. It will @@ -694,7 +742,7 @@ func checkConnHeaders(req *http.Request) error { // req.ContentLength, where 0 actually means zero (not unknown) and -1 // means unknown. func actualContentLength(req *http.Request) int64 { - if req.Body == nil { + if req.Body == nil || reqBodyIsNoBody(req.Body) { return 0 } if req.ContentLength != 0 { @@ -718,15 +766,14 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) { hasTrailers := trailers != "" cc.mu.Lock() - cc.lastActive = time.Now() - if cc.closed || !cc.canTakeNewRequestLocked() { + if err := cc.awaitOpenSlotForRequest(req); err != nil { cc.mu.Unlock() - return nil, errClientConnUnusable + return nil, err } body := req.Body - hasBody := body != nil contentLen := actualContentLength(req) + hasBody := contentLen != 0 // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? var requestedGzip bool @@ -816,14 +863,13 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) { cs.abortRequestBodyWrite(errStopReqBodyWrite) } if re.err != nil { - if re.err == errClientConnGotGoAway { - cc.mu.Lock() - if cs.startedWrite { - re.err = errClientConnGotGoAwayAfterSomeReqBody - } - cc.mu.Unlock() - } + cc.mu.Lock() + afterBodyWrite := cs.startedWrite + cc.mu.Unlock() cc.forgetStreamID(cs.ID) + if afterBodyWrite { + return nil, afterReqBodyWriteError{re.err} + } return nil, re.err } res.Request = req @@ -836,31 +882,31 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) { case re := <-readLoopResCh: return handleReadLoopResponse(re) case <-respHeaderTimer: - cc.forgetStreamID(cs.ID) if !hasBody || bodyWritten { cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) } else { bodyWriter.cancel() cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) } + cc.forgetStreamID(cs.ID) return nil, errTimeout case <-ctx.Done(): - cc.forgetStreamID(cs.ID) if !hasBody || bodyWritten { cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) } else { bodyWriter.cancel() cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) } + cc.forgetStreamID(cs.ID) return nil, ctx.Err() case <-req.Cancel: - cc.forgetStreamID(cs.ID) if !hasBody || bodyWritten { cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) } else { bodyWriter.cancel() cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) } + cc.forgetStreamID(cs.ID) return nil, errRequestCanceled case <-cs.peerReset: // processResetStream already removed the @@ -887,6 +933,45 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) { } } +// awaitOpenSlotForRequest waits until len(streams) < maxConcurrentStreams. +// Must hold cc.mu. +func (cc *ClientConn) awaitOpenSlotForRequest(req *http.Request) error { + var waitingForConn chan struct{} + var waitingForConnErr error // guarded by cc.mu + for { + cc.lastActive = time.Now() + if cc.closed || !cc.canTakeNewRequestLocked() { + return errClientConnUnusable + } + if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) { + if waitingForConn != nil { + close(waitingForConn) + } + return nil + } + // Unfortunately, we cannot wait on a condition variable and channel at + // the same time, so instead, we spin up a goroutine to check if the + // request is canceled while we wait for a slot to open in the connection. + if waitingForConn == nil { + waitingForConn = make(chan struct{}) + go func() { + if err := awaitRequestCancel(req, waitingForConn); err != nil { + cc.mu.Lock() + waitingForConnErr = err + cc.cond.Broadcast() + cc.mu.Unlock() + } + }() + } + cc.pendingRequests++ + cc.cond.Wait() + cc.pendingRequests-- + if waitingForConnErr != nil { + return waitingForConnErr + } + } +} + // requires cc.wmu be held func (cc *ClientConn) writeHeaders(streamID uint32, endStream bool, hdrs []byte) error { first := true // first frame written (HEADERS is first, then CONTINUATION) @@ -1002,8 +1087,13 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) ( var trls []byte if hasTrailers { cc.mu.Lock() - defer cc.mu.Unlock() - trls = cc.encodeTrailers(req) + trls, err = cc.encodeTrailers(req) + cc.mu.Unlock() + if err != nil { + cc.writeStreamReset(cs.ID, ErrCodeInternal, err) + cc.forgetStreamID(cs.ID) + return err + } } cc.wmu.Lock() @@ -1106,62 +1196,86 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail } } - // 8.1.2.3 Request Pseudo-Header Fields - // The :path pseudo-header field includes the path and query parts of the - // target URI (the path-absolute production and optionally a '?' character - // followed by the query production (see Sections 3.3 and 3.4 of - // [RFC3986]). - cc.writeHeader(":authority", host) - cc.writeHeader(":method", req.Method) - if req.Method != "CONNECT" { - cc.writeHeader(":path", path) - cc.writeHeader(":scheme", req.URL.Scheme) - } - if trailers != "" { - cc.writeHeader("trailer", trailers) + enumerateHeaders := func(f func(name, value string)) { + // 8.1.2.3 Request Pseudo-Header Fields + // The :path pseudo-header field includes the path and query parts of the + // target URI (the path-absolute production and optionally a '?' character + // followed by the query production (see Sections 3.3 and 3.4 of + // [RFC3986]). + f(":authority", host) + f(":method", req.Method) + if req.Method != "CONNECT" { + f(":path", path) + f(":scheme", req.URL.Scheme) + } + if trailers != "" { + f("trailer", trailers) + } + + var didUA bool + for k, vv := range req.Header { + if strings.EqualFold(k, "host") || strings.EqualFold(k, "content-length") { + // Host is :authority, already sent. + // Content-Length is automatic, set below. + continue + } else if strings.EqualFold(k, "connection") || strings.EqualFold(k, "proxy-connection") || + strings.EqualFold(k, "transfer-encoding") || strings.EqualFold(k, "upgrade") || + strings.EqualFold(k, "keep-alive") { + // Per 8.1.2.2 Connection-Specific Header + // Fields, don't send connection-specific + // fields. We have already checked if any + // are error-worthy so just ignore the rest. + continue + } else if strings.EqualFold(k, "user-agent") { + // Match Go's http1 behavior: at most one + // User-Agent. If set to nil or empty string, + // then omit it. Otherwise if not mentioned, + // include the default (below). + didUA = true + if len(vv) < 1 { + continue + } + vv = vv[:1] + if vv[0] == "" { + continue + } + + } + + for _, v := range vv { + f(k, v) + } + } + if shouldSendReqContentLength(req.Method, contentLength) { + f("content-length", strconv.FormatInt(contentLength, 10)) + } + if addGzipHeader { + f("accept-encoding", "gzip") + } + if !didUA { + f("user-agent", defaultUserAgent) + } } - var didUA bool - for k, vv := range req.Header { - lowKey := strings.ToLower(k) - switch lowKey { - case "host", "content-length": - // Host is :authority, already sent. - // Content-Length is automatic, set below. - continue - case "connection", "proxy-connection", "transfer-encoding", "upgrade", "keep-alive": - // Per 8.1.2.2 Connection-Specific Header - // Fields, don't send connection-specific - // fields. We have already checked if any - // are error-worthy so just ignore the rest. - continue - case "user-agent": - // Match Go's http1 behavior: at most one - // User-Agent. If set to nil or empty string, - // then omit it. Otherwise if not mentioned, - // include the default (below). - didUA = true - if len(vv) < 1 { - continue - } - vv = vv[:1] - if vv[0] == "" { - continue - } - } - for _, v := range vv { - cc.writeHeader(lowKey, v) - } - } - if shouldSendReqContentLength(req.Method, contentLength) { - cc.writeHeader("content-length", strconv.FormatInt(contentLength, 10)) - } - if addGzipHeader { - cc.writeHeader("accept-encoding", "gzip") - } - if !didUA { - cc.writeHeader("user-agent", defaultUserAgent) + // Do a first pass over the headers counting bytes to ensure + // we don't exceed cc.peerMaxHeaderListSize. This is done as a + // separate pass before encoding the headers to prevent + // modifying the hpack state. + hlSize := uint64(0) + enumerateHeaders(func(name, value string) { + hf := hpack.HeaderField{Name: name, Value: value} + hlSize += uint64(hf.Size()) + }) + + if hlSize > cc.peerMaxHeaderListSize { + return nil, errRequestHeaderListSize } + + // Header list size is ok. Write the headers. + enumerateHeaders(func(name, value string) { + cc.writeHeader(strings.ToLower(name), value) + }) + return cc.hbuf.Bytes(), nil } @@ -1188,17 +1302,29 @@ func shouldSendReqContentLength(method string, contentLength int64) bool { } // requires cc.mu be held. -func (cc *ClientConn) encodeTrailers(req *http.Request) []byte { +func (cc *ClientConn) encodeTrailers(req *http.Request) ([]byte, error) { cc.hbuf.Reset() + + hlSize := uint64(0) for k, vv := range req.Trailer { - // Transfer-Encoding, etc.. have already been filter at the + for _, v := range vv { + hf := hpack.HeaderField{Name: k, Value: v} + hlSize += uint64(hf.Size()) + } + } + if hlSize > cc.peerMaxHeaderListSize { + return nil, errRequestHeaderListSize + } + + for k, vv := range req.Trailer { + // Transfer-Encoding, etc.. have already been filtered at the // start of RoundTrip lowKey := strings.ToLower(k) for _, v := range vv { cc.writeHeader(lowKey, v) } } - return cc.hbuf.Bytes() + return cc.hbuf.Bytes(), nil } func (cc *ClientConn) writeHeader(name, value string) { @@ -1246,7 +1372,9 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream { cc.idleTimer.Reset(cc.idleTimeout) } close(cs.done) - cc.cond.Broadcast() // wake up checkResetOrDone via clientStream.awaitFlowControl + // Wake up checkResetOrDone via clientStream.awaitFlowControl and + // wake up RoundTrip if there is a pending request. + cc.cond.Broadcast() } return cs } @@ -1345,8 +1473,9 @@ func (rl *clientConnReadLoop) run() error { cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err) } if se, ok := err.(StreamError); ok { - if cs := cc.streamByID(se.StreamID, true /*ended; remove it*/); cs != nil { + if cs := cc.streamByID(se.StreamID, false); cs != nil { cs.cc.writeStreamReset(cs.ID, se.Code, err) + cs.cc.forgetStreamID(cs.ID) if se.Cause == nil { se.Cause = cc.fr.errDetail } @@ -1407,7 +1536,17 @@ func (rl *clientConnReadLoop) run() error { func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error { cc := rl.cc - cs := cc.streamByID(f.StreamID, f.StreamEnded()) + if f.StreamEnded() { + // Issue 20521: If the stream has ended, streamByID() causes + // clientStream.done to be closed, which causes the request's bodyWriter + // to be closed with an errStreamClosed, which may be received by + // clientConn.RoundTrip before the result of processing these headers. + // Deferring stream closure allows the header processing to occur first. + // clientConn.RoundTrip may still receive the bodyWriter error first, but + // the fix for issue 16102 prioritises any response. + defer cc.streamByID(f.StreamID, true) + } + cs := cc.streamByID(f.StreamID, false) if cs == nil { // We'd get here if we canceled a request while the // server had its response still in flight. So if this @@ -1668,6 +1807,7 @@ func (b transportResponseBody) Close() error { } cs.bufPipe.BreakWithError(errClosedResponseBody) + cc.forgetStreamID(cs.ID) return nil } @@ -1702,6 +1842,14 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error { } return nil } + if !cs.firstByte { + cc.logf("protocol error: received DATA before a HEADERS frame") + rl.endStreamError(cs, StreamError{ + StreamID: f.StreamID, + Code: ErrCodeProtocol, + }) + return nil + } if f.Length > 0 { // Check connection-level flow control. cc.mu.Lock() @@ -1713,16 +1861,27 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error { } // Return any padded flow control now, since we won't // refund it later on body reads. - if pad := int32(f.Length) - int32(len(data)); pad > 0 { - cs.inflow.add(pad) - cc.inflow.add(pad) + var refund int + if pad := int(f.Length) - len(data); pad > 0 { + refund += pad + } + // Return len(data) now if the stream is already closed, + // since data will never be read. + didReset := cs.didReset + if didReset { + refund += len(data) + } + if refund > 0 { + cc.inflow.add(int32(refund)) cc.wmu.Lock() - cc.fr.WriteWindowUpdate(0, uint32(pad)) - cc.fr.WriteWindowUpdate(cs.ID, uint32(pad)) + cc.fr.WriteWindowUpdate(0, uint32(refund)) + if !didReset { + cs.inflow.add(int32(refund)) + cc.fr.WriteWindowUpdate(cs.ID, uint32(refund)) + } cc.bw.Flush() cc.wmu.Unlock() } - didReset := cs.didReset cc.mu.Unlock() if len(data) > 0 && !didReset { @@ -1805,6 +1964,8 @@ func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error { cc.maxFrameSize = s.Val case SettingMaxConcurrentStreams: cc.maxConcurrentStreams = s.Val + case SettingMaxHeaderListSize: + cc.peerMaxHeaderListSize = uint64(s.Val) case SettingInitialWindowSize: // Values above the maximum flow-control // window size of 2^31-1 MUST be treated as a @@ -1971,6 +2132,7 @@ func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, err error) var ( errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit") + errRequestHeaderListSize = errors.New("http2: request header list larger than peer's advertised limit") errPseudoTrailers = errors.New("http2: invalid pseudo header in trailers") ) diff --git a/components/engine/vendor/golang.org/x/net/http2/write.go b/components/engine/vendor/golang.org/x/net/http2/write.go index 6b0dfae319..54ab4a88e7 100644 --- a/components/engine/vendor/golang.org/x/net/http2/write.go +++ b/components/engine/vendor/golang.org/x/net/http2/write.go @@ -10,7 +10,6 @@ import ( "log" "net/http" "net/url" - "time" "golang.org/x/net/http2/hpack" "golang.org/x/net/lex/httplex" @@ -90,11 +89,7 @@ type writeGoAway struct { func (p *writeGoAway) writeFrame(ctx writeContext) error { err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil) - if p.code != 0 { - ctx.Flush() // ignore error: we're hanging up on them anyway - time.Sleep(50 * time.Millisecond) - ctx.CloseConn() - } + ctx.Flush() // ignore error: we're hanging up on them anyway return err } diff --git a/components/engine/vendor/golang.org/x/net/idna/idna.go b/components/engine/vendor/golang.org/x/net/idna/idna.go index ee2dbda6d0..ec8232b2e6 100644 --- a/components/engine/vendor/golang.org/x/net/idna/idna.go +++ b/components/engine/vendor/golang.org/x/net/idna/idna.go @@ -67,6 +67,15 @@ func VerifyDNSLength(verify bool) Option { return func(o *options) { o.verifyDNSLength = verify } } +// RemoveLeadingDots removes leading label separators. Leading runes that map to +// dots, such as U+3002, are removed as well. +// +// This is the behavior suggested by the UTS #46 and is adopted by some +// browsers. +func RemoveLeadingDots(remove bool) Option { + return func(o *options) { o.removeLeadingDots = remove } +} + // ValidateLabels sets whether to check the mandatory label validation criteria // as defined in Section 5.4 of RFC 5891. This includes testing for correct use // of hyphens ('-'), normalization, validity of runes, and the context rules. @@ -133,14 +142,16 @@ func MapForLookup() Option { o.mapping = validateAndMap StrictDomainName(true)(o) ValidateLabels(true)(o) + RemoveLeadingDots(true)(o) } } type options struct { - transitional bool - useSTD3Rules bool - validateLabels bool - verifyDNSLength bool + transitional bool + useSTD3Rules bool + validateLabels bool + verifyDNSLength bool + removeLeadingDots bool trie *idnaTrie @@ -156,7 +167,7 @@ type options struct { bidirule func(s string) bool } -// A Profile defines the configuration of a IDNA mapper. +// A Profile defines the configuration of an IDNA mapper. type Profile struct { options } @@ -240,21 +251,23 @@ var ( punycode = &Profile{} lookup = &Profile{options{ - transitional: true, - useSTD3Rules: true, - validateLabels: true, - trie: trie, - fromPuny: validateFromPunycode, - mapping: validateAndMap, - bidirule: bidirule.ValidString, + transitional: true, + useSTD3Rules: true, + validateLabels: true, + removeLeadingDots: true, + trie: trie, + fromPuny: validateFromPunycode, + mapping: validateAndMap, + bidirule: bidirule.ValidString, }} display = &Profile{options{ - useSTD3Rules: true, - validateLabels: true, - trie: trie, - fromPuny: validateFromPunycode, - mapping: validateAndMap, - bidirule: bidirule.ValidString, + useSTD3Rules: true, + validateLabels: true, + removeLeadingDots: true, + trie: trie, + fromPuny: validateFromPunycode, + mapping: validateAndMap, + bidirule: bidirule.ValidString, }} registration = &Profile{options{ useSTD3Rules: true, @@ -293,7 +306,9 @@ func (p *Profile) process(s string, toASCII bool) (string, error) { s, err = p.mapping(p, s) } // Remove leading empty labels. - for ; len(s) > 0 && s[0] == '.'; s = s[1:] { + if p.removeLeadingDots { + for ; len(s) > 0 && s[0] == '.'; s = s[1:] { + } } // It seems like we should only create this error on ToASCII, but the // UTS 46 conformance tests suggests we should always check this. @@ -373,23 +388,20 @@ func validateRegistration(p *Profile, s string) (string, error) { if !norm.NFC.IsNormalString(s) { return s, &labelError{s, "V1"} } - var err error for i := 0; i < len(s); { v, sz := trie.lookupString(s[i:]) - i += sz // Copy bytes not copied so far. switch p.simplify(info(v).category()) { // TODO: handle the NV8 defined in the Unicode idna data set to allow // for strict conformance to IDNA2008. case valid, deviation: case disallowed, mapped, unknown, ignored: - if err == nil { - r, _ := utf8.DecodeRuneInString(s[i:]) - err = runeError(r) - } + r, _ := utf8.DecodeRuneInString(s[i:]) + return s, runeError(r) } + i += sz } - return s, err + return s, nil } func validateAndMap(p *Profile, s string) (string, error) { @@ -408,7 +420,7 @@ func validateAndMap(p *Profile, s string) (string, error) { continue case disallowed: if err == nil { - r, _ := utf8.DecodeRuneInString(s[i:]) + r, _ := utf8.DecodeRuneInString(s[start:]) err = runeError(r) } continue diff --git a/components/engine/vendor/golang.org/x/net/proxy/per_host.go b/components/engine/vendor/golang.org/x/net/proxy/per_host.go index f540b196f7..0689bb6a70 100644 --- a/components/engine/vendor/golang.org/x/net/proxy/per_host.go +++ b/components/engine/vendor/golang.org/x/net/proxy/per_host.go @@ -9,7 +9,7 @@ import ( "strings" ) -// A PerHost directs connections to a default Dialer unless the hostname +// A PerHost directs connections to a default Dialer unless the host name // requested matches one of a number of exceptions. type PerHost struct { def, bypass Dialer @@ -61,7 +61,7 @@ func (p *PerHost) dialerForRequest(host string) Dialer { return p.bypass } if host == zone[1:] { - // For a zone "example.com", we match "example.com" + // For a zone ".example.com", we match "example.com" // too. return p.bypass } @@ -76,7 +76,7 @@ func (p *PerHost) dialerForRequest(host string) Dialer { // AddFromString parses a string that contains comma-separated values // specifying hosts that should use the bypass proxy. Each value is either an -// IP address, a CIDR range, a zone (*.example.com) or a hostname +// IP address, a CIDR range, a zone (*.example.com) or a host name // (localhost). A best effort is made to parse the string and errors are // ignored. func (p *PerHost) AddFromString(s string) { @@ -131,7 +131,7 @@ func (p *PerHost) AddZone(zone string) { p.bypassZones = append(p.bypassZones, zone) } -// AddHost specifies a hostname that will use the bypass proxy. +// AddHost specifies a host name that will use the bypass proxy. func (p *PerHost) AddHost(host string) { if strings.HasSuffix(host, ".") { host = host[:len(host)-1] diff --git a/components/engine/vendor/golang.org/x/net/proxy/proxy.go b/components/engine/vendor/golang.org/x/net/proxy/proxy.go index 78a8b7bee9..553ead7cf0 100644 --- a/components/engine/vendor/golang.org/x/net/proxy/proxy.go +++ b/components/engine/vendor/golang.org/x/net/proxy/proxy.go @@ -11,6 +11,7 @@ import ( "net" "net/url" "os" + "sync" ) // A Dialer is a means to establish a connection. @@ -27,7 +28,7 @@ type Auth struct { // FromEnvironment returns the dialer specified by the proxy related variables in // the environment. func FromEnvironment() Dialer { - allProxy := os.Getenv("all_proxy") + allProxy := allProxyEnv.Get() if len(allProxy) == 0 { return Direct } @@ -41,7 +42,7 @@ func FromEnvironment() Dialer { return Direct } - noProxy := os.Getenv("no_proxy") + noProxy := noProxyEnv.Get() if len(noProxy) == 0 { return proxy } @@ -92,3 +93,42 @@ func FromURL(u *url.URL, forward Dialer) (Dialer, error) { return nil, errors.New("proxy: unknown scheme: " + u.Scheme) } + +var ( + allProxyEnv = &envOnce{ + names: []string{"ALL_PROXY", "all_proxy"}, + } + noProxyEnv = &envOnce{ + names: []string{"NO_PROXY", "no_proxy"}, + } +) + +// envOnce looks up an environment variable (optionally by multiple +// names) once. It mitigates expensive lookups on some platforms +// (e.g. Windows). +// (Borrowed from net/http/transport.go) +type envOnce struct { + names []string + once sync.Once + val string +} + +func (e *envOnce) Get() string { + e.once.Do(e.init) + return e.val +} + +func (e *envOnce) init() { + for _, n := range e.names { + e.val = os.Getenv(n) + if e.val != "" { + return + } + } +} + +// reset is used by tests +func (e *envOnce) reset() { + e.once = sync.Once{} + e.val = "" +} diff --git a/components/engine/vendor/golang.org/x/net/proxy/socks5.go b/components/engine/vendor/golang.org/x/net/proxy/socks5.go index 973f57f197..3fed38ef1c 100644 --- a/components/engine/vendor/golang.org/x/net/proxy/socks5.go +++ b/components/engine/vendor/golang.org/x/net/proxy/socks5.go @@ -12,7 +12,7 @@ import ( ) // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address -// with an optional username and password. See RFC 1928. +// with an optional username and password. See RFC 1928 and RFC 1929. func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) { s := &socks5{ network: network, @@ -60,7 +60,7 @@ var socks5Errors = []string{ "address type not supported", } -// Dial connects to the address addr on the network net via the SOCKS5 proxy. +// Dial connects to the address addr on the given network via the SOCKS5 proxy. func (s *socks5) Dial(network, addr string) (net.Conn, error) { switch network { case "tcp", "tcp6", "tcp4": @@ -120,6 +120,7 @@ func (s *socks5) connect(conn net.Conn, target string) error { return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") } + // See RFC 1929 if buf[1] == socks5AuthPassword { buf = buf[:0] buf = append(buf, 1 /* password protocol version */) @@ -154,7 +155,7 @@ func (s *socks5) connect(conn net.Conn, target string) error { buf = append(buf, ip...) } else { if len(host) > 255 { - return errors.New("proxy: destination hostname too long: " + host) + return errors.New("proxy: destination host name too long: " + host) } buf = append(buf, socks5Domain) buf = append(buf, byte(len(host))) diff --git a/components/engine/vendor/golang.org/x/net/trace/events.go b/components/engine/vendor/golang.org/x/net/trace/events.go index d8daec1a79..c646a6952e 100644 --- a/components/engine/vendor/golang.org/x/net/trace/events.go +++ b/components/engine/vendor/golang.org/x/net/trace/events.go @@ -39,9 +39,9 @@ var buckets = []bucket{ } // RenderEvents renders the HTML page typically served at /debug/events. -// It does not do any auth checking; see AuthRequest for the default auth check -// used by the handler registered on http.DefaultServeMux. -// req may be nil. +// It does not do any auth checking. The request may be nil. +// +// Most users will use the Events handler. func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) { now := time.Now() data := &struct { diff --git a/components/engine/vendor/golang.org/x/net/trace/trace.go b/components/engine/vendor/golang.org/x/net/trace/trace.go index 3d9b646111..bb72a527e8 100644 --- a/components/engine/vendor/golang.org/x/net/trace/trace.go +++ b/components/engine/vendor/golang.org/x/net/trace/trace.go @@ -110,30 +110,46 @@ var AuthRequest = func(req *http.Request) (any, sensitive bool) { } func init() { - http.HandleFunc("/debug/requests", func(w http.ResponseWriter, req *http.Request) { - any, sensitive := AuthRequest(req) - if !any { - http.Error(w, "not allowed", http.StatusUnauthorized) - return - } - w.Header().Set("Content-Type", "text/html; charset=utf-8") - Render(w, req, sensitive) - }) - http.HandleFunc("/debug/events", func(w http.ResponseWriter, req *http.Request) { - any, sensitive := AuthRequest(req) - if !any { - http.Error(w, "not allowed", http.StatusUnauthorized) - return - } - w.Header().Set("Content-Type", "text/html; charset=utf-8") - RenderEvents(w, req, sensitive) - }) + // TODO(jbd): Serve Traces from /debug/traces in the future? + // There is no requirement for a request to be present to have traces. + http.HandleFunc("/debug/requests", Traces) + http.HandleFunc("/debug/events", Events) +} + +// Traces responds with traces from the program. +// The package initialization registers it in http.DefaultServeMux +// at /debug/requests. +// +// It performs authorization by running AuthRequest. +func Traces(w http.ResponseWriter, req *http.Request) { + any, sensitive := AuthRequest(req) + if !any { + http.Error(w, "not allowed", http.StatusUnauthorized) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + Render(w, req, sensitive) +} + +// Events responds with a page of events collected by EventLogs. +// The package initialization registers it in http.DefaultServeMux +// at /debug/events. +// +// It performs authorization by running AuthRequest. +func Events(w http.ResponseWriter, req *http.Request) { + any, sensitive := AuthRequest(req) + if !any { + http.Error(w, "not allowed", http.StatusUnauthorized) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + RenderEvents(w, req, sensitive) } // Render renders the HTML page typically served at /debug/requests. -// It does not do any auth checking; see AuthRequest for the default auth check -// used by the handler registered on http.DefaultServeMux. -// req may be nil. +// It does not do any auth checking. The request may be nil. +// +// Most users will use the Traces handler. func Render(w io.Writer, req *http.Request, sensitive bool) { data := &struct { Families []string diff --git a/components/engine/volume/drivers/adapter.go b/components/engine/volume/drivers/adapter.go index 8415c7f6c9..d5d9ea823e 100644 --- a/components/engine/volume/drivers/adapter.go +++ b/components/engine/volume/drivers/adapter.go @@ -1,4 +1,4 @@ -package volumedrivers // import "github.com/docker/docker/volume/drivers" +package drivers // import "github.com/docker/docker/volume/drivers" import ( "errors" diff --git a/components/engine/volume/drivers/extpoint.go b/components/engine/volume/drivers/extpoint.go index 15cf850d74..14e3b4f625 100644 --- a/components/engine/volume/drivers/extpoint.go +++ b/components/engine/volume/drivers/extpoint.go @@ -1,12 +1,13 @@ //go:generate pluginrpc-gen -i $GOFILE -o proxy.go -type volumeDriver -name VolumeDriver -package volumedrivers // import "github.com/docker/docker/volume/drivers" +package drivers // import "github.com/docker/docker/volume/drivers" import ( "fmt" "sort" "sync" + "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/locker" getter "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/volume" @@ -14,14 +15,6 @@ import ( "github.com/sirupsen/logrus" ) -// currently created by hand. generation tool would generate this like: -// $ extpoint-gen Driver > volume/extpoint.go - -var drivers = &driverExtpoint{ - extensions: make(map[string]volume.Driver), - driverLock: &locker.Locker{}, -} - const extName = "VolumeDriver" // NewVolumeDriver returns a driver has the given name mapped on the given client. @@ -53,53 +46,21 @@ type volumeDriver interface { Capabilities() (capabilities volume.Capability, err error) } -type driverExtpoint struct { - extensions map[string]volume.Driver - sync.Mutex +// Store is an in-memory store for volume drivers +type Store struct { + extensions map[string]volume.Driver + mu sync.Mutex driverLock *locker.Locker - plugingetter getter.PluginGetter + pluginGetter getter.PluginGetter } -// RegisterPluginGetter sets the plugingetter -func RegisterPluginGetter(plugingetter getter.PluginGetter) { - drivers.plugingetter = plugingetter -} - -// Register associates the given driver to the given name, checking if -// the name is already associated -func Register(extension volume.Driver, name string) bool { - if name == "" { - return false +// NewStore creates a new volume driver store +func NewStore(pg getter.PluginGetter) *Store { + return &Store{ + extensions: make(map[string]volume.Driver), + driverLock: locker.New(), + pluginGetter: pg, } - - drivers.Lock() - defer drivers.Unlock() - - _, exists := drivers.extensions[name] - if exists { - return false - } - - if err := validateDriver(extension); err != nil { - return false - } - - drivers.extensions[name] = extension - - return true -} - -// Unregister dissociates the name from its driver, if the association exists. -func Unregister(name string) bool { - drivers.Lock() - defer drivers.Unlock() - - _, exists := drivers.extensions[name] - if !exists { - return false - } - delete(drivers.extensions, name) - return true } type driverNotFoundError string @@ -113,18 +74,21 @@ func (driverNotFoundError) NotFound() {} // lookup returns the driver associated with the given name. If a // driver with the given name has not been registered it checks if // there is a VolumeDriver plugin available with the given name. -func lookup(name string, mode int) (volume.Driver, error) { - drivers.driverLock.Lock(name) - defer drivers.driverLock.Unlock(name) +func (s *Store) lookup(name string, mode int) (volume.Driver, error) { + if name == "" { + return nil, errdefs.InvalidParameter(errors.New("driver name cannot be empty")) + } + s.driverLock.Lock(name) + defer s.driverLock.Unlock(name) - drivers.Lock() - ext, ok := drivers.extensions[name] - drivers.Unlock() + s.mu.Lock() + ext, ok := s.extensions[name] + s.mu.Unlock() if ok { return ext, nil } - if drivers.plugingetter != nil { - p, err := drivers.plugingetter.Get(name, extName, mode) + if s.pluginGetter != nil { + p, err := s.pluginGetter.Get(name, extName, mode) if err != nil { return nil, errors.Wrap(err, "error looking up volume plugin "+name) } @@ -133,7 +97,7 @@ func lookup(name string, mode int) (volume.Driver, error) { if err := validateDriver(d); err != nil { if mode > 0 { // Undo any reference count changes from the initial `Get` - if _, err := drivers.plugingetter.Get(name, extName, mode*-1); err != nil { + if _, err := s.pluginGetter.Get(name, extName, mode*-1); err != nil { logrus.WithError(err).WithField("action", "validate-driver").WithField("plugin", name).Error("error releasing reference to plugin") } } @@ -141,9 +105,9 @@ func lookup(name string, mode int) (volume.Driver, error) { } if p.IsV1() { - drivers.Lock() - drivers.extensions[name] = d - drivers.Unlock() + s.mu.Lock() + s.extensions[name] = d + s.mu.Unlock() } return d, nil } @@ -158,75 +122,88 @@ func validateDriver(vd volume.Driver) error { return nil } +// Register associates the given driver to the given name, checking if +// the name is already associated +func (s *Store) Register(d volume.Driver, name string) bool { + if name == "" { + return false + } + + s.mu.Lock() + defer s.mu.Unlock() + + if _, exists := s.extensions[name]; exists { + return false + } + + if err := validateDriver(d); err != nil { + return false + } + + s.extensions[name] = d + return true +} + // GetDriver returns a volume driver by its name. // If the driver is empty, it looks for the local driver. -func GetDriver(name string) (volume.Driver, error) { - if name == "" { - name = volume.DefaultDriverName - } - return lookup(name, getter.Lookup) +func (s *Store) GetDriver(name string) (volume.Driver, error) { + return s.lookup(name, getter.Lookup) } // CreateDriver returns a volume driver by its name and increments RefCount. // If the driver is empty, it looks for the local driver. -func CreateDriver(name string) (volume.Driver, error) { - if name == "" { - name = volume.DefaultDriverName - } - return lookup(name, getter.Acquire) +func (s *Store) CreateDriver(name string) (volume.Driver, error) { + return s.lookup(name, getter.Acquire) } // ReleaseDriver returns a volume driver by its name and decrements RefCount.. // If the driver is empty, it looks for the local driver. -func ReleaseDriver(name string) (volume.Driver, error) { - if name == "" { - name = volume.DefaultDriverName - } - return lookup(name, getter.Release) +func (s *Store) ReleaseDriver(name string) (volume.Driver, error) { + return s.lookup(name, getter.Release) } // GetDriverList returns list of volume drivers registered. // If no driver is registered, empty string list will be returned. -func GetDriverList() []string { +func (s *Store) GetDriverList() []string { var driverList []string - drivers.Lock() - for driverName := range drivers.extensions { + s.mu.Lock() + for driverName := range s.extensions { driverList = append(driverList, driverName) } - drivers.Unlock() + s.mu.Unlock() sort.Strings(driverList) return driverList } // GetAllDrivers lists all the registered drivers -func GetAllDrivers() ([]volume.Driver, error) { +func (s *Store) GetAllDrivers() ([]volume.Driver, error) { var plugins []getter.CompatPlugin - if drivers.plugingetter != nil { + if s.pluginGetter != nil { var err error - plugins, err = drivers.plugingetter.GetAllByCap(extName) + plugins, err = s.pluginGetter.GetAllByCap(extName) if err != nil { return nil, fmt.Errorf("error listing plugins: %v", err) } } var ds []volume.Driver - drivers.Lock() - defer drivers.Unlock() + s.mu.Lock() + defer s.mu.Unlock() - for _, d := range drivers.extensions { + for _, d := range s.extensions { ds = append(ds, d) } for _, p := range plugins { name := p.Name() - if _, ok := drivers.extensions[name]; ok { + if _, ok := s.extensions[name]; ok { continue } ext := NewVolumeDriver(name, p.ScopedPath, p.Client()) if p.IsV1() { - drivers.extensions[name] = ext + s.extensions[name] = ext } ds = append(ds, ext) } diff --git a/components/engine/volume/drivers/extpoint_test.go b/components/engine/volume/drivers/extpoint_test.go index fa18322a5d..384742ea00 100644 --- a/components/engine/volume/drivers/extpoint_test.go +++ b/components/engine/volume/drivers/extpoint_test.go @@ -1,4 +1,4 @@ -package volumedrivers // import "github.com/docker/docker/volume/drivers" +package drivers // import "github.com/docker/docker/volume/drivers" import ( "testing" @@ -7,13 +7,14 @@ import ( ) func TestGetDriver(t *testing.T) { - _, err := GetDriver("missing") + s := NewStore(nil) + _, err := s.GetDriver("missing") if err == nil { t.Fatal("Expected error, was nil") } - Register(volumetestutils.NewFakeDriver("fake"), "fake") + s.Register(volumetestutils.NewFakeDriver("fake"), "fake") - d, err := GetDriver("fake") + d, err := s.GetDriver("fake") if err != nil { t.Fatal(err) } diff --git a/components/engine/volume/drivers/proxy.go b/components/engine/volume/drivers/proxy.go index 6715e0ea9b..8a44faeddc 100644 --- a/components/engine/volume/drivers/proxy.go +++ b/components/engine/volume/drivers/proxy.go @@ -1,6 +1,6 @@ // generated code - DO NOT EDIT -package volumedrivers // import "github.com/docker/docker/volume/drivers" +package drivers // import "github.com/docker/docker/volume/drivers" import ( "errors" diff --git a/components/engine/volume/drivers/proxy_test.go b/components/engine/volume/drivers/proxy_test.go index 220186d298..79af956333 100644 --- a/components/engine/volume/drivers/proxy_test.go +++ b/components/engine/volume/drivers/proxy_test.go @@ -1,4 +1,4 @@ -package volumedrivers // import "github.com/docker/docker/volume/drivers" +package drivers // import "github.com/docker/docker/volume/drivers" import ( "fmt" diff --git a/components/engine/volume/lcow_parser.go b/components/engine/volume/mounts/lcow_parser.go similarity index 93% rename from components/engine/volume/lcow_parser.go rename to components/engine/volume/mounts/lcow_parser.go index dba0eb66cd..bafb7b07f8 100644 --- a/components/engine/volume/lcow_parser.go +++ b/components/engine/volume/mounts/lcow_parser.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "errors" diff --git a/components/engine/volume/linux_parser.go b/components/engine/volume/mounts/linux_parser.go similarity index 98% rename from components/engine/volume/linux_parser.go rename to components/engine/volume/mounts/linux_parser.go index 6eb796b678..8e436aec0e 100644 --- a/components/engine/volume/linux_parser.go +++ b/components/engine/volume/mounts/linux_parser.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "errors" @@ -9,6 +9,7 @@ import ( "github.com/docker/docker/api/types/mount" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/volume" ) type linuxParser struct { @@ -405,7 +406,7 @@ func (p *linuxParser) ValidateVolumeName(name string) error { } func (p *linuxParser) IsBackwardCompatible(m *MountPoint) bool { - return len(m.Source) > 0 || m.Driver == DefaultDriverName + return len(m.Source) > 0 || m.Driver == volume.DefaultDriverName } func (p *linuxParser) ValidateTmpfsMountDestination(dest string) error { diff --git a/components/engine/volume/mounts/mounts.go b/components/engine/volume/mounts/mounts.go new file mode 100644 index 0000000000..8f255a5482 --- /dev/null +++ b/components/engine/volume/mounts/mounts.go @@ -0,0 +1,170 @@ +package mounts // import "github.com/docker/docker/volume/mounts" + +import ( + "fmt" + "os" + "path/filepath" + "syscall" + + mounttypes "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/volume" + "github.com/opencontainers/selinux/go-selinux/label" + "github.com/pkg/errors" +) + +// MountPoint is the intersection point between a volume and a container. It +// specifies which volume is to be used and where inside a container it should +// be mounted. +// +// Note that this type is embedded in `container.Container` object and persisted to disk. +// Changes to this struct need to by synced with on disk state. +type MountPoint struct { + // Source is the source path of the mount. + // E.g. `mount --bind /foo /bar`, `/foo` is the `Source`. + Source string + // Destination is the path relative to the container root (`/`) to the mount point + // It is where the `Source` is mounted to + Destination string + // RW is set to true when the mountpoint should be mounted as read-write + RW bool + // Name is the name reference to the underlying data defined by `Source` + // e.g., the volume name + Name string + // Driver is the volume driver used to create the volume (if it is a volume) + Driver string + // Type of mount to use, see `Type` definitions in github.com/docker/docker/api/types/mount + Type mounttypes.Type `json:",omitempty"` + // Volume is the volume providing data to this mountpoint. + // This is nil unless `Type` is set to `TypeVolume` + Volume volume.Volume `json:"-"` + + // Mode is the comma separated list of options supplied by the user when creating + // the bind/volume mount. + // Note Mode is not used on Windows + Mode string `json:"Relabel,omitempty"` // Originally field was `Relabel`" + + // Propagation describes how the mounts are propagated from the host into the + // mount point, and vice-versa. + // See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt + // Note Propagation is not used on Windows + Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string + + // Specifies if data should be copied from the container before the first mount + // Use a pointer here so we can tell if the user set this value explicitly + // This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated + CopyData bool `json:"-"` + // ID is the opaque ID used to pass to the volume driver. + // This should be set by calls to `Mount` and unset by calls to `Unmount` + ID string `json:",omitempty"` + + // Sepc is a copy of the API request that created this mount. + Spec mounttypes.Mount + + // Track usage of this mountpoint + // Specifically needed for containers which are running and calls to `docker cp` + // because both these actions require mounting the volumes. + active int +} + +// Cleanup frees resources used by the mountpoint +func (m *MountPoint) Cleanup() error { + if m.Volume == nil || m.ID == "" { + return nil + } + + if err := m.Volume.Unmount(m.ID); err != nil { + return errors.Wrapf(err, "error unmounting volume %s", m.Volume.Name()) + } + + m.active-- + if m.active == 0 { + m.ID = "" + } + return nil +} + +// Setup sets up a mount point by either mounting the volume if it is +// configured, or creating the source directory if supplied. +// The, optional, checkFun parameter allows doing additional checking +// before creating the source directory on the host. +func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.IDPair, checkFun func(m *MountPoint) error) (path string, err error) { + defer func() { + if err != nil || !label.RelabelNeeded(m.Mode) { + return + } + + var sourcePath string + sourcePath, err = filepath.EvalSymlinks(m.Source) + if err != nil { + path = "" + err = errors.Wrapf(err, "error evaluating symlinks from mount source %q", m.Source) + return + } + err = label.Relabel(sourcePath, mountLabel, label.IsShared(m.Mode)) + if err == syscall.ENOTSUP { + err = nil + } + if err != nil { + path = "" + err = errors.Wrapf(err, "error setting label on mount source '%s'", sourcePath) + } + }() + + if m.Volume != nil { + id := m.ID + if id == "" { + id = stringid.GenerateNonCryptoID() + } + path, err := m.Volume.Mount(id) + if err != nil { + return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source) + } + + m.ID = id + m.active++ + return path, nil + } + + if len(m.Source) == 0 { + return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined") + } + + if m.Type == mounttypes.TypeBind { + // Before creating the source directory on the host, invoke checkFun if it's not nil. One of + // the use case is to forbid creating the daemon socket as a directory if the daemon is in + // the process of shutting down. + if checkFun != nil { + if err := checkFun(m); err != nil { + return "", err + } + } + // idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory) + // also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it + if err := idtools.MkdirAllAndChownNew(m.Source, 0755, rootIDs); err != nil { + if perr, ok := err.(*os.PathError); ok { + if perr.Err != syscall.ENOTDIR { + return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source) + } + } + } + } + return m.Source, nil +} + +// Path returns the path of a volume in a mount point. +func (m *MountPoint) Path() string { + if m.Volume != nil { + return m.Volume.Path() + } + return m.Source +} + +func errInvalidMode(mode string) error { + return errors.Errorf("invalid mode: %v", mode) +} + +func errInvalidSpec(spec string) error { + return errors.Errorf("invalid volume specification: '%s'", spec) +} diff --git a/components/engine/volume/parser.go b/components/engine/volume/mounts/parser.go similarity index 95% rename from components/engine/volume/parser.go rename to components/engine/volume/mounts/parser.go index 9a10267819..73681750ea 100644 --- a/components/engine/volume/parser.go +++ b/components/engine/volume/mounts/parser.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "errors" diff --git a/components/engine/volume/volume_test.go b/components/engine/volume/mounts/parser_test.go similarity index 99% rename from components/engine/volume/volume_test.go rename to components/engine/volume/mounts/parser_test.go index f5bc1b0f54..347f7d9c4d 100644 --- a/components/engine/volume/volume_test.go +++ b/components/engine/volume/mounts/parser_test.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "io/ioutil" diff --git a/components/engine/volume/validate.go b/components/engine/volume/mounts/validate.go similarity index 90% rename from components/engine/volume/validate.go rename to components/engine/volume/mounts/validate.go index 6512fb11ba..0b71526901 100644 --- a/components/engine/volume/validate.go +++ b/components/engine/volume/mounts/validate.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "fmt" diff --git a/components/engine/volume/validate_test.go b/components/engine/volume/mounts/validate_test.go similarity index 97% rename from components/engine/volume/validate_test.go rename to components/engine/volume/mounts/validate_test.go index d230ef3193..4f83856043 100644 --- a/components/engine/volume/validate_test.go +++ b/components/engine/volume/mounts/validate_test.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "errors" diff --git a/components/engine/volume/validate_unix_test.go b/components/engine/volume/mounts/validate_unix_test.go similarity index 57% rename from components/engine/volume/validate_unix_test.go rename to components/engine/volume/mounts/validate_unix_test.go index 5375db380e..a319371451 100644 --- a/components/engine/volume/validate_unix_test.go +++ b/components/engine/volume/mounts/validate_unix_test.go @@ -1,6 +1,6 @@ // +build !windows -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" var ( testDestinationPath = "/foo" diff --git a/components/engine/volume/validate_windows_test.go b/components/engine/volume/mounts/validate_windows_test.go similarity index 52% rename from components/engine/volume/validate_windows_test.go rename to components/engine/volume/mounts/validate_windows_test.go index 9053a01a8b..74b40a6c30 100644 --- a/components/engine/volume/validate_windows_test.go +++ b/components/engine/volume/mounts/validate_windows_test.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" var ( testDestinationPath = `c:\foo` diff --git a/components/engine/volume/volume_copy.go b/components/engine/volume/mounts/volume_copy.go similarity index 87% rename from components/engine/volume/volume_copy.go rename to components/engine/volume/mounts/volume_copy.go index c0dc1cf2c2..04056fa50a 100644 --- a/components/engine/volume/volume_copy.go +++ b/components/engine/volume/mounts/volume_copy.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import "strings" diff --git a/components/engine/volume/volume_unix.go b/components/engine/volume/mounts/volume_unix.go similarity index 86% rename from components/engine/volume/volume_unix.go rename to components/engine/volume/mounts/volume_unix.go index 228c6d73a3..c6d51e0710 100644 --- a/components/engine/volume/volume_unix.go +++ b/components/engine/volume/mounts/volume_unix.go @@ -1,6 +1,6 @@ // +build linux freebsd darwin -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "fmt" diff --git a/components/engine/volume/volume_windows.go b/components/engine/volume/mounts/volume_windows.go similarity index 74% rename from components/engine/volume/volume_windows.go rename to components/engine/volume/mounts/volume_windows.go index 1179dd4f4f..773e7db88a 100644 --- a/components/engine/volume/volume_windows.go +++ b/components/engine/volume/mounts/volume_windows.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" func (p *windowsParser) HasResource(m *MountPoint, absolutePath string) bool { return false diff --git a/components/engine/volume/windows_parser.go b/components/engine/volume/mounts/windows_parser.go similarity index 99% rename from components/engine/volume/windows_parser.go rename to components/engine/volume/mounts/windows_parser.go index 84b6717c95..ac61044043 100644 --- a/components/engine/volume/windows_parser.go +++ b/components/engine/volume/mounts/windows_parser.go @@ -1,4 +1,4 @@ -package volume // import "github.com/docker/docker/volume" +package mounts // import "github.com/docker/docker/volume/mounts" import ( "errors" diff --git a/components/engine/volume/store/restore.go b/components/engine/volume/store/restore.go index 790a5b65f7..2e072ec087 100644 --- a/components/engine/volume/store/restore.go +++ b/components/engine/volume/store/restore.go @@ -5,7 +5,6 @@ import ( "github.com/boltdb/bolt" "github.com/docker/docker/volume" - "github.com/docker/docker/volume/drivers" "github.com/sirupsen/logrus" ) @@ -33,7 +32,7 @@ func (s *VolumeStore) restore() { var v volume.Volume var err error if meta.Driver != "" { - v, err = lookupVolume(meta.Driver, meta.Name) + v, err = lookupVolume(s.drivers, meta.Driver, meta.Name) if err != nil && err != errNoSuchVolume { logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", meta.Name).Warn("Error restoring volume") return @@ -59,7 +58,7 @@ func (s *VolumeStore) restore() { } // increment driver refcount - volumedrivers.CreateDriver(meta.Driver) + s.drivers.CreateDriver(meta.Driver) // cache the volume s.globalLock.Lock() diff --git a/components/engine/volume/store/restore_test.go b/components/engine/volume/store/restore_test.go index 680735a384..5c3c6df72c 100644 --- a/components/engine/volume/store/restore_test.go +++ b/components/engine/volume/store/restore_test.go @@ -18,11 +18,11 @@ func TestRestore(t *testing.T) { assert.NilError(t, err) defer os.RemoveAll(dir) + drivers := volumedrivers.NewStore(nil) driverName := "test-restore" - volumedrivers.Register(volumetestutils.NewFakeDriver(driverName), driverName) - defer volumedrivers.Unregister("test-restore") + drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName) - s, err := New(dir) + s, err := New(dir, drivers) assert.NilError(t, err) defer s.Shutdown() @@ -36,7 +36,7 @@ func TestRestore(t *testing.T) { s.Shutdown() - s, err = New(dir) + s, err = New(dir, drivers) assert.NilError(t, err) v, err := s.Get("test1") diff --git a/components/engine/volume/store/store.go b/components/engine/volume/store/store.go index 70dd8b2d89..67b4e7d7ef 100644 --- a/components/engine/volume/store/store.go +++ b/components/engine/volume/store/store.go @@ -14,6 +14,7 @@ import ( "github.com/docker/docker/pkg/locker" "github.com/docker/docker/volume" "github.com/docker/docker/volume/drivers" + volumemounts "github.com/docker/docker/volume/mounts" "github.com/sirupsen/logrus" ) @@ -66,13 +67,14 @@ func (v volumeWrapper) CachedPath() string { // New initializes a VolumeStore to keep // reference counting of volumes in the system. -func New(rootPath string) (*VolumeStore, error) { +func New(rootPath string, drivers *drivers.Store) (*VolumeStore, error) { vs := &VolumeStore{ locks: &locker.Locker{}, names: make(map[string]volume.Volume), refs: make(map[string]map[string]struct{}), labels: make(map[string]map[string]string), options: make(map[string]map[string]string), + drivers: drivers, } if rootPath != "" { @@ -157,7 +159,7 @@ func (s *VolumeStore) Purge(name string) { v, exists := s.names[name] if exists { driverName := v.DriverName() - if _, err := volumedrivers.ReleaseDriver(driverName); err != nil { + if _, err := s.drivers.ReleaseDriver(driverName); err != nil { logrus.WithError(err).WithField("driver", driverName).Error("Error releasing reference to volume driver") } } @@ -175,7 +177,8 @@ func (s *VolumeStore) Purge(name string) { type VolumeStore struct { // locks ensures that only one action is being performed on a particular volume at a time without locking the entire store // since actions on volumes can be quite slow, this ensures the store is free to handle requests for other volumes. - locks *locker.Locker + locks *locker.Locker + drivers *drivers.Store // globalLock is used to protect access to mutable structures used by the store object globalLock sync.RWMutex // names stores the volume name -> volume relationship. @@ -226,7 +229,7 @@ func (s *VolumeStore) list() ([]volume.Volume, []string, error) { warnings []string ) - drivers, err := volumedrivers.GetAllDrivers() + drivers, err := s.drivers.GetAllDrivers() if err != nil { return nil, nil, err } @@ -329,7 +332,7 @@ func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, err if driverName != "" { // Retrieve canonical driver name to avoid inconsistencies (for example // "plugin" vs. "plugin:latest") - vd, err := volumedrivers.GetDriver(driverName) + vd, err := s.drivers.GetDriver(driverName) if err != nil { return nil, err } @@ -341,7 +344,7 @@ func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, err // let's check if the found volume ref // is stale by checking with the driver if it still exists - exists, err := volumeExists(v) + exists, err := volumeExists(s.drivers, v) if err != nil { return nil, errors.Wrapf(errNameConflict, "found reference to volume '%s' in driver '%s', but got an error while checking the driver: %v", name, vDriverName, err) } @@ -366,8 +369,8 @@ func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, err // volumeExists returns if the volume is still present in the driver. // An error is returned if there was an issue communicating with the driver. -func volumeExists(v volume.Volume) (bool, error) { - exists, err := lookupVolume(v.DriverName(), v.Name()) +func volumeExists(store *drivers.Store, v volume.Volume) (bool, error) { + exists, err := lookupVolume(store, v.DriverName(), v.Name()) if err != nil { return false, err } @@ -385,7 +388,7 @@ func (s *VolumeStore) create(name, driverName string, opts, labels map[string]st // volume name validation is specific to the host os and not on container image // windows/lcow should have an equivalent volumename validation logic so we create a parser for current host OS - parser := volume.NewParser(runtime.GOOS) + parser := volumemounts.NewParser(runtime.GOOS) err := parser.ValidateVolumeName(name) if err != nil { return nil, err @@ -412,7 +415,10 @@ func (s *VolumeStore) create(name, driverName string, opts, labels map[string]st } } - vd, err := volumedrivers.CreateDriver(driverName) + if driverName == "" { + driverName = volume.DefaultDriverName + } + vd, err := s.drivers.CreateDriver(driverName) if err != nil { return nil, &OpErr{Op: "create", Name: name, Err: err} } @@ -421,7 +427,7 @@ func (s *VolumeStore) create(name, driverName string, opts, labels map[string]st if v, _ = vd.Get(name); v == nil { v, err = vd.Create(name, opts) if err != nil { - if _, err := volumedrivers.ReleaseDriver(driverName); err != nil { + if _, err := s.drivers.ReleaseDriver(driverName); err != nil { logrus.WithError(err).WithField("driver", driverName).Error("Error releasing reference to volume driver") } return nil, err @@ -455,7 +461,10 @@ func (s *VolumeStore) GetWithRef(name, driverName, ref string) (volume.Volume, e s.locks.Lock(name) defer s.locks.Unlock(name) - vd, err := volumedrivers.GetDriver(driverName) + if driverName == "" { + driverName = volume.DefaultDriverName + } + vd, err := s.drivers.GetDriver(driverName) if err != nil { return nil, &OpErr{Err: err, Name: name, Op: "get"} } @@ -510,7 +519,7 @@ func (s *VolumeStore) getVolume(name string) (volume.Volume, error) { } if meta.Driver != "" { - vol, err := lookupVolume(meta.Driver, name) + vol, err := lookupVolume(s.drivers, meta.Driver, name) if err != nil { return nil, err } @@ -520,7 +529,7 @@ func (s *VolumeStore) getVolume(name string) (volume.Volume, error) { } var scope string - vd, err := volumedrivers.GetDriver(meta.Driver) + vd, err := s.drivers.GetDriver(meta.Driver) if err == nil { scope = vd.Scope() } @@ -528,7 +537,7 @@ func (s *VolumeStore) getVolume(name string) (volume.Volume, error) { } logrus.Debugf("Probing all drivers for volume with name: %s", name) - drivers, err := volumedrivers.GetAllDrivers() + drivers, err := s.drivers.GetAllDrivers() if err != nil { return nil, err } @@ -552,8 +561,11 @@ func (s *VolumeStore) getVolume(name string) (volume.Volume, error) { // If the driver returns an error that is not communication related the // error is logged but not returned. // If the volume is not found it will return `nil, nil`` -func lookupVolume(driverName, volumeName string) (volume.Volume, error) { - vd, err := volumedrivers.GetDriver(driverName) +func lookupVolume(store *drivers.Store, driverName, volumeName string) (volume.Volume, error) { + if driverName == "" { + driverName = volume.DefaultDriverName + } + vd, err := store.GetDriver(driverName) if err != nil { return nil, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", volumeName, driverName) } @@ -585,7 +597,7 @@ func (s *VolumeStore) Remove(v volume.Volume) error { return &OpErr{Err: errVolumeInUse, Name: v.Name(), Op: "remove", Refs: s.getRefs(name)} } - vd, err := volumedrivers.GetDriver(v.DriverName()) + vd, err := s.drivers.GetDriver(v.DriverName()) if err != nil { return &OpErr{Err: err, Name: v.DriverName(), Op: "remove"} } @@ -627,7 +639,7 @@ func (s *VolumeStore) Refs(v volume.Volume) []string { // FilterByDriver returns the available volumes filtered by driver name func (s *VolumeStore) FilterByDriver(name string) ([]volume.Volume, error) { - vd, err := volumedrivers.GetDriver(name) + vd, err := s.drivers.GetDriver(name) if err != nil { return nil, &OpErr{Err: err, Name: name, Op: "list"} } @@ -686,3 +698,10 @@ func unwrapVolume(v volume.Volume) volume.Volume { func (s *VolumeStore) Shutdown() error { return s.db.Close() } + +// GetDriverList gets the list of volume drivers from the configured volume driver +// store. +// TODO(@cpuguy83): This should be factored out into a separate service. +func (s *VolumeStore) GetDriverList() []string { + return s.drivers.GetDriverList() +} diff --git a/components/engine/volume/store/store_test.go b/components/engine/volume/store/store_test.go index faf4035e29..288a4ce824 100644 --- a/components/engine/volume/store/store_test.go +++ b/components/engine/volume/store/store_test.go @@ -10,7 +10,7 @@ import ( "testing" "github.com/docker/docker/volume" - "github.com/docker/docker/volume/drivers" + volumedrivers "github.com/docker/docker/volume/drivers" volumetestutils "github.com/docker/docker/volume/testutils" "github.com/google/go-cmp/cmp" "github.com/gotestyourself/gotestyourself/assert" @@ -18,18 +18,12 @@ import ( ) func TestCreate(t *testing.T) { - volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") - defer volumedrivers.Unregister("fake") - dir, err := ioutil.TempDir("", "test-create") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) + t.Parallel() + + s, cleanup := setupTest(t) + defer cleanup() + s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") - s, err := New(dir) - if err != nil { - t.Fatal(err) - } v, err := s.Create("fake1", "fake", nil, nil) if err != nil { t.Fatal(err) @@ -53,19 +47,13 @@ func TestCreate(t *testing.T) { } func TestRemove(t *testing.T) { - volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") - volumedrivers.Register(volumetestutils.NewFakeDriver("noop"), "noop") - defer volumedrivers.Unregister("fake") - defer volumedrivers.Unregister("noop") - dir, err := ioutil.TempDir("", "test-remove") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - s, err := New(dir) - if err != nil { - t.Fatal(err) - } + t.Parallel() + + s, cleanup := setupTest(t) + defer cleanup() + + s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") + s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop") // doing string compare here since this error comes directly from the driver expected := "no such volume" @@ -91,20 +79,19 @@ func TestRemove(t *testing.T) { } func TestList(t *testing.T) { - volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") - volumedrivers.Register(volumetestutils.NewFakeDriver("fake2"), "fake2") - defer volumedrivers.Unregister("fake") - defer volumedrivers.Unregister("fake2") + t.Parallel() + dir, err := ioutil.TempDir("", "test-list") - if err != nil { - t.Fatal(err) - } + assert.NilError(t, err) defer os.RemoveAll(dir) - s, err := New(dir) - if err != nil { - t.Fatal(err) - } + drivers := volumedrivers.NewStore(nil) + drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") + drivers.Register(volumetestutils.NewFakeDriver("fake2"), "fake2") + + s, err := New(dir, drivers) + assert.NilError(t, err) + if _, err := s.Create("test", "fake", nil, nil); err != nil { t.Fatal(err) } @@ -124,7 +111,7 @@ func TestList(t *testing.T) { } // and again with a new store - s, err = New(dir) + s, err = New(dir, drivers) if err != nil { t.Fatal(err) } @@ -138,18 +125,12 @@ func TestList(t *testing.T) { } func TestFilterByDriver(t *testing.T) { - volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") - volumedrivers.Register(volumetestutils.NewFakeDriver("noop"), "noop") - defer volumedrivers.Unregister("fake") - defer volumedrivers.Unregister("noop") - dir, err := ioutil.TempDir("", "test-filter-driver") - if err != nil { - t.Fatal(err) - } - s, err := New(dir) - if err != nil { - t.Fatal(err) - } + t.Parallel() + s, cleanup := setupTest(t) + defer cleanup() + + s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") + s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop") if _, err := s.Create("fake1", "fake", nil, nil); err != nil { t.Fatal(err) @@ -171,17 +152,12 @@ func TestFilterByDriver(t *testing.T) { } func TestFilterByUsed(t *testing.T) { - volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") - volumedrivers.Register(volumetestutils.NewFakeDriver("noop"), "noop") - dir, err := ioutil.TempDir("", "test-filter-used") - if err != nil { - t.Fatal(err) - } + t.Parallel() + s, cleanup := setupTest(t) + defer cleanup() - s, err := New(dir) - if err != nil { - t.Fatal(err) - } + s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") + s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop") if _, err := s.CreateWithRef("fake1", "fake", "volReference", nil, nil); err != nil { t.Fatal(err) @@ -213,16 +189,10 @@ func TestFilterByUsed(t *testing.T) { } func TestDerefMultipleOfSameRef(t *testing.T) { - volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") - dir, err := ioutil.TempDir("", "test-same-deref") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - s, err := New(dir) - if err != nil { - t.Fatal(err) - } + t.Parallel() + s, cleanup := setupTest(t) + defer cleanup() + s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake") v, err := s.CreateWithRef("fake1", "fake", "volReference", nil, nil) if err != nil { @@ -240,17 +210,12 @@ func TestDerefMultipleOfSameRef(t *testing.T) { } func TestCreateKeepOptsLabelsWhenExistsRemotely(t *testing.T) { + t.Parallel() + s, cleanup := setupTest(t) + defer cleanup() + vd := volumetestutils.NewFakeDriver("fake") - volumedrivers.Register(vd, "fake") - dir, err := ioutil.TempDir("", "test-same-deref") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - s, err := New(dir) - if err != nil { - t.Fatal(err) - } + s.drivers.Register(vd, "fake") // Create a volume in the driver directly if _, err := vd.Create("foo", nil); err != nil { @@ -273,6 +238,8 @@ func TestCreateKeepOptsLabelsWhenExistsRemotely(t *testing.T) { } func TestDefererencePluginOnCreateError(t *testing.T) { + t.Parallel() + var ( l net.Listener err error @@ -286,6 +253,9 @@ func TestDefererencePluginOnCreateError(t *testing.T) { } defer l.Close() + s, cleanup := setupTest(t) + defer cleanup() + d := volumetestutils.NewFakeDriver("TestDefererencePluginOnCreateError") p, err := volumetestutils.MakeFakePlugin(d, l) if err != nil { @@ -293,19 +263,7 @@ func TestDefererencePluginOnCreateError(t *testing.T) { } pg := volumetestutils.NewFakePluginGetter(p) - volumedrivers.RegisterPluginGetter(pg) - defer volumedrivers.RegisterPluginGetter(nil) - - dir, err := ioutil.TempDir("", "test-plugin-deref-err") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - s, err := New(dir) - if err != nil { - t.Fatal(err) - } + s.drivers = volumedrivers.NewStore(pg) // create a good volume so we have a plugin reference _, err = s.Create("fake1", d.Name(), nil, nil) @@ -329,8 +287,9 @@ func TestRefDerefRemove(t *testing.T) { t.Parallel() driverName := "test-ref-deref-remove" - s, cleanup := setupTest(t, driverName) - defer cleanup(t) + s, cleanup := setupTest(t) + defer cleanup() + s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName) v, err := s.CreateWithRef("test", driverName, "test-ref", nil, nil) assert.NilError(t, err) @@ -348,8 +307,9 @@ func TestGet(t *testing.T) { t.Parallel() driverName := "test-get" - s, cleanup := setupTest(t, driverName) - defer cleanup(t) + s, cleanup := setupTest(t) + defer cleanup() + s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName) _, err := s.Get("not-exist") assert.Assert(t, is.ErrorContains(err, "")) @@ -373,8 +333,9 @@ func TestGetWithRef(t *testing.T) { t.Parallel() driverName := "test-get-with-ref" - s, cleanup := setupTest(t, driverName) - defer cleanup(t) + s, cleanup := setupTest(t) + defer cleanup() + s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName) _, err := s.GetWithRef("not-exist", driverName, "test-ref") assert.Assert(t, is.ErrorContains(err, "")) @@ -397,32 +358,22 @@ func TestGetWithRef(t *testing.T) { var cmpVolume = cmp.AllowUnexported(volumetestutils.FakeVolume{}, volumeWrapper{}) -func setupTest(t *testing.T, name string) (*VolumeStore, func(*testing.T)) { - t.Helper() - s, cleanup := newTestStore(t) - - volumedrivers.Register(volumetestutils.NewFakeDriver(name), name) - return s, func(t *testing.T) { - cleanup(t) - volumedrivers.Unregister(name) - } -} - -func newTestStore(t *testing.T) (*VolumeStore, func(*testing.T)) { +func setupTest(t *testing.T) (*VolumeStore, func()) { t.Helper() - dir, err := ioutil.TempDir("", "store-root") + dirName := strings.Replace(t.Name(), string(os.PathSeparator), "_", -1) + dir, err := ioutil.TempDir("", dirName) assert.NilError(t, err) - cleanup := func(t *testing.T) { + cleanup := func() { err := os.RemoveAll(dir) assert.Check(t, err) } - s, err := New(dir) + s, err := New(dir, volumedrivers.NewStore(nil)) assert.Check(t, err) - return s, func(t *testing.T) { + return s, func() { s.Shutdown() - cleanup(t) + cleanup() } } diff --git a/components/engine/volume/volume.go b/components/engine/volume/volume.go index 0b1d4e8657..61c8243979 100644 --- a/components/engine/volume/volume.go +++ b/components/engine/volume/volume.go @@ -1,17 +1,7 @@ package volume // import "github.com/docker/docker/volume" import ( - "fmt" - "os" - "path/filepath" - "syscall" "time" - - mounttypes "github.com/docker/docker/api/types/mount" - "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/stringid" - "github.com/opencontainers/selinux/go-selinux/label" - "github.com/pkg/errors" ) // DefaultDriverName is the driver name used for the driver @@ -77,155 +67,3 @@ type DetailedVolume interface { Scope() string Volume } - -// MountPoint is the intersection point between a volume and a container. It -// specifies which volume is to be used and where inside a container it should -// be mounted. -type MountPoint struct { - // Source is the source path of the mount. - // E.g. `mount --bind /foo /bar`, `/foo` is the `Source`. - Source string - // Destination is the path relative to the container root (`/`) to the mount point - // It is where the `Source` is mounted to - Destination string - // RW is set to true when the mountpoint should be mounted as read-write - RW bool - // Name is the name reference to the underlying data defined by `Source` - // e.g., the volume name - Name string - // Driver is the volume driver used to create the volume (if it is a volume) - Driver string - // Type of mount to use, see `Type` definitions in github.com/docker/docker/api/types/mount - Type mounttypes.Type `json:",omitempty"` - // Volume is the volume providing data to this mountpoint. - // This is nil unless `Type` is set to `TypeVolume` - Volume Volume `json:"-"` - - // Mode is the comma separated list of options supplied by the user when creating - // the bind/volume mount. - // Note Mode is not used on Windows - Mode string `json:"Relabel,omitempty"` // Originally field was `Relabel`" - - // Propagation describes how the mounts are propagated from the host into the - // mount point, and vice-versa. - // See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt - // Note Propagation is not used on Windows - Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string - - // Specifies if data should be copied from the container before the first mount - // Use a pointer here so we can tell if the user set this value explicitly - // This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated - CopyData bool `json:"-"` - // ID is the opaque ID used to pass to the volume driver. - // This should be set by calls to `Mount` and unset by calls to `Unmount` - ID string `json:",omitempty"` - - // Sepc is a copy of the API request that created this mount. - Spec mounttypes.Mount - - // Track usage of this mountpoint - // Specifically needed for containers which are running and calls to `docker cp` - // because both these actions require mounting the volumes. - active int -} - -// Cleanup frees resources used by the mountpoint -func (m *MountPoint) Cleanup() error { - if m.Volume == nil || m.ID == "" { - return nil - } - - if err := m.Volume.Unmount(m.ID); err != nil { - return errors.Wrapf(err, "error unmounting volume %s", m.Volume.Name()) - } - - m.active-- - if m.active == 0 { - m.ID = "" - } - return nil -} - -// Setup sets up a mount point by either mounting the volume if it is -// configured, or creating the source directory if supplied. -// The, optional, checkFun parameter allows doing additional checking -// before creating the source directory on the host. -func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.IDPair, checkFun func(m *MountPoint) error) (path string, err error) { - defer func() { - if err != nil || !label.RelabelNeeded(m.Mode) { - return - } - - var sourcePath string - sourcePath, err = filepath.EvalSymlinks(m.Source) - if err != nil { - path = "" - err = errors.Wrapf(err, "error evaluating symlinks from mount source %q", m.Source) - return - } - err = label.Relabel(sourcePath, mountLabel, label.IsShared(m.Mode)) - if err == syscall.ENOTSUP { - err = nil - } - if err != nil { - path = "" - err = errors.Wrapf(err, "error setting label on mount source '%s'", sourcePath) - } - }() - - if m.Volume != nil { - id := m.ID - if id == "" { - id = stringid.GenerateNonCryptoID() - } - path, err := m.Volume.Mount(id) - if err != nil { - return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source) - } - - m.ID = id - m.active++ - return path, nil - } - - if len(m.Source) == 0 { - return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined") - } - - if m.Type == mounttypes.TypeBind { - // Before creating the source directory on the host, invoke checkFun if it's not nil. One of - // the use case is to forbid creating the daemon socket as a directory if the daemon is in - // the process of shutting down. - if checkFun != nil { - if err := checkFun(m); err != nil { - return "", err - } - } - // idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory) - // also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it - if err := idtools.MkdirAllAndChownNew(m.Source, 0755, rootIDs); err != nil { - if perr, ok := err.(*os.PathError); ok { - if perr.Err != syscall.ENOTDIR { - return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source) - } - } - } - } - return m.Source, nil -} - -// Path returns the path of a volume in a mount point. -func (m *MountPoint) Path() string { - if m.Volume != nil { - return m.Volume.Path() - } - return m.Source -} - -func errInvalidMode(mode string) error { - return errors.Errorf("invalid mode: %v", mode) -} - -func errInvalidSpec(spec string) error { - return errors.Errorf("invalid volume specification: '%s'", spec) -}