From 87c07240764e3355de6baff4074ebc36a5ca8efc Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Sun, 20 Aug 2017 13:50:52 +1000 Subject: [PATCH 01/94] devicemapper: remove container rootfs mountPath after umount libdm currently has a fairly substantial DoS bug that makes certain operations fail on a libdm device if the device has active references through mountpoints. This is a significant problem with the advent of mount namespaces and MS_PRIVATE, and can cause certain --volume mounts to cause libdm to no longer be able to remove containers: % docker run -d --name testA busybox top % docker run -d --name testB -v /var/lib/docker:/docker busybox top % docker rm -f testA [fails on libdm with dm_task_run errors.] This also solves the problem of unprivileged users being able to DoS docker by using unprivileged mount namespaces to preseve mounts that Docker has dropped. Signed-off-by: Aleksa Sarai Upstream-commit: 92e45b81e0a8b68d9567a2068247460a1ba59600 Component: engine --- .../engine/daemon/graphdriver/devmapper/deviceset.go | 12 ++++++++++++ .../engine/daemon/graphdriver/devmapper/driver.go | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/components/engine/daemon/graphdriver/devmapper/deviceset.go b/components/engine/daemon/graphdriver/devmapper/deviceset.go index 32b35c9acd..5f239a126c 100644 --- a/components/engine/daemon/graphdriver/devmapper/deviceset.go +++ b/components/engine/daemon/graphdriver/devmapper/deviceset.go @@ -2416,6 +2416,18 @@ func (devices *DeviceSet) UnmountDevice(hash, mountPath string) error { } logrus.Debug("devmapper: Unmount done") + // Remove the mountpoint here. Removing the mountpoint (in newer kernels) + // will cause all other instances of this mount in other mount namespaces + // to be killed (this is an anti-DoS measure that is necessary for things + // like devicemapper). This is necessary to avoid cases where a libdm mount + // that is present in another namespace will cause subsequent RemoveDevice + // operations to fail. We ignore any errors here because this may fail on + // 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) + } + return devices.deactivateDevice(info) } diff --git a/components/engine/daemon/graphdriver/devmapper/driver.go b/components/engine/daemon/graphdriver/devmapper/driver.go index 02ee0124f1..1efd5f44e0 100644 --- a/components/engine/daemon/graphdriver/devmapper/driver.go +++ b/components/engine/daemon/graphdriver/devmapper/driver.go @@ -228,10 +228,12 @@ func (d *Driver) Put(id string) error { if count := d.ctr.Decrement(mp); count > 0 { return nil } + err := d.DeviceSet.UnmountDevice(id, mp) if err != nil { - logrus.Errorf("devmapper: Error unmounting device %s: %s", id, err) + logrus.Errorf("devmapper: Error unmounting device %s: %v", id, err) } + return err } From 5103c6279fd0fbb9680033dd14e3858f5081eca0 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 26 Jul 2017 15:26:55 -0700 Subject: [PATCH 02/94] builder: fix build cache hash for broken symlink Signed-off-by: Tonis Tiigi Upstream-commit: 793ebdbf4b187b3680aed0073643040ddbeef523 Component: engine --- components/engine/builder/remotecontext/archive.go | 3 --- components/engine/builder/remotecontext/lazycontext.go | 10 +++++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/components/engine/builder/remotecontext/archive.go b/components/engine/builder/remotecontext/archive.go index fc18c5da31..b62d9dd0b7 100644 --- a/components/engine/builder/remotecontext/archive.go +++ b/components/engine/builder/remotecontext/archive.go @@ -122,8 +122,5 @@ func normalize(path string, root containerfs.ContainerFS) (cleanPath, fullPath s if err != nil { return "", "", errors.Wrapf(err, "forbidden path outside the build context: %s (%s)", path, cleanPath) } - if _, err := root.Lstat(fullPath); err != nil { - return "", "", errors.WithStack(convertPathError(err, path)) - } return } diff --git a/components/engine/builder/remotecontext/lazycontext.go b/components/engine/builder/remotecontext/lazycontext.go index 66f36defd4..14848baa13 100644 --- a/components/engine/builder/remotecontext/lazycontext.go +++ b/components/engine/builder/remotecontext/lazycontext.go @@ -40,16 +40,16 @@ func (c *lazySource) Hash(path string) (string, error) { return "", err } - fi, err := c.root.Lstat(fullPath) - if err != nil { - return "", errors.WithStack(err) - } - relPath, err := Rel(c.root, fullPath) if err != nil { return "", errors.WithStack(convertPathError(err, cleanPath)) } + fi, err := os.Lstat(fullPath) + if err != nil { + return relPath, nil + } + sum, ok := c.sums[relPath] if !ok { sum, err = c.prepareHash(relPath, fi) From d069c92fec9971e261dc5e79e0a2ec3f8582f038 Mon Sep 17 00:00:00 2001 From: Mike Estes Date: Tue, 3 Oct 2017 12:42:54 -0700 Subject: [PATCH 03/94] Add gelf log driver plugin to Windows build Signed-off-by: Mike Estes Upstream-commit: 10b449b997373df37f285594a624545ad3e173b2 Component: engine --- components/engine/daemon/logdrivers_windows.go | 1 + components/engine/daemon/logger/gelf/gelf.go | 2 -- components/engine/daemon/logger/gelf/gelf_unsupported.go | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 components/engine/daemon/logger/gelf/gelf_unsupported.go diff --git a/components/engine/daemon/logdrivers_windows.go b/components/engine/daemon/logdrivers_windows.go index f3002b97e2..9f99c618c6 100644 --- a/components/engine/daemon/logdrivers_windows.go +++ b/components/engine/daemon/logdrivers_windows.go @@ -6,6 +6,7 @@ import ( _ "github.com/docker/docker/daemon/logger/awslogs" _ "github.com/docker/docker/daemon/logger/etwlogs" _ "github.com/docker/docker/daemon/logger/fluentd" + _ "github.com/docker/docker/daemon/logger/gelf" _ "github.com/docker/docker/daemon/logger/jsonfilelog" _ "github.com/docker/docker/daemon/logger/logentries" _ "github.com/docker/docker/daemon/logger/splunk" diff --git a/components/engine/daemon/logger/gelf/gelf.go b/components/engine/daemon/logger/gelf/gelf.go index de209ce9bf..90b563c211 100644 --- a/components/engine/daemon/logger/gelf/gelf.go +++ b/components/engine/daemon/logger/gelf/gelf.go @@ -1,5 +1,3 @@ -// +build linux - // Package gelf provides the log driver for forwarding server logs to // endpoints that support the Graylog Extended Log Format. package gelf diff --git a/components/engine/daemon/logger/gelf/gelf_unsupported.go b/components/engine/daemon/logger/gelf/gelf_unsupported.go deleted file mode 100644 index 266f73b18b..0000000000 --- a/components/engine/daemon/logger/gelf/gelf_unsupported.go +++ /dev/null @@ -1,3 +0,0 @@ -// +build !linux - -package gelf From a2e7813d7e48bcd9d4a39c3286906f6a02985670 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 26 Jul 2017 16:23:31 -0700 Subject: [PATCH 04/94] Fix tests after Hash() behavior change Signed-off-by: Tonis Tiigi Upstream-commit: d5b15f0d3ee6b9575a919ca5ba568631957425e0 Component: engine --- .../builder/remotecontext/lazycontext.go | 2 ++ .../builder/remotecontext/tarsum_test.go | 22 ++++++------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/components/engine/builder/remotecontext/lazycontext.go b/components/engine/builder/remotecontext/lazycontext.go index 14848baa13..08b8058549 100644 --- a/components/engine/builder/remotecontext/lazycontext.go +++ b/components/engine/builder/remotecontext/lazycontext.go @@ -47,6 +47,8 @@ func (c *lazySource) Hash(path string) (string, error) { fi, err := os.Lstat(fullPath) if err != nil { + // Backwards compatibility: a missing file returns a path as hash. + // This is reached in the case of a broken symlink. return relPath, nil } diff --git a/components/engine/builder/remotecontext/tarsum_test.go b/components/engine/builder/remotecontext/tarsum_test.go index 6d2b41d3d4..9395460916 100644 --- a/components/engine/builder/remotecontext/tarsum_test.go +++ b/components/engine/builder/remotecontext/tarsum_test.go @@ -104,17 +104,6 @@ func TestHashSubdir(t *testing.T) { } } -func TestStatNotExisting(t *testing.T) { - contextDir, cleanup := createTestTempDir(t, "", "builder-tarsum-test") - defer cleanup() - - src := makeTestArchiveContext(t, contextDir) - _, err := src.Hash("not-existing") - if !os.IsNotExist(errors.Cause(err)) { - t.Fatalf("This file should not exist: %s", err) - } -} - func TestRemoveDirectory(t *testing.T) { contextDir, cleanup := createTestTempDir(t, "", "builder-tarsum-test") defer cleanup() @@ -129,17 +118,20 @@ func TestRemoveDirectory(t *testing.T) { src := makeTestArchiveContext(t, contextDir) - tarSum := src.(modifiableContext) + _, err = src.Root().Stat(src.Root().Join(src.Root().Path(), relativePath)) + if err != nil { + t.Fatalf("Statting %s shouldn't fail: %+v", relativePath, err) + } + tarSum := src.(modifiableContext) err = tarSum.Remove(relativePath) if err != nil { t.Fatalf("Error when executing Remove: %s", err) } - _, err = src.Hash(contextSubdir) - + _, err = src.Root().Stat(src.Root().Join(src.Root().Path(), relativePath)) if !os.IsNotExist(errors.Cause(err)) { - t.Fatal("Directory should not exist at this point") + t.Fatalf("Directory should not exist at this point: %+v ", err) } } From 1c3f4cf12c1ec8a65e86da3d0da1a3308e0dcc7c Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Fri, 20 Oct 2017 15:39:47 -0400 Subject: [PATCH 05/94] Fixup some issues with plugin refcounting In some circumstances we were not properly releasing plugin references, leading to failures in removing a plugin with no way to recover other than restarting the daemon. 1. If volume create fails (in the driver) 2. If a driver validation fails (should be rare) 3. If trying to get a plugin that does not match the passed in capability Ideally the test for 1 and 2 would just be a unit test, however the plugin interfaces are too complicated as `plugingetter` relies on github.com/pkg/plugin/Client (a concrete type), which will require spinning up services from within the unit test... it just wouldn't be a unit test at this point. I attempted to refactor this a bit, but since both libnetwork and swarmkit are reliant on `plugingetter` as well, this would not work. This really requires a re-write of the lower-level plugin management to decouple these pieces. Signed-off-by: Brian Goff Upstream-commit: 3816b514387efd24394f0b8e61d55502aa6ac9ac Component: engine --- .../integration/plugin/volume/cmd/cmd_test.go | 1 + .../plugin/volume/cmd/create-error/main.go | 23 +++++++ .../volume/cmd/create-error/main_test.go | 1 + .../integration/plugin/volume/create_test.go | 51 ++++++++++++++ .../integration/plugin/volume/main_test.go | 69 +++++++++++++++++++ components/engine/pkg/plugingetter/getter.go | 4 +- components/engine/plugin/store.go | 9 ++- components/engine/plugin/store_test.go | 31 +++++++++ components/engine/volume/drivers/extpoint.go | 11 ++- components/engine/volume/store/store.go | 5 +- 10 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 components/engine/integration/plugin/volume/cmd/cmd_test.go create mode 100644 components/engine/integration/plugin/volume/cmd/create-error/main.go create mode 100644 components/engine/integration/plugin/volume/cmd/create-error/main_test.go create mode 100644 components/engine/integration/plugin/volume/create_test.go create mode 100644 components/engine/integration/plugin/volume/main_test.go diff --git a/components/engine/integration/plugin/volume/cmd/cmd_test.go b/components/engine/integration/plugin/volume/cmd/cmd_test.go new file mode 100644 index 0000000000..1d619dd05e --- /dev/null +++ b/components/engine/integration/plugin/volume/cmd/cmd_test.go @@ -0,0 +1 @@ +package cmd diff --git a/components/engine/integration/plugin/volume/cmd/create-error/main.go b/components/engine/integration/plugin/volume/cmd/create-error/main.go new file mode 100644 index 0000000000..f23be51d66 --- /dev/null +++ b/components/engine/integration/plugin/volume/cmd/create-error/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "net" + "net/http" +) + +func main() { + l, err := net.Listen("unix", "/run/docker/plugins/plugin.sock") + if err != nil { + panic(err) + } + + mux := http.NewServeMux() + server := http.Server{ + Addr: l.Addr().String(), + Handler: http.NewServeMux(), + } + mux.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "error during create", http.StatusInternalServerError) + }) + server.Serve(l) +} diff --git a/components/engine/integration/plugin/volume/cmd/create-error/main_test.go b/components/engine/integration/plugin/volume/cmd/create-error/main_test.go new file mode 100644 index 0000000000..06ab7d0f9a --- /dev/null +++ b/components/engine/integration/plugin/volume/cmd/create-error/main_test.go @@ -0,0 +1 @@ +package main diff --git a/components/engine/integration/plugin/volume/create_test.go b/components/engine/integration/plugin/volume/create_test.go new file mode 100644 index 0000000000..ce9b4dca43 --- /dev/null +++ b/components/engine/integration/plugin/volume/create_test.go @@ -0,0 +1,51 @@ +// +build linux + +package volume + +import ( + "context" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/volume" + "github.com/docker/docker/integration-cli/daemon" +) + +// TestCreateDerefOnError ensures that if a volume create fails, that the plugin is dereferenced +// Normally 1 volume == 1 reference to a plugin, which prevents a plugin from being removed. +// If the volume create fails, we should make sure to dereference the plugin. +func TestCreateDerefOnError(t *testing.T) { + t.Parallel() + + d := daemon.New(t, "", dockerdBinary, daemon.Config{}) + d.Start(t) + defer d.Stop(t) + + c, err := d.NewClient() + if err != nil { + t.Fatal(err) + } + + pName := "testderef" + createPlugin(t, c, pName, "create-error", asVolumeDriver) + + if err := c.PluginEnable(context.Background(), pName, types.PluginEnableOptions{Timeout: 30}); err != nil { + t.Fatal(err) + } + + _, err = c.VolumeCreate(context.Background(), volume.VolumesCreateBody{ + Driver: pName, + Name: "fake", + }) + if err == nil { + t.Fatal("volume create should have failed") + } + + if err := c.PluginDisable(context.Background(), pName, types.PluginDisableOptions{}); err != nil { + t.Fatal(err) + } + + if err := c.PluginRemove(context.Background(), pName, types.PluginRemoveOptions{}); err != nil { + t.Fatal(err) + } +} diff --git a/components/engine/integration/plugin/volume/main_test.go b/components/engine/integration/plugin/volume/main_test.go new file mode 100644 index 0000000000..8cfbf37978 --- /dev/null +++ b/components/engine/integration/plugin/volume/main_test.go @@ -0,0 +1,69 @@ +package volume + +import ( + "context" + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/integration-cli/fixtures/plugin" + "github.com/docker/docker/pkg/locker" + "github.com/pkg/errors" +) + +const dockerdBinary = "dockerd" + +var pluginBuildLock = locker.New() + +func ensurePlugin(t *testing.T, name string) string { + pluginBuildLock.Lock(name) + defer pluginBuildLock.Unlock(name) + + installPath := filepath.Join(os.Getenv("GOPATH"), "bin", name) + if _, err := os.Stat(installPath); err == nil { + return installPath + } + + goBin, err := exec.LookPath("go") + if err != nil { + t.Fatal(err) + } + + cmd := exec.Command(goBin, "build", "-o", installPath, "./"+filepath.Join("cmd", name)) + cmd.Env = append(cmd.Env, "CGO_ENABLED=0") + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatal(errors.Wrapf(err, "error building basic plugin bin: %s", string(out))) + } + + return installPath +} + +func asVolumeDriver(cfg *plugin.Config) { + cfg.Interface.Types = []types.PluginInterfaceType{ + {Capability: "volumedriver", Prefix: "docker", Version: "1.0"}, + } +} + +func withSockPath(name string) func(*plugin.Config) { + return func(cfg *plugin.Config) { + cfg.Interface.Socket = name + } +} + +func createPlugin(t *testing.T, client plugin.CreateClient, alias, bin string, opts ...plugin.CreateOpt) { + pluginBin := ensurePlugin(t, bin) + + opts = append(opts, withSockPath("plugin.sock")) + opts = append(opts, plugin.WithBinary(pluginBin)) + + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + err := plugin.Create(ctx, client, alias, opts...) + cancel() + + if err != nil { + t.Fatal(err) + } +} diff --git a/components/engine/pkg/plugingetter/getter.go b/components/engine/pkg/plugingetter/getter.go index b04b7bc828..b9ffa547a4 100644 --- a/components/engine/pkg/plugingetter/getter.go +++ b/components/engine/pkg/plugingetter/getter.go @@ -1,6 +1,8 @@ package plugingetter -import "github.com/docker/docker/pkg/plugins" +import ( + "github.com/docker/docker/pkg/plugins" +) const ( // Lookup doesn't update RefCount diff --git a/components/engine/plugin/store.go b/components/engine/plugin/store.go index 8349f34f1c..b3398145d7 100644 --- a/components/engine/plugin/store.go +++ b/components/engine/plugin/store.go @@ -115,10 +115,15 @@ func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlug if ps != nil { p, err := ps.GetV2Plugin(name) if err == nil { - p.AddRefCount(mode) if p.IsEnabled() { - return p.FilterByCap(capability) + fp, err := p.FilterByCap(capability) + if err != nil { + return nil, err + } + p.AddRefCount(mode) + return fp, nil } + // Plugin was found but it is disabled, so we should not fall back to legacy plugins // but we should error out right away return nil, errDisabled(name) diff --git a/components/engine/plugin/store_test.go b/components/engine/plugin/store_test.go index d3876daa34..5c61cc61c7 100644 --- a/components/engine/plugin/store_test.go +++ b/components/engine/plugin/store_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/plugin/v2" ) @@ -31,3 +32,33 @@ func TestFilterByCapPos(t *testing.T) { t.Fatalf("expected no error, got %v", err) } } + +func TestStoreGetPluginNotMatchCapRefs(t *testing.T) { + s := NewStore() + p := v2.Plugin{PluginObj: types.Plugin{Name: "test:latest"}} + + iType := types.PluginInterfaceType{Capability: "whatever", Prefix: "docker", Version: "1.0"} + i := types.PluginConfigInterface{Socket: "plugins.sock", Types: []types.PluginInterfaceType{iType}} + p.PluginObj.Config.Interface = i + + if err := s.Add(&p); err != nil { + t.Fatal(err) + } + + if _, err := s.Get("test", "volumedriver", plugingetter.Acquire); err == nil { + t.Fatal("exepcted error when getting plugin that doesn't match the passed in capability") + } + + if refs := p.GetRefCount(); refs != 0 { + t.Fatalf("reference count should be 0, got: %d", refs) + } + + p.PluginObj.Enabled = true + if _, err := s.Get("test", "volumedriver", plugingetter.Acquire); err == nil { + t.Fatal("exepcted error when getting plugin that doesn't match the passed in capability") + } + + if refs := p.GetRefCount(); refs != 0 { + t.Fatalf("reference count should be 0, got: %d", refs) + } +} diff --git a/components/engine/volume/drivers/extpoint.go b/components/engine/volume/drivers/extpoint.go index ee42f2f5ed..c360b37a2b 100644 --- a/components/engine/volume/drivers/extpoint.go +++ b/components/engine/volume/drivers/extpoint.go @@ -11,6 +11,7 @@ import ( getter "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/volume" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // currently created by hand. generation tool would generate this like: @@ -130,6 +131,12 @@ func lookup(name string, mode int) (volume.Driver, error) { d := NewVolumeDriver(p.Name(), p.BasePath(), p.Client()) 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 { + logrus.WithError(err).WithField("action", "validate-driver").WithField("plugin", name).Error("error releasing reference to plugin") + } + } return nil, err } @@ -169,9 +176,9 @@ func CreateDriver(name string) (volume.Driver, error) { return lookup(name, getter.Acquire) } -// RemoveDriver returns a volume driver by its name and decrements RefCount.. +// ReleaseDriver returns a volume driver by its name and decrements RefCount.. // If the driver is empty, it looks for the local driver. -func RemoveDriver(name string) (volume.Driver, error) { +func ReleaseDriver(name string) (volume.Driver, error) { if name == "" { name = volume.DefaultDriverName } diff --git a/components/engine/volume/store/store.go b/components/engine/volume/store/store.go index e47ec0e7da..5402c6bd9f 100644 --- a/components/engine/volume/store/store.go +++ b/components/engine/volume/store/store.go @@ -145,7 +145,7 @@ func (s *VolumeStore) Purge(name string) { s.globalLock.Lock() v, exists := s.names[name] if exists { - if _, err := volumedrivers.RemoveDriver(v.DriverName()); err != nil { + if _, err := volumedrivers.ReleaseDriver(v.DriverName()); err != nil { logrus.Errorf("Error dereferencing volume driver: %v", err) } } @@ -409,6 +409,9 @@ func (s *VolumeStore) create(name, driverName string, opts, labels map[string]st } v, err = vd.Create(name, opts) if err != nil { + if _, err := volumedrivers.ReleaseDriver(driverName); err != nil { + logrus.WithError(err).WithField("driver", driverName).Error("Error releasing reference to volume driver") + } return nil, err } s.globalLock.Lock() From 1377e81acb4152f4814a1e50fbea8503b6cce376 Mon Sep 17 00:00:00 2001 From: Simon Ferquel Date: Mon, 7 Aug 2017 16:07:17 +0200 Subject: [PATCH 06/94] Added support for swarm service isolation mode Signed-off-by: Simon Ferquel Upstream-commit: f28cb422e69bd4239401e6ea32b65b56485d6691 Component: engine --- components/engine/api/swagger.yaml | 8 +- .../engine/api/types/container/host_config.go | 21 + .../api/types/container/hostconfig_windows.go | 14 - .../engine/api/types/swarm/container.go | 9 +- .../daemon/cluster/convert/container.go | 25 + .../daemon/cluster/convert/service_test.go | 84 ++++ .../cluster/executor/container/container.go | 5 + .../executor/container/container_test.go | 37 ++ components/engine/docs/api/version-history.md | 1 + .../integration-cli/daemon/daemon_swarm.go | 17 + .../docker_cli_swarm_unix_test.go | 2 +- .../integration/service/inspect_test.go | 2 + components/engine/vendor.conf | 3 +- .../docker/swarmkit/agent/exec/controller.go | 2 + .../docker/swarmkit/api/specs.pb.go | 469 +++++++++++------- .../docker/swarmkit/api/specs.proto | 21 + .../docker/swarmkit/api/types.pb.go | 7 +- .../docker/swarmkit/api/types.proto | 7 +- .../swarmkit/manager/allocator/network.go | 8 +- .../constraintenforcer/constraint_enforcer.go | 3 +- .../swarmkit/manager/scheduler/filter.go | 2 +- .../swarmkit/manager/scheduler/scheduler.go | 10 +- .../swarmkit/manager/state/raft/raft.go | 15 +- .../manager/state/raft/transport/peer.go | 9 +- .../manager/state/raft/transport/transport.go | 1 + 25 files changed, 575 insertions(+), 207 deletions(-) create mode 100644 components/engine/daemon/cluster/executor/container/container_test.go diff --git a/components/engine/api/swagger.yaml b/components/engine/api/swagger.yaml index b0c0575fc0..b30c8e35ea 100644 --- a/components/engine/api/swagger.yaml +++ b/components/engine/api/swagger.yaml @@ -2680,7 +2680,13 @@ definitions: ConfigName is the name of the config that this references, but this is just provided for lookup/display purposes. The config in the reference will be identified by its ID. type: "string" - + Isolation: + type: "string" + description: "Isolation technology of the containers running the service. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" Resources: description: "Resource requirements which apply to each individual container created as part of the service." type: "object" diff --git a/components/engine/api/types/container/host_config.go b/components/engine/api/types/container/host_config.go index bb421b3889..568cdcca93 100644 --- a/components/engine/api/types/container/host_config.go +++ b/components/engine/api/types/container/host_config.go @@ -20,6 +20,27 @@ func (i Isolation) IsDefault() bool { return strings.ToLower(string(i)) == "default" || string(i) == "" } +// IsHyperV indicates the use of a Hyper-V partition for isolation +func (i Isolation) IsHyperV() bool { + return strings.ToLower(string(i)) == "hyperv" +} + +// IsProcess indicates the use of process isolation +func (i Isolation) IsProcess() bool { + return strings.ToLower(string(i)) == "process" +} + +const ( + // IsolationEmpty is unspecified (same behavior as default) + IsolationEmpty = Isolation("") + // IsolationDefault is the default isolation mode on current daemon + IsolationDefault = Isolation("default") + // IsolationProcess is process isolation mode + IsolationProcess = Isolation("process") + // IsolationHyperV is HyperV isolation mode + IsolationHyperV = Isolation("hyperv") +) + // IpcMode represents the container ipc stack. type IpcMode string diff --git a/components/engine/api/types/container/hostconfig_windows.go b/components/engine/api/types/container/hostconfig_windows.go index 469923f7e9..3374d737f1 100644 --- a/components/engine/api/types/container/hostconfig_windows.go +++ b/components/engine/api/types/container/hostconfig_windows.go @@ -1,9 +1,5 @@ package container -import ( - "strings" -) - // IsBridge indicates whether container uses the bridge network stack // in windows it is given the name NAT func (n NetworkMode) IsBridge() bool { @@ -21,16 +17,6 @@ func (n NetworkMode) IsUserDefined() bool { return !n.IsDefault() && !n.IsNone() && !n.IsBridge() && !n.IsContainer() } -// IsHyperV indicates the use of a Hyper-V partition for isolation -func (i Isolation) IsHyperV() bool { - return strings.ToLower(string(i)) == "hyperv" -} - -// IsProcess indicates the use of process isolation -func (i Isolation) IsProcess() bool { - return strings.ToLower(string(i)) == "process" -} - // IsValid indicates if an isolation technology is valid func (i Isolation) IsValid() bool { return i.IsDefault() || i.IsHyperV() || i.IsProcess() diff --git a/components/engine/api/types/swarm/container.go b/components/engine/api/types/swarm/container.go index 6f8b45f6bb..734236c4b0 100644 --- a/components/engine/api/types/swarm/container.go +++ b/components/engine/api/types/swarm/container.go @@ -65,8 +65,9 @@ type ContainerSpec struct { // The format of extra hosts on swarmkit is specified in: // http://man7.org/linux/man-pages/man5/hosts.5.html // IP_address canonical_hostname [aliases...] - Hosts []string `json:",omitempty"` - DNSConfig *DNSConfig `json:",omitempty"` - Secrets []*SecretReference `json:",omitempty"` - Configs []*ConfigReference `json:",omitempty"` + Hosts []string `json:",omitempty"` + DNSConfig *DNSConfig `json:",omitempty"` + Secrets []*SecretReference `json:",omitempty"` + Configs []*ConfigReference `json:",omitempty"` + Isolation container.Isolation `json:",omitempty"` } diff --git a/components/engine/daemon/cluster/convert/container.go b/components/engine/daemon/cluster/convert/container.go index 795e944ae1..3f3ea349a9 100644 --- a/components/engine/daemon/cluster/convert/container.go +++ b/components/engine/daemon/cluster/convert/container.go @@ -34,6 +34,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec { Hosts: c.Hosts, Secrets: secretReferencesFromGRPC(c.Secrets), Configs: configReferencesFromGRPC(c.Configs), + Isolation: IsolationFromGRPC(c.Isolation), } if c.DNSConfig != nil { @@ -232,6 +233,7 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) { Hosts: c.Hosts, Secrets: secretReferencesToGRPC(c.Secrets), Configs: configReferencesToGRPC(c.Configs), + Isolation: isolationToGRPC(c.Isolation), } if c.DNSConfig != nil { @@ -354,3 +356,26 @@ func healthConfigToGRPC(h *container.HealthConfig) *swarmapi.HealthConfig { StartPeriod: gogotypes.DurationProto(h.StartPeriod), } } + +// IsolationFromGRPC converts a swarm api container isolation to a moby isolation representation +func IsolationFromGRPC(i swarmapi.ContainerSpec_Isolation) container.Isolation { + switch i { + case swarmapi.ContainerIsolationHyperV: + return container.IsolationHyperV + case swarmapi.ContainerIsolationProcess: + return container.IsolationProcess + case swarmapi.ContainerIsolationDefault: + return container.IsolationDefault + } + return container.IsolationEmpty +} + +func isolationToGRPC(i container.Isolation) swarmapi.ContainerSpec_Isolation { + if i.IsHyperV() { + return swarmapi.ContainerIsolationHyperV + } + if i.IsProcess() { + return swarmapi.ContainerIsolationProcess + } + return swarmapi.ContainerIsolationDefault +} diff --git a/components/engine/daemon/cluster/convert/service_test.go b/components/engine/daemon/cluster/convert/service_test.go index 1b6598974b..a5c7cc4ddf 100644 --- a/components/engine/daemon/cluster/convert/service_test.go +++ b/components/engine/daemon/cluster/convert/service_test.go @@ -3,10 +3,12 @@ package convert import ( "testing" + containertypes "github.com/docker/docker/api/types/container" swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm/runtime" swarmapi "github.com/docker/swarmkit/api" google_protobuf3 "github.com/gogo/protobuf/types" + "github.com/stretchr/testify/require" ) func TestServiceConvertFromGRPCRuntimeContainer(t *testing.T) { @@ -148,3 +150,85 @@ func TestServiceConvertToGRPCGenericRuntimeCustom(t *testing.T) { t.Fatal(err) } } + +func TestServiceConvertToGRPCIsolation(t *testing.T) { + cases := []struct { + name string + from containertypes.Isolation + to swarmapi.ContainerSpec_Isolation + }{ + {name: "empty", from: containertypes.IsolationEmpty, to: swarmapi.ContainerIsolationDefault}, + {name: "default", from: containertypes.IsolationDefault, to: swarmapi.ContainerIsolationDefault}, + {name: "process", from: containertypes.IsolationProcess, to: swarmapi.ContainerIsolationProcess}, + {name: "hyperv", from: containertypes.IsolationHyperV, to: swarmapi.ContainerIsolationHyperV}, + {name: "proCess", from: containertypes.Isolation("proCess"), to: swarmapi.ContainerIsolationProcess}, + {name: "hypErv", from: containertypes.Isolation("hypErv"), to: swarmapi.ContainerIsolationHyperV}, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + s := swarmtypes.ServiceSpec{ + TaskTemplate: swarmtypes.TaskSpec{ + ContainerSpec: &swarmtypes.ContainerSpec{ + Image: "alpine:latest", + Isolation: c.from, + }, + }, + Mode: swarmtypes.ServiceMode{ + Global: &swarmtypes.GlobalService{}, + }, + } + res, err := ServiceSpecToGRPC(s) + require.NoError(t, err) + v, ok := res.Task.Runtime.(*swarmapi.TaskSpec_Container) + if !ok { + t.Fatal("expected type swarmapi.TaskSpec_Container") + } + require.Equal(t, c.to, v.Container.Isolation) + }) + } +} + +func TestServiceConvertFromGRPCIsolation(t *testing.T) { + cases := []struct { + name string + from swarmapi.ContainerSpec_Isolation + to containertypes.Isolation + }{ + {name: "default", to: containertypes.IsolationDefault, from: swarmapi.ContainerIsolationDefault}, + {name: "process", to: containertypes.IsolationProcess, from: swarmapi.ContainerIsolationProcess}, + {name: "hyperv", to: containertypes.IsolationHyperV, from: swarmapi.ContainerIsolationHyperV}, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + gs := swarmapi.Service{ + Meta: swarmapi.Meta{ + Version: swarmapi.Version{ + Index: 1, + }, + CreatedAt: nil, + UpdatedAt: nil, + }, + SpecVersion: &swarmapi.Version{ + Index: 1, + }, + Spec: swarmapi.ServiceSpec{ + Task: swarmapi.TaskSpec{ + Runtime: &swarmapi.TaskSpec_Container{ + Container: &swarmapi.ContainerSpec{ + Image: "alpine:latest", + Isolation: c.from, + }, + }, + }, + }, + } + + svc, err := ServiceFromGRPC(gs) + if err != nil { + t.Fatal(err) + } + + require.Equal(t, c.to, svc.Spec.TaskTemplate.ContainerSpec.Isolation) + }) + } +} diff --git a/components/engine/daemon/cluster/executor/container/container.go b/components/engine/daemon/cluster/executor/container/container.go index 59ac9bf215..4f41fb3e23 100644 --- a/components/engine/daemon/cluster/executor/container/container.go +++ b/components/engine/daemon/cluster/executor/container/container.go @@ -168,6 +168,10 @@ func (c *containerConfig) portBindings() nat.PortMap { return portBindings } +func (c *containerConfig) isolation() enginecontainer.Isolation { + return convert.IsolationFromGRPC(c.spec().Isolation) +} + func (c *containerConfig) exposedPorts() map[nat.Port]struct{} { exposedPorts := make(map[nat.Port]struct{}) if c.task.Endpoint == nil { @@ -350,6 +354,7 @@ func (c *containerConfig) hostConfig() *enginecontainer.HostConfig { PortBindings: c.portBindings(), Mounts: c.mounts(), ReadonlyRootfs: c.spec().ReadOnly, + Isolation: c.isolation(), } if c.spec().DNSConfig != nil { diff --git a/components/engine/daemon/cluster/executor/container/container_test.go b/components/engine/daemon/cluster/executor/container/container_test.go new file mode 100644 index 0000000000..a583d14c20 --- /dev/null +++ b/components/engine/daemon/cluster/executor/container/container_test.go @@ -0,0 +1,37 @@ +package container + +import ( + "testing" + + container "github.com/docker/docker/api/types/container" + swarmapi "github.com/docker/swarmkit/api" + "github.com/stretchr/testify/require" +) + +func TestIsolationConversion(t *testing.T) { + cases := []struct { + name string + from swarmapi.ContainerSpec_Isolation + to container.Isolation + }{ + {name: "default", from: swarmapi.ContainerIsolationDefault, to: container.IsolationDefault}, + {name: "process", from: swarmapi.ContainerIsolationProcess, to: container.IsolationProcess}, + {name: "hyperv", from: swarmapi.ContainerIsolationHyperV, to: container.IsolationHyperV}, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + task := swarmapi.Task{ + Spec: swarmapi.TaskSpec{ + Runtime: &swarmapi.TaskSpec_Container{ + Container: &swarmapi.ContainerSpec{ + Image: "alpine:latest", + Isolation: c.from, + }, + }, + }, + } + config := containerConfig{task: &task} + require.Equal(t, c.to, config.hostConfig().Isolation) + }) + } +} diff --git a/components/engine/docs/api/version-history.md b/components/engine/docs/api/version-history.md index 77b8545bcc..effeb39dfd 100644 --- a/components/engine/docs/api/version-history.md +++ b/components/engine/docs/api/version-history.md @@ -23,6 +23,7 @@ keywords: "API, Docker, rcli, REST, documentation" If `Error` is `null`, container removal has succeeded, otherwise the test of an error message indicating why container removal has failed is available from `Error.Message` field. +* `POST /services/create` and `POST /services/(id)/update` now accept an `Isolation` field on container spec ## v1.33 API changes diff --git a/components/engine/integration-cli/daemon/daemon_swarm.go b/components/engine/integration-cli/daemon/daemon_swarm.go index c37c7269f5..bd1ada0b42 100644 --- a/components/engine/integration-cli/daemon/daemon_swarm.go +++ b/components/engine/integration-cli/daemon/daemon_swarm.go @@ -198,6 +198,23 @@ func (d *Swarm) CheckServiceTasksInState(service string, state swarm.TaskState, } } +// CheckServiceTasksInStateWithError returns the number of tasks with a matching state, +// and optional message substring. +func (d *Swarm) CheckServiceTasksInStateWithError(service string, state swarm.TaskState, errorMessage string) func(*check.C) (interface{}, check.CommentInterface) { + return func(c *check.C) (interface{}, check.CommentInterface) { + tasks := d.GetServiceTasks(c, service) + var count int + for _, task := range tasks { + if task.Status.State == state { + if errorMessage == "" || strings.Contains(task.Status.Err, errorMessage) { + count++ + } + } + } + return count, nil + } +} + // CheckServiceRunningTasks returns the number of running tasks for the specified service func (d *Swarm) CheckServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) { return d.CheckServiceTasksInState(service, swarm.TaskStateRunning, "") diff --git a/components/engine/integration-cli/docker_cli_swarm_unix_test.go b/components/engine/integration-cli/docker_cli_swarm_unix_test.go index 16b27ef19b..3b890bcc69 100644 --- a/components/engine/integration-cli/docker_cli_swarm_unix_test.go +++ b/components/engine/integration-cli/docker_cli_swarm_unix_test.go @@ -19,7 +19,7 @@ func (s *DockerSwarmSuite) TestSwarmVolumePlugin(c *check.C) { c.Assert(err, checker.IsNil, check.Commentf(out)) // Make sure task stays pending before plugin is available - waitAndAssert(c, defaultReconciliationTimeout, d.CheckServiceTasksInState("top", swarm.TaskStatePending, "missing plugin on 1 node"), checker.Equals, 1) + waitAndAssert(c, defaultReconciliationTimeout, d.CheckServiceTasksInStateWithError("top", swarm.TaskStatePending, "missing plugin on 1 node"), checker.Equals, 1) plugin := newVolumePlugin(c, "customvolumedriver") defer plugin.Close() diff --git a/components/engine/integration/service/inspect_test.go b/components/engine/integration/service/inspect_test.go index 61831b72f1..5129aa2923 100644 --- a/components/engine/integration/service/inspect_test.go +++ b/components/engine/integration/service/inspect_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" @@ -76,6 +77,7 @@ func fullSwarmServiceSpec(name string, replicas uint64) swarm.ServiceSpec { Nameservers: []string{"8.8.8.8"}, Search: []string{"somedomain"}, }, + Isolation: container.IsolationDefault, }, RestartPolicy: &swarm.RestartPolicy{ Delay: &restartDelay, diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index c3c551eb00..f52ae38992 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -103,7 +103,6 @@ github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 # containerd -github.com/stevvooe/continuity cd7a8e21e2b6f84799f5dd4b65faf49c8d3ee02d github.com/containerd/containerd 992280e8e265f491f7a624ab82f3e238be086e49 github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6 github.com/containerd/continuity 35d55c5e8dd23b32037d56cf97174aff3efdfa83 @@ -114,7 +113,7 @@ github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788 github.com/dmcgowan/go-tar 2e2c51242e8993c50445dab7c03c8e7febddd0cf # cluster -github.com/docker/swarmkit 872861d2ae46958af7ead1d5fffb092c73afbaf0 +github.com/docker/swarmkit 28f91d87bd3f75fd039dbb9be49bfd2381019261 github.com/gogo/protobuf v0.4 github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e diff --git a/components/engine/vendor/github.com/docker/swarmkit/agent/exec/controller.go b/components/engine/vendor/github.com/docker/swarmkit/agent/exec/controller.go index 85110ba971..9b4fc7bca1 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/agent/exec/controller.go +++ b/components/engine/vendor/github.com/docker/swarmkit/agent/exec/controller.go @@ -119,6 +119,7 @@ func Resolve(ctx context.Context, task *api.Task, executor Executor) (Controller // we always want to proceed to accepted when we resolve the controller status.Message = "accepted" status.State = api.TaskStateAccepted + status.Err = "" } return ctlr, status, err @@ -158,6 +159,7 @@ func Do(ctx context.Context, task *api.Task, ctlr Controller) (*api.TaskStatus, current := status.State status.State = state status.Message = msg + status.Err = "" if current > state { panic("invalid state transition") diff --git a/components/engine/vendor/github.com/docker/swarmkit/api/specs.pb.go b/components/engine/vendor/github.com/docker/swarmkit/api/specs.pb.go index bda30a3dfc..b150d6f81e 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/api/specs.pb.go +++ b/components/engine/vendor/github.com/docker/swarmkit/api/specs.pb.go @@ -10,6 +10,7 @@ import math "math" import _ "github.com/gogo/protobuf/gogoproto" import google_protobuf1 "github.com/gogo/protobuf/types" import google_protobuf3 "github.com/gogo/protobuf/types" +import google_protobuf4 "github.com/gogo/protobuf/types" import github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" @@ -74,6 +75,35 @@ func (x NodeSpec_Availability) String() string { } func (NodeSpec_Availability) EnumDescriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{0, 1} } +type ContainerSpec_Isolation int32 + +const ( + // ISOLATION_DEFAULT uses whatever default value from the container runtime + ContainerIsolationDefault ContainerSpec_Isolation = 0 + // ISOLATION_PROCESS forces windows container isolation + ContainerIsolationProcess ContainerSpec_Isolation = 1 + // ISOLATION_HYPERV forces Hyper-V isolation + ContainerIsolationHyperV ContainerSpec_Isolation = 2 +) + +var ContainerSpec_Isolation_name = map[int32]string{ + 0: "ISOLATION_DEFAULT", + 1: "ISOLATION_PROCESS", + 2: "ISOLATION_HYPERV", +} +var ContainerSpec_Isolation_value = map[string]int32{ + "ISOLATION_DEFAULT": 0, + "ISOLATION_PROCESS": 1, + "ISOLATION_HYPERV": 2, +} + +func (x ContainerSpec_Isolation) String() string { + return proto.EnumName(ContainerSpec_Isolation_name, int32(x)) +} +func (ContainerSpec_Isolation) EnumDescriptor() ([]byte, []int) { + return fileDescriptorSpecs, []int{8, 0} +} + // ResolutionMode specifies the mode of resolution to use for // internal loadbalancing between tasks which are all within // the cluster. This is sometimes calls east-west data path. @@ -542,6 +572,8 @@ type ContainerSpec struct { Groups []string `protobuf:"bytes,11,rep,name=groups" json:"groups,omitempty"` // Privileges specifies security configuration/permissions. Privileges *Privileges `protobuf:"bytes,22,opt,name=privileges" json:"privileges,omitempty"` + // Init declares that a custom init will be running inside the container, if null, use the daemon's configured settings + Init *google_protobuf4.BoolValue `protobuf:"bytes,23,opt,name=init" json:"init,omitempty"` // TTY declares that a TTY should be attached to the standard streams, // including stdin if it is still open. TTY bool `protobuf:"varint,13,opt,name=tty,proto3" json:"tty,omitempty"` @@ -585,6 +617,9 @@ type ContainerSpec struct { // task will exit and a new task will be rescheduled elsewhere. A container // is considered unhealthy after `Retries` number of consecutive failures. Healthcheck *HealthConfig `protobuf:"bytes,16,opt,name=healthcheck" json:"healthcheck,omitempty"` + // Isolation defines the isolation level for windows containers (default, process, hyperv). + // Runtimes that don't support it ignore that field + Isolation ContainerSpec_Isolation `protobuf:"varint,24,opt,name=isolation,proto3,enum=docker.swarmkit.v1.ContainerSpec_Isolation" json:"isolation,omitempty"` } func (m *ContainerSpec) Reset() { *m = ContainerSpec{} } @@ -830,6 +865,7 @@ func init() { proto.RegisterType((*ConfigSpec)(nil), "docker.swarmkit.v1.ConfigSpec") proto.RegisterEnum("docker.swarmkit.v1.NodeSpec_Membership", NodeSpec_Membership_name, NodeSpec_Membership_value) proto.RegisterEnum("docker.swarmkit.v1.NodeSpec_Availability", NodeSpec_Availability_name, NodeSpec_Availability_value) + proto.RegisterEnum("docker.swarmkit.v1.ContainerSpec_Isolation", ContainerSpec_Isolation_name, ContainerSpec_Isolation_value) proto.RegisterEnum("docker.swarmkit.v1.EndpointSpec_ResolutionMode", EndpointSpec_ResolutionMode_name, EndpointSpec_ResolutionMode_value) } @@ -1090,6 +1126,10 @@ func (m *ContainerSpec) CopyFrom(src interface{}) { m.Privileges = &Privileges{} github_com_docker_swarmkit_api_deepcopy.Copy(m.Privileges, o.Privileges) } + if o.Init != nil { + m.Init = &google_protobuf4.BoolValue{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Init, o.Init) + } if o.Mounts != nil { m.Mounts = make([]Mount, len(o.Mounts)) for i := range m.Mounts { @@ -1996,6 +2036,25 @@ func (m *ContainerSpec) MarshalTo(dAtA []byte) (int, error) { } i += n23 } + if m.Init != nil { + dAtA[i] = 0xba + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintSpecs(dAtA, i, uint64(m.Init.Size())) + n24, err := m.Init.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n24 + } + if m.Isolation != 0 { + dAtA[i] = 0xc0 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintSpecs(dAtA, i, uint64(m.Isolation)) + } return i, nil } @@ -2141,20 +2200,20 @@ func (m *NetworkSpec) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size())) - n24, err := m.Annotations.MarshalTo(dAtA[i:]) + n25, err := m.Annotations.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n24 + i += n25 if m.DriverConfig != nil { dAtA[i] = 0x12 i++ i = encodeVarintSpecs(dAtA, i, uint64(m.DriverConfig.Size())) - n25, err := m.DriverConfig.MarshalTo(dAtA[i:]) + n26, err := m.DriverConfig.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n25 + i += n26 } if m.Ipv6Enabled { dAtA[i] = 0x18 @@ -2180,11 +2239,11 @@ func (m *NetworkSpec) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x2a i++ i = encodeVarintSpecs(dAtA, i, uint64(m.IPAM.Size())) - n26, err := m.IPAM.MarshalTo(dAtA[i:]) + n27, err := m.IPAM.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n26 + i += n27 } if m.Attachable { dAtA[i] = 0x30 @@ -2207,11 +2266,11 @@ func (m *NetworkSpec) MarshalTo(dAtA []byte) (int, error) { i++ } if m.ConfigFrom != nil { - nn27, err := m.ConfigFrom.MarshalTo(dAtA[i:]) + nn28, err := m.ConfigFrom.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += nn27 + i += nn28 } return i, nil } @@ -2242,67 +2301,67 @@ func (m *ClusterSpec) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size())) - n28, err := m.Annotations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n28 - dAtA[i] = 0x12 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.AcceptancePolicy.Size())) - n29, err := m.AcceptancePolicy.MarshalTo(dAtA[i:]) + n29, err := m.Annotations.MarshalTo(dAtA[i:]) if err != nil { return 0, err } i += n29 - dAtA[i] = 0x1a + dAtA[i] = 0x12 i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Orchestration.Size())) - n30, err := m.Orchestration.MarshalTo(dAtA[i:]) + i = encodeVarintSpecs(dAtA, i, uint64(m.AcceptancePolicy.Size())) + n30, err := m.AcceptancePolicy.MarshalTo(dAtA[i:]) if err != nil { return 0, err } i += n30 - dAtA[i] = 0x22 + dAtA[i] = 0x1a i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Raft.Size())) - n31, err := m.Raft.MarshalTo(dAtA[i:]) + i = encodeVarintSpecs(dAtA, i, uint64(m.Orchestration.Size())) + n31, err := m.Orchestration.MarshalTo(dAtA[i:]) if err != nil { return 0, err } i += n31 - dAtA[i] = 0x2a + dAtA[i] = 0x22 i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Dispatcher.Size())) - n32, err := m.Dispatcher.MarshalTo(dAtA[i:]) + i = encodeVarintSpecs(dAtA, i, uint64(m.Raft.Size())) + n32, err := m.Raft.MarshalTo(dAtA[i:]) if err != nil { return 0, err } i += n32 - dAtA[i] = 0x32 + dAtA[i] = 0x2a i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.CAConfig.Size())) - n33, err := m.CAConfig.MarshalTo(dAtA[i:]) + i = encodeVarintSpecs(dAtA, i, uint64(m.Dispatcher.Size())) + n33, err := m.Dispatcher.MarshalTo(dAtA[i:]) if err != nil { return 0, err } i += n33 - dAtA[i] = 0x3a + dAtA[i] = 0x32 i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.TaskDefaults.Size())) - n34, err := m.TaskDefaults.MarshalTo(dAtA[i:]) + i = encodeVarintSpecs(dAtA, i, uint64(m.CAConfig.Size())) + n34, err := m.CAConfig.MarshalTo(dAtA[i:]) if err != nil { return 0, err } i += n34 - dAtA[i] = 0x42 + dAtA[i] = 0x3a i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.EncryptionConfig.Size())) - n35, err := m.EncryptionConfig.MarshalTo(dAtA[i:]) + i = encodeVarintSpecs(dAtA, i, uint64(m.TaskDefaults.Size())) + n35, err := m.TaskDefaults.MarshalTo(dAtA[i:]) if err != nil { return 0, err } i += n35 + dAtA[i] = 0x42 + i++ + i = encodeVarintSpecs(dAtA, i, uint64(m.EncryptionConfig.Size())) + n36, err := m.EncryptionConfig.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n36 return i, nil } @@ -2324,11 +2383,11 @@ func (m *SecretSpec) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size())) - n36, err := m.Annotations.MarshalTo(dAtA[i:]) + n37, err := m.Annotations.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n36 + i += n37 if len(m.Data) > 0 { dAtA[i] = 0x12 i++ @@ -2339,21 +2398,21 @@ func (m *SecretSpec) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintSpecs(dAtA, i, uint64(m.Templating.Size())) - n37, err := m.Templating.MarshalTo(dAtA[i:]) + n38, err := m.Templating.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n37 + i += n38 } if m.Driver != nil { dAtA[i] = 0x22 i++ i = encodeVarintSpecs(dAtA, i, uint64(m.Driver.Size())) - n38, err := m.Driver.MarshalTo(dAtA[i:]) + n39, err := m.Driver.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n38 + i += n39 } return i, nil } @@ -2376,11 +2435,11 @@ func (m *ConfigSpec) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size())) - n39, err := m.Annotations.MarshalTo(dAtA[i:]) + n40, err := m.Annotations.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n39 + i += n40 if len(m.Data) > 0 { dAtA[i] = 0x12 i++ @@ -2391,11 +2450,11 @@ func (m *ConfigSpec) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintSpecs(dAtA, i, uint64(m.Templating.Size())) - n40, err := m.Templating.MarshalTo(dAtA[i:]) + n41, err := m.Templating.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n40 + i += n41 } return i, nil } @@ -2721,6 +2780,13 @@ func (m *ContainerSpec) Size() (n int) { l = m.Privileges.Size() n += 2 + l + sovSpecs(uint64(l)) } + if m.Init != nil { + l = m.Init.Size() + n += 2 + l + sovSpecs(uint64(l)) + } + if m.Isolation != 0 { + n += 2 + sovSpecs(uint64(m.Isolation)) + } return n } @@ -3066,6 +3132,8 @@ func (this *ContainerSpec) String() string { `StopSignal:` + fmt.Sprintf("%v", this.StopSignal) + `,`, `Configs:` + strings.Replace(fmt.Sprintf("%v", this.Configs), "ConfigReference", "ConfigReference", 1) + `,`, `Privileges:` + strings.Replace(fmt.Sprintf("%v", this.Privileges), "Privileges", "Privileges", 1) + `,`, + `Init:` + strings.Replace(fmt.Sprintf("%v", this.Init), "BoolValue", "google_protobuf4.BoolValue", 1) + `,`, + `Isolation:` + fmt.Sprintf("%v", this.Isolation) + `,`, `}`, }, "") return s @@ -5141,6 +5209,58 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 23: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Init", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSpecs + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Init == nil { + m.Init = &google_protobuf4.BoolValue{} + } + if err := m.Init.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 24: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Isolation", wireType) + } + m.Isolation = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Isolation |= (ContainerSpec_Isolation(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipSpecs(dAtA[iNdEx:]) @@ -6452,129 +6572,138 @@ var ( func init() { proto.RegisterFile("github.com/docker/swarmkit/api/specs.proto", fileDescriptorSpecs) } var fileDescriptorSpecs = []byte{ - // 1975 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0xcf, 0x6f, 0x1b, 0xb9, - 0x15, 0xb6, 0x6c, 0x59, 0x3f, 0xde, 0xc8, 0x89, 0xc2, 0xcd, 0xa6, 0x13, 0xa5, 0x6b, 0x2b, 0xda, - 0x6c, 0xea, 0xdd, 0x45, 0x25, 0xd4, 0x2d, 0xb6, 0xd9, 0x4d, 0xb7, 0xad, 0x64, 0xa9, 0x8e, 0x9b, - 0xc6, 0x11, 0x68, 0x6f, 0xda, 0x00, 0x05, 0x04, 0x6a, 0x86, 0x1e, 0x0d, 0x3c, 0x1a, 0x4e, 0x39, - 0x1c, 0x2d, 0x74, 0xeb, 0x71, 0x91, 0x1e, 0x7b, 0x0e, 0x7a, 0x28, 0x7a, 0xef, 0x9f, 0x91, 0x63, - 0x8f, 0xed, 0xc5, 0xe8, 0xea, 0x5f, 0xe8, 0xad, 0x97, 0x16, 0xe4, 0x70, 0x46, 0xa3, 0x64, 0x6c, - 0x07, 0x68, 0x0e, 0xbd, 0x91, 0x8f, 0xdf, 0xf7, 0x48, 0x3e, 0x7e, 0x8f, 0x7c, 0x84, 0x4f, 0x1c, - 0x57, 0x4c, 0xa2, 0x71, 0xdb, 0x62, 0xd3, 0x8e, 0xcd, 0xac, 0x33, 0xca, 0x3b, 0xe1, 0xd7, 0x84, - 0x4f, 0xcf, 0x5c, 0xd1, 0x21, 0x81, 0xdb, 0x09, 0x03, 0x6a, 0x85, 0xed, 0x80, 0x33, 0xc1, 0x10, - 0x8a, 0x01, 0xed, 0x04, 0xd0, 0x9e, 0xfd, 0xa0, 0x71, 0x15, 0x5f, 0xcc, 0x03, 0xaa, 0xf9, 0x8d, - 0x9b, 0x0e, 0x73, 0x98, 0x6a, 0x76, 0x64, 0x4b, 0x5b, 0xb7, 0x1d, 0xc6, 0x1c, 0x8f, 0x76, 0x54, - 0x6f, 0x1c, 0x9d, 0x76, 0xec, 0x88, 0x13, 0xe1, 0x32, 0x5f, 0x8f, 0xdf, 0x7e, 0x7d, 0x9c, 0xf8, - 0xf3, 0x78, 0xa8, 0xf5, 0xb2, 0x08, 0x95, 0x23, 0x66, 0xd3, 0xe3, 0x80, 0x5a, 0xe8, 0x00, 0x0c, - 0xe2, 0xfb, 0x4c, 0x28, 0x6e, 0x68, 0x16, 0x9a, 0x85, 0x5d, 0x63, 0x6f, 0xa7, 0xfd, 0xe6, 0x9a, - 0xdb, 0xdd, 0x25, 0xac, 0x57, 0x7c, 0x75, 0xbe, 0xb3, 0x86, 0xb3, 0x4c, 0xf4, 0x33, 0xa8, 0xd9, - 0x34, 0x74, 0x39, 0xb5, 0x47, 0x9c, 0x79, 0xd4, 0x5c, 0x6f, 0x16, 0x76, 0xaf, 0xed, 0x7d, 0x37, - 0xcf, 0x93, 0x9c, 0x1c, 0x33, 0x8f, 0x62, 0x43, 0x33, 0x64, 0x07, 0x1d, 0x00, 0x4c, 0xe9, 0x74, - 0x4c, 0x79, 0x38, 0x71, 0x03, 0x73, 0x43, 0xd1, 0xbf, 0x77, 0x11, 0x5d, 0xae, 0xbd, 0xfd, 0x24, - 0x85, 0xe3, 0x0c, 0x15, 0x3d, 0x81, 0x1a, 0x99, 0x11, 0xd7, 0x23, 0x63, 0xd7, 0x73, 0xc5, 0xdc, - 0x2c, 0x2a, 0x57, 0x1f, 0x5f, 0xea, 0xaa, 0x9b, 0x21, 0xe0, 0x15, 0x7a, 0xcb, 0x06, 0x58, 0x4e, - 0x84, 0xee, 0x43, 0x79, 0x38, 0x38, 0xea, 0x1f, 0x1e, 0x1d, 0xd4, 0xd7, 0x1a, 0xb7, 0x5f, 0xbc, - 0x6c, 0xbe, 0x2f, 0x7d, 0x2c, 0x01, 0x43, 0xea, 0xdb, 0xae, 0xef, 0xa0, 0x5d, 0xa8, 0x74, 0xf7, - 0xf7, 0x07, 0xc3, 0x93, 0x41, 0xbf, 0x5e, 0x68, 0x34, 0x5e, 0xbc, 0x6c, 0xde, 0x5a, 0x05, 0x76, - 0x2d, 0x8b, 0x06, 0x82, 0xda, 0x8d, 0xe2, 0x37, 0x7f, 0xde, 0x5e, 0x6b, 0x7d, 0x53, 0x80, 0x5a, - 0x76, 0x11, 0xe8, 0x3e, 0x94, 0xba, 0xfb, 0x27, 0x87, 0xcf, 0x06, 0xf5, 0xb5, 0x25, 0x3d, 0x8b, - 0xe8, 0x5a, 0xc2, 0x9d, 0x51, 0x74, 0x0f, 0x36, 0x87, 0xdd, 0xaf, 0x8e, 0x07, 0xf5, 0xc2, 0x72, - 0x39, 0x59, 0xd8, 0x90, 0x44, 0xa1, 0x42, 0xf5, 0x71, 0xf7, 0xf0, 0xa8, 0xbe, 0x9e, 0x8f, 0xea, - 0x73, 0xe2, 0xfa, 0x7a, 0x29, 0x7f, 0x2a, 0x82, 0x71, 0x4c, 0xf9, 0xcc, 0xb5, 0xde, 0xb1, 0x44, - 0x3e, 0x83, 0xa2, 0x20, 0xe1, 0x99, 0x92, 0x86, 0x91, 0x2f, 0x8d, 0x13, 0x12, 0x9e, 0xc9, 0x49, - 0x35, 0x5d, 0xe1, 0xa5, 0x32, 0x38, 0x0d, 0x3c, 0xd7, 0x22, 0x82, 0xda, 0x4a, 0x19, 0xc6, 0xde, - 0x47, 0x79, 0x6c, 0x9c, 0xa2, 0xf4, 0xfa, 0x1f, 0xad, 0xe1, 0x0c, 0x15, 0x3d, 0x84, 0x92, 0xe3, - 0xb1, 0x31, 0xf1, 0x94, 0x26, 0x8c, 0xbd, 0xbb, 0x79, 0x4e, 0x0e, 0x14, 0x62, 0xe9, 0x40, 0x53, - 0xd0, 0x03, 0x28, 0x45, 0x81, 0x4d, 0x04, 0x35, 0x4b, 0x8a, 0xdc, 0xcc, 0x23, 0x7f, 0xa5, 0x10, - 0xfb, 0xcc, 0x3f, 0x75, 0x1d, 0xac, 0xf1, 0xe8, 0x31, 0x54, 0x7c, 0x2a, 0xbe, 0x66, 0xfc, 0x2c, - 0x34, 0xcb, 0xcd, 0x8d, 0x5d, 0x63, 0xef, 0xd3, 0x5c, 0x31, 0xc6, 0x98, 0xae, 0x10, 0xc4, 0x9a, - 0x4c, 0xa9, 0x2f, 0x62, 0x37, 0xbd, 0x75, 0xb3, 0x80, 0x53, 0x07, 0xe8, 0x27, 0x50, 0xa1, 0xbe, - 0x1d, 0x30, 0xd7, 0x17, 0x66, 0xe5, 0xe2, 0x85, 0x0c, 0x34, 0x46, 0x06, 0x13, 0xa7, 0x0c, 0xc9, - 0xe6, 0xcc, 0xf3, 0xc6, 0xc4, 0x3a, 0x33, 0xab, 0x6f, 0xb9, 0x8d, 0x94, 0xd1, 0x2b, 0x41, 0x71, - 0xca, 0x6c, 0xda, 0xea, 0xc0, 0x8d, 0x37, 0x42, 0x8d, 0x1a, 0x50, 0xd1, 0xa1, 0x8e, 0x35, 0x52, - 0xc4, 0x69, 0xbf, 0x75, 0x1d, 0xb6, 0x56, 0xc2, 0xda, 0xfa, 0xeb, 0x26, 0x54, 0x92, 0xb3, 0x46, - 0x5d, 0xa8, 0x5a, 0xcc, 0x17, 0xc4, 0xf5, 0x29, 0xd7, 0xf2, 0xca, 0x3d, 0x99, 0xfd, 0x04, 0x24, - 0x59, 0x8f, 0xd6, 0xf0, 0x92, 0x85, 0x7e, 0x01, 0x55, 0x4e, 0x43, 0x16, 0x71, 0x8b, 0x86, 0x5a, - 0x5f, 0xbb, 0xf9, 0x0a, 0x89, 0x41, 0x98, 0xfe, 0x2e, 0x72, 0x39, 0x95, 0x51, 0x0e, 0xf1, 0x92, - 0x8a, 0x1e, 0x42, 0x99, 0xd3, 0x50, 0x10, 0x2e, 0x2e, 0x93, 0x08, 0x8e, 0x21, 0x43, 0xe6, 0xb9, - 0xd6, 0x1c, 0x27, 0x0c, 0xf4, 0x10, 0xaa, 0x81, 0x47, 0x2c, 0xe5, 0xd5, 0xdc, 0x54, 0xf4, 0x0f, - 0xf2, 0xe8, 0xc3, 0x04, 0x84, 0x97, 0x78, 0xf4, 0x39, 0x80, 0xc7, 0x9c, 0x91, 0xcd, 0xdd, 0x19, - 0xe5, 0x5a, 0x62, 0x8d, 0x3c, 0x76, 0x5f, 0x21, 0x70, 0xd5, 0x63, 0x4e, 0xdc, 0x44, 0x07, 0xff, - 0x93, 0xbe, 0x32, 0xda, 0x7a, 0x0c, 0x40, 0xd2, 0x51, 0xad, 0xae, 0x8f, 0xdf, 0xca, 0x95, 0x3e, - 0x91, 0x0c, 0x1d, 0xdd, 0x85, 0xda, 0x29, 0xe3, 0x16, 0x1d, 0xe9, 0xac, 0xa9, 0x2a, 0x4d, 0x18, - 0xca, 0x16, 0xeb, 0x0b, 0xf5, 0xa0, 0xec, 0x50, 0x9f, 0x72, 0xd7, 0x32, 0x41, 0x4d, 0x76, 0x3f, - 0x37, 0x21, 0x63, 0x08, 0x8e, 0x7c, 0xe1, 0x4e, 0xa9, 0x9e, 0x29, 0x21, 0xa2, 0xdf, 0xc2, 0x7b, - 0xc9, 0xf1, 0x8d, 0x38, 0x3d, 0xa5, 0x9c, 0xfa, 0x52, 0x03, 0x86, 0x8a, 0xc3, 0x47, 0x97, 0x6b, - 0x40, 0xa3, 0xf5, 0x65, 0x83, 0xf8, 0xeb, 0x03, 0x61, 0xaf, 0x0a, 0x65, 0x1e, 0xcf, 0xdb, 0xfa, - 0x43, 0x41, 0xaa, 0xfe, 0x35, 0x04, 0xea, 0x80, 0x91, 0x4e, 0xef, 0xda, 0x4a, 0xbd, 0xd5, 0xde, - 0xb5, 0xc5, 0xf9, 0x0e, 0x24, 0xd8, 0xc3, 0xbe, 0xbc, 0x83, 0x74, 0xdb, 0x46, 0x03, 0xd8, 0x4a, - 0x09, 0xf2, 0x99, 0xd7, 0x0f, 0x65, 0xf3, 0xb2, 0x95, 0x9e, 0xcc, 0x03, 0x8a, 0x6b, 0x3c, 0xd3, - 0x6b, 0xfd, 0x06, 0xd0, 0x9b, 0x71, 0x41, 0x08, 0x8a, 0x67, 0xae, 0xaf, 0x97, 0x81, 0x55, 0x1b, - 0xb5, 0xa1, 0x1c, 0x90, 0xb9, 0xc7, 0x88, 0xad, 0x13, 0xe3, 0x66, 0x3b, 0xae, 0x0d, 0xda, 0x49, - 0x6d, 0xd0, 0xee, 0xfa, 0x73, 0x9c, 0x80, 0x5a, 0x8f, 0xe1, 0xfd, 0xdc, 0xe3, 0x45, 0x7b, 0x50, - 0x4b, 0x13, 0x6e, 0xb9, 0xd7, 0xeb, 0x8b, 0xf3, 0x1d, 0x23, 0xcd, 0xcc, 0xc3, 0x3e, 0x36, 0x52, - 0xd0, 0xa1, 0xdd, 0xfa, 0x63, 0x15, 0xb6, 0x56, 0xd2, 0x16, 0xdd, 0x84, 0x4d, 0x77, 0x4a, 0x1c, - 0xaa, 0xd7, 0x18, 0x77, 0xd0, 0x00, 0x4a, 0x1e, 0x19, 0x53, 0x4f, 0x26, 0xaf, 0x3c, 0xb8, 0xef, - 0x5f, 0x99, 0xff, 0xed, 0x5f, 0x29, 0xfc, 0xc0, 0x17, 0x7c, 0x8e, 0x35, 0x19, 0x99, 0x50, 0xb6, - 0xd8, 0x74, 0x4a, 0x7c, 0xf9, 0x4c, 0x6c, 0xec, 0x56, 0x71, 0xd2, 0x95, 0x91, 0x21, 0xdc, 0x09, - 0xcd, 0xa2, 0x32, 0xab, 0x36, 0xaa, 0xc3, 0x06, 0xf5, 0x67, 0xe6, 0xa6, 0x32, 0xc9, 0xa6, 0xb4, - 0xd8, 0x6e, 0x9c, 0x7d, 0x55, 0x2c, 0x9b, 0x92, 0x17, 0x85, 0x94, 0x9b, 0xe5, 0x38, 0xa2, 0xb2, - 0x8d, 0x7e, 0x0c, 0xa5, 0x29, 0x8b, 0x7c, 0x11, 0x9a, 0x15, 0xb5, 0xd8, 0xdb, 0x79, 0x8b, 0x7d, - 0x22, 0x11, 0x5a, 0x59, 0x1a, 0x8e, 0x06, 0x70, 0x23, 0x14, 0x2c, 0x18, 0x39, 0x9c, 0x58, 0x74, - 0x14, 0x50, 0xee, 0x32, 0x5b, 0x5f, 0xc3, 0xb7, 0xdf, 0x38, 0x94, 0xbe, 0x2e, 0xe8, 0xf0, 0x75, - 0xc9, 0x39, 0x90, 0x94, 0xa1, 0x62, 0xa0, 0x21, 0xd4, 0x82, 0xc8, 0xf3, 0x46, 0x2c, 0x88, 0x5f, - 0xe4, 0x38, 0x77, 0xde, 0x22, 0x64, 0xc3, 0xc8, 0xf3, 0x9e, 0xc6, 0x24, 0x6c, 0x04, 0xcb, 0x0e, - 0xba, 0x05, 0x25, 0x87, 0xb3, 0x28, 0x88, 0xf3, 0xa6, 0x8a, 0x75, 0x0f, 0x7d, 0x09, 0xe5, 0x90, - 0x5a, 0x9c, 0x8a, 0xd0, 0xac, 0xa9, 0xad, 0x7e, 0x98, 0x37, 0xc9, 0xb1, 0x82, 0xa4, 0x39, 0x81, - 0x13, 0x0e, 0xba, 0x0d, 0x1b, 0x42, 0xcc, 0xcd, 0xad, 0x66, 0x61, 0xb7, 0xd2, 0x2b, 0x2f, 0xce, - 0x77, 0x36, 0x4e, 0x4e, 0x9e, 0x63, 0x69, 0x93, 0xaf, 0xc5, 0x84, 0x85, 0xc2, 0x27, 0x53, 0x6a, - 0x5e, 0x53, 0xb1, 0x4d, 0xfb, 0xe8, 0x39, 0x80, 0xed, 0x87, 0x23, 0x4b, 0x5d, 0x4f, 0xe6, 0x75, - 0xb5, 0xbb, 0x4f, 0xaf, 0xde, 0x5d, 0xff, 0xe8, 0x58, 0xbf, 0x98, 0x5b, 0x8b, 0xf3, 0x9d, 0x6a, - 0xda, 0xc5, 0x55, 0xdb, 0x0f, 0xe3, 0x26, 0xea, 0x81, 0x31, 0xa1, 0xc4, 0x13, 0x13, 0x6b, 0x42, - 0xad, 0x33, 0xb3, 0x7e, 0xf1, 0x13, 0xf8, 0x48, 0xc1, 0xb4, 0x87, 0x2c, 0x49, 0x2a, 0x58, 0x2e, - 0x35, 0x34, 0x6f, 0xa8, 0x58, 0xc5, 0x1d, 0xf4, 0x01, 0x00, 0x0b, 0xa8, 0x3f, 0x0a, 0x85, 0xed, - 0xfa, 0x26, 0x92, 0x5b, 0xc6, 0x55, 0x69, 0x39, 0x96, 0x06, 0x74, 0x47, 0x3e, 0x50, 0xc4, 0x1e, - 0x31, 0xdf, 0x9b, 0x9b, 0xef, 0xa9, 0xd1, 0x8a, 0x34, 0x3c, 0xf5, 0xbd, 0x39, 0xda, 0x01, 0x43, - 0xe9, 0x22, 0x74, 0x1d, 0x9f, 0x78, 0xe6, 0x4d, 0x15, 0x0f, 0x90, 0xa6, 0x63, 0x65, 0x91, 0xe7, - 0x10, 0x47, 0x23, 0x34, 0xdf, 0xbf, 0xf8, 0x1c, 0xf4, 0x62, 0x97, 0xe7, 0xa0, 0x39, 0xe8, 0xa7, - 0x00, 0x01, 0x77, 0x67, 0xae, 0x47, 0x1d, 0x1a, 0x9a, 0xb7, 0xd4, 0xa6, 0xb7, 0x73, 0x5f, 0xa6, - 0x14, 0x85, 0x33, 0x8c, 0xc6, 0xe7, 0x60, 0x64, 0xb2, 0x4d, 0x66, 0xc9, 0x19, 0x9d, 0xeb, 0x04, - 0x96, 0x4d, 0x19, 0x92, 0x19, 0xf1, 0xa2, 0xf8, 0x32, 0xab, 0xe2, 0xb8, 0xf3, 0xc5, 0xfa, 0x83, - 0x42, 0x63, 0x0f, 0x8c, 0x8c, 0xea, 0xd0, 0x87, 0xf2, 0xf6, 0x73, 0xdc, 0x50, 0xf0, 0xf9, 0x88, - 0x44, 0x62, 0x62, 0xfe, 0x5c, 0x11, 0x6a, 0x89, 0xb1, 0x1b, 0x89, 0x49, 0x63, 0x04, 0xcb, 0xc3, - 0x43, 0x4d, 0x30, 0xa4, 0x28, 0x42, 0xca, 0x67, 0x94, 0xcb, 0xca, 0x42, 0xc6, 0x3c, 0x6b, 0x92, - 0xe2, 0x0d, 0x29, 0xe1, 0xd6, 0x44, 0xdd, 0x1d, 0x55, 0xac, 0x7b, 0xf2, 0x32, 0x48, 0x32, 0x44, - 0x5f, 0x06, 0xba, 0xdb, 0xfa, 0x57, 0x01, 0x6a, 0xd9, 0x02, 0x09, 0xed, 0xc7, 0x85, 0x8d, 0xda, - 0xd2, 0xb5, 0xbd, 0xce, 0x55, 0x05, 0x95, 0xba, 0x98, 0xbd, 0x48, 0x3a, 0x7b, 0x22, 0xff, 0x32, - 0x8a, 0x8c, 0x7e, 0x04, 0x9b, 0x01, 0xe3, 0x22, 0xb9, 0xc2, 0xf2, 0x03, 0xcc, 0x78, 0xf2, 0xec, - 0xc6, 0xe0, 0xd6, 0x04, 0xae, 0xad, 0x7a, 0x43, 0xf7, 0x60, 0xe3, 0xd9, 0xe1, 0xb0, 0xbe, 0xd6, - 0xb8, 0xf3, 0xe2, 0x65, 0xf3, 0x3b, 0xab, 0x83, 0xcf, 0x5c, 0x2e, 0x22, 0xe2, 0x1d, 0x0e, 0xd1, - 0x27, 0xb0, 0xd9, 0x3f, 0x3a, 0xc6, 0xb8, 0x5e, 0x68, 0xec, 0xbc, 0x78, 0xd9, 0xbc, 0xb3, 0x8a, - 0x93, 0x43, 0x2c, 0xf2, 0x6d, 0xcc, 0xc6, 0x69, 0x5d, 0xff, 0xef, 0x75, 0x30, 0xf4, 0xcd, 0xfe, - 0xae, 0xbf, 0x7e, 0x5b, 0x71, 0xd9, 0x92, 0xa4, 0xec, 0xfa, 0x95, 0xd5, 0x4b, 0x2d, 0x26, 0xe8, - 0x33, 0xbe, 0x0b, 0x35, 0x37, 0x98, 0x7d, 0x36, 0xa2, 0x3e, 0x19, 0x7b, 0xba, 0xc4, 0xaf, 0x60, - 0x43, 0xda, 0x06, 0xb1, 0x49, 0xde, 0x17, 0xae, 0x2f, 0x28, 0xf7, 0x75, 0xf1, 0x5e, 0xc1, 0x69, - 0x1f, 0x7d, 0x09, 0x45, 0x37, 0x20, 0x53, 0x5d, 0x72, 0xe5, 0xee, 0xe0, 0x70, 0xd8, 0x7d, 0xa2, - 0x35, 0xd8, 0xab, 0x2c, 0xce, 0x77, 0x8a, 0xd2, 0x80, 0x15, 0x0d, 0x6d, 0x27, 0x55, 0x8f, 0x9c, - 0x49, 0xdd, 0xfd, 0x15, 0x9c, 0xb1, 0x48, 0x1d, 0xb9, 0xbe, 0xc3, 0x69, 0x18, 0xaa, 0x57, 0xa0, - 0x82, 0x93, 0x2e, 0x6a, 0x40, 0x59, 0xd7, 0x4e, 0xaa, 0x58, 0xaa, 0xca, 0xba, 0x44, 0x1b, 0x7a, - 0x5b, 0x60, 0xc4, 0xd1, 0x18, 0x9d, 0x72, 0x36, 0x6d, 0xfd, 0xa7, 0x08, 0xc6, 0xbe, 0x17, 0x85, - 0x42, 0x3f, 0x83, 0xef, 0x2c, 0xf8, 0xcf, 0xe1, 0x06, 0x51, 0x5f, 0x49, 0xe2, 0xcb, 0x37, 0x45, - 0x95, 0xa4, 0xfa, 0x00, 0xee, 0xe5, 0xba, 0x4b, 0xc1, 0x71, 0xf9, 0xda, 0x2b, 0x49, 0x9f, 0x66, - 0x01, 0xd7, 0xc9, 0x6b, 0x23, 0xe8, 0x18, 0xb6, 0x18, 0xb7, 0x26, 0x34, 0x14, 0xf1, 0x4b, 0xa4, - 0xbf, 0x5e, 0xb9, 0x9f, 0xf2, 0xa7, 0x59, 0xa0, 0xbe, 0x86, 0xe3, 0xd5, 0xae, 0xfa, 0x40, 0x0f, - 0xa0, 0xc8, 0xc9, 0x69, 0x52, 0x5e, 0xe7, 0x26, 0x09, 0x26, 0xa7, 0x62, 0xc5, 0x85, 0x62, 0xa0, - 0x5f, 0x02, 0xd8, 0x6e, 0x18, 0x10, 0x61, 0x4d, 0x28, 0xd7, 0x87, 0x9d, 0xbb, 0xc5, 0x7e, 0x8a, - 0x5a, 0xf1, 0x92, 0x61, 0xa3, 0xc7, 0x50, 0xb5, 0x48, 0x22, 0xd7, 0xd2, 0xc5, 0xff, 0xd1, 0xfd, - 0xae, 0x76, 0x51, 0x97, 0x2e, 0x16, 0xe7, 0x3b, 0x95, 0xc4, 0x82, 0x2b, 0x16, 0xd1, 0xf2, 0x7d, - 0x0c, 0x5b, 0xf2, 0x9f, 0x3a, 0xb2, 0xe9, 0x29, 0x89, 0x3c, 0x11, 0xcb, 0xe4, 0x82, 0x67, 0x45, - 0x7e, 0x7a, 0xfa, 0x1a, 0xa7, 0xd7, 0x55, 0x13, 0x19, 0x1b, 0xfa, 0x35, 0xdc, 0xa0, 0xbe, 0xc5, - 0xe7, 0x4a, 0xac, 0xc9, 0x0a, 0x2b, 0x17, 0x6f, 0x76, 0x90, 0x82, 0x57, 0x36, 0x5b, 0xa7, 0xaf, - 0xd9, 0x5b, 0xff, 0x28, 0x00, 0xc4, 0x2f, 0xf5, 0xbb, 0x15, 0x20, 0x82, 0xa2, 0x4d, 0x04, 0x51, - 0x9a, 0xab, 0x61, 0xd5, 0x46, 0x5f, 0x00, 0x08, 0x3a, 0x0d, 0x3c, 0x22, 0x5c, 0xdf, 0xd1, 0xb2, - 0xb9, 0xec, 0x3a, 0xc8, 0xa0, 0xd1, 0x1e, 0x94, 0xf4, 0x27, 0xa8, 0x78, 0x25, 0x4f, 0x23, 0x5b, - 0x7f, 0x29, 0x00, 0xc4, 0xdb, 0xfc, 0xbf, 0xde, 0x5b, 0xcf, 0x7c, 0xf5, 0xed, 0xf6, 0xda, 0xdf, - 0xbf, 0xdd, 0x5e, 0xfb, 0xfd, 0x62, 0xbb, 0xf0, 0x6a, 0xb1, 0x5d, 0xf8, 0xdb, 0x62, 0xbb, 0xf0, - 0xcf, 0xc5, 0x76, 0x61, 0x5c, 0x52, 0x75, 0xdf, 0x0f, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xae, - 0x88, 0xf9, 0x3c, 0x5a, 0x14, 0x00, 0x00, + // 2114 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x6f, 0xdb, 0xc8, + 0x15, 0xb7, 0x6c, 0x59, 0x96, 0x1e, 0xe5, 0x44, 0x9e, 0x4d, 0xb2, 0xb4, 0xb2, 0xb1, 0x15, 0x6d, + 0x36, 0xf5, 0xee, 0xa2, 0x32, 0xea, 0x2e, 0xb6, 0xd9, 0x4d, 0xb7, 0xad, 0x64, 0x69, 0x1d, 0x35, + 0x89, 0x2d, 0x8c, 0x1c, 0xb7, 0x01, 0x0a, 0x08, 0x63, 0x72, 0x2c, 0x11, 0xa6, 0x38, 0xec, 0x70, + 0xe8, 0x40, 0xb7, 0x1e, 0x17, 0xee, 0x67, 0x30, 0x7a, 0x28, 0x7a, 0x6f, 0xbf, 0x42, 0x4f, 0x39, + 0xf6, 0xd8, 0x5e, 0x8c, 0xae, 0xbf, 0x42, 0x6f, 0xbd, 0xb4, 0x98, 0xe1, 0x90, 0xa2, 0x1c, 0x3a, + 0x0e, 0xd0, 0x1c, 0x7a, 0x9b, 0x79, 0xfc, 0xfd, 0xde, 0xfc, 0xfb, 0xbd, 0x37, 0x6f, 0x08, 0x9f, + 0x0d, 0x1d, 0x31, 0x0a, 0x0f, 0x1b, 0x16, 0x1b, 0x6f, 0xda, 0xcc, 0x3a, 0xa6, 0x7c, 0x33, 0x78, + 0x45, 0xf8, 0xf8, 0xd8, 0x11, 0x9b, 0xc4, 0x77, 0x36, 0x03, 0x9f, 0x5a, 0x41, 0xc3, 0xe7, 0x4c, + 0x30, 0x84, 0x22, 0x40, 0x23, 0x06, 0x34, 0x4e, 0x7e, 0x54, 0xbd, 0x8e, 0x2f, 0x26, 0x3e, 0xd5, + 0xfc, 0xea, 0xad, 0x21, 0x1b, 0x32, 0xd5, 0xdc, 0x94, 0x2d, 0x6d, 0x5d, 0x1b, 0x32, 0x36, 0x74, + 0xe9, 0xa6, 0xea, 0x1d, 0x86, 0x47, 0x9b, 0x76, 0xc8, 0x89, 0x70, 0x98, 0xa7, 0xbf, 0xaf, 0x5e, + 0xfe, 0x4e, 0xbc, 0xc9, 0x55, 0xd4, 0x57, 0x9c, 0xf8, 0x3e, 0xe5, 0x7a, 0xc0, 0xfa, 0x59, 0x1e, + 0x8a, 0xbb, 0xcc, 0xa6, 0x7d, 0x9f, 0x5a, 0x68, 0x07, 0x0c, 0xe2, 0x79, 0x4c, 0x28, 0xdf, 0x81, + 0x99, 0xab, 0xe5, 0x36, 0x8c, 0xad, 0xf5, 0xc6, 0x9b, 0x6b, 0x6a, 0x34, 0xa7, 0xb0, 0x56, 0xfe, + 0xf5, 0xf9, 0xfa, 0x1c, 0x4e, 0x33, 0xd1, 0xcf, 0xa1, 0x6c, 0xd3, 0xc0, 0xe1, 0xd4, 0x1e, 0x70, + 0xe6, 0x52, 0x73, 0xbe, 0x96, 0xdb, 0xb8, 0xb1, 0xf5, 0x51, 0x96, 0x27, 0x39, 0x38, 0x66, 0x2e, + 0xc5, 0x86, 0x66, 0xc8, 0x0e, 0xda, 0x01, 0x18, 0xd3, 0xf1, 0x21, 0xe5, 0xc1, 0xc8, 0xf1, 0xcd, + 0x05, 0x45, 0xff, 0xc1, 0x55, 0x74, 0x39, 0xf7, 0xc6, 0xf3, 0x04, 0x8e, 0x53, 0x54, 0xf4, 0x1c, + 0xca, 0xe4, 0x84, 0x38, 0x2e, 0x39, 0x74, 0x5c, 0x47, 0x4c, 0xcc, 0xbc, 0x72, 0xf5, 0xe9, 0x5b, + 0x5d, 0x35, 0x53, 0x04, 0x3c, 0x43, 0xaf, 0xdb, 0x00, 0xd3, 0x81, 0xd0, 0x43, 0x58, 0xea, 0x75, + 0x76, 0xdb, 0xdd, 0xdd, 0x9d, 0xca, 0x5c, 0x75, 0xf5, 0xf4, 0xac, 0x76, 0x5b, 0xfa, 0x98, 0x02, + 0x7a, 0xd4, 0xb3, 0x1d, 0x6f, 0x88, 0x36, 0xa0, 0xd8, 0xdc, 0xde, 0xee, 0xf4, 0xf6, 0x3b, 0xed, + 0x4a, 0xae, 0x5a, 0x3d, 0x3d, 0xab, 0xdd, 0x99, 0x05, 0x36, 0x2d, 0x8b, 0xfa, 0x82, 0xda, 0xd5, + 0xfc, 0x77, 0x7f, 0x5c, 0x9b, 0xab, 0x7f, 0x97, 0x83, 0x72, 0x7a, 0x12, 0xe8, 0x21, 0x14, 0x9a, + 0xdb, 0xfb, 0xdd, 0x83, 0x4e, 0x65, 0x6e, 0x4a, 0x4f, 0x23, 0x9a, 0x96, 0x70, 0x4e, 0x28, 0x7a, + 0x00, 0x8b, 0xbd, 0xe6, 0x8b, 0x7e, 0xa7, 0x92, 0x9b, 0x4e, 0x27, 0x0d, 0xeb, 0x91, 0x30, 0x50, + 0xa8, 0x36, 0x6e, 0x76, 0x77, 0x2b, 0xf3, 0xd9, 0xa8, 0x36, 0x27, 0x8e, 0xa7, 0xa7, 0xf2, 0x87, + 0x3c, 0x18, 0x7d, 0xca, 0x4f, 0x1c, 0xeb, 0x3d, 0x4b, 0xe4, 0x4b, 0xc8, 0x0b, 0x12, 0x1c, 0x2b, + 0x69, 0x18, 0xd9, 0xd2, 0xd8, 0x27, 0xc1, 0xb1, 0x1c, 0x54, 0xd3, 0x15, 0x5e, 0x2a, 0x83, 0x53, + 0xdf, 0x75, 0x2c, 0x22, 0xa8, 0xad, 0x94, 0x61, 0x6c, 0x7d, 0x92, 0xc5, 0xc6, 0x09, 0x4a, 0xcf, + 0xff, 0xc9, 0x1c, 0x4e, 0x51, 0xd1, 0x63, 0x28, 0x0c, 0x5d, 0x76, 0x48, 0x5c, 0xa5, 0x09, 0x63, + 0xeb, 0x7e, 0x96, 0x93, 0x1d, 0x85, 0x98, 0x3a, 0xd0, 0x14, 0xf4, 0x08, 0x0a, 0xa1, 0x6f, 0x13, + 0x41, 0xcd, 0x82, 0x22, 0xd7, 0xb2, 0xc8, 0x2f, 0x14, 0x62, 0x9b, 0x79, 0x47, 0xce, 0x10, 0x6b, + 0x3c, 0x7a, 0x0a, 0x45, 0x8f, 0x8a, 0x57, 0x8c, 0x1f, 0x07, 0xe6, 0x52, 0x6d, 0x61, 0xc3, 0xd8, + 0xfa, 0x3c, 0x53, 0x8c, 0x11, 0xa6, 0x29, 0x04, 0xb1, 0x46, 0x63, 0xea, 0x89, 0xc8, 0x4d, 0x6b, + 0xde, 0xcc, 0xe1, 0xc4, 0x01, 0xfa, 0x29, 0x14, 0xa9, 0x67, 0xfb, 0xcc, 0xf1, 0x84, 0x59, 0xbc, + 0x7a, 0x22, 0x1d, 0x8d, 0x91, 0x9b, 0x89, 0x13, 0x86, 0x64, 0x73, 0xe6, 0xba, 0x87, 0xc4, 0x3a, + 0x36, 0x4b, 0xef, 0xb8, 0x8c, 0x84, 0xd1, 0x2a, 0x40, 0x7e, 0xcc, 0x6c, 0x5a, 0xdf, 0x84, 0x95, + 0x37, 0xb6, 0x1a, 0x55, 0xa1, 0xa8, 0xb7, 0x3a, 0xd2, 0x48, 0x1e, 0x27, 0xfd, 0xfa, 0x4d, 0x58, + 0x9e, 0xd9, 0xd6, 0xfa, 0x9f, 0x17, 0xa1, 0x18, 0x9f, 0x35, 0x6a, 0x42, 0xc9, 0x62, 0x9e, 0x20, + 0x8e, 0x47, 0xb9, 0x96, 0x57, 0xe6, 0xc9, 0x6c, 0xc7, 0x20, 0xc9, 0x7a, 0x32, 0x87, 0xa7, 0x2c, + 0xf4, 0x2d, 0x94, 0x38, 0x0d, 0x58, 0xc8, 0x2d, 0x1a, 0x68, 0x7d, 0x6d, 0x64, 0x2b, 0x24, 0x02, + 0x61, 0xfa, 0xdb, 0xd0, 0xe1, 0x54, 0xee, 0x72, 0x80, 0xa7, 0x54, 0xf4, 0x18, 0x96, 0x38, 0x0d, + 0x04, 0xe1, 0xe2, 0x6d, 0x12, 0xc1, 0x11, 0xa4, 0xc7, 0x5c, 0xc7, 0x9a, 0xe0, 0x98, 0x81, 0x1e, + 0x43, 0xc9, 0x77, 0x89, 0xa5, 0xbc, 0x9a, 0x8b, 0x8a, 0x7e, 0x2f, 0x8b, 0xde, 0x8b, 0x41, 0x78, + 0x8a, 0x47, 0x5f, 0x01, 0xb8, 0x6c, 0x38, 0xb0, 0xb9, 0x73, 0x42, 0xb9, 0x96, 0x58, 0x35, 0x8b, + 0xdd, 0x56, 0x08, 0x5c, 0x72, 0xd9, 0x30, 0x6a, 0xa2, 0x9d, 0xff, 0x49, 0x5f, 0x29, 0x6d, 0x3d, + 0x05, 0x20, 0xc9, 0x57, 0xad, 0xae, 0x4f, 0xdf, 0xc9, 0x95, 0x3e, 0x91, 0x14, 0x1d, 0xdd, 0x87, + 0xf2, 0x11, 0xe3, 0x16, 0x1d, 0xe8, 0xa8, 0x29, 0x29, 0x4d, 0x18, 0xca, 0x16, 0xe9, 0x0b, 0xb5, + 0x60, 0x69, 0x48, 0x3d, 0xca, 0x1d, 0xcb, 0x04, 0x35, 0xd8, 0xc3, 0xcc, 0x80, 0x8c, 0x20, 0x38, + 0xf4, 0x84, 0x33, 0xa6, 0x7a, 0xa4, 0x98, 0x88, 0x7e, 0x03, 0x1f, 0xc4, 0xc7, 0x37, 0xe0, 0xf4, + 0x88, 0x72, 0xea, 0x49, 0x0d, 0x18, 0x6a, 0x1f, 0x3e, 0x79, 0xbb, 0x06, 0x34, 0x5a, 0x27, 0x1b, + 0xc4, 0x2f, 0x7f, 0x08, 0x5a, 0x25, 0x58, 0xe2, 0xd1, 0xb8, 0xf5, 0xdf, 0xe7, 0xa4, 0xea, 0x2f, + 0x21, 0xd0, 0x26, 0x18, 0xc9, 0xf0, 0x8e, 0xad, 0xd4, 0x5b, 0x6a, 0xdd, 0xb8, 0x38, 0x5f, 0x87, + 0x18, 0xdb, 0x6d, 0xcb, 0x1c, 0xa4, 0xdb, 0x36, 0xea, 0xc0, 0x72, 0x42, 0x90, 0x65, 0x80, 0xbe, + 0x28, 0x6b, 0x6f, 0x9b, 0xe9, 0xfe, 0xc4, 0xa7, 0xb8, 0xcc, 0x53, 0xbd, 0xfa, 0xaf, 0x01, 0xbd, + 0xb9, 0x2f, 0x08, 0x41, 0xfe, 0xd8, 0xf1, 0xf4, 0x34, 0xb0, 0x6a, 0xa3, 0x06, 0x2c, 0xf9, 0x64, + 0xe2, 0x32, 0x62, 0xeb, 0xc0, 0xb8, 0xd5, 0x88, 0x0a, 0x84, 0x46, 0x5c, 0x20, 0x34, 0x9a, 0xde, + 0x04, 0xc7, 0xa0, 0xfa, 0x53, 0xb8, 0x9d, 0x79, 0xbc, 0x68, 0x0b, 0xca, 0x49, 0xc0, 0x4d, 0xd7, + 0x7a, 0xf3, 0xe2, 0x7c, 0xdd, 0x48, 0x22, 0xb3, 0xdb, 0xc6, 0x46, 0x02, 0xea, 0xda, 0xf5, 0xbf, + 0x1a, 0xb0, 0x3c, 0x13, 0xb6, 0xe8, 0x16, 0x2c, 0x3a, 0x63, 0x32, 0xa4, 0x7a, 0x8e, 0x51, 0x07, + 0x75, 0xa0, 0xe0, 0x92, 0x43, 0xea, 0xca, 0xe0, 0x95, 0x07, 0xf7, 0xc3, 0x6b, 0xe3, 0xbf, 0xf1, + 0x4c, 0xe1, 0x3b, 0x9e, 0xe0, 0x13, 0xac, 0xc9, 0xc8, 0x84, 0x25, 0x8b, 0x8d, 0xc7, 0xc4, 0x93, + 0xd7, 0xc4, 0xc2, 0x46, 0x09, 0xc7, 0x5d, 0xb9, 0x33, 0x84, 0x0f, 0x03, 0x33, 0xaf, 0xcc, 0xaa, + 0x8d, 0x2a, 0xb0, 0x40, 0xbd, 0x13, 0x73, 0x51, 0x99, 0x64, 0x53, 0x5a, 0x6c, 0x27, 0x8a, 0xbe, + 0x12, 0x96, 0x4d, 0xc9, 0x0b, 0x03, 0xca, 0xcd, 0xa5, 0x68, 0x47, 0x65, 0x1b, 0xfd, 0x04, 0x0a, + 0x63, 0x16, 0x7a, 0x22, 0x30, 0x8b, 0x6a, 0xb2, 0xab, 0x59, 0x93, 0x7d, 0x2e, 0x11, 0x5a, 0x59, + 0x1a, 0x8e, 0x3a, 0xb0, 0x12, 0x08, 0xe6, 0x0f, 0x86, 0x9c, 0x58, 0x74, 0xe0, 0x53, 0xee, 0x30, + 0x5b, 0xa7, 0xe1, 0xd5, 0x37, 0x0e, 0xa5, 0xad, 0x0b, 0x3e, 0x7c, 0x53, 0x72, 0x76, 0x24, 0xa5, + 0xa7, 0x18, 0xa8, 0x07, 0x65, 0x3f, 0x74, 0xdd, 0x01, 0xf3, 0xa3, 0x1b, 0x39, 0x8a, 0x9d, 0x77, + 0xd8, 0xb2, 0x5e, 0xe8, 0xba, 0x7b, 0x11, 0x09, 0x1b, 0xfe, 0xb4, 0x83, 0xee, 0x40, 0x61, 0xc8, + 0x59, 0xe8, 0x47, 0x71, 0x53, 0xc2, 0xba, 0x87, 0xbe, 0x81, 0xa5, 0x80, 0x5a, 0x9c, 0x8a, 0xc0, + 0x2c, 0xab, 0xa5, 0x7e, 0x9c, 0x35, 0x48, 0x5f, 0x41, 0x92, 0x98, 0xc0, 0x31, 0x07, 0xad, 0xc2, + 0x82, 0x10, 0x13, 0x73, 0xb9, 0x96, 0xdb, 0x28, 0xb6, 0x96, 0x2e, 0xce, 0xd7, 0x17, 0xf6, 0xf7, + 0x5f, 0x62, 0x69, 0x93, 0xb7, 0xc5, 0x88, 0x05, 0xc2, 0x23, 0x63, 0x6a, 0xde, 0x50, 0x7b, 0x9b, + 0xf4, 0xd1, 0x4b, 0x00, 0xdb, 0x0b, 0x06, 0x96, 0x4a, 0x4f, 0xe6, 0x4d, 0xb5, 0xba, 0xcf, 0xaf, + 0x5f, 0x5d, 0x7b, 0xb7, 0xaf, 0x6f, 0xcc, 0xe5, 0x8b, 0xf3, 0xf5, 0x52, 0xd2, 0xc5, 0x25, 0xdb, + 0x0b, 0xa2, 0x26, 0x6a, 0x81, 0x31, 0xa2, 0xc4, 0x15, 0x23, 0x6b, 0x44, 0xad, 0x63, 0xb3, 0x72, + 0xf5, 0x15, 0xf8, 0x44, 0xc1, 0xb4, 0x87, 0x34, 0x49, 0x2a, 0x58, 0x4e, 0x35, 0x30, 0x57, 0xd4, + 0x5e, 0x45, 0x1d, 0x74, 0x0f, 0x80, 0xf9, 0xd4, 0x1b, 0x04, 0xc2, 0x76, 0x3c, 0x13, 0xc9, 0x25, + 0xe3, 0x92, 0xb4, 0xf4, 0xa5, 0x01, 0xdd, 0x95, 0x17, 0x14, 0xb1, 0x07, 0xcc, 0x73, 0x27, 0xe6, + 0x07, 0xea, 0x6b, 0x51, 0x1a, 0xf6, 0x3c, 0x77, 0x82, 0xd6, 0xc1, 0x50, 0xba, 0x08, 0x9c, 0xa1, + 0x47, 0x5c, 0xf3, 0x96, 0xda, 0x0f, 0x90, 0xa6, 0xbe, 0xb2, 0xc8, 0x73, 0x88, 0x76, 0x23, 0x30, + 0x6f, 0x5f, 0x7d, 0x0e, 0x7a, 0xb2, 0xd3, 0x73, 0xd0, 0x1c, 0xf4, 0x33, 0x00, 0x9f, 0x3b, 0x27, + 0x8e, 0x4b, 0x87, 0x34, 0x30, 0xef, 0xa8, 0x45, 0xaf, 0x65, 0xde, 0x4c, 0x09, 0x0a, 0xa7, 0x18, + 0xa8, 0x01, 0x79, 0xc7, 0x73, 0x84, 0xf9, 0xa1, 0xbe, 0x95, 0x2e, 0x4b, 0xb5, 0xc5, 0x98, 0x7b, + 0x40, 0xdc, 0x90, 0x62, 0x85, 0x43, 0x5d, 0x28, 0x39, 0x01, 0x73, 0x95, 0x7c, 0x4d, 0x53, 0xe5, + 0xb7, 0x77, 0x38, 0xbf, 0x6e, 0x4c, 0xc1, 0x53, 0x76, 0xf5, 0x2b, 0x30, 0x52, 0x81, 0x2e, 0x03, + 0xf4, 0x98, 0x4e, 0x74, 0xee, 0x90, 0x4d, 0x79, 0x1a, 0x27, 0x72, 0x68, 0x95, 0xdc, 0x4a, 0x38, + 0xea, 0x7c, 0x3d, 0xff, 0x28, 0x57, 0xdd, 0x02, 0x23, 0x25, 0x78, 0xf4, 0xb1, 0x4c, 0xbc, 0x43, + 0x27, 0x10, 0x7c, 0x32, 0x20, 0xa1, 0x18, 0x99, 0xbf, 0x50, 0x84, 0x72, 0x6c, 0x6c, 0x86, 0x62, + 0x54, 0x1d, 0xc0, 0x54, 0x37, 0xa8, 0x06, 0x86, 0xd4, 0x63, 0x40, 0xf9, 0x09, 0xe5, 0xb2, 0xa8, + 0x91, 0xc7, 0x9d, 0x36, 0xc9, 0xb8, 0x09, 0x28, 0xe1, 0xd6, 0x48, 0xa5, 0xad, 0x12, 0xd6, 0x3d, + 0x99, 0x87, 0xe2, 0xe0, 0xd4, 0x79, 0x48, 0x77, 0xeb, 0x7f, 0xc9, 0x41, 0x29, 0x59, 0x28, 0xfa, + 0x02, 0x56, 0xba, 0xfd, 0xbd, 0x67, 0xcd, 0xfd, 0xee, 0xde, 0xee, 0xa0, 0xdd, 0xf9, 0xb6, 0xf9, + 0xe2, 0xd9, 0x7e, 0x65, 0xae, 0x7a, 0xef, 0xf4, 0xac, 0xb6, 0x3a, 0xcd, 0xa9, 0x31, 0xbc, 0x4d, + 0x8f, 0x48, 0xe8, 0x8a, 0x59, 0x56, 0x0f, 0xef, 0x6d, 0x77, 0xfa, 0xfd, 0x4a, 0xee, 0x2a, 0x56, + 0x8f, 0x33, 0x8b, 0x06, 0x01, 0xda, 0x82, 0xca, 0x94, 0xf5, 0xe4, 0x65, 0xaf, 0x83, 0x0f, 0x2a, + 0xf3, 0xd5, 0x8f, 0x4e, 0xcf, 0x6a, 0xe6, 0x9b, 0xa4, 0x27, 0x13, 0x9f, 0xf2, 0x03, 0xfd, 0x20, + 0xf8, 0x57, 0x0e, 0xca, 0xe9, 0x7a, 0x12, 0x6d, 0x47, 0x75, 0xa0, 0x3a, 0x86, 0x1b, 0x5b, 0x9b, + 0xd7, 0xd5, 0x9f, 0xea, 0x1e, 0x73, 0x43, 0xe9, 0xf7, 0xb9, 0x7c, 0xfa, 0x29, 0x32, 0xfa, 0x02, + 0x16, 0x7d, 0xc6, 0x45, 0x9c, 0xf1, 0xb3, 0xf5, 0xc8, 0x78, 0x5c, 0xa5, 0x44, 0xe0, 0xfa, 0x08, + 0x6e, 0xcc, 0x7a, 0x43, 0x0f, 0x60, 0xe1, 0xa0, 0xdb, 0xab, 0xcc, 0x55, 0xef, 0x9e, 0x9e, 0xd5, + 0x3e, 0x9c, 0xfd, 0x78, 0xe0, 0x70, 0x11, 0x12, 0xb7, 0xdb, 0x43, 0x9f, 0xc1, 0x62, 0x7b, 0xb7, + 0x8f, 0x71, 0x25, 0x57, 0x5d, 0x3f, 0x3d, 0xab, 0xdd, 0x9d, 0xc5, 0xc9, 0x4f, 0x2c, 0xf4, 0x6c, + 0xcc, 0x0e, 0x93, 0x67, 0xd0, 0xbf, 0xe7, 0xc1, 0xd0, 0x17, 0xe1, 0xfb, 0x7e, 0x29, 0x2f, 0x47, + 0x55, 0x5e, 0x9c, 0xe1, 0xe6, 0xaf, 0x2d, 0xf6, 0xca, 0x11, 0x41, 0xeb, 0xf2, 0x3e, 0x94, 0x1d, + 0xff, 0xe4, 0xcb, 0x01, 0xf5, 0xc8, 0xa1, 0xab, 0x5f, 0x44, 0x45, 0x6c, 0x48, 0x5b, 0x27, 0x32, + 0xc9, 0xf4, 0xea, 0x78, 0x82, 0x72, 0x4f, 0xbf, 0x75, 0x8a, 0x38, 0xe9, 0xa3, 0x6f, 0x20, 0xef, + 0xf8, 0x64, 0xac, 0x2b, 0xd4, 0xcc, 0x15, 0x74, 0x7b, 0xcd, 0xe7, 0x3a, 0x6e, 0x5a, 0xc5, 0x8b, + 0xf3, 0xf5, 0xbc, 0x34, 0x60, 0x45, 0x43, 0x6b, 0x71, 0x91, 0x28, 0x47, 0x52, 0x57, 0x65, 0x11, + 0xa7, 0x2c, 0x52, 0xfb, 0x8e, 0x37, 0xe4, 0x34, 0x08, 0xd4, 0xa5, 0x59, 0xc4, 0x71, 0x17, 0x55, + 0x61, 0x49, 0x97, 0x9a, 0xaa, 0xb6, 0x2c, 0xc9, 0x32, 0x4e, 0x1b, 0x5a, 0xcb, 0x60, 0x44, 0xbb, + 0x31, 0x38, 0xe2, 0x6c, 0x5c, 0xff, 0x4f, 0x1e, 0x8c, 0x6d, 0x37, 0x0c, 0x84, 0xae, 0x1a, 0xde, + 0xdb, 0xe6, 0xbf, 0x84, 0x15, 0xa2, 0x5e, 0xde, 0xc4, 0x93, 0x57, 0xb0, 0xaa, 0xe0, 0xf5, 0x01, + 0x3c, 0xc8, 0x74, 0x97, 0x80, 0xa3, 0x6a, 0xbf, 0x55, 0x90, 0x3e, 0xcd, 0x1c, 0xae, 0x90, 0x4b, + 0x5f, 0x50, 0x1f, 0x96, 0x19, 0xb7, 0x46, 0x34, 0x10, 0xd1, 0xc5, 0xad, 0x5f, 0xaa, 0x99, 0xff, + 0x30, 0xf6, 0xd2, 0x40, 0x7d, 0x6b, 0x45, 0xb3, 0x9d, 0xf5, 0x81, 0x1e, 0x41, 0x9e, 0x93, 0xa3, + 0xf8, 0x35, 0x92, 0x19, 0x24, 0x98, 0x1c, 0x89, 0x19, 0x17, 0x8a, 0x81, 0x7e, 0x09, 0x60, 0x3b, + 0x81, 0x4f, 0x84, 0x35, 0xa2, 0x5c, 0x1f, 0x76, 0xe6, 0x12, 0xdb, 0x09, 0x6a, 0xc6, 0x4b, 0x8a, + 0x8d, 0x9e, 0x42, 0xc9, 0x22, 0xb1, 0x5c, 0x0b, 0x57, 0x3f, 0xdf, 0xb7, 0x9b, 0xda, 0x45, 0x45, + 0xba, 0xb8, 0x38, 0x5f, 0x2f, 0xc6, 0x16, 0x5c, 0xb4, 0x88, 0x96, 0xef, 0x53, 0x58, 0x96, 0xcf, + 0xfa, 0x81, 0x1d, 0xa5, 0xb3, 0x48, 0x26, 0x57, 0xdc, 0xc2, 0xf2, 0x8d, 0xa8, 0xd3, 0x5e, 0x7c, + 0x9c, 0x65, 0x91, 0xb2, 0xa1, 0x5f, 0xc1, 0x0a, 0xf5, 0x2c, 0x3e, 0x51, 0x62, 0x8d, 0x67, 0x58, + 0xbc, 0x7a, 0xb1, 0x9d, 0x04, 0x3c, 0xb3, 0xd8, 0x0a, 0xbd, 0x64, 0xaf, 0xff, 0x23, 0x07, 0x10, + 0x15, 0x36, 0xef, 0x57, 0x80, 0x08, 0xf2, 0x36, 0x11, 0x44, 0x69, 0xae, 0x8c, 0x55, 0x1b, 0x7d, + 0x0d, 0x20, 0xe8, 0xd8, 0x97, 0xa9, 0xd7, 0x1b, 0x6a, 0xd9, 0xbc, 0x2d, 0x1d, 0xa4, 0xd0, 0x68, + 0x0b, 0x0a, 0xfa, 0xcd, 0x98, 0xbf, 0x96, 0xa7, 0x91, 0xf5, 0x3f, 0xe5, 0x00, 0xa2, 0x65, 0xfe, + 0x5f, 0xaf, 0xad, 0x65, 0xbe, 0xfe, 0x7e, 0x6d, 0xee, 0xef, 0xdf, 0xaf, 0xcd, 0xfd, 0xee, 0x62, + 0x2d, 0xf7, 0xfa, 0x62, 0x2d, 0xf7, 0xb7, 0x8b, 0xb5, 0xdc, 0x3f, 0x2f, 0xd6, 0x72, 0x87, 0x05, + 0x55, 0x7b, 0xfc, 0xf8, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x1a, 0xbd, 0x13, 0xac, 0xa9, 0x15, + 0x00, 0x00, } diff --git a/components/engine/vendor/github.com/docker/swarmkit/api/specs.proto b/components/engine/vendor/github.com/docker/swarmkit/api/specs.proto index 8955027b55..2b002c54f2 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/api/specs.proto +++ b/components/engine/vendor/github.com/docker/swarmkit/api/specs.proto @@ -6,6 +6,7 @@ import "github.com/docker/swarmkit/api/types.proto"; import "gogoproto/gogo.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/any.proto"; +import "google/protobuf/wrappers.proto"; // Specs are container objects for user provided input. All creations and // updates are done through spec types. As a convention, user input from a spec @@ -215,6 +216,9 @@ message ContainerSpec { // Privileges specifies security configuration/permissions. Privileges privileges = 22; + // Init declares that a custom init will be running inside the container, if null, use the daemon's configured settings + google.protobuf.BoolValue init = 23; + // TTY declares that a TTY should be attached to the standard streams, // including stdin if it is still open. bool tty = 13 [(gogoproto.customname) = "TTY"]; @@ -293,6 +297,23 @@ message ContainerSpec { // task will exit and a new task will be rescheduled elsewhere. A container // is considered unhealthy after `Retries` number of consecutive failures. HealthConfig healthcheck = 16; + + enum Isolation { + option (gogoproto.goproto_enum_prefix) = false; + + // ISOLATION_DEFAULT uses whatever default value from the container runtime + ISOLATION_DEFAULT = 0 [(gogoproto.enumvalue_customname) = "ContainerIsolationDefault"]; + + // ISOLATION_PROCESS forces windows container isolation + ISOLATION_PROCESS = 1 [(gogoproto.enumvalue_customname) = "ContainerIsolationProcess"]; + + // ISOLATION_HYPERV forces Hyper-V isolation + ISOLATION_HYPERV = 2 [(gogoproto.enumvalue_customname) = "ContainerIsolationHyperV"]; + } + + // Isolation defines the isolation level for windows containers (default, process, hyperv). + // Runtimes that don't support it ignore that field + Isolation isolation = 24; } // EndpointSpec defines the properties that can be configured to diff --git a/components/engine/vendor/github.com/docker/swarmkit/api/types.pb.go b/components/engine/vendor/github.com/docker/swarmkit/api/types.pb.go index 9ce04eb0b1..33e2281b67 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/api/types.pb.go +++ b/components/engine/vendor/github.com/docker/swarmkit/api/types.pb.go @@ -1085,12 +1085,17 @@ type TaskStatus struct { // because the task is prepared, we would put "already prepared" in this // field. Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` - // Err is set if the task is in an error state. + // Err is set if the task is in an error state, or is unable to + // progress from an earlier state because a precondition is + // unsatisfied. // // The following states should report a companion error: // // FAILED, REJECTED // + // In general, messages that should be surfaced to users belong in the + // Err field, and notes on routine state transitions belong in Message. + // // TODO(stevvooe) Integrate this field with the error interface. Err string `protobuf:"bytes,4,opt,name=err,proto3" json:"err,omitempty"` // Container status contains container specific status information. diff --git a/components/engine/vendor/github.com/docker/swarmkit/api/types.proto b/components/engine/vendor/github.com/docker/swarmkit/api/types.proto index 890b3cfc3f..635d12b200 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/api/types.proto +++ b/components/engine/vendor/github.com/docker/swarmkit/api/types.proto @@ -509,12 +509,17 @@ message TaskStatus { // field. string message = 3; - // Err is set if the task is in an error state. + // Err is set if the task is in an error state, or is unable to + // progress from an earlier state because a precondition is + // unsatisfied. // // The following states should report a companion error: // // FAILED, REJECTED // + // In general, messages that should be surfaced to users belong in the + // Err field, and notes on routine state transitions belong in Message. + // // TODO(stevvooe) Integrate this field with the error interface. string err = 4; diff --git a/components/engine/vendor/github.com/docker/swarmkit/manager/allocator/network.go b/components/engine/vendor/github.com/docker/swarmkit/manager/allocator/network.go index c7715128a1..ac798c954f 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/manager/allocator/network.go +++ b/components/engine/vendor/github.com/docker/swarmkit/manager/allocator/network.go @@ -1284,9 +1284,11 @@ func PredefinedNetworks() []networkallocator.PredefinedNetworkData { // updateTaskStatus sets TaskStatus and updates timestamp. func updateTaskStatus(t *api.Task, newStatus api.TaskState, message string) { - t.Status.State = newStatus - t.Status.Message = message - t.Status.Timestamp = ptypes.MustTimestampProto(time.Now()) + t.Status = api.TaskStatus{ + State: newStatus, + Message: message, + Timestamp: ptypes.MustTimestampProto(time.Now()), + } } // IsIngressNetwork returns whether the passed network is an ingress network. diff --git a/components/engine/vendor/github.com/docker/swarmkit/manager/orchestrator/constraintenforcer/constraint_enforcer.go b/components/engine/vendor/github.com/docker/swarmkit/manager/orchestrator/constraintenforcer/constraint_enforcer.go index 2978898ccb..7aa7651db7 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/manager/orchestrator/constraintenforcer/constraint_enforcer.go +++ b/components/engine/vendor/github.com/docker/swarmkit/manager/orchestrator/constraintenforcer/constraint_enforcer.go @@ -159,7 +159,8 @@ loop: // restarting the task on another node // (if applicable). t.Status.State = api.TaskStateRejected - t.Status.Message = "assigned node no longer meets constraints" + t.Status.Message = "task rejected by constraint enforcer" + t.Status.Err = "assigned node no longer meets constraints" t.Status.Timestamp = ptypes.MustTimestampProto(time.Now()) return store.UpdateTask(tx, t) }) diff --git a/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/filter.go b/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/filter.go index 36b601c4b4..3b1c73fe2d 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/filter.go +++ b/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/filter.go @@ -169,7 +169,7 @@ func (f *PluginFilter) Check(n *NodeInfo) bool { } } - if f.t.Spec.LogDriver != nil { + if f.t.Spec.LogDriver != nil && f.t.Spec.LogDriver.Name != "none" { // If there are no log driver types in the list at all, most likely this is // an older daemon that did not report this information. In this case don't filter if typeFound, exists := f.pluginExistsOnNode("Log", f.t.Spec.LogDriver.Name, nodePlugins); !exists && typeFound { diff --git a/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go b/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go index 99685959bc..6d5b4e551b 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go +++ b/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go @@ -446,7 +446,9 @@ func (s *Scheduler) applySchedulingDecisions(ctx context.Context, schedulingDeci continue } - if t.Status.State == decision.new.Status.State && t.Status.Message == decision.new.Status.Message { + if t.Status.State == decision.new.Status.State && + t.Status.Message == decision.new.Status.Message && + t.Status.Err == decision.new.Status.Err { // No changes, ignore continue } @@ -502,7 +504,7 @@ func (s *Scheduler) taskFitNode(ctx context.Context, t *api.Task, nodeID string) if !s.pipeline.Process(&nodeInfo) { // this node cannot accommodate this task newT.Status.Timestamp = ptypes.MustTimestampProto(time.Now()) - newT.Status.Message = s.pipeline.Explain() + newT.Status.Err = s.pipeline.Explain() s.allTasks[t.ID] = &newT return &newT @@ -702,9 +704,9 @@ func (s *Scheduler) noSuitableNode(ctx context.Context, taskGroup map[string]*ap newT := *t newT.Status.Timestamp = ptypes.MustTimestampProto(time.Now()) if explanation != "" { - newT.Status.Message = "no suitable node (" + explanation + ")" + newT.Status.Err = "no suitable node (" + explanation + ")" } else { - newT.Status.Message = "no suitable node" + newT.Status.Err = "no suitable node" } s.allTasks[t.ID] = &newT schedulingDecisions[t.ID] = schedulingDecision{old: t, new: &newT} diff --git a/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go b/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go index afdf2ca4eb..28c7cfa47e 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go +++ b/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go @@ -180,9 +180,12 @@ type NodeOptions struct { ClockSource clock.Clock // SendTimeout is the timeout on the sending messages to other raft // nodes. Leave this as 0 to get the default value. - SendTimeout time.Duration - TLSCredentials credentials.TransportCredentials - KeyRotator EncryptionKeyRotator + SendTimeout time.Duration + // LargeSendTimeout is the timeout on the sending snapshots to other raft + // nodes. Leave this as 0 to get the default value. + LargeSendTimeout time.Duration + TLSCredentials credentials.TransportCredentials + KeyRotator EncryptionKeyRotator // DisableStackDump prevents Run from dumping goroutine stacks when the // store becomes stuck. DisableStackDump bool @@ -204,6 +207,11 @@ func NewNode(opts NodeOptions) *Node { if opts.SendTimeout == 0 { opts.SendTimeout = 2 * time.Second } + if opts.LargeSendTimeout == 0 { + // a "slow" 100Mbps connection can send over 240MB data in 20 seconds + // which is well over the gRPC message limit of 128MB allowed by SwarmKit + opts.LargeSendTimeout = 20 * time.Second + } raftStore := raft.NewMemoryStorage() @@ -349,6 +357,7 @@ func (n *Node) initTransport() { transportConfig := &transport.Config{ HeartbeatInterval: time.Duration(n.Config.ElectionTick) * n.opts.TickInterval, SendTimeout: n.opts.SendTimeout, + LargeSendTimeout: n.opts.LargeSendTimeout, Credentials: n.opts.TLSCredentials, Raft: n, } diff --git a/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/peer.go b/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/peer.go index 55639af13f..8c7ca75458 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/peer.go +++ b/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/peer.go @@ -133,7 +133,14 @@ func (p *peer) resolveAddr(ctx context.Context, id uint64) (string, error) { } func (p *peer) sendProcessMessage(ctx context.Context, m raftpb.Message) error { - ctx, cancel := context.WithTimeout(ctx, p.tr.config.SendTimeout) + timeout := p.tr.config.SendTimeout + // if a snapshot is being sent, set timeout to LargeSendTimeout because + // sending snapshots can take more time than other messages sent between peers. + // The same applies to AppendEntries as well, where messages can get large. + if m.Type == raftpb.MsgSnap || m.Type == raftpb.MsgApp { + timeout = p.tr.config.LargeSendTimeout + } + ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() _, err := api.NewRaftClient(p.conn()).ProcessRaftMessage(ctx, &api.ProcessRaftMessageRequest{Message: &m}) if grpc.Code(err) == codes.NotFound && grpc.ErrorDesc(err) == membership.ErrMemberRemoved.Error() { diff --git a/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go b/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go index b259013d8a..bd5a04eec7 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go +++ b/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go @@ -35,6 +35,7 @@ type Raft interface { type Config struct { HeartbeatInterval time.Duration SendTimeout time.Duration + LargeSendTimeout time.Duration Credentials credentials.TransportCredentials RaftID string From b617f5a57fefe809587e59b0ec8b073412e51d7a Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Mon, 30 Oct 2017 18:39:11 -0400 Subject: [PATCH 07/94] Remove a test that was moved to docker/cli Signed-off-by: Daniel Nephin Upstream-commit: 1a03bd396b21f0c291499e1c5bd185d23e20ffc1 Component: engine --- .../docker_cli_cp_from_container_test.go | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/components/engine/integration-cli/docker_cli_cp_from_container_test.go b/components/engine/integration-cli/docker_cli_cp_from_container_test.go index 687aec8b5f..0a282f5c09 100644 --- a/components/engine/integration-cli/docker_cli_cp_from_container_test.go +++ b/components/engine/integration-cli/docker_cli_cp_from_container_test.go @@ -50,33 +50,6 @@ func (s *DockerSuite) TestCpFromErrSrcNotDir(c *check.C) { c.Assert(isCpNotDir(err), checker.True, check.Commentf("expected IsNotDir error, but got %T: %s", err, err)) } -// Test for error when SRC is a valid file or directory, -// bu the DST parent directory does not exist. -func (s *DockerSuite) TestCpFromErrDstParentNotExists(c *check.C) { - testRequires(c, DaemonIsLinux) - containerID := makeTestContainer(c, testContainerOptions{addContent: true}) - - tmpDir := getTestDir(c, "test-cp-from-err-dst-parent-not-exists") - defer os.RemoveAll(tmpDir) - - makeTestContentInDir(c, tmpDir) - - // Try with a file source. - srcPath := containerCpPath(containerID, "/file1") - dstPath := cpPath(tmpDir, "notExists", "file1") - _, dstStatErr := os.Lstat(filepath.Dir(dstPath)) - c.Assert(os.IsNotExist(dstStatErr), checker.True) - - err := runDockerCp(c, srcPath, dstPath, nil) - c.Assert(err.Error(), checker.Contains, dstStatErr.Error()) - - // Try with a directory source. - srcPath = containerCpPath(containerID, "/dir1") - - err = runDockerCp(c, srcPath, dstPath, nil) - c.Assert(err.Error(), checker.Contains, dstStatErr.Error()) -} - // Test for error when DST ends in a trailing // path separator but exists as a file. func (s *DockerSuite) TestCpFromErrDstNotDir(c *check.C) { From f64b1b1e6796ff2c628c2099bbe2a2c247608180 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 31 Oct 2017 10:32:03 +0100 Subject: [PATCH 08/94] Simplify Utsname string conversion Update golang.org/x/sys to 95c6576299259db960f6c5b9b69ea52422860fce in order to get the unix.Utsname with byte array instead of int8/uint8 members. This allows to use simple byte slice to string conversions instead of using charsToString or its open-coded version. Also see golang/go#20753 for details. Signed-off-by: Tobias Klauser Upstream-commit: 6d068bc25b35a420e63e295ea4ab4ac4a6e6665b Component: engine --- .../engine/pkg/parsers/kernel/kernel_unix.go | 12 +-- .../engine/pkg/platform/architecture_linux.go | 4 +- .../engine/pkg/platform/utsname_int8.go | 18 ----- .../engine/pkg/platform/utsname_int8_test.go | 16 ---- .../engine/pkg/platform/utsname_uint8.go | 18 ----- .../engine/pkg/platform/utsname_uint8_test.go | 16 ---- components/engine/vendor.conf | 2 +- .../vendor/golang.org/x/sys/unix/env_unix.go | 2 +- .../vendor/golang.org/x/sys/unix/env_unset.go | 2 +- .../vendor/golang.org/x/sys/unix/gccgo.go | 4 +- .../vendor/golang.org/x/sys/unix/gccgo_c.c | 2 +- .../x/sys/unix/gccgo_linux_amd64.go | 2 +- .../golang.org/x/sys/unix/pagesize_unix.go | 2 +- .../vendor/golang.org/x/sys/unix/race.go | 2 +- .../vendor/golang.org/x/sys/unix/race0.go | 2 +- .../golang.org/x/sys/unix/sockcmsg_linux.go | 2 +- .../vendor/golang.org/x/sys/unix/syscall.go | 24 +----- .../golang.org/x/sys/unix/syscall_bsd.go | 11 ++- .../golang.org/x/sys/unix/syscall_darwin.go | 3 +- .../x/sys/unix/syscall_darwin_386.go | 17 ++-- .../x/sys/unix/syscall_darwin_amd64.go | 17 ++-- .../x/sys/unix/syscall_darwin_arm.go | 17 ++-- .../x/sys/unix/syscall_darwin_arm64.go | 17 ++-- .../x/sys/unix/syscall_dragonfly.go | 2 - .../x/sys/unix/syscall_dragonfly_amd64.go | 15 +--- .../golang.org/x/sys/unix/syscall_freebsd.go | 3 +- .../x/sys/unix/syscall_freebsd_386.go | 15 +--- .../x/sys/unix/syscall_freebsd_amd64.go | 15 +--- .../x/sys/unix/syscall_freebsd_arm.go | 15 +--- .../golang.org/x/sys/unix/syscall_linux.go | 18 ++--- .../x/sys/unix/syscall_linux_386.go | 19 ++--- .../x/sys/unix/syscall_linux_amd64.go | 15 +--- .../x/sys/unix/syscall_linux_arm.go | 15 +--- .../x/sys/unix/syscall_linux_arm64.go | 15 +--- .../x/sys/unix/syscall_linux_mips64x.go | 15 +--- .../x/sys/unix/syscall_linux_mipsx.go | 15 +--- .../x/sys/unix/syscall_linux_ppc64x.go | 15 +--- .../x/sys/unix/syscall_linux_s390x.go | 15 +--- .../x/sys/unix/syscall_linux_sparc64.go | 15 +--- .../golang.org/x/sys/unix/syscall_netbsd.go | 1 - .../x/sys/unix/syscall_netbsd_386.go | 15 +--- .../x/sys/unix/syscall_netbsd_amd64.go | 15 +--- .../x/sys/unix/syscall_netbsd_arm.go | 15 +--- .../golang.org/x/sys/unix/syscall_openbsd.go | 1 - .../x/sys/unix/syscall_openbsd_386.go | 15 +--- .../x/sys/unix/syscall_openbsd_amd64.go | 15 +--- .../x/sys/unix/syscall_openbsd_arm.go | 15 +--- .../golang.org/x/sys/unix/syscall_solaris.go | 13 ++- .../x/sys/unix/syscall_solaris_amd64.go | 15 +--- .../golang.org/x/sys/unix/timestruct.go | 62 ++++++++++++++ .../x/sys/unix/zerrors_linux_386.go | 15 +++- .../x/sys/unix/zerrors_linux_amd64.go | 17 +++- .../x/sys/unix/zerrors_linux_arm.go | 15 +++- .../x/sys/unix/zerrors_linux_arm64.go | 17 +++- .../x/sys/unix/zerrors_linux_mips.go | 15 +++- .../x/sys/unix/zerrors_linux_mips64.go | 17 +++- .../x/sys/unix/zerrors_linux_mips64le.go | 17 +++- .../x/sys/unix/zerrors_linux_mipsle.go | 15 +++- .../x/sys/unix/zerrors_linux_ppc64.go | 17 +++- .../x/sys/unix/zerrors_linux_ppc64le.go | 17 +++- .../x/sys/unix/zerrors_linux_s390x.go | 17 +++- .../golang.org/x/sys/unix/zptrace386_linux.go | 80 +++++++++++++++++++ .../golang.org/x/sys/unix/zptracearm_linux.go | 41 ++++++++++ .../x/sys/unix/zptracemips_linux.go | 50 ++++++++++++ .../x/sys/unix/zptracemipsle_linux.go | 50 ++++++++++++ .../x/sys/unix/zsyscall_darwin_386.go | 11 +++ .../x/sys/unix/zsyscall_darwin_amd64.go | 11 +++ .../x/sys/unix/zsyscall_darwin_arm.go | 11 +++ .../x/sys/unix/zsyscall_darwin_arm64.go | 11 +++ .../x/sys/unix/zsyscall_dragonfly_amd64.go | 11 +++ .../x/sys/unix/zsyscall_freebsd_386.go | 11 +++ .../x/sys/unix/zsyscall_freebsd_amd64.go | 11 +++ .../x/sys/unix/zsyscall_freebsd_arm.go | 11 +++ .../x/sys/unix/zsyscall_netbsd_386.go | 11 +++ .../x/sys/unix/zsyscall_netbsd_amd64.go | 11 +++ .../x/sys/unix/zsyscall_netbsd_arm.go | 11 +++ .../x/sys/unix/zsyscall_openbsd_386.go | 11 +++ .../x/sys/unix/zsyscall_openbsd_amd64.go | 11 +++ .../x/sys/unix/zsyscall_openbsd_arm.go | 11 +++ .../x/sys/unix/zsyscall_solaris_amd64.go | 12 +++ .../x/sys/unix/ztypes_darwin_386.go | 19 +++++ .../x/sys/unix/ztypes_darwin_amd64.go | 19 +++++ .../x/sys/unix/ztypes_darwin_arm.go | 19 +++++ .../x/sys/unix/ztypes_darwin_arm64.go | 24 +++++- .../x/sys/unix/ztypes_dragonfly_amd64.go | 19 +++++ .../x/sys/unix/ztypes_freebsd_386.go | 20 +++++ .../x/sys/unix/ztypes_freebsd_amd64.go | 20 +++++ .../x/sys/unix/ztypes_freebsd_arm.go | 20 +++++ .../golang.org/x/sys/unix/ztypes_linux_386.go | 12 +-- .../x/sys/unix/ztypes_linux_amd64.go | 12 +-- .../golang.org/x/sys/unix/ztypes_linux_arm.go | 12 +-- .../x/sys/unix/ztypes_linux_arm64.go | 12 +-- .../x/sys/unix/ztypes_linux_mips.go | 12 +-- .../x/sys/unix/ztypes_linux_mips64.go | 12 +-- .../x/sys/unix/ztypes_linux_mips64le.go | 12 +-- .../x/sys/unix/ztypes_linux_mipsle.go | 12 +-- .../x/sys/unix/ztypes_linux_ppc64.go | 12 +-- .../x/sys/unix/ztypes_linux_ppc64le.go | 12 +-- .../x/sys/unix/ztypes_linux_s390x.go | 12 +-- .../x/sys/unix/ztypes_linux_sparc64.go | 14 ++-- .../x/sys/unix/ztypes_netbsd_386.go | 23 +++++- .../x/sys/unix/ztypes_netbsd_amd64.go | 23 +++++- .../x/sys/unix/ztypes_netbsd_arm.go | 23 +++++- .../x/sys/unix/ztypes_openbsd_386.go | 23 +++++- .../x/sys/unix/ztypes_openbsd_amd64.go | 23 +++++- .../x/sys/unix/ztypes_openbsd_arm.go | 23 +++++- .../x/sys/unix/ztypes_solaris_amd64.go | 29 +++++-- .../golang.org/x/sys/windows/dll_windows.go | 9 ++- .../golang.org/x/sys/windows/env_unset.go | 2 +- .../golang.org/x/sys/windows/env_windows.go | 2 +- .../x/sys/windows/memory_windows.go | 2 +- .../golang.org/x/sys/windows/mksyscall.go | 2 +- .../vendor/golang.org/x/sys/windows/race.go | 2 +- .../vendor/golang.org/x/sys/windows/race0.go | 2 +- .../x/sys/windows/security_windows.go | 2 +- .../golang.org/x/sys/windows/svc/go12.go | 2 +- .../golang.org/x/sys/windows/svc/go13.go | 2 +- .../golang.org/x/sys/windows/syscall.go | 6 +- .../x/sys/windows/syscall_windows.go | 2 +- .../golang.org/x/sys/windows/types_windows.go | 2 +- .../x/sys/windows/types_windows_386.go | 2 +- .../x/sys/windows/types_windows_amd64.go | 2 +- 122 files changed, 1163 insertions(+), 533 deletions(-) delete mode 100644 components/engine/pkg/platform/utsname_int8.go delete mode 100644 components/engine/pkg/platform/utsname_int8_test.go delete mode 100644 components/engine/pkg/platform/utsname_uint8.go delete mode 100644 components/engine/pkg/platform/utsname_uint8_test.go create mode 100644 components/engine/vendor/golang.org/x/sys/unix/timestruct.go create mode 100644 components/engine/vendor/golang.org/x/sys/unix/zptrace386_linux.go create mode 100644 components/engine/vendor/golang.org/x/sys/unix/zptracearm_linux.go create mode 100644 components/engine/vendor/golang.org/x/sys/unix/zptracemips_linux.go create mode 100644 components/engine/vendor/golang.org/x/sys/unix/zptracemipsle_linux.go diff --git a/components/engine/pkg/parsers/kernel/kernel_unix.go b/components/engine/pkg/parsers/kernel/kernel_unix.go index 76e1e499f3..46ef7a622f 100644 --- a/components/engine/pkg/parsers/kernel/kernel_unix.go +++ b/components/engine/pkg/parsers/kernel/kernel_unix.go @@ -17,18 +17,8 @@ func GetKernelVersion() (*VersionInfo, error) { return nil, err } - release := make([]byte, len(uts.Release)) - - i := 0 - for _, c := range uts.Release { - release[i] = byte(c) - i++ - } - // Remove the \x00 from the release for Atoi to parse correctly - release = release[:bytes.IndexByte(release, 0)] - - return ParseRelease(string(release)) + return ParseRelease(string(uts.Release[:bytes.IndexByte(uts.Release[:], 0)])) } // CheckKernelVersion checks if current kernel is newer than (or equal to) diff --git a/components/engine/pkg/platform/architecture_linux.go b/components/engine/pkg/platform/architecture_linux.go index 061127cd24..55c38b2748 100644 --- a/components/engine/pkg/platform/architecture_linux.go +++ b/components/engine/pkg/platform/architecture_linux.go @@ -3,6 +3,8 @@ package platform import ( + "bytes" + "golang.org/x/sys/unix" ) @@ -12,5 +14,5 @@ func runtimeArchitecture() (string, error) { if err := unix.Uname(utsname); err != nil { return "", err } - return charsToString(utsname.Machine), nil + return string(utsname.Machine[:bytes.IndexByte(utsname.Machine[:], 0)]), nil } diff --git a/components/engine/pkg/platform/utsname_int8.go b/components/engine/pkg/platform/utsname_int8.go deleted file mode 100644 index a948873cd5..0000000000 --- a/components/engine/pkg/platform/utsname_int8.go +++ /dev/null @@ -1,18 +0,0 @@ -// +build linux,386 linux,amd64 linux,arm64 s390x -// see golang's sources golang.org/x/sys/unix/ztypes_linux_*.go that use int8 - -package platform - -// Convert the OS/ARCH-specific utsname.Machine to string -// given as an array of signed int8 -func charsToString(ca [65]int8) string { - s := make([]byte, len(ca)) - var lens int - for ; lens < len(ca); lens++ { - if ca[lens] == 0 { - break - } - s[lens] = uint8(ca[lens]) - } - return string(s[0:lens]) -} diff --git a/components/engine/pkg/platform/utsname_int8_test.go b/components/engine/pkg/platform/utsname_int8_test.go deleted file mode 100644 index 7cd7208f65..0000000000 --- a/components/engine/pkg/platform/utsname_int8_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build linux,386 linux,amd64 linux,arm64 s390x - -package platform - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestCharToString(t *testing.T) { - machineInBytes := [65]int8{120, 56, 54, 95, 54, 52} - machineInString := charsToString(machineInBytes) - assert.NotNil(t, machineInString, "Unable to convert char into string.") - assert.Equal(t, string("x86_64"), machineInString, "Parsed machine code not equal.") -} diff --git a/components/engine/pkg/platform/utsname_uint8.go b/components/engine/pkg/platform/utsname_uint8.go deleted file mode 100644 index b4af7a5c8e..0000000000 --- a/components/engine/pkg/platform/utsname_uint8.go +++ /dev/null @@ -1,18 +0,0 @@ -// +build linux,arm linux,ppc64 linux,ppc64le -// see golang's sources golang.org/x/sys/unix/ztypes_linux_*.go that use uint8 - -package platform - -// Convert the OS/ARCH-specific utsname.Machine to string -// given as an array of unsigned uint8 -func charsToString(ca [65]uint8) string { - s := make([]byte, len(ca)) - var lens int - for ; lens < len(ca); lens++ { - if ca[lens] == 0 { - break - } - s[lens] = ca[lens] - } - return string(s[0:lens]) -} diff --git a/components/engine/pkg/platform/utsname_uint8_test.go b/components/engine/pkg/platform/utsname_uint8_test.go deleted file mode 100644 index 835eaa9306..0000000000 --- a/components/engine/pkg/platform/utsname_uint8_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build linux,arm linux,ppc64 linux,ppc64le - -package platform - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestTestCharToString(t *testing.T) { - machineInBytes := [65]uint8{120, 56, 54, 95, 54, 52} - machineInString := charsToString(machineInBytes) - assert.NotNil(t, machineInString, "Unable to convert char into string.") - assert.Equal(t, string("x86_64"), machineInString, "Parsed machine code not equal.") -} diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index ee52979e67..7cd66f3314 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -14,7 +14,7 @@ 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/sys 8dbc5d05d6edcc104950cc299a1ce6641235bc86 +golang.org/x/sys 95c6576299259db960f6c5b9b69ea52422860fce github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1 github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756 diff --git a/components/engine/vendor/golang.org/x/sys/unix/env_unix.go b/components/engine/vendor/golang.org/x/sys/unix/env_unix.go index 45e281a047..2e06b33f2e 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/env_unix.go +++ b/components/engine/vendor/golang.org/x/sys/unix/env_unix.go @@ -1,4 +1,4 @@ -// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2010 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. diff --git a/components/engine/vendor/golang.org/x/sys/unix/env_unset.go b/components/engine/vendor/golang.org/x/sys/unix/env_unset.go index 9222262559..c44fdc4afa 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/env_unset.go +++ b/components/engine/vendor/golang.org/x/sys/unix/env_unset.go @@ -1,4 +1,4 @@ -// Copyright 2014 The Go Authors. All rights reserved. +// 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. diff --git a/components/engine/vendor/golang.org/x/sys/unix/gccgo.go b/components/engine/vendor/golang.org/x/sys/unix/gccgo.go index 94c8232124..40bed3fa80 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/gccgo.go +++ b/components/engine/vendor/golang.org/x/sys/unix/gccgo.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Go Authors. All rights reserved. +// Copyright 2015 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. @@ -8,7 +8,7 @@ package unix import "syscall" -// We can't use the gc-syntax .s files for gccgo. On the plus side +// We can't use the gc-syntax .s files for gccgo. On the plus side // much of the functionality can be written directly in Go. //extern gccgoRealSyscall diff --git a/components/engine/vendor/golang.org/x/sys/unix/gccgo_c.c b/components/engine/vendor/golang.org/x/sys/unix/gccgo_c.c index 07f6be0392..99a774f2be 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/gccgo_c.c +++ b/components/engine/vendor/golang.org/x/sys/unix/gccgo_c.c @@ -1,4 +1,4 @@ -// Copyright 2015 The Go Authors. All rights reserved. +// Copyright 2015 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. diff --git a/components/engine/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go index bffe1a77db..251a977a81 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Go Authors. All rights reserved. +// Copyright 2015 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. diff --git a/components/engine/vendor/golang.org/x/sys/unix/pagesize_unix.go b/components/engine/vendor/golang.org/x/sys/unix/pagesize_unix.go index 45afcf72d6..83c85e0196 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/pagesize_unix.go +++ b/components/engine/vendor/golang.org/x/sys/unix/pagesize_unix.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Go Authors. All rights reserved. +// 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. diff --git a/components/engine/vendor/golang.org/x/sys/unix/race.go b/components/engine/vendor/golang.org/x/sys/unix/race.go index 3c7627eb5c..61712b51c9 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/race.go +++ b/components/engine/vendor/golang.org/x/sys/unix/race.go @@ -1,4 +1,4 @@ -// Copyright 2012 The Go Authors. All rights reserved. +// Copyright 2012 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. diff --git a/components/engine/vendor/golang.org/x/sys/unix/race0.go b/components/engine/vendor/golang.org/x/sys/unix/race0.go index f8678e0d21..dd0820431e 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/race0.go +++ b/components/engine/vendor/golang.org/x/sys/unix/race0.go @@ -1,4 +1,4 @@ -// Copyright 2012 The Go Authors. All rights reserved. +// Copyright 2012 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. diff --git a/components/engine/vendor/golang.org/x/sys/unix/sockcmsg_linux.go b/components/engine/vendor/golang.org/x/sys/unix/sockcmsg_linux.go index d9ff4731a2..6079eb4ac1 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/sockcmsg_linux.go +++ b/components/engine/vendor/golang.org/x/sys/unix/sockcmsg_linux.go @@ -1,4 +1,4 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2011 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. diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall.go b/components/engine/vendor/golang.org/x/sys/unix/syscall.go index 85e35020e2..857d2a42d4 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall.go @@ -5,10 +5,10 @@ // +build darwin dragonfly freebsd linux netbsd openbsd solaris // Package unix contains an interface to the low-level operating system -// primitives. OS details vary depending on the underlying system, and +// primitives. OS details vary depending on the underlying system, and // by default, godoc will display OS-specific documentation for the current -// system. If you want godoc to display OS documentation for another -// system, set $GOOS and $GOARCH to the desired system. For example, if +// system. If you want godoc to display OS documentation for another +// system, set $GOOS and $GOARCH to the desired system. For example, if // you want to view documentation for freebsd/arm on linux/amd64, set $GOOS // to freebsd and $GOARCH to arm. // The primary use of this package is inside other packages that provide a more @@ -49,21 +49,3 @@ func BytePtrFromString(s string) (*byte, error) { // Single-word zero for use when we need a valid pointer to 0 bytes. // See mkunix.pl. var _zero uintptr - -func (ts *Timespec) Unix() (sec int64, nsec int64) { - return int64(ts.Sec), int64(ts.Nsec) -} - -func (tv *Timeval) Unix() (sec int64, nsec int64) { - return int64(tv.Sec), int64(tv.Usec) * 1000 -} - -func (ts *Timespec) Nano() int64 { - return int64(ts.Sec)*1e9 + int64(ts.Nsec) -} - -func (tv *Timeval) Nano() int64 { - return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000 -} - -func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 } diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_bsd.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_bsd.go index c2846b32d6..4119edd730 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_bsd.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_bsd.go @@ -34,7 +34,7 @@ func Getgroups() (gids []int, err error) { return nil, nil } - // Sanity check group count. Max is 16 on BSD. + // Sanity check group count. Max is 16 on BSD. if n < 0 || n > 1000 { return nil, EINVAL } @@ -607,6 +607,15 @@ func Futimes(fd int, tv []Timeval) error { //sys fcntl(fd int, cmd int, arg int) (val int, err error) +//sys poll(fds *PollFd, nfds int, timeout int) (n int, err error) + +func Poll(fds []PollFd, timeout int) (n int, err error) { + if len(fds) == 0 { + return poll(nil, 0, timeout) + } + return poll(&fds[0], len(fds), timeout) +} + // TODO: wrap // Acct(name nil-string) (err error) // Gethostuuid(uuid *byte, timeout *Timespec) (err error) diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin.go index ad74a11fb3..f6a8fccad1 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -54,7 +54,7 @@ func nametomib(name string) (mib []_C_int, err error) { // NOTE(rsc): It seems strange to set the buffer to have // size CTL_MAXNAME+2 but use only CTL_MAXNAME - // as the size. I don't know why the +2 is here, but the + // as the size. I don't know why the +2 is here, but the // kernel uses +2 for its own implementation of this function. // I am scared that if we don't include the +2 here, the kernel // will silently write 2 words farther than we specify @@ -377,7 +377,6 @@ func IoctlGetTermios(fd int, req uint) (*Termios, error) { // Searchfs // Delete // Copyfile -// Poll // Watchevent // Waitevent // Modwatch diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_386.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_386.go index 76634f7ab1..b3ac109a2f 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_386.go @@ -11,25 +11,18 @@ import ( "unsafe" ) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int32(nsec / 1e9) - ts.Nsec = int32(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: int32(sec), Nsec: int32(nsec)} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = int32(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: int32(sec), Usec: int32(usec)} } //sysnb gettimeofday(tp *Timeval) (sec int32, usec int32, err error) func Gettimeofday(tv *Timeval) (err error) { // The tv passed to gettimeofday must be non-nil - // but is otherwise unused. The answers come back + // but is otherwise unused. The answers come back // in the two registers. sec, usec, err := gettimeofday(tv) tv.Sec = int32(sec) diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go index 7be02dab9d..75219444a8 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go @@ -11,25 +11,18 @@ import ( "unsafe" ) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = int64(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: int32(usec)} } //sysnb gettimeofday(tp *Timeval) (sec int64, usec int32, err error) func Gettimeofday(tv *Timeval) (err error) { // The tv passed to gettimeofday must be non-nil - // but is otherwise unused. The answers come back + // but is otherwise unused. The answers come back // in the two registers. sec, usec, err := gettimeofday(tv) tv.Sec = sec diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_arm.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_arm.go index 26b66972f0..47ab664859 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_arm.go @@ -9,25 +9,18 @@ import ( "unsafe" ) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int32(nsec / 1e9) - ts.Nsec = int32(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: int32(sec), Nsec: int32(nsec)} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = int32(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: int32(sec), Usec: int32(usec)} } //sysnb gettimeofday(tp *Timeval) (sec int32, usec int32, err error) func Gettimeofday(tv *Timeval) (err error) { // The tv passed to gettimeofday must be non-nil - // but is otherwise unused. The answers come back + // but is otherwise unused. The answers come back // in the two registers. sec, usec, err := gettimeofday(tv) tv.Sec = int32(sec) diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go index 4d67a87427..d6d9628014 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go @@ -11,25 +11,18 @@ import ( "unsafe" ) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = int64(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: int32(usec)} } //sysnb gettimeofday(tp *Timeval) (sec int64, usec int32, err error) func Gettimeofday(tv *Timeval) (err error) { // The tv passed to gettimeofday must be non-nil - // but is otherwise unused. The answers come back + // but is otherwise unused. The answers come back // in the two registers. sec, usec, err := gettimeofday(tv) tv.Sec = sec diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_dragonfly.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_dragonfly.go index 3a483373dc..fee06839fd 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_dragonfly.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_dragonfly.go @@ -257,7 +257,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { // Searchfs // Delete // Copyfile -// Poll // Watchevent // Waitevent // Modwatch @@ -403,7 +402,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { // Pread_nocancel // Pwrite_nocancel // Waitid_nocancel -// Poll_nocancel // Msgsnd_nocancel // Msgrcv_nocancel // Sem_wait_nocancel diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go index 6d8952d5a1..9babb31ea7 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go @@ -11,19 +11,12 @@ import ( "unsafe" ) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = nsec % 1e9 / 1e3 - tv.Sec = int64(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd.go index d26e52eaef..8f7ab16d1f 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd.go @@ -32,7 +32,7 @@ func nametomib(name string) (mib []_C_int, err error) { // NOTE(rsc): It seems strange to set the buffer to have // size CTL_MAXNAME+2 but use only CTL_MAXNAME - // as the size. I don't know why the +2 is here, but the + // as the size. I don't know why the +2 is here, but the // kernel uses +2 for its own implementation of this function. // I am scared that if we don't include the +2 here, the kernel // will silently write 2 words farther than we specify @@ -550,7 +550,6 @@ func IoctlGetTermios(fd int, req uint) (*Termios, error) { // Searchfs // Delete // Copyfile -// Poll // Watchevent // Waitevent // Modwatch diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go index 4cf5f453f5..21e03958cd 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go @@ -11,19 +11,12 @@ import ( "unsafe" ) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int32(nsec / 1e9) - ts.Nsec = int32(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: int32(sec), Nsec: int32(nsec)} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = int32(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: int32(sec), Usec: int32(usec)} } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go index b8036e7268..9c945a6579 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go @@ -11,19 +11,12 @@ import ( "unsafe" ) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = nsec % 1e9 / 1e3 - tv.Sec = int64(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go index 5a3bb6a154..5cd6243f2a 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go @@ -11,19 +11,12 @@ import ( "unsafe" ) -func TimespecToNsec(ts Timespec) int64 { return ts.Sec*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = int32(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: int32(nsec)} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = nsec / 1e9 - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: int32(usec)} } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux.go index 4520328abf..b98a7e1544 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -255,7 +255,7 @@ func Getgroups() (gids []int, err error) { return nil, nil } - // Sanity check group count. Max is 1<<16 on Linux. + // Sanity check group count. Max is 1<<16 on Linux. if n < 0 || n > 1<<20 { return nil, EINVAL } @@ -290,8 +290,8 @@ type WaitStatus uint32 // 0x7F (stopped), or a signal number that caused an exit. // The 0x80 bit is whether there was a core dump. // An extra number (exit code, signal causing a stop) -// is in the high bits. At least that's the idea. -// There are various irregularities. For example, the +// is in the high bits. At least that's the idea. +// There are various irregularities. For example, the // "continued" status is 0xFFFF, distinguishing itself // from stopped via the core dump bit. @@ -926,7 +926,7 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from msg.Namelen = uint32(SizeofSockaddrAny) var iov Iovec if len(p) > 0 { - iov.Base = (*byte)(unsafe.Pointer(&p[0])) + iov.Base = &p[0] iov.SetLen(len(p)) } var dummy byte @@ -941,7 +941,7 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from iov.Base = &dummy iov.SetLen(1) } - msg.Control = (*byte)(unsafe.Pointer(&oob[0])) + msg.Control = &oob[0] msg.SetControllen(len(oob)) } msg.Iov = &iov @@ -974,11 +974,11 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) } } var msg Msghdr - msg.Name = (*byte)(unsafe.Pointer(ptr)) + msg.Name = (*byte)(ptr) msg.Namelen = uint32(salen) var iov Iovec if len(p) > 0 { - iov.Base = (*byte)(unsafe.Pointer(&p[0])) + iov.Base = &p[0] iov.SetLen(len(p)) } var dummy byte @@ -993,7 +993,7 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) iov.Base = &dummy iov.SetLen(1) } - msg.Control = (*byte)(unsafe.Pointer(&oob[0])) + msg.Control = &oob[0] msg.SetControllen(len(oob)) } msg.Iov = &iov @@ -1023,7 +1023,7 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err erro var buf [sizeofPtr]byte - // Leading edge. PEEKTEXT/PEEKDATA don't require aligned + // Leading edge. PEEKTEXT/PEEKDATA don't require aligned // access (PEEKUSER warns that it might), but if we don't // align our reads, we might straddle an unmapped page // boundary and not get the bytes leading up to the page diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_386.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_386.go index f4c826a456..4774fa363e 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_386.go @@ -14,19 +14,12 @@ import ( "unsafe" ) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int32(nsec / 1e9) - ts.Nsec = int32(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: int32(sec), Nsec: int32(nsec)} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Sec = int32(nsec / 1e9) - tv.Usec = int32(nsec % 1e9 / 1e3) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: int32(sec), Usec: int32(usec)} } //sysnb pipe(p *[2]_C_int) (err error) @@ -183,9 +176,9 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { // On x86 Linux, all the socket calls go through an extra indirection, // I think because the 5-register system call interface can't handle -// the 6-argument calls like sendto and recvfrom. Instead the +// the 6-argument calls like sendto and recvfrom. Instead the // arguments to the underlying system call are the number below -// and a pointer to an array of uintptr. We hide the pointer in the +// and a pointer to an array of uintptr. We hide the pointer in the // socketcall assembly to avoid allocation on every system call. const ( diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go index 0715200dcf..3707f6b7c9 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go @@ -83,19 +83,12 @@ func Time(t *Time_t) (tt Time_t, err error) { //sys Utime(path string, buf *Utimbuf) (err error) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Sec = nsec / 1e9 - tv.Usec = nsec % 1e9 / 1e3 - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} } //sysnb pipe(p *[2]_C_int) (err error) diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_arm.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_arm.go index 2b79c84a67..226be100f5 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_arm.go @@ -11,19 +11,12 @@ import ( "unsafe" ) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int32(nsec / 1e9) - ts.Nsec = int32(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: int32(sec), Nsec: int32(nsec)} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Sec = int32(nsec / 1e9) - tv.Usec = int32(nsec % 1e9 / 1e3) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: int32(sec), Usec: int32(usec)} } func Pipe(p []int) (err error) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go index e16a0d141e..9a8e6e4117 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go @@ -73,19 +73,12 @@ func Lstat(path string, stat *Stat_t) (err error) { //sysnb Gettimeofday(tv *Timeval) (err error) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Sec = nsec / 1e9 - tv.Usec = nsec % 1e9 / 1e3 - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} } func Time(t *Time_t) (Time_t, error) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go index 92e620ea5b..cdda11a9fa 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go @@ -76,19 +76,12 @@ func Time(t *Time_t) (tt Time_t, err error) { //sys Utime(path string, buf *Utimbuf) (err error) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Sec = nsec / 1e9 - tv.Usec = nsec % 1e9 / 1e3 - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} } func Pipe(p []int) (err error) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go index 25a5a0da5a..a114ba8cb3 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go @@ -99,19 +99,12 @@ func Seek(fd int, offset int64, whence int) (off int64, err error) { return } -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int32(nsec / 1e9) - ts.Nsec = int32(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: int32(sec), Nsec: int32(nsec)} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Sec = int32(nsec / 1e9) - tv.Usec = int32(nsec % 1e9 / 1e3) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: int32(sec), Usec: int32(usec)} } //sysnb pipe2(p *[2]_C_int, flags int) (err error) diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go index a4a8e4ee1e..7cae936c45 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go @@ -66,19 +66,12 @@ package unix //sys Utime(path string, buf *Utimbuf) (err error) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Sec = nsec / 1e9 - tv.Usec = nsec % 1e9 / 1e3 - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} } func (r *PtraceRegs) PC() uint64 { return r.Nip } diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go index 3845fc9c43..e96a40cb21 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go @@ -62,19 +62,12 @@ func Time(t *Time_t) (tt Time_t, err error) { //sys Utime(path string, buf *Utimbuf) (err error) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Sec = nsec / 1e9 - tv.Usec = nsec % 1e9 / 1e3 - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} } //sysnb pipe2(p *[2]_C_int, flags int) (err error) diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go index bd9de3e9d0..012a3285ef 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go @@ -82,19 +82,12 @@ func Time(t *Time_t) (tt Time_t, err error) { //sys Utime(path string, buf *Utimbuf) (err error) -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Sec = nsec / 1e9 - tv.Usec = int32(nsec % 1e9 / 1e3) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: int32(usec)} } func (r *PtraceRegs) PC() uint64 { return r.Tpc } diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd.go index e129668459..1caa5b3266 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd.go @@ -422,7 +422,6 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e // ntp_adjtime // pmc_control // pmc_get_info -// poll // pollts // preadv // profil diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go index baefa411ec..24f74e58ce 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go @@ -6,19 +6,12 @@ package unix -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int64(nsec / 1e9) - ts.Nsec = int32(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: int32(nsec)} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = int64(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: int32(usec)} } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go index 59c2ab7eba..6878bf7ff9 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go @@ -6,19 +6,12 @@ package unix -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int64(nsec / 1e9) - ts.Nsec = int64(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = int64(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: int32(usec)} } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go index 7208108a31..dbbfcf71db 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go @@ -6,19 +6,12 @@ package unix -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int64(nsec / 1e9) - ts.Nsec = int32(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: int32(nsec)} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = int64(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: int32(usec)} } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd.go index 408e63081c..03a0fac61d 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd.go @@ -243,7 +243,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { // nfssvc // nnpfspioctl // openat -// poll // preadv // profil // pwritev diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go index d3809b426c..994964a916 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go @@ -6,19 +6,12 @@ package unix -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int64(nsec / 1e9) - ts.Nsec = int32(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: int32(nsec)} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = int64(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: int32(usec)} } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go index 9a9dfceffd..649e67fccc 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go @@ -6,19 +6,12 @@ package unix -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = nsec % 1e9 / 1e3 - tv.Sec = nsec / 1e9 - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go index ba8649056f..59844f5041 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go @@ -6,19 +6,12 @@ package unix -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int64(nsec / 1e9) - ts.Nsec = int32(nsec % 1e9) - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: int32(nsec)} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = int64(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: int32(usec)} } func SetKevent(k *Kevent_t, fd, mode, flags int) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_solaris.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_solaris.go index 35e5d72baf..3ab9e07c8c 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_solaris.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_solaris.go @@ -166,7 +166,7 @@ func Getwd() (wd string, err error) { func Getgroups() (gids []int, err error) { n, err := getgroups(0, nil) - // Check for error and sanity check group count. Newer versions of + // Check for error and sanity check group count. Newer versions of // Solaris allow up to 1024 (NGROUPS_MAX). if n < 0 || n > 1024 { if err != nil { @@ -350,7 +350,7 @@ func Futimesat(dirfd int, path string, tv []Timeval) error { } // Solaris doesn't have an futimes function because it allows NULL to be -// specified as the path for futimesat. However, Go doesn't like +// specified as the path for futimesat. However, Go doesn't like // NULL-style string interfaces, so this simple wrapper is provided. func Futimes(fd int, tv []Timeval) error { if tv == nil { @@ -578,6 +578,15 @@ func IoctlGetTermio(fd int, req uint) (*Termio, error) { return &value, err } +//sys poll(fds *PollFd, nfds int, timeout int) (n int, err error) + +func Poll(fds []PollFd, timeout int) (n int, err error) { + if len(fds) == 0 { + return poll(nil, 0, timeout) + } + return poll(&fds[0], len(fds), timeout) +} + /* * Exposed directly */ diff --git a/components/engine/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go index 5aff62c3bb..9d4e7a678f 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go @@ -6,19 +6,12 @@ package unix -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } - -func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = nsec / 1e9 - ts.Nsec = nsec % 1e9 - return +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} } -func NsecToTimeval(nsec int64) (tv Timeval) { - nsec += 999 // round up to microsecond - tv.Usec = nsec % 1e9 / 1e3 - tv.Sec = int64(nsec / 1e9) - return +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} } func (iov *Iovec) SetLen(length int) { diff --git a/components/engine/vendor/golang.org/x/sys/unix/timestruct.go b/components/engine/vendor/golang.org/x/sys/unix/timestruct.go new file mode 100644 index 0000000000..139fbbebbb --- /dev/null +++ b/components/engine/vendor/golang.org/x/sys/unix/timestruct.go @@ -0,0 +1,62 @@ +// 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 darwin dragonfly freebsd linux netbsd openbsd solaris + +package unix + +// TimespecToNsec converts a Timespec value into a number of +// nanoseconds since the Unix epoch. +func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } + +// NsecToTimespec takes a number of nanoseconds since the Unix epoch +// and returns the corresponding Timespec value. +func NsecToTimespec(nsec int64) Timespec { + sec := nsec / 1e9 + nsec = nsec % 1e9 + if nsec < 0 { + nsec += 1e9 + sec-- + } + return setTimespec(sec, nsec) +} + +// TimevalToNsec converts a Timeval value into a number of nanoseconds +// since the Unix epoch. +func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 } + +// NsecToTimeval takes a number of nanoseconds since the Unix epoch +// and returns the corresponding Timeval value. +func NsecToTimeval(nsec int64) Timeval { + nsec += 999 // round up to microsecond + usec := nsec % 1e9 / 1e3 + sec := nsec / 1e9 + if usec < 0 { + usec += 1e6 + sec-- + } + return setTimeval(sec, usec) +} + +// Unix returns ts as the number of seconds and nanoseconds elapsed since the +// Unix epoch. +func (ts *Timespec) Unix() (sec int64, nsec int64) { + return int64(ts.Sec), int64(ts.Nsec) +} + +// Unix returns tv as the number of seconds and nanoseconds elapsed since the +// Unix epoch. +func (tv *Timeval) Unix() (sec int64, nsec int64) { + return int64(tv.Sec), int64(tv.Usec) * 1000 +} + +// Nano returns ts as the number of nanoseconds elapsed since the Unix epoch. +func (ts *Timespec) Nano() int64 { + return int64(ts.Sec)*1e9 + int64(ts.Nsec) +} + +// Nano returns tv as the number of nanoseconds elapsed since the Unix epoch. +func (tv *Timeval) Nano() int64 { + return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000 +} diff --git a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 4066ad1e0f..bb8a7724bc 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -1277,7 +1277,7 @@ const ( RLIMIT_RTTIME = 0xf RLIMIT_SIGPENDING = 0xb RLIMIT_STACK = 0x3 - RLIM_INFINITY = -0x1 + RLIM_INFINITY = 0xffffffffffffffff RTAX_ADVMSS = 0x8 RTAX_CC_ALGO = 0x10 RTAX_CWND = 0x7 @@ -1842,6 +1842,8 @@ const ( TUNSETVNETHDRSZ = 0x400454d8 TUNSETVNETLE = 0x400454dc UMOUNT_NOFOLLOW = 0x8 + UTIME_NOW = 0x3fffffff + UTIME_OMIT = 0x3ffffffe VDISCARD = 0xd VEOF = 0x4 VEOL = 0xb @@ -1871,6 +1873,17 @@ const ( WALL = 0x40000000 WCLONE = 0x80000000 WCONTINUED = 0x8 + WDIOC_GETBOOTSTATUS = 0x80045702 + WDIOC_GETPRETIMEOUT = 0x80045709 + WDIOC_GETSTATUS = 0x80045701 + WDIOC_GETSUPPORT = 0x80285700 + WDIOC_GETTEMP = 0x80045703 + WDIOC_GETTIMELEFT = 0x8004570a + WDIOC_GETTIMEOUT = 0x80045707 + WDIOC_KEEPALIVE = 0x80045705 + WDIOC_SETOPTIONS = 0x80045704 + WDIOC_SETPRETIMEOUT = 0xc0045708 + WDIOC_SETTIMEOUT = 0xc0045706 WEXITED = 0x4 WNOHANG = 0x1 WNOTHREAD = 0x20000000 diff --git a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index c9f53b0b37..cf0b2249f7 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -1187,7 +1187,7 @@ const ( PR_SET_NO_NEW_PRIVS = 0x26 PR_SET_PDEATHSIG = 0x1 PR_SET_PTRACER = 0x59616d61 - PR_SET_PTRACER_ANY = -0x1 + PR_SET_PTRACER_ANY = 0xffffffffffffffff PR_SET_SECCOMP = 0x16 PR_SET_SECUREBITS = 0x1c PR_SET_THP_DISABLE = 0x29 @@ -1278,7 +1278,7 @@ const ( RLIMIT_RTTIME = 0xf RLIMIT_SIGPENDING = 0xb RLIMIT_STACK = 0x3 - RLIM_INFINITY = -0x1 + RLIM_INFINITY = 0xffffffffffffffff RTAX_ADVMSS = 0x8 RTAX_CC_ALGO = 0x10 RTAX_CWND = 0x7 @@ -1843,6 +1843,8 @@ const ( TUNSETVNETHDRSZ = 0x400454d8 TUNSETVNETLE = 0x400454dc UMOUNT_NOFOLLOW = 0x8 + UTIME_NOW = 0x3fffffff + UTIME_OMIT = 0x3ffffffe VDISCARD = 0xd VEOF = 0x4 VEOL = 0xb @@ -1872,6 +1874,17 @@ const ( WALL = 0x40000000 WCLONE = 0x80000000 WCONTINUED = 0x8 + WDIOC_GETBOOTSTATUS = 0x80045702 + WDIOC_GETPRETIMEOUT = 0x80045709 + WDIOC_GETSTATUS = 0x80045701 + WDIOC_GETSUPPORT = 0x80285700 + WDIOC_GETTEMP = 0x80045703 + WDIOC_GETTIMELEFT = 0x8004570a + WDIOC_GETTIMEOUT = 0x80045707 + WDIOC_KEEPALIVE = 0x80045705 + WDIOC_SETOPTIONS = 0x80045704 + WDIOC_SETPRETIMEOUT = 0xc0045708 + WDIOC_SETTIMEOUT = 0xc0045706 WEXITED = 0x4 WNOHANG = 0x1 WNOTHREAD = 0x20000000 diff --git a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index 3e8c2c7aa6..57cfcf3fe0 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -1282,7 +1282,7 @@ const ( RLIMIT_RTTIME = 0xf RLIMIT_SIGPENDING = 0xb RLIMIT_STACK = 0x3 - RLIM_INFINITY = -0x1 + RLIM_INFINITY = 0xffffffffffffffff RTAX_ADVMSS = 0x8 RTAX_CC_ALGO = 0x10 RTAX_CWND = 0x7 @@ -1847,6 +1847,8 @@ const ( TUNSETVNETHDRSZ = 0x400454d8 TUNSETVNETLE = 0x400454dc UMOUNT_NOFOLLOW = 0x8 + UTIME_NOW = 0x3fffffff + UTIME_OMIT = 0x3ffffffe VDISCARD = 0xd VEOF = 0x4 VEOL = 0xb @@ -1876,6 +1878,17 @@ const ( WALL = 0x40000000 WCLONE = 0x80000000 WCONTINUED = 0x8 + WDIOC_GETBOOTSTATUS = 0x80045702 + WDIOC_GETPRETIMEOUT = 0x80045709 + WDIOC_GETSTATUS = 0x80045701 + WDIOC_GETSUPPORT = 0x80285700 + WDIOC_GETTEMP = 0x80045703 + WDIOC_GETTIMELEFT = 0x8004570a + WDIOC_GETTIMEOUT = 0x80045707 + WDIOC_KEEPALIVE = 0x80045705 + WDIOC_SETOPTIONS = 0x80045704 + WDIOC_SETPRETIMEOUT = 0xc0045708 + WDIOC_SETTIMEOUT = 0xc0045706 WEXITED = 0x4 WNOHANG = 0x1 WNOTHREAD = 0x20000000 diff --git a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index 383453349f..b6e5b090ec 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -1188,7 +1188,7 @@ const ( PR_SET_NO_NEW_PRIVS = 0x26 PR_SET_PDEATHSIG = 0x1 PR_SET_PTRACER = 0x59616d61 - PR_SET_PTRACER_ANY = -0x1 + PR_SET_PTRACER_ANY = 0xffffffffffffffff PR_SET_SECCOMP = 0x16 PR_SET_SECUREBITS = 0x1c PR_SET_THP_DISABLE = 0x29 @@ -1268,7 +1268,7 @@ const ( RLIMIT_RTTIME = 0xf RLIMIT_SIGPENDING = 0xb RLIMIT_STACK = 0x3 - RLIM_INFINITY = -0x1 + RLIM_INFINITY = 0xffffffffffffffff RTAX_ADVMSS = 0x8 RTAX_CC_ALGO = 0x10 RTAX_CWND = 0x7 @@ -1833,6 +1833,8 @@ const ( TUNSETVNETHDRSZ = 0x400454d8 TUNSETVNETLE = 0x400454dc UMOUNT_NOFOLLOW = 0x8 + UTIME_NOW = 0x3fffffff + UTIME_OMIT = 0x3ffffffe VDISCARD = 0xd VEOF = 0x4 VEOL = 0xb @@ -1862,6 +1864,17 @@ const ( WALL = 0x40000000 WCLONE = 0x80000000 WCONTINUED = 0x8 + WDIOC_GETBOOTSTATUS = 0x80045702 + WDIOC_GETPRETIMEOUT = 0x80045709 + WDIOC_GETSTATUS = 0x80045701 + WDIOC_GETSUPPORT = 0x80285700 + WDIOC_GETTEMP = 0x80045703 + WDIOC_GETTIMELEFT = 0x8004570a + WDIOC_GETTIMEOUT = 0x80045707 + WDIOC_KEEPALIVE = 0x80045705 + WDIOC_SETOPTIONS = 0x80045704 + WDIOC_SETPRETIMEOUT = 0xc0045708 + WDIOC_SETTIMEOUT = 0xc0045706 WEXITED = 0x4 WNOHANG = 0x1 WNOTHREAD = 0x20000000 diff --git a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index bde8f7d023..0113e1f6ab 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -1279,7 +1279,7 @@ const ( RLIMIT_RTTIME = 0xf RLIMIT_SIGPENDING = 0xb RLIMIT_STACK = 0x3 - RLIM_INFINITY = -0x1 + RLIM_INFINITY = 0xffffffffffffffff RTAX_ADVMSS = 0x8 RTAX_CC_ALGO = 0x10 RTAX_CWND = 0x7 @@ -1846,6 +1846,8 @@ const ( TUNSETVNETHDRSZ = 0x800454d8 TUNSETVNETLE = 0x800454dc UMOUNT_NOFOLLOW = 0x8 + UTIME_NOW = 0x3fffffff + UTIME_OMIT = 0x3ffffffe VDISCARD = 0xd VEOF = 0x10 VEOL = 0x11 @@ -1876,6 +1878,17 @@ const ( WALL = 0x40000000 WCLONE = 0x80000000 WCONTINUED = 0x8 + WDIOC_GETBOOTSTATUS = 0x40045702 + WDIOC_GETPRETIMEOUT = 0x40045709 + WDIOC_GETSTATUS = 0x40045701 + WDIOC_GETSUPPORT = 0x40285700 + WDIOC_GETTEMP = 0x40045703 + WDIOC_GETTIMELEFT = 0x4004570a + WDIOC_GETTIMEOUT = 0x40045707 + WDIOC_KEEPALIVE = 0x40045705 + WDIOC_SETOPTIONS = 0x40045704 + WDIOC_SETPRETIMEOUT = 0xc0045708 + WDIOC_SETTIMEOUT = 0xc0045706 WEXITED = 0x4 WNOHANG = 0x1 WNOTHREAD = 0x20000000 diff --git a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index 42b6397d5d..6857657a50 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -1187,7 +1187,7 @@ const ( PR_SET_NO_NEW_PRIVS = 0x26 PR_SET_PDEATHSIG = 0x1 PR_SET_PTRACER = 0x59616d61 - PR_SET_PTRACER_ANY = -0x1 + PR_SET_PTRACER_ANY = 0xffffffffffffffff PR_SET_SECCOMP = 0x16 PR_SET_SECUREBITS = 0x1c PR_SET_THP_DISABLE = 0x29 @@ -1279,7 +1279,7 @@ const ( RLIMIT_RTTIME = 0xf RLIMIT_SIGPENDING = 0xb RLIMIT_STACK = 0x3 - RLIM_INFINITY = -0x1 + RLIM_INFINITY = 0xffffffffffffffff RTAX_ADVMSS = 0x8 RTAX_CC_ALGO = 0x10 RTAX_CWND = 0x7 @@ -1846,6 +1846,8 @@ const ( TUNSETVNETHDRSZ = 0x800454d8 TUNSETVNETLE = 0x800454dc UMOUNT_NOFOLLOW = 0x8 + UTIME_NOW = 0x3fffffff + UTIME_OMIT = 0x3ffffffe VDISCARD = 0xd VEOF = 0x10 VEOL = 0x11 @@ -1876,6 +1878,17 @@ const ( WALL = 0x40000000 WCLONE = 0x80000000 WCONTINUED = 0x8 + WDIOC_GETBOOTSTATUS = 0x40045702 + WDIOC_GETPRETIMEOUT = 0x40045709 + WDIOC_GETSTATUS = 0x40045701 + WDIOC_GETSUPPORT = 0x40285700 + WDIOC_GETTEMP = 0x40045703 + WDIOC_GETTIMELEFT = 0x4004570a + WDIOC_GETTIMEOUT = 0x40045707 + WDIOC_KEEPALIVE = 0x40045705 + WDIOC_SETOPTIONS = 0x40045704 + WDIOC_SETPRETIMEOUT = 0xc0045708 + WDIOC_SETTIMEOUT = 0xc0045706 WEXITED = 0x4 WNOHANG = 0x1 WNOTHREAD = 0x20000000 diff --git a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index bd4ff81474..14f7e0e056 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -1187,7 +1187,7 @@ const ( PR_SET_NO_NEW_PRIVS = 0x26 PR_SET_PDEATHSIG = 0x1 PR_SET_PTRACER = 0x59616d61 - PR_SET_PTRACER_ANY = -0x1 + PR_SET_PTRACER_ANY = 0xffffffffffffffff PR_SET_SECCOMP = 0x16 PR_SET_SECUREBITS = 0x1c PR_SET_THP_DISABLE = 0x29 @@ -1279,7 +1279,7 @@ const ( RLIMIT_RTTIME = 0xf RLIMIT_SIGPENDING = 0xb RLIMIT_STACK = 0x3 - RLIM_INFINITY = -0x1 + RLIM_INFINITY = 0xffffffffffffffff RTAX_ADVMSS = 0x8 RTAX_CC_ALGO = 0x10 RTAX_CWND = 0x7 @@ -1846,6 +1846,8 @@ const ( TUNSETVNETHDRSZ = 0x800454d8 TUNSETVNETLE = 0x800454dc UMOUNT_NOFOLLOW = 0x8 + UTIME_NOW = 0x3fffffff + UTIME_OMIT = 0x3ffffffe VDISCARD = 0xd VEOF = 0x10 VEOL = 0x11 @@ -1876,6 +1878,17 @@ const ( WALL = 0x40000000 WCLONE = 0x80000000 WCONTINUED = 0x8 + WDIOC_GETBOOTSTATUS = 0x40045702 + WDIOC_GETPRETIMEOUT = 0x40045709 + WDIOC_GETSTATUS = 0x40045701 + WDIOC_GETSUPPORT = 0x40285700 + WDIOC_GETTEMP = 0x40045703 + WDIOC_GETTIMELEFT = 0x4004570a + WDIOC_GETTIMEOUT = 0x40045707 + WDIOC_KEEPALIVE = 0x40045705 + WDIOC_SETOPTIONS = 0x40045704 + WDIOC_SETPRETIMEOUT = 0xc0045708 + WDIOC_SETTIMEOUT = 0xc0045706 WEXITED = 0x4 WNOHANG = 0x1 WNOTHREAD = 0x20000000 diff --git a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index 6dfc95c40f..f795862d87 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -1279,7 +1279,7 @@ const ( RLIMIT_RTTIME = 0xf RLIMIT_SIGPENDING = 0xb RLIMIT_STACK = 0x3 - RLIM_INFINITY = -0x1 + RLIM_INFINITY = 0xffffffffffffffff RTAX_ADVMSS = 0x8 RTAX_CC_ALGO = 0x10 RTAX_CWND = 0x7 @@ -1846,6 +1846,8 @@ const ( TUNSETVNETHDRSZ = 0x800454d8 TUNSETVNETLE = 0x800454dc UMOUNT_NOFOLLOW = 0x8 + UTIME_NOW = 0x3fffffff + UTIME_OMIT = 0x3ffffffe VDISCARD = 0xd VEOF = 0x10 VEOL = 0x11 @@ -1876,6 +1878,17 @@ const ( WALL = 0x40000000 WCLONE = 0x80000000 WCONTINUED = 0x8 + WDIOC_GETBOOTSTATUS = 0x40045702 + WDIOC_GETPRETIMEOUT = 0x40045709 + WDIOC_GETSTATUS = 0x40045701 + WDIOC_GETSUPPORT = 0x40285700 + WDIOC_GETTEMP = 0x40045703 + WDIOC_GETTIMELEFT = 0x4004570a + WDIOC_GETTIMEOUT = 0x40045707 + WDIOC_KEEPALIVE = 0x40045705 + WDIOC_SETOPTIONS = 0x40045704 + WDIOC_SETPRETIMEOUT = 0xc0045708 + WDIOC_SETTIMEOUT = 0xc0045706 WEXITED = 0x4 WNOHANG = 0x1 WNOTHREAD = 0x20000000 diff --git a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index 46b09d320d..2544c4b632 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -1189,7 +1189,7 @@ const ( PR_SET_NO_NEW_PRIVS = 0x26 PR_SET_PDEATHSIG = 0x1 PR_SET_PTRACER = 0x59616d61 - PR_SET_PTRACER_ANY = -0x1 + PR_SET_PTRACER_ANY = 0xffffffffffffffff PR_SET_SECCOMP = 0x16 PR_SET_SECUREBITS = 0x1c PR_SET_THP_DISABLE = 0x29 @@ -1335,7 +1335,7 @@ const ( RLIMIT_RTTIME = 0xf RLIMIT_SIGPENDING = 0xb RLIMIT_STACK = 0x3 - RLIM_INFINITY = -0x1 + RLIM_INFINITY = 0xffffffffffffffff RTAX_ADVMSS = 0x8 RTAX_CC_ALGO = 0x10 RTAX_CWND = 0x7 @@ -1904,6 +1904,8 @@ const ( TUNSETVNETHDRSZ = 0x800454d8 TUNSETVNETLE = 0x800454dc UMOUNT_NOFOLLOW = 0x8 + UTIME_NOW = 0x3fffffff + UTIME_OMIT = 0x3ffffffe VDISCARD = 0x10 VEOF = 0x4 VEOL = 0x6 @@ -1933,6 +1935,17 @@ const ( WALL = 0x40000000 WCLONE = 0x80000000 WCONTINUED = 0x8 + WDIOC_GETBOOTSTATUS = 0x40045702 + WDIOC_GETPRETIMEOUT = 0x40045709 + WDIOC_GETSTATUS = 0x40045701 + WDIOC_GETSUPPORT = 0x40285700 + WDIOC_GETTEMP = 0x40045703 + WDIOC_GETTIMELEFT = 0x4004570a + WDIOC_GETTIMEOUT = 0x40045707 + WDIOC_KEEPALIVE = 0x40045705 + WDIOC_SETOPTIONS = 0x40045704 + WDIOC_SETPRETIMEOUT = 0xc0045708 + WDIOC_SETTIMEOUT = 0xc0045706 WEXITED = 0x4 WNOHANG = 0x1 WNOTHREAD = 0x20000000 diff --git a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index 08adb1d8fc..133bdf5847 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -1189,7 +1189,7 @@ const ( PR_SET_NO_NEW_PRIVS = 0x26 PR_SET_PDEATHSIG = 0x1 PR_SET_PTRACER = 0x59616d61 - PR_SET_PTRACER_ANY = -0x1 + PR_SET_PTRACER_ANY = 0xffffffffffffffff PR_SET_SECCOMP = 0x16 PR_SET_SECUREBITS = 0x1c PR_SET_THP_DISABLE = 0x29 @@ -1335,7 +1335,7 @@ const ( RLIMIT_RTTIME = 0xf RLIMIT_SIGPENDING = 0xb RLIMIT_STACK = 0x3 - RLIM_INFINITY = -0x1 + RLIM_INFINITY = 0xffffffffffffffff RTAX_ADVMSS = 0x8 RTAX_CC_ALGO = 0x10 RTAX_CWND = 0x7 @@ -1904,6 +1904,8 @@ const ( TUNSETVNETHDRSZ = 0x800454d8 TUNSETVNETLE = 0x800454dc UMOUNT_NOFOLLOW = 0x8 + UTIME_NOW = 0x3fffffff + UTIME_OMIT = 0x3ffffffe VDISCARD = 0x10 VEOF = 0x4 VEOL = 0x6 @@ -1933,6 +1935,17 @@ const ( WALL = 0x40000000 WCLONE = 0x80000000 WCONTINUED = 0x8 + WDIOC_GETBOOTSTATUS = 0x40045702 + WDIOC_GETPRETIMEOUT = 0x40045709 + WDIOC_GETSTATUS = 0x40045701 + WDIOC_GETSUPPORT = 0x40285700 + WDIOC_GETTEMP = 0x40045703 + WDIOC_GETTIMELEFT = 0x4004570a + WDIOC_GETTIMEOUT = 0x40045707 + WDIOC_KEEPALIVE = 0x40045705 + WDIOC_SETOPTIONS = 0x40045704 + WDIOC_SETPRETIMEOUT = 0xc0045708 + WDIOC_SETTIMEOUT = 0xc0045706 WEXITED = 0x4 WNOHANG = 0x1 WNOTHREAD = 0x20000000 diff --git a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 70bc1a2fc5..b921fb17a4 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -1186,7 +1186,7 @@ const ( PR_SET_NO_NEW_PRIVS = 0x26 PR_SET_PDEATHSIG = 0x1 PR_SET_PTRACER = 0x59616d61 - PR_SET_PTRACER_ANY = -0x1 + PR_SET_PTRACER_ANY = 0xffffffffffffffff PR_SET_SECCOMP = 0x16 PR_SET_SECUREBITS = 0x1c PR_SET_THP_DISABLE = 0x29 @@ -1339,7 +1339,7 @@ const ( RLIMIT_RTTIME = 0xf RLIMIT_SIGPENDING = 0xb RLIMIT_STACK = 0x3 - RLIM_INFINITY = -0x1 + RLIM_INFINITY = 0xffffffffffffffff RTAX_ADVMSS = 0x8 RTAX_CC_ALGO = 0x10 RTAX_CWND = 0x7 @@ -1904,6 +1904,8 @@ const ( TUNSETVNETHDRSZ = 0x400454d8 TUNSETVNETLE = 0x400454dc UMOUNT_NOFOLLOW = 0x8 + UTIME_NOW = 0x3fffffff + UTIME_OMIT = 0x3ffffffe VDISCARD = 0xd VEOF = 0x4 VEOL = 0xb @@ -1933,6 +1935,17 @@ const ( WALL = 0x40000000 WCLONE = 0x80000000 WCONTINUED = 0x8 + WDIOC_GETBOOTSTATUS = 0x80045702 + WDIOC_GETPRETIMEOUT = 0x80045709 + WDIOC_GETSTATUS = 0x80045701 + WDIOC_GETSUPPORT = 0x80285700 + WDIOC_GETTEMP = 0x80045703 + WDIOC_GETTIMELEFT = 0x8004570a + WDIOC_GETTIMEOUT = 0x80045707 + WDIOC_KEEPALIVE = 0x80045705 + WDIOC_SETOPTIONS = 0x80045704 + WDIOC_SETPRETIMEOUT = 0xc0045708 + WDIOC_SETTIMEOUT = 0xc0045706 WEXITED = 0x4 WNOHANG = 0x1 WNOTHREAD = 0x20000000 diff --git a/components/engine/vendor/golang.org/x/sys/unix/zptrace386_linux.go b/components/engine/vendor/golang.org/x/sys/unix/zptrace386_linux.go new file mode 100644 index 0000000000..2d21c49e12 --- /dev/null +++ b/components/engine/vendor/golang.org/x/sys/unix/zptrace386_linux.go @@ -0,0 +1,80 @@ +// Code generated by linux/mkall.go generatePtracePair(386, amd64). DO NOT EDIT. + +// +build linux +// +build 386 amd64 + +package unix + +import "unsafe" + +// PtraceRegs386 is the registers used by 386 binaries. +type PtraceRegs386 struct { + Ebx int32 + Ecx int32 + Edx int32 + Esi int32 + Edi int32 + Ebp int32 + Eax int32 + Xds int32 + Xes int32 + Xfs int32 + Xgs int32 + Orig_eax int32 + Eip int32 + Xcs int32 + Eflags int32 + Esp int32 + Xss int32 +} + +// PtraceGetRegs386 fetches the registers used by 386 binaries. +func PtraceGetRegs386(pid int, regsout *PtraceRegs386) error { + return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) +} + +// PtraceSetRegs386 sets the registers used by 386 binaries. +func PtraceSetRegs386(pid int, regs *PtraceRegs386) error { + return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) +} + +// PtraceRegsAmd64 is the registers used by amd64 binaries. +type PtraceRegsAmd64 struct { + R15 uint64 + R14 uint64 + R13 uint64 + R12 uint64 + Rbp uint64 + Rbx uint64 + R11 uint64 + R10 uint64 + R9 uint64 + R8 uint64 + Rax uint64 + Rcx uint64 + Rdx uint64 + Rsi uint64 + Rdi uint64 + Orig_rax uint64 + Rip uint64 + Cs uint64 + Eflags uint64 + Rsp uint64 + Ss uint64 + Fs_base uint64 + Gs_base uint64 + Ds uint64 + Es uint64 + Fs uint64 + Gs uint64 +} + +// PtraceGetRegsAmd64 fetches the registers used by amd64 binaries. +func PtraceGetRegsAmd64(pid int, regsout *PtraceRegsAmd64) error { + return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) +} + +// PtraceSetRegsAmd64 sets the registers used by amd64 binaries. +func PtraceSetRegsAmd64(pid int, regs *PtraceRegsAmd64) error { + return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) +} diff --git a/components/engine/vendor/golang.org/x/sys/unix/zptracearm_linux.go b/components/engine/vendor/golang.org/x/sys/unix/zptracearm_linux.go new file mode 100644 index 0000000000..faf23bbed9 --- /dev/null +++ b/components/engine/vendor/golang.org/x/sys/unix/zptracearm_linux.go @@ -0,0 +1,41 @@ +// Code generated by linux/mkall.go generatePtracePair(arm, arm64). DO NOT EDIT. + +// +build linux +// +build arm arm64 + +package unix + +import "unsafe" + +// PtraceRegsArm is the registers used by arm binaries. +type PtraceRegsArm struct { + Uregs [18]uint32 +} + +// PtraceGetRegsArm fetches the registers used by arm binaries. +func PtraceGetRegsArm(pid int, regsout *PtraceRegsArm) error { + return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) +} + +// PtraceSetRegsArm sets the registers used by arm binaries. +func PtraceSetRegsArm(pid int, regs *PtraceRegsArm) error { + return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) +} + +// PtraceRegsArm64 is the registers used by arm64 binaries. +type PtraceRegsArm64 struct { + Regs [31]uint64 + Sp uint64 + Pc uint64 + Pstate uint64 +} + +// PtraceGetRegsArm64 fetches the registers used by arm64 binaries. +func PtraceGetRegsArm64(pid int, regsout *PtraceRegsArm64) error { + return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) +} + +// PtraceSetRegsArm64 sets the registers used by arm64 binaries. +func PtraceSetRegsArm64(pid int, regs *PtraceRegsArm64) error { + return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) +} diff --git a/components/engine/vendor/golang.org/x/sys/unix/zptracemips_linux.go b/components/engine/vendor/golang.org/x/sys/unix/zptracemips_linux.go new file mode 100644 index 0000000000..c431131e63 --- /dev/null +++ b/components/engine/vendor/golang.org/x/sys/unix/zptracemips_linux.go @@ -0,0 +1,50 @@ +// Code generated by linux/mkall.go generatePtracePair(mips, mips64). DO NOT EDIT. + +// +build linux +// +build mips mips64 + +package unix + +import "unsafe" + +// PtraceRegsMips is the registers used by mips binaries. +type PtraceRegsMips struct { + Regs [32]uint64 + Lo uint64 + Hi uint64 + Epc uint64 + Badvaddr uint64 + Status uint64 + Cause uint64 +} + +// PtraceGetRegsMips fetches the registers used by mips binaries. +func PtraceGetRegsMips(pid int, regsout *PtraceRegsMips) error { + return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) +} + +// PtraceSetRegsMips sets the registers used by mips binaries. +func PtraceSetRegsMips(pid int, regs *PtraceRegsMips) error { + return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) +} + +// PtraceRegsMips64 is the registers used by mips64 binaries. +type PtraceRegsMips64 struct { + Regs [32]uint64 + Lo uint64 + Hi uint64 + Epc uint64 + Badvaddr uint64 + Status uint64 + Cause uint64 +} + +// PtraceGetRegsMips64 fetches the registers used by mips64 binaries. +func PtraceGetRegsMips64(pid int, regsout *PtraceRegsMips64) error { + return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) +} + +// PtraceSetRegsMips64 sets the registers used by mips64 binaries. +func PtraceSetRegsMips64(pid int, regs *PtraceRegsMips64) error { + return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) +} diff --git a/components/engine/vendor/golang.org/x/sys/unix/zptracemipsle_linux.go b/components/engine/vendor/golang.org/x/sys/unix/zptracemipsle_linux.go new file mode 100644 index 0000000000..dc3d6d3732 --- /dev/null +++ b/components/engine/vendor/golang.org/x/sys/unix/zptracemipsle_linux.go @@ -0,0 +1,50 @@ +// Code generated by linux/mkall.go generatePtracePair(mipsle, mips64le). DO NOT EDIT. + +// +build linux +// +build mipsle mips64le + +package unix + +import "unsafe" + +// PtraceRegsMipsle is the registers used by mipsle binaries. +type PtraceRegsMipsle struct { + Regs [32]uint64 + Lo uint64 + Hi uint64 + Epc uint64 + Badvaddr uint64 + Status uint64 + Cause uint64 +} + +// PtraceGetRegsMipsle fetches the registers used by mipsle binaries. +func PtraceGetRegsMipsle(pid int, regsout *PtraceRegsMipsle) error { + return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) +} + +// PtraceSetRegsMipsle sets the registers used by mipsle binaries. +func PtraceSetRegsMipsle(pid int, regs *PtraceRegsMipsle) error { + return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) +} + +// PtraceRegsMips64le is the registers used by mips64le binaries. +type PtraceRegsMips64le struct { + Regs [32]uint64 + Lo uint64 + Hi uint64 + Epc uint64 + Badvaddr uint64 + Status uint64 + Cause uint64 +} + +// PtraceGetRegsMips64le fetches the registers used by mips64le binaries. +func PtraceGetRegsMips64le(pid int, regsout *PtraceRegsMips64le) error { + return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) +} + +// PtraceSetRegsMips64le sets the registers used by mips64le binaries. +func PtraceSetRegsMips64le(pid int, regs *PtraceRegsMips64le) error { + return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) +} diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.go index 10491e9ed3..9fb1b31f48 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.go @@ -408,6 +408,17 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Access(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go index 5f1f6bfef7..1e0fb46b01 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go @@ -408,6 +408,17 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Access(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.go index 7a40974594..e1026a88a5 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.go @@ -408,6 +408,17 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Access(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go index 07c6ebc9f4..37fb210a08 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go @@ -408,6 +408,17 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Access(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go index 7fa205cd03..75761477d5 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go @@ -266,6 +266,17 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Madvise(b []byte, behav int) (err error) { var _p0 unsafe.Pointer if len(b) > 0 { diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go index 1a0bb4cb0e..8bcecfb9b6 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go @@ -266,6 +266,17 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Madvise(b []byte, behav int) (err error) { var _p0 unsafe.Pointer if len(b) > 0 { diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go index ac1e8e0136..61c0cf99bb 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go @@ -266,6 +266,17 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Madvise(b []byte, behav int) (err error) { var _p0 unsafe.Pointer if len(b) > 0 { diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go index 2b4e6acf04..ffd01073c1 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go @@ -266,6 +266,17 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Madvise(b []byte, behav int) (err error) { var _p0 unsafe.Pointer if len(b) > 0 { diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go index db99fd0c99..cfdea854ff 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go @@ -266,6 +266,17 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Madvise(b []byte, behav int) (err error) { var _p0 unsafe.Pointer if len(b) > 0 { diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go index 7b6c2c87e6..244a3c7618 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go @@ -266,6 +266,17 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Madvise(b []byte, behav int) (err error) { var _p0 unsafe.Pointer if len(b) > 0 { diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go index 0f4cc3b528..e891adc3a8 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go @@ -266,6 +266,17 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Madvise(b []byte, behav int) (err error) { var _p0 unsafe.Pointer if len(b) > 0 { diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index 7baea87c7b..f48beb0917 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -266,6 +266,17 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Madvise(b []byte, behav int) (err error) { var _p0 unsafe.Pointer if len(b) > 0 { diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index 0d69ce6b52..44a3faf77f 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -266,6 +266,17 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Madvise(b []byte, behav int) (err error) { var _p0 unsafe.Pointer if len(b) > 0 { diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index 41572c26e4..1563752dd5 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -266,6 +266,17 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Madvise(b []byte, behav int) (err error) { var _p0 unsafe.Pointer if len(b) > 0 { diff --git a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go index 98b2665500..1d45276498 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go @@ -29,6 +29,7 @@ import ( //go:cgo_import_dynamic libc___major __major "libc.so" //go:cgo_import_dynamic libc___minor __minor "libc.so" //go:cgo_import_dynamic libc_ioctl ioctl "libc.so" +//go:cgo_import_dynamic libc_poll poll "libc.so" //go:cgo_import_dynamic libc_access access "libc.so" //go:cgo_import_dynamic libc_adjtime adjtime "libc.so" //go:cgo_import_dynamic libc_chdir chdir "libc.so" @@ -153,6 +154,7 @@ import ( //go:linkname proc__major libc___major //go:linkname proc__minor libc___minor //go:linkname procioctl libc_ioctl +//go:linkname procpoll libc_poll //go:linkname procAccess libc_access //go:linkname procAdjtime libc_adjtime //go:linkname procChdir libc_chdir @@ -278,6 +280,7 @@ var ( proc__major, proc__minor, procioctl, + procpoll, procAccess, procAdjtime, procChdir, @@ -557,6 +560,15 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { return } +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procpoll)), 3, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout), 0, 0, 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + func Access(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_386.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_386.go index e61d78a54f..4667c7b277 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_386.go @@ -460,3 +460,22 @@ const ( AT_SYMLINK_FOLLOW = 0x40 AT_SYMLINK_NOFOLLOW = 0x20 ) + +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go index 2619155ff8..3f33b18fc7 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go @@ -470,3 +470,22 @@ const ( AT_SYMLINK_FOLLOW = 0x40 AT_SYMLINK_NOFOLLOW = 0x20 ) + +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_arm.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_arm.go index 4dca0d4db2..463a28ba6f 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_arm.go @@ -461,3 +461,22 @@ const ( AT_SYMLINK_FOLLOW = 0x40 AT_SYMLINK_NOFOLLOW = 0x20 ) + +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go index f2881fd142..1ec20a0025 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go @@ -1,6 +1,7 @@ +// cgo -godefs types_darwin.go | go run mkpost.go +// Code generated by the command above; see README.md. DO NOT EDIT. + // +build arm64,darwin -// Created by cgo -godefs - DO NOT EDIT -// cgo -godefs types_darwin.go package unix @@ -469,3 +470,22 @@ const ( AT_SYMLINK_FOLLOW = 0x40 AT_SYMLINK_NOFOLLOW = 0x20 ) + +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go index 67c6bf883c..ab515c3e1a 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go @@ -446,3 +446,22 @@ const ( AT_FDCWD = 0xfffafdcd AT_SYMLINK_NOFOLLOW = 0x1 ) + +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go index 5b28bcbbac..18f7816009 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go @@ -516,6 +516,26 @@ const ( AT_SYMLINK_NOFOLLOW = 0x200 ) +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLINIGNEOF = 0x2000 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) + type CapRights struct { Rights [2]uint64 } diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go index c65d89e497..dd0db2a5ea 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go @@ -519,6 +519,26 @@ const ( AT_SYMLINK_NOFOLLOW = 0x200 ) +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLINIGNEOF = 0x2000 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) + type CapRights struct { Rights [2]uint64 } diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go index 42c0a502cf..473d3dcf08 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go @@ -519,6 +519,26 @@ const ( AT_SYMLINK_NOFOLLOW = 0x200 ) +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLINIGNEOF = 0x2000 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) + type CapRights struct { Rights [2]uint64 } diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_386.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_386.go index 8b30c69975..c6de94269d 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_386.go @@ -621,12 +621,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]int8 - Nodename [65]int8 - Release [65]int8 - Version [65]int8 - Machine [65]int8 - Domainname [65]int8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go index cf03589862..4ea42dfc2e 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go @@ -637,12 +637,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]int8 - Nodename [65]int8 - Release [65]int8 - Version [65]int8 - Machine [65]int8 - Domainname [65]int8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go index 8ef7d85f17..f86d683882 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go @@ -609,12 +609,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]uint8 - Nodename [65]uint8 - Release [65]uint8 - Version [65]uint8 - Machine [65]uint8 - Domainname [65]uint8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go index 3110268673..45c10b7429 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go @@ -615,12 +615,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]int8 - Nodename [65]int8 - Release [65]int8 - Version [65]int8 - Machine [65]int8 - Domainname [65]int8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go index d2c1bc2c83..4cc0a1c91f 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go @@ -614,12 +614,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]int8 - Nodename [65]int8 - Release [65]int8 - Version [65]int8 - Machine [65]int8 - Domainname [65]int8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go index ec7a0cd275..d9df08789f 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go @@ -618,12 +618,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]int8 - Nodename [65]int8 - Release [65]int8 - Version [65]int8 - Machine [65]int8 - Domainname [65]int8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go index bbe08d7db7..15e6b4b4b1 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go @@ -618,12 +618,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]int8 - Nodename [65]int8 - Release [65]int8 - Version [65]int8 - Machine [65]int8 - Domainname [65]int8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go index 75ee05ab47..b6c2d32dd8 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go @@ -614,12 +614,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]int8 - Nodename [65]int8 - Release [65]int8 - Version [65]int8 - Machine [65]int8 - Domainname [65]int8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go index 30a257f83c..3803e1062b 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go @@ -625,12 +625,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]uint8 - Nodename [65]uint8 - Release [65]uint8 - Version [65]uint8 - Machine [65]uint8 - Domainname [65]uint8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go index bebed6f11c..7ef31fe213 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go @@ -625,12 +625,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]uint8 - Nodename [65]uint8 - Release [65]uint8 - Version [65]uint8 - Machine [65]uint8 - Domainname [65]uint8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go index 286661b35b..cb194f4717 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go @@ -642,12 +642,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]int8 - Nodename [65]int8 - Release [65]int8 - Version [65]int8 - Machine [65]int8 - Domainname [65]int8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go index 22bdab9614..9dbbb1ce52 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go @@ -601,12 +601,12 @@ type Sysinfo_t struct { } type Utsname struct { - Sysname [65]int8 - Nodename [65]int8 - Release [65]int8 - Version [65]int8 - Machine [65]int8 - Domainname [65]int8 + Sysname [65]byte + Nodename [65]byte + Release [65]byte + Version [65]byte + Machine [65]byte + Domainname [65]byte } type Ustat_t struct { @@ -652,8 +652,6 @@ type Sigset_t struct { X__val [16]uint64 } -const _SC_PAGESIZE = 0x1e - type Termios struct { Iflag uint32 Oflag uint32 diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go index 42f99c0a30..dfe446bff7 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go @@ -1,5 +1,5 @@ -// Created by cgo -godefs - DO NOT EDIT -// cgo -godefs types_netbsd.go +// cgo -godefs types_netbsd.go | go run mkpost.go +// Code generated by the command above; see README.md. DO NOT EDIT. // +build 386,netbsd @@ -387,6 +387,25 @@ const ( AT_SYMLINK_NOFOLLOW = 0x200 ) +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) + type Sysctlnode struct { Flags uint32 Num int32 diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go index ff290ba069..1498c23c22 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go @@ -1,5 +1,5 @@ -// Created by cgo -godefs - DO NOT EDIT -// cgo -godefs types_netbsd.go +// cgo -godefs types_netbsd.go | go run mkpost.go +// Code generated by the command above; see README.md. DO NOT EDIT. // +build amd64,netbsd @@ -394,6 +394,25 @@ const ( AT_SYMLINK_NOFOLLOW = 0x200 ) +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) + type Sysctlnode struct { Flags uint32 Num int32 diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go index 66dbd7c050..d6711ce170 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go @@ -1,5 +1,5 @@ -// Created by cgo -godefs - DO NOT EDIT -// cgo -godefs types_netbsd.go +// cgo -godefs types_netbsd.go | go run mkpost.go +// Code generated by the command above; see README.md. DO NOT EDIT. // +build arm,netbsd @@ -392,6 +392,25 @@ const ( AT_SYMLINK_NOFOLLOW = 0x200 ) +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) + type Sysctlnode struct { Flags uint32 Num int32 diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go index 20fc9f450c..af295c3d0c 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go @@ -1,5 +1,5 @@ -// Created by cgo -godefs - DO NOT EDIT -// cgo -godefs types_openbsd.go +// cgo -godefs types_openbsd.go | go run mkpost.go +// Code generated by the command above; see README.md. DO NOT EDIT. // +build 386,openbsd @@ -444,3 +444,22 @@ const ( AT_FDCWD = -0x64 AT_SYMLINK_NOFOLLOW = 0x2 ) + +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go index 46fe9490c8..ae153e70c0 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go @@ -1,5 +1,5 @@ -// Created by cgo -godefs - DO NOT EDIT -// cgo -godefs types_openbsd.go +// cgo -godefs types_openbsd.go | go run mkpost.go +// Code generated by the command above; see README.md. DO NOT EDIT. // +build amd64,openbsd @@ -451,3 +451,22 @@ const ( AT_FDCWD = -0x64 AT_SYMLINK_NOFOLLOW = 0x2 ) + +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go index 62e1f7c04d..35bb6195bf 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go @@ -1,5 +1,5 @@ -// Created by cgo -godefs - DO NOT EDIT -// cgo -godefs types_openbsd.go +// cgo -godefs types_openbsd.go | go run mkpost.go +// Code generated by the command above; see README.md. DO NOT EDIT. // +build arm,openbsd @@ -437,3 +437,22 @@ const ( AT_FDCWD = -0x64 AT_SYMLINK_NOFOLLOW = 0x2 ) + +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) diff --git a/components/engine/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go b/components/engine/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go index a979a33d51..d445452486 100644 --- a/components/engine/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go @@ -263,11 +263,11 @@ type FdSet struct { } type Utsname struct { - Sysname [257]int8 - Nodename [257]int8 - Release [257]int8 - Version [257]int8 - Machine [257]int8 + Sysname [257]byte + Nodename [257]byte + Release [257]byte + Version [257]byte + Machine [257]byte } type Ustat_t struct { @@ -438,3 +438,22 @@ type Winsize struct { Xpixel uint16 Ypixel uint16 } + +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) diff --git a/components/engine/vendor/golang.org/x/sys/windows/dll_windows.go b/components/engine/vendor/golang.org/x/sys/windows/dll_windows.go index e77a370550..e92c05b213 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/dll_windows.go +++ b/components/engine/vendor/golang.org/x/sys/windows/dll_windows.go @@ -1,4 +1,4 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2011 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. @@ -116,7 +116,7 @@ func (p *Proc) Addr() uintptr { //go:uintptrescapes -// Call executes procedure p with arguments a. It will panic, if more then 15 arguments +// Call executes procedure p with arguments a. It will panic, if more than 15 arguments // are supplied. // // The returned error is always non-nil, constructed from the result of GetLastError. @@ -289,6 +289,7 @@ func (p *LazyProc) mustFind() { // Addr returns the address of the procedure represented by p. // The return value can be passed to Syscall to run the procedure. +// It will panic if the procedure cannot be found. func (p *LazyProc) Addr() uintptr { p.mustFind() return p.proc.Addr() @@ -296,8 +297,8 @@ func (p *LazyProc) Addr() uintptr { //go:uintptrescapes -// Call executes procedure p with arguments a. It will panic, if more then 15 arguments -// are supplied. +// Call executes procedure p with arguments a. It will panic, if more than 15 arguments +// are supplied. It will also panic if the procedure cannot be found. // // The returned error is always non-nil, constructed from the result of GetLastError. // Callers must inspect the primary return value to decide whether an error occurred diff --git a/components/engine/vendor/golang.org/x/sys/windows/env_unset.go b/components/engine/vendor/golang.org/x/sys/windows/env_unset.go index 4ed03aeefc..b712c6604a 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/env_unset.go +++ b/components/engine/vendor/golang.org/x/sys/windows/env_unset.go @@ -1,4 +1,4 @@ -// Copyright 2014 The Go Authors. All rights reserved. +// 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/env_windows.go b/components/engine/vendor/golang.org/x/sys/windows/env_windows.go index a9d8ef4b7d..e8292386c0 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/env_windows.go +++ b/components/engine/vendor/golang.org/x/sys/windows/env_windows.go @@ -1,4 +1,4 @@ -// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2010 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/memory_windows.go b/components/engine/vendor/golang.org/x/sys/windows/memory_windows.go index f63e899acb..f80a4204f0 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/memory_windows.go +++ b/components/engine/vendor/golang.org/x/sys/windows/memory_windows.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Go Authors. All rights reserved. +// 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/mksyscall.go b/components/engine/vendor/golang.org/x/sys/windows/mksyscall.go index e1c88c9c71..fb7db0ef8d 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/mksyscall.go +++ b/components/engine/vendor/golang.org/x/sys/windows/mksyscall.go @@ -1,4 +1,4 @@ -// Copyright 2009 The Go Authors. All rights reserved. +// Copyright 2009 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/race.go b/components/engine/vendor/golang.org/x/sys/windows/race.go index 343e18ab69..a74e3e24b5 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/race.go +++ b/components/engine/vendor/golang.org/x/sys/windows/race.go @@ -1,4 +1,4 @@ -// Copyright 2012 The Go Authors. All rights reserved. +// Copyright 2012 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/race0.go b/components/engine/vendor/golang.org/x/sys/windows/race0.go index 17af843b91..e44a3cbf67 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/race0.go +++ b/components/engine/vendor/golang.org/x/sys/windows/race0.go @@ -1,4 +1,4 @@ -// Copyright 2012 The Go Authors. All rights reserved. +// Copyright 2012 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/security_windows.go b/components/engine/vendor/golang.org/x/sys/windows/security_windows.go index ca09bdd701..d8e7ff2ec5 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/security_windows.go +++ b/components/engine/vendor/golang.org/x/sys/windows/security_windows.go @@ -1,4 +1,4 @@ -// Copyright 2012 The Go Authors. All rights reserved. +// Copyright 2012 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/svc/go12.go b/components/engine/vendor/golang.org/x/sys/windows/svc/go12.go index 6f0a924eaf..cd8b913c99 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/svc/go12.go +++ b/components/engine/vendor/golang.org/x/sys/windows/svc/go12.go @@ -1,4 +1,4 @@ -// Copyright 2014 The Go Authors. All rights reserved. +// 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/svc/go13.go b/components/engine/vendor/golang.org/x/sys/windows/svc/go13.go index 432a9e796a..9d7f3cec54 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/svc/go13.go +++ b/components/engine/vendor/golang.org/x/sys/windows/svc/go13.go @@ -1,4 +1,4 @@ -// Copyright 2014 The Go Authors. All rights reserved. +// 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/syscall.go b/components/engine/vendor/golang.org/x/sys/windows/syscall.go index 4e2fbe86e2..b07bc2305d 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/syscall.go +++ b/components/engine/vendor/golang.org/x/sys/windows/syscall.go @@ -5,10 +5,10 @@ // +build windows // Package windows contains an interface to the low-level operating system -// primitives. OS details vary depending on the underlying system, and +// primitives. OS details vary depending on the underlying system, and // by default, godoc will display the OS-specific documentation for the current -// system. If you want godoc to display syscall documentation for another -// system, set $GOOS and $GOARCH to the desired system. For example, if +// system. If you want godoc to display syscall documentation for another +// system, set $GOOS and $GOARCH to the desired system. For example, if // you want to view documentation for freebsd/arm on linux/amd64, set $GOOS // to freebsd and $GOARCH to arm. // The primary use of this package is inside other packages that provide a more diff --git a/components/engine/vendor/golang.org/x/sys/windows/syscall_windows.go b/components/engine/vendor/golang.org/x/sys/windows/syscall_windows.go index acd06e3693..bb778dbd2e 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/components/engine/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -1,4 +1,4 @@ -// Copyright 2009 The Go Authors. All rights reserved. +// Copyright 2009 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/types_windows.go b/components/engine/vendor/golang.org/x/sys/windows/types_windows.go index 401a5f2d9a..0229f79cfc 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/types_windows.go +++ b/components/engine/vendor/golang.org/x/sys/windows/types_windows.go @@ -1,4 +1,4 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2011 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/types_windows_386.go b/components/engine/vendor/golang.org/x/sys/windows/types_windows_386.go index 10f33be0b7..fe0ddd0316 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/types_windows_386.go +++ b/components/engine/vendor/golang.org/x/sys/windows/types_windows_386.go @@ -1,4 +1,4 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2011 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. diff --git a/components/engine/vendor/golang.org/x/sys/windows/types_windows_amd64.go b/components/engine/vendor/golang.org/x/sys/windows/types_windows_amd64.go index 3f272c2499..7e154c2df2 100644 --- a/components/engine/vendor/golang.org/x/sys/windows/types_windows_amd64.go +++ b/components/engine/vendor/golang.org/x/sys/windows/types_windows_amd64.go @@ -1,4 +1,4 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2011 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. From 6b11145c275bdb4acb1fba7ac0949efb516ccec2 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 31 Oct 2017 12:38:39 +0100 Subject: [PATCH 09/94] Bump API version to 1.35 Signed-off-by: Sebastiaan van Stijn Upstream-commit: ba6b5aa7f28dbc76c6e0c494eb228a652a0d55a7 Component: engine --- components/engine/api/swagger.yaml | 32 ++++++++++++------- components/engine/docs/api/version-history.md | 3 ++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/components/engine/api/swagger.yaml b/components/engine/api/swagger.yaml index b0c0575fc0..fb77e4f7ce 100644 --- a/components/engine/api/swagger.yaml +++ b/components/engine/api/swagger.yaml @@ -19,10 +19,10 @@ produces: consumes: - "application/json" - "text/plain" -basePath: "/v1.34" +basePath: "/v1.35" info: title: "Docker Engine API" - version: "1.34" + version: "1.35" x-logo: url: "https://docs.docker.com/images/logo-docker-main.png" description: | @@ -42,22 +42,32 @@ info: # Versioning - The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break. + The API is usually changed in each release, so API calls are versioned to + ensure that clients don't break. To lock to a specific version of the API, + you prefix the URL with its version, for example, call `/v1.30/info` to use + the v1.30 version of the `/info` endpoint. If the API version specified in + the URL is not supported by the daemon, a HTTP `400 Bad Request` error message + is returned. - For Docker Engine 17.10, the API version is 1.33. To lock to this version, you prefix the URL with `/v1.33`. For example, calling `/info` is the same as calling `/v1.33/info`. + If you omit the version-prefix, the current version of the API (v1.35) is used. + For example, calling `/info` is the same as calling `/v1.35/info`. Using the + API without a version-prefix is deprecated and will be removed in a future release. - Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine. + Engine releases in the near future should support this version of the API, + so your client will continue to work even if it is talking to a newer Engine. - In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker. + The API uses an open schema model, which means server may add extra properties + to responses. Likewise, the server will ignore any extra query parameters and + request body properties. When you write clients, you need to ignore additional + properties in responses to ensure they do not break when talking to newer + daemons. - If the API version specified in the URL is not supported by the daemon, a HTTP `400 Bad Request` error message is returned. - - The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons. - - This documentation is for version 1.34 of the API. Use this table to find documentation for previous versions of the API: + This documentation is for version v1.35 of the API. Use this table to find + documentation for previous versions of the API: Docker version | API version | Changes ----------------|-------------|--------- + 17.11.x | [1.34](https://docs.docker.com/engine/api/v1.34/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-34-api-changes) 17.10.x | [1.33](https://docs.docker.com/engine/api/v1.33/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-33-api-changes) 17.09.x | [1.32](https://docs.docker.com/engine/api/v1.32/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-32-api-changes) 17.07.x | [1.31](https://docs.docker.com/engine/api/v1.31/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-31-api-changes) diff --git a/components/engine/docs/api/version-history.md b/components/engine/docs/api/version-history.md index 77b8545bcc..6ff5338e31 100644 --- a/components/engine/docs/api/version-history.md +++ b/components/engine/docs/api/version-history.md @@ -13,6 +13,9 @@ keywords: "API, Docker, rcli, REST, documentation" will be rejected. --> +## v1.35 API changes + + ## v1.34 API changes [Docker Engine API v1.34](https://docs.docker.com/engine/api/v1.34/) documentation From b5133b1bd804ffcbebcc3ceaaa4eaed7c05a635e Mon Sep 17 00:00:00 2001 From: joppich Date: Thu, 19 Oct 2017 16:35:32 +0200 Subject: [PATCH 10/94] =?UTF-8?q?Update=20names-generator.go=20to=20includ?= =?UTF-8?q?e=20C=C3=A9dric=20Villani=20and=20Valentina=20Tershkova?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jorit Kleine-Möllhoff Upstream-commit: f69f8f367987717a154d4c95ea9261bc9416734a Component: engine --- components/engine/pkg/namesgenerator/names-generator.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/engine/pkg/namesgenerator/names-generator.go b/components/engine/pkg/namesgenerator/names-generator.go index 2f869ed925..6352bc7ceb 100644 --- a/components/engine/pkg/namesgenerator/names-generator.go +++ b/components/engine/pkg/namesgenerator/names-generator.go @@ -539,6 +539,9 @@ var ( // Bertha Swirles was a theoretical physicist who made a number of contributions to early quantum theory. https://en.wikipedia.org/wiki/Bertha_Swirles "swirles", + // Valentina Tereshkova is a russian engineer, cosmonaut and politician. She was the first woman flying to space in 1963. In 2013, at the age of 76, she offered to go on a one-way mission to mars. https://en.wikipedia.org/wiki/Valentina_Tereshkova + "tereshkova", + // Nikola Tesla invented the AC electric system and every gadget ever used by a James Bond villain. https://en.wikipedia.org/wiki/Nikola_Tesla "tesla", @@ -560,6 +563,9 @@ var ( // Christiane Nüsslein-Volhard - German biologist, won Nobel Prize in Physiology or Medicine in 1995 for research on the genetic control of embryonic development. https://en.wikipedia.org/wiki/Christiane_N%C3%BCsslein-Volhard "volhard", + // Cédric Villani - French mathematician, won Fields Medal, Fermat Prize and Poincaré Price for his work in differential geometry and statistical mechanics. https://en.wikipedia.org/wiki/C%C3%A9dric_Villani + "villani", + // Marlyn Wescoff - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Marlyn_Meltzer "wescoff", From 95c878aac56e4066f4ea8ec246d6fb59c39d6b2b Mon Sep 17 00:00:00 2001 From: Shijun Qin Date: Tue, 31 Oct 2017 22:22:07 +0800 Subject: [PATCH 11/94] Fix grammatical problems in several annotations Signed-off-by: qinshijun <272700767@qq.com> Upstream-commit: 3c376a882b89bc1ff4f7b47667f49440a516bd70 Component: engine --- components/engine/api/errdefs/is.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/engine/api/errdefs/is.go b/components/engine/api/errdefs/is.go index ce574cdd1e..b0be0b8147 100644 --- a/components/engine/api/errdefs/is.go +++ b/components/engine/api/errdefs/is.go @@ -25,7 +25,7 @@ func getImplementer(err error) error { } } -// IsNotFound returns if the passed in error is a ErrNotFound +// IsNotFound returns if the passed in error is an ErrNotFound func IsNotFound(err error) bool { _, ok := getImplementer(err).(ErrNotFound) return ok @@ -37,7 +37,7 @@ func IsInvalidParameter(err error) bool { return ok } -// IsConflict returns if the passed in error is a ErrConflict +// IsConflict returns if the passed in error is an ErrConflict func IsConflict(err error) bool { _, ok := getImplementer(err).(ErrConflict) return ok @@ -55,13 +55,13 @@ func IsUnavailable(err error) bool { return ok } -// IsForbidden returns if the passed in error is a ErrForbidden +// IsForbidden returns if the passed in error is an ErrForbidden func IsForbidden(err error) bool { _, ok := getImplementer(err).(ErrForbidden) return ok } -// IsSystem returns if the passed in error is a ErrSystem +// IsSystem returns if the passed in error is an ErrSystem func IsSystem(err error) bool { _, ok := getImplementer(err).(ErrSystem) return ok @@ -73,7 +73,7 @@ func IsNotModified(err error) bool { return ok } -// IsNotImplemented returns if the passed in error is a ErrNotImplemented +// IsNotImplemented returns if the passed in error is an ErrNotImplemented func IsNotImplemented(err error) bool { _, ok := getImplementer(err).(ErrNotImplemented) return ok From 3c035adcf7315c60f840df0fdc66e6108dd776cb Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 31 Oct 2017 11:50:29 -0400 Subject: [PATCH 12/94] Add containerd static compile Fixes #35349 Signed-off-by: Michael Crosby Upstream-commit: 007db062f0c39bf31e1150aa26d045778ab7d24e Component: engine --- .../engine/hack/dockerfile/install-binaries.sh | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/components/engine/hack/dockerfile/install-binaries.sh b/components/engine/hack/dockerfile/install-binaries.sh index 0a45ef9b8c..e97385ebcc 100755 --- a/components/engine/hack/dockerfile/install-binaries.sh +++ b/components/engine/hack/dockerfile/install-binaries.sh @@ -34,7 +34,21 @@ install_containerd() { git checkout -q "$CONTAINERD_COMMIT" ( export GOPATH - make $1 + make + ) + cp bin/containerd /usr/local/bin/docker-containerd + cp bin/containerd-shim /usr/local/bin/docker-containerd-shim + cp bin/ctr /usr/local/bin/docker-containerd-ctr +} + +install_containerd_static() { + echo "Install containerd version $CONTAINERD_COMMIT" + git clone https://github.com/containerd/containerd.git "$GOPATH/src/github.com/containerd/containerd" + cd "$GOPATH/src/github.com/containerd/containerd" + git checkout -q "$CONTAINERD_COMMIT" + ( + export GOPATH + make EXTRA_FLAGS="-buildmode pie" EXTRA_LDFLAGS="-extldflags \\\"-fno-PIC -static\\\"" ) cp bin/containerd /usr/local/bin/docker-containerd cp bin/containerd-shim /usr/local/bin/docker-containerd-shim @@ -106,7 +120,7 @@ do ;; containerd) - install_containerd + install_containerd_static ;; containerd-dynamic) From d76235b88c1dcb31e92fa63b5c39e1af110bb2d5 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 31 Oct 2017 15:53:11 +0100 Subject: [PATCH 13/94] bump opencontainers/selinux to b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd This vendoring fixes two issues. 1. When a user specifies an SELinux MCS Label (level) to override moby picking an unigue MCS label, the code currently picks a label then overrides with the user selected. This works fine, except the unique MCS Label is leaked and will not be used until the daemon is restarted. 2. The override label, is not reserved. This could potentially cause an issue where the daemon could pick the same MCS Label again for a different container. (~ 1/500,000 Chance). The updated selinux go bindings, now release the overriden unigue label, and reserve the one specified by the user. Signed-off-by: Sebastiaan van Stijn Upstream-commit: 73c82386148fe14a47cc515c622bd23b9b7d99b9 Component: engine --- components/engine/vendor.conf | 2 +- .../opencontainers/selinux/go-selinux/label/label_selinux.go | 2 ++ .../github.com/opencontainers/selinux/go-selinux/selinux.go | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index c3c551eb00..f5a6002d56 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -143,7 +143,7 @@ github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github. # metrics github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18 -github.com/opencontainers/selinux v1.0.0-rc1 +github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd # archive/tar # mkdir -p ./vendor/archive diff --git a/components/engine/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go b/components/engine/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go index 569dcf0841..c008a387bf 100644 --- a/components/engine/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go +++ b/components/engine/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go @@ -49,8 +49,10 @@ func InitLabels(options []string) (string, string, error) { mcon[con[0]] = con[1] } } + _ = ReleaseLabel(processLabel) processLabel = pcon.Get() mountLabel = mcon.Get() + _ = ReserveLabel(processLabel) } return processLabel, mountLabel, nil } diff --git a/components/engine/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go b/components/engine/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go index 4cf2c45de7..de9316c2e2 100644 --- a/components/engine/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go +++ b/components/engine/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go @@ -213,7 +213,7 @@ func SetFileLabel(path string, label string) error { return lsetxattr(path, xattrNameSelinux, []byte(label), 0) } -// Filecon returns the SELinux label for this path or returns an error. +// FileLabel returns the SELinux label for this path or returns an error. func FileLabel(path string) (string, error) { label, err := lgetxattr(path, xattrNameSelinux) if err != nil { @@ -331,7 +331,7 @@ func EnforceMode() int { } /* -SetEnforce sets the current SELinux mode Enforcing, Permissive. +SetEnforceMode sets the current SELinux mode Enforcing, Permissive. Disabled is not valid, since this needs to be set at boot time. */ func SetEnforceMode(mode int) error { From 0954691f2fb42b2af3bf89c9bba222ecdaefb1c2 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Tue, 31 Oct 2017 12:58:59 -0400 Subject: [PATCH 14/94] Remove packaging scripts These scripts have not been used for a while now, and should not be used again because they are for releasing docker, not moby Signed-off-by: Daniel Nephin Upstream-commit: eef85648e43fef2ec73462fd3d734947384cb16e Component: engine --- components/engine/hack/make/.build-deb/compat | 1 - .../engine/hack/make/.build-deb/control | 29 -- .../.build-deb/docker-engine.bash-completion | 1 - .../.build-deb/docker-engine.docker.default | 1 - .../make/.build-deb/docker-engine.docker.init | 1 - .../.build-deb/docker-engine.docker.upstart | 1 - .../make/.build-deb/docker-engine.install | 12 - .../make/.build-deb/docker-engine.manpages | 1 - .../make/.build-deb/docker-engine.postinst | 20 -- .../hack/make/.build-deb/docker-engine.udev | 1 - components/engine/hack/make/.build-deb/docs | 1 - components/engine/hack/make/.build-deb/rules | 53 --- .../.build-rpm/docker-engine-selinux.spec | 99 ------ .../hack/make/.build-rpm/docker-engine.spec | 249 -------------- components/engine/hack/make/build-deb | 91 ----- components/engine/hack/make/build-rpm | 148 --------- components/engine/hack/make/clean-apt-repo | 43 --- components/engine/hack/make/clean-yum-repo | 20 -- components/engine/hack/make/cover | 15 - .../engine/hack/make/generate-index-listing | 74 ----- components/engine/hack/make/install-binary | 23 +- .../engine/hack/make/install-binary-daemon | 29 -- components/engine/hack/make/release-deb | 163 --------- components/engine/hack/make/release-rpm | 71 ---- components/engine/hack/make/sign-repos | 65 ---- components/engine/hack/make/test-old-apt-repo | 29 -- components/engine/hack/make/ubuntu | 190 ----------- components/engine/hack/make/update-apt-repo | 70 ---- components/engine/hack/make/win | 20 -- components/engine/hack/release.sh | 313 ------------------ 30 files changed, 22 insertions(+), 1812 deletions(-) delete mode 100644 components/engine/hack/make/.build-deb/compat delete mode 100644 components/engine/hack/make/.build-deb/control delete mode 100644 components/engine/hack/make/.build-deb/docker-engine.bash-completion delete mode 120000 components/engine/hack/make/.build-deb/docker-engine.docker.default delete mode 120000 components/engine/hack/make/.build-deb/docker-engine.docker.init delete mode 120000 components/engine/hack/make/.build-deb/docker-engine.docker.upstart delete mode 100644 components/engine/hack/make/.build-deb/docker-engine.install delete mode 100644 components/engine/hack/make/.build-deb/docker-engine.manpages delete mode 100644 components/engine/hack/make/.build-deb/docker-engine.postinst delete mode 120000 components/engine/hack/make/.build-deb/docker-engine.udev delete mode 100644 components/engine/hack/make/.build-deb/docs delete mode 100755 components/engine/hack/make/.build-deb/rules delete mode 100644 components/engine/hack/make/.build-rpm/docker-engine-selinux.spec delete mode 100644 components/engine/hack/make/.build-rpm/docker-engine.spec delete mode 100644 components/engine/hack/make/build-deb delete mode 100644 components/engine/hack/make/build-rpm delete mode 100755 components/engine/hack/make/clean-apt-repo delete mode 100755 components/engine/hack/make/clean-yum-repo delete mode 100644 components/engine/hack/make/cover delete mode 100755 components/engine/hack/make/generate-index-listing mode change 100755 => 100644 components/engine/hack/make/install-binary delete mode 100644 components/engine/hack/make/install-binary-daemon delete mode 100755 components/engine/hack/make/release-deb delete mode 100755 components/engine/hack/make/release-rpm delete mode 100755 components/engine/hack/make/sign-repos delete mode 100755 components/engine/hack/make/test-old-apt-repo delete mode 100644 components/engine/hack/make/ubuntu delete mode 100755 components/engine/hack/make/update-apt-repo delete mode 100644 components/engine/hack/make/win delete mode 100755 components/engine/hack/release.sh diff --git a/components/engine/hack/make/.build-deb/compat b/components/engine/hack/make/.build-deb/compat deleted file mode 100644 index ec635144f6..0000000000 --- a/components/engine/hack/make/.build-deb/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/components/engine/hack/make/.build-deb/control b/components/engine/hack/make/.build-deb/control deleted file mode 100644 index 0f5439947c..0000000000 --- a/components/engine/hack/make/.build-deb/control +++ /dev/null @@ -1,29 +0,0 @@ -Source: docker-engine -Section: admin -Priority: optional -Maintainer: Docker -Standards-Version: 3.9.6 -Homepage: https://dockerproject.org -Vcs-Browser: https://github.com/docker/docker -Vcs-Git: git://github.com/docker/docker.git - -Package: docker-engine -Architecture: linux-any -Depends: iptables, ${misc:Depends}, ${perl:Depends}, ${shlibs:Depends} -Recommends: aufs-tools, - ca-certificates, - cgroupfs-mount | cgroup-lite, - git, - xz-utils, - ${apparmor:Recommends} -Conflicts: docker (<< 1.5~), docker.io, lxc-docker, lxc-docker-virtual-package, docker-engine-cs -Description: Docker: the open-source application container engine - Docker is an open source project to build, ship and run any application as a - lightweight container - . - Docker containers are both hardware-agnostic and platform-agnostic. This means - they can run anywhere, from your laptop to the largest EC2 compute instance and - everything in between - and they don't require you to use a particular - language, framework or packaging system. That makes them great building blocks - for deploying and scaling web apps, databases, and backend services without - depending on a particular stack or provider. diff --git a/components/engine/hack/make/.build-deb/docker-engine.bash-completion b/components/engine/hack/make/.build-deb/docker-engine.bash-completion deleted file mode 100644 index 6ea1119308..0000000000 --- a/components/engine/hack/make/.build-deb/docker-engine.bash-completion +++ /dev/null @@ -1 +0,0 @@ -contrib/completion/bash/docker diff --git a/components/engine/hack/make/.build-deb/docker-engine.docker.default b/components/engine/hack/make/.build-deb/docker-engine.docker.default deleted file mode 120000 index 4278533d65..0000000000 --- a/components/engine/hack/make/.build-deb/docker-engine.docker.default +++ /dev/null @@ -1 +0,0 @@ -../../../contrib/init/sysvinit-debian/docker.default \ No newline at end of file diff --git a/components/engine/hack/make/.build-deb/docker-engine.docker.init b/components/engine/hack/make/.build-deb/docker-engine.docker.init deleted file mode 120000 index 8cb89d30dd..0000000000 --- a/components/engine/hack/make/.build-deb/docker-engine.docker.init +++ /dev/null @@ -1 +0,0 @@ -../../../contrib/init/sysvinit-debian/docker \ No newline at end of file diff --git a/components/engine/hack/make/.build-deb/docker-engine.docker.upstart b/components/engine/hack/make/.build-deb/docker-engine.docker.upstart deleted file mode 120000 index 7e1b64a3e6..0000000000 --- a/components/engine/hack/make/.build-deb/docker-engine.docker.upstart +++ /dev/null @@ -1 +0,0 @@ -../../../contrib/init/upstart/docker.conf \ No newline at end of file diff --git a/components/engine/hack/make/.build-deb/docker-engine.install b/components/engine/hack/make/.build-deb/docker-engine.install deleted file mode 100644 index dc6b25f04f..0000000000 --- a/components/engine/hack/make/.build-deb/docker-engine.install +++ /dev/null @@ -1,12 +0,0 @@ -#contrib/syntax/vim/doc/* /usr/share/vim/vimfiles/doc/ -#contrib/syntax/vim/ftdetect/* /usr/share/vim/vimfiles/ftdetect/ -#contrib/syntax/vim/syntax/* /usr/share/vim/vimfiles/syntax/ -contrib/*-integration usr/share/docker-engine/contrib/ -contrib/check-config.sh usr/share/docker-engine/contrib/ -contrib/completion/fish/docker.fish usr/share/fish/vendor_completions.d/ -contrib/completion/zsh/_docker usr/share/zsh/vendor-completions/ -contrib/init/systemd/docker.service lib/systemd/system/ -contrib/init/systemd/docker.socket lib/systemd/system/ -contrib/mk* usr/share/docker-engine/contrib/ -contrib/nuke-graph-directory.sh usr/share/docker-engine/contrib/ -contrib/syntax/nano/Dockerfile.nanorc usr/share/nano/ diff --git a/components/engine/hack/make/.build-deb/docker-engine.manpages b/components/engine/hack/make/.build-deb/docker-engine.manpages deleted file mode 100644 index 1aa62186a6..0000000000 --- a/components/engine/hack/make/.build-deb/docker-engine.manpages +++ /dev/null @@ -1 +0,0 @@ -man/man*/* diff --git a/components/engine/hack/make/.build-deb/docker-engine.postinst b/components/engine/hack/make/.build-deb/docker-engine.postinst deleted file mode 100644 index eeef6ca801..0000000000 --- a/components/engine/hack/make/.build-deb/docker-engine.postinst +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -set -e - -case "$1" in - configure) - if [ -z "$2" ]; then - if ! getent group docker > /dev/null; then - groupadd --system docker - fi - fi - ;; - abort-*) - # How'd we get here?? - exit 1 - ;; - *) - ;; -esac - -#DEBHELPER# diff --git a/components/engine/hack/make/.build-deb/docker-engine.udev b/components/engine/hack/make/.build-deb/docker-engine.udev deleted file mode 120000 index 914a361959..0000000000 --- a/components/engine/hack/make/.build-deb/docker-engine.udev +++ /dev/null @@ -1 +0,0 @@ -../../../contrib/udev/80-docker.rules \ No newline at end of file diff --git a/components/engine/hack/make/.build-deb/docs b/components/engine/hack/make/.build-deb/docs deleted file mode 100644 index b43bf86b50..0000000000 --- a/components/engine/hack/make/.build-deb/docs +++ /dev/null @@ -1 +0,0 @@ -README.md diff --git a/components/engine/hack/make/.build-deb/rules b/components/engine/hack/make/.build-deb/rules deleted file mode 100755 index 19557ed50c..0000000000 --- a/components/engine/hack/make/.build-deb/rules +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/make -f - -VERSION = $(shell cat VERSION) -SYSTEMD_VERSION := $(shell dpkg-query -W -f='$${Version}\n' systemd | cut -d- -f1) -SYSTEMD_GT_227 := $(shell [ '$(SYSTEMD_VERSION)' ] && [ '$(SYSTEMD_VERSION)' -gt 227 ] && echo true ) - -override_dh_gencontrol: - # if we're on Ubuntu, we need to Recommends: apparmor - echo 'apparmor:Recommends=$(shell dpkg-vendor --is Ubuntu && echo apparmor)' >> debian/docker-engine.substvars - dh_gencontrol - -override_dh_auto_build: - ./hack/make.sh dynbinary - # ./man/md2man-all.sh runs outside the build container (if at all), since we don't have go-md2man here - -override_dh_auto_test: - ./bundles/$(VERSION)/dynbinary-daemon/dockerd -v - -override_dh_strip: - # Go has lots of problems with stripping, so just don't - -override_dh_auto_install: - mkdir -p debian/docker-engine/usr/bin - cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary-daemon/dockerd)" debian/docker-engine/usr/bin/dockerd - cp -aT /usr/local/bin/docker-proxy debian/docker-engine/usr/bin/docker-proxy - cp -aT /usr/local/bin/docker-containerd debian/docker-engine/usr/bin/docker-containerd - cp -aT /usr/local/bin/docker-containerd-shim debian/docker-engine/usr/bin/docker-containerd-shim - cp -aT /usr/local/bin/docker-containerd-ctr debian/docker-engine/usr/bin/docker-containerd-ctr - cp -aT /usr/local/bin/docker-runc debian/docker-engine/usr/bin/docker-runc - cp -aT /usr/local/bin/docker-init debian/docker-engine/usr/bin/docker-init - mkdir -p debian/docker-engine/usr/lib/docker - -override_dh_installinit: - # use "docker" as our service name, not "docker-engine" - dh_installinit --name=docker -ifeq (true, $(SYSTEMD_GT_227)) - $(warning "Setting TasksMax=infinity") - sed -i -- 's/#TasksMax=infinity/TasksMax=infinity/' debian/docker-engine/lib/systemd/system/docker.service -endif - -override_dh_installudev: - # match our existing priority - dh_installudev --priority=z80 - -override_dh_install: - dh_install - dh_apparmor --profile-name=docker-engine -pdocker-engine - -override_dh_shlibdeps: - dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info - -%: - dh $@ --with=bash-completion $(shell command -v dh_systemd_enable > /dev/null 2>&1 && echo --with=systemd) diff --git a/components/engine/hack/make/.build-rpm/docker-engine-selinux.spec b/components/engine/hack/make/.build-rpm/docker-engine-selinux.spec deleted file mode 100644 index 6a4b6c0c3a..0000000000 --- a/components/engine/hack/make/.build-rpm/docker-engine-selinux.spec +++ /dev/null @@ -1,99 +0,0 @@ -# Some bits borrowed from the openstack-selinux package -Name: docker-engine-selinux -Version: %{_version} -Release: %{_release}%{?dist} -Summary: SELinux Policies for the open-source application container engine -BuildArch: noarch -Group: Tools/Docker - -License: GPLv2 -Source: %{name}.tar.gz - -URL: https://dockerproject.org -Vendor: Docker -Packager: Docker - -%global selinux_policyver 3.13.1-102 -%if 0%{?oraclelinux} >= 7 -%global selinux_policyver 3.13.1-102.0.3.el7_3.15 -%endif # oraclelinux 7 -%global selinuxtype targeted -%global moduletype services -%global modulenames docker - -Requires(post): selinux-policy-base >= %{selinux_policyver}, selinux-policy-targeted >= %{selinux_policyver}, policycoreutils, policycoreutils-python libselinux-utils -BuildRequires: selinux-policy selinux-policy-devel - -# conflicting packages -Conflicts: docker-selinux - -# Usage: _format var format -# Expand 'modulenames' into various formats as needed -# Format must contain '$x' somewhere to do anything useful -%global _format() export %1=""; for x in %{modulenames}; do %1+=%2; %1+=" "; done; - -# Relabel files -%global relabel_files() \ - /sbin/restorecon -R %{_bindir}/docker %{_localstatedir}/run/docker.sock %{_localstatedir}/run/docker.pid %{_sysconfdir}/docker %{_localstatedir}/log/docker %{_localstatedir}/log/lxc %{_localstatedir}/lock/lxc %{_usr}/lib/systemd/system/docker.service /root/.docker &> /dev/null || : \ - -%description -SELinux policy modules for use with Docker - -%prep -%if 0%{?centos} <= 6 -%setup -n %{name} -%else -%autosetup -n %{name} -%endif - -%build -make SHARE="%{_datadir}" TARGETS="%{modulenames}" - -%install - -# Install SELinux interfaces -%_format INTERFACES $x.if -install -d %{buildroot}%{_datadir}/selinux/devel/include/%{moduletype} -install -p -m 644 $INTERFACES %{buildroot}%{_datadir}/selinux/devel/include/%{moduletype} - -# Install policy modules -%_format MODULES $x.pp.bz2 -install -d %{buildroot}%{_datadir}/selinux/packages -install -m 0644 $MODULES %{buildroot}%{_datadir}/selinux/packages - -%post -# -# Install all modules in a single transaction -# -if [ $1 -eq 1 ]; then - %{_sbindir}/setsebool -P -N virt_use_nfs=1 virt_sandbox_use_all_caps=1 -fi -%_format MODULES %{_datadir}/selinux/packages/$x.pp.bz2 -%{_sbindir}/semodule -n -s %{selinuxtype} -i $MODULES -if %{_sbindir}/selinuxenabled ; then - %{_sbindir}/load_policy - %relabel_files - if [ $1 -eq 1 ]; then - restorecon -R %{_sharedstatedir}/docker - fi -fi - -%postun -if [ $1 -eq 0 ]; then - %{_sbindir}/semodule -n -r %{modulenames} &> /dev/null || : - if %{_sbindir}/selinuxenabled ; then - %{_sbindir}/load_policy - %relabel_files - fi -fi - -%files -%doc LICENSE -%defattr(-,root,root,0755) -%attr(0644,root,root) %{_datadir}/selinux/packages/*.pp.bz2 -%attr(0644,root,root) %{_datadir}/selinux/devel/include/%{moduletype}/*.if - -%changelog -* Tue Dec 1 2015 Jessica Frazelle 1.9.1-1 -- add licence to rpm -- add selinux-policy and docker-engine-selinux rpm diff --git a/components/engine/hack/make/.build-rpm/docker-engine.spec b/components/engine/hack/make/.build-rpm/docker-engine.spec deleted file mode 100644 index 6225bb74f2..0000000000 --- a/components/engine/hack/make/.build-rpm/docker-engine.spec +++ /dev/null @@ -1,249 +0,0 @@ -Name: docker-engine -Version: %{_version} -Release: %{_release}%{?dist} -Summary: The open-source application container engine -Group: Tools/Docker - -License: ASL 2.0 -Source: %{name}.tar.gz - -URL: https://dockerproject.org -Vendor: Docker -Packager: Docker - -# is_systemd conditional -%if 0%{?fedora} >= 21 || 0%{?centos} >= 7 || 0%{?rhel} >= 7 || 0%{?suse_version} >= 1210 -%global is_systemd 1 -%endif - -# required packages for build -# most are already in the container (see contrib/builder/rpm/ARCH/generate.sh) -# only require systemd on those systems -%if 0%{?is_systemd} -%if 0%{?suse_version} >= 1210 -BuildRequires: systemd-rpm-macros -%{?systemd_requires} -%else -%if 0%{?fedora} >= 25 -# Systemd 230 and up no longer have libsystemd-journal (see https://bugzilla.redhat.com/show_bug.cgi?id=1350301) -BuildRequires: pkgconfig(systemd) -Requires: systemd-units -%else -BuildRequires: pkgconfig(systemd) -Requires: systemd-units -BuildRequires: pkgconfig(libsystemd-journal) -%endif -%endif -%else -Requires(post): chkconfig -Requires(preun): chkconfig -# This is for /sbin/service -Requires(preun): initscripts -%endif - -# required packages on install -Requires: /bin/sh -Requires: iptables -%if !0%{?suse_version} -Requires: libcgroup -%else -Requires: libcgroup1 -%endif -Requires: tar -Requires: xz -%if 0%{?fedora} >= 21 || 0%{?centos} >= 7 || 0%{?rhel} >= 7 || 0%{?oraclelinux} >= 7 || 0%{?amzn} >= 1 -# Resolves: rhbz#1165615 -Requires: device-mapper-libs >= 1.02.90-1 -%endif -%if 0%{?oraclelinux} >= 6 -# Require Oracle Unbreakable Enterprise Kernel R4 and newer device-mapper -Requires: kernel-uek >= 4.1 -Requires: device-mapper >= 1.02.90-2 -%endif - -# docker-selinux conditional -%if 0%{?fedora} >= 20 || 0%{?centos} >= 7 || 0%{?rhel} >= 7 || 0%{?oraclelinux} >= 7 -%global with_selinux 1 -%endif - -# DWZ problem with multiple golang binary, see bug -# https://bugzilla.redhat.com/show_bug.cgi?id=995136#c12 -%if 0%{?fedora} >= 20 || 0%{?rhel} >= 7 || 0%{?oraclelinux} >= 7 -%global _dwz_low_mem_die_limit 0 -%endif - -# start if with_selinux -%if 0%{?with_selinux} - -%if 0%{?centos} >= 7 || 0%{?rhel} >= 7 || 0%{?fedora} >= 25 -Requires: container-selinux >= 2.9 -%endif# centos 7, rhel 7, fedora 25 - -%if 0%{?oraclelinux} >= 7 -%global selinux_policyver 3.13.1-102.0.3.el7_3.15 -%endif # oraclelinux 7 -%if 0%{?fedora} == 24 -%global selinux_policyver 3.13.1-191 -%endif # fedora 24 -- container-selinux on fedora24 does not properly set dockerd, for now just carry docker-engine-selinux for it -%if 0%{?oraclelinux} >= 7 || 0%{?fedora} == 24 -Requires: selinux-policy >= %{selinux_policyver} -Requires(pre): %{name}-selinux >= %{version}-%{release} -%endif # selinux-policy for oraclelinux-7, fedora-24 - -%endif # with_selinux - -# conflicting packages -Conflicts: docker -Conflicts: docker-io -Conflicts: docker-engine-cs - -%description -Docker is an open source project to build, ship and run any application as a -lightweight container. - -Docker containers are both hardware-agnostic and platform-agnostic. This means -they can run anywhere, from your laptop to the largest EC2 compute instance and -everything in between - and they don't require you to use a particular -language, framework or packaging system. That makes them great building blocks -for deploying and scaling web apps, databases, and backend services without -depending on a particular stack or provider. - -%prep -%if 0%{?centos} <= 6 || 0%{?oraclelinux} <=6 -%setup -n %{name} -%else -%autosetup -n %{name} -%endif - -%build -export DOCKER_GITCOMMIT=%{_gitcommit} -./hack/make.sh dynbinary -# ./man/md2man-all.sh runs outside the build container (if at all), since we don't have go-md2man here - -%check -./bundles/%{_origversion}/dynbinary-daemon/dockerd -v - -%install -# install binary -install -d $RPM_BUILD_ROOT/%{_bindir} -install -p -m 755 bundles/%{_origversion}/dynbinary-daemon/dockerd-%{_origversion} $RPM_BUILD_ROOT/%{_bindir}/dockerd - -# install proxy -install -p -m 755 /usr/local/bin/docker-proxy $RPM_BUILD_ROOT/%{_bindir}/docker-proxy - -# install containerd -install -p -m 755 /usr/local/bin/docker-containerd $RPM_BUILD_ROOT/%{_bindir}/docker-containerd -install -p -m 755 /usr/local/bin/docker-containerd-shim $RPM_BUILD_ROOT/%{_bindir}/docker-containerd-shim -install -p -m 755 /usr/local/bin/docker-containerd-ctr $RPM_BUILD_ROOT/%{_bindir}/docker-containerd-ctr - -# install runc -install -p -m 755 /usr/local/bin/docker-runc $RPM_BUILD_ROOT/%{_bindir}/docker-runc - -# install tini -install -p -m 755 /usr/local/bin/docker-init $RPM_BUILD_ROOT/%{_bindir}/docker-init - -# install udev rules -install -d $RPM_BUILD_ROOT/%{_sysconfdir}/udev/rules.d -install -p -m 644 contrib/udev/80-docker.rules $RPM_BUILD_ROOT/%{_sysconfdir}/udev/rules.d/80-docker.rules - -# add init scripts -install -d $RPM_BUILD_ROOT/etc/sysconfig -install -d $RPM_BUILD_ROOT/%{_initddir} - - -%if 0%{?is_systemd} -install -d $RPM_BUILD_ROOT/%{_unitdir} -install -p -m 644 contrib/init/systemd/docker.service.rpm $RPM_BUILD_ROOT/%{_unitdir}/docker.service -%else -install -p -m 644 contrib/init/sysvinit-redhat/docker.sysconfig $RPM_BUILD_ROOT/etc/sysconfig/docker -install -p -m 755 contrib/init/sysvinit-redhat/docker $RPM_BUILD_ROOT/%{_initddir}/docker -%endif -# add bash, zsh, and fish completions -install -d $RPM_BUILD_ROOT/usr/share/bash-completion/completions -install -d $RPM_BUILD_ROOT/usr/share/zsh/vendor-completions -install -d $RPM_BUILD_ROOT/usr/share/fish/vendor_completions.d -install -p -m 644 contrib/completion/bash/docker $RPM_BUILD_ROOT/usr/share/bash-completion/completions/docker -install -p -m 644 contrib/completion/zsh/_docker $RPM_BUILD_ROOT/usr/share/zsh/vendor-completions/_docker -install -p -m 644 contrib/completion/fish/docker.fish $RPM_BUILD_ROOT/usr/share/fish/vendor_completions.d/docker.fish - -# install manpages -install -d %{buildroot}%{_mandir}/man1 -install -p -m 644 man/man1/*.1 $RPM_BUILD_ROOT/%{_mandir}/man1 -install -d %{buildroot}%{_mandir}/man5 -install -p -m 644 man/man5/*.5 $RPM_BUILD_ROOT/%{_mandir}/man5 -install -d %{buildroot}%{_mandir}/man8 -install -p -m 644 man/man8/*.8 $RPM_BUILD_ROOT/%{_mandir}/man8 - -# add vimfiles -install -d $RPM_BUILD_ROOT/usr/share/vim/vimfiles/doc -install -d $RPM_BUILD_ROOT/usr/share/vim/vimfiles/ftdetect -install -d $RPM_BUILD_ROOT/usr/share/vim/vimfiles/syntax -install -p -m 644 contrib/syntax/vim/doc/dockerfile.txt $RPM_BUILD_ROOT/usr/share/vim/vimfiles/doc/dockerfile.txt -install -p -m 644 contrib/syntax/vim/ftdetect/dockerfile.vim $RPM_BUILD_ROOT/usr/share/vim/vimfiles/ftdetect/dockerfile.vim -install -p -m 644 contrib/syntax/vim/syntax/dockerfile.vim $RPM_BUILD_ROOT/usr/share/vim/vimfiles/syntax/dockerfile.vim - -# add nano -install -d $RPM_BUILD_ROOT/usr/share/nano -install -p -m 644 contrib/syntax/nano/Dockerfile.nanorc $RPM_BUILD_ROOT/usr/share/nano/Dockerfile.nanorc - -# list files owned by the package here -%files -%doc AUTHORS CHANGELOG.md CONTRIBUTING.md LICENSE MAINTAINERS NOTICE README.md -/%{_bindir}/docker -/%{_bindir}/dockerd -/%{_bindir}/docker-containerd -/%{_bindir}/docker-containerd-shim -/%{_bindir}/docker-containerd-ctr -/%{_bindir}/docker-proxy -/%{_bindir}/docker-runc -/%{_bindir}/docker-init -/%{_sysconfdir}/udev/rules.d/80-docker.rules -%if 0%{?is_systemd} -/%{_unitdir}/docker.service -%else -%config(noreplace,missingok) /etc/sysconfig/docker -/%{_initddir}/docker -%endif -/usr/share/bash-completion/completions/docker -/usr/share/zsh/vendor-completions/_docker -/usr/share/fish/vendor_completions.d/docker.fish -%doc -/%{_mandir}/man1/* -/%{_mandir}/man5/* -/%{_mandir}/man8/* -/usr/share/vim/vimfiles/doc/dockerfile.txt -/usr/share/vim/vimfiles/ftdetect/dockerfile.vim -/usr/share/vim/vimfiles/syntax/dockerfile.vim -/usr/share/nano/Dockerfile.nanorc - -%post -%if 0%{?is_systemd} -%systemd_post docker -%else -# This adds the proper /etc/rc*.d links for the script -/sbin/chkconfig --add docker -%endif -if ! getent group docker > /dev/null; then - groupadd --system docker -fi - -%preun -%if 0%{?is_systemd} -%systemd_preun docker -%else -if [ $1 -eq 0 ] ; then - /sbin/service docker stop >/dev/null 2>&1 - /sbin/chkconfig --del docker -fi -%endif - -%postun -%if 0%{?is_systemd} -%systemd_postun_with_restart docker -%else -if [ "$1" -ge "1" ] ; then - /sbin/service docker condrestart >/dev/null 2>&1 || : -fi -%endif - -%changelog diff --git a/components/engine/hack/make/build-deb b/components/engine/hack/make/build-deb deleted file mode 100644 index a698323535..0000000000 --- a/components/engine/hack/make/build-deb +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env bash -set -e - -# subshell so that we can export PATH and TZ without breaking other things -( - export TZ=UTC # make sure our "date" variables are UTC-based - bundle .integration-daemon-start - bundle .detect-daemon-osarch - - # TODO consider using frozen images for the dockercore/builder-deb tags - - tilde='~' # ouch Bash 4.2 vs 4.3, you keel me - debVersion="${VERSION//-/$tilde}" # using \~ or '~' here works in 4.3, but not 4.2; just ~ causes $HOME to be inserted, hence the $tilde - # if we have a "-dev" suffix or have change in Git, let's make this package version more complex so it works better - if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then - gitUnix="$(git log -1 --pretty='%at')" - gitDate="$(date --date "@$gitUnix" +'%Y%m%d.%H%M%S')" - gitCommit="$(git log -1 --pretty='%h')" - gitVersion="git${gitDate}.0.${gitCommit}" - # gitVersion is now something like 'git20150128.112847.0.17e840a' - debVersion="$debVersion~$gitVersion" - - # $ dpkg --compare-versions 1.5.0 gt 1.5.0~rc1 && echo true || echo false - # true - # $ dpkg --compare-versions 1.5.0~rc1 gt 1.5.0~git20150128.112847.17e840a && echo true || echo false - # true - # $ dpkg --compare-versions 1.5.0~git20150128.112847.17e840a gt 1.5.0~dev~git20150128.112847.17e840a && echo true || echo false - # true - - # ie, 1.5.0 > 1.5.0~rc1 > 1.5.0~git20150128.112847.17e840a > 1.5.0~dev~git20150128.112847.17e840a - fi - - debSource="$(awk -F ': ' '$1 == "Source" { print $2; exit }' hack/make/.build-deb/control)" - debMaintainer="$(awk -F ': ' '$1 == "Maintainer" { print $2; exit }' hack/make/.build-deb/control)" - debDate="$(date --rfc-2822)" - - # if go-md2man is available, pre-generate the man pages - make manpages - - builderDir="contrib/builder/deb/${PACKAGE_ARCH}" - pkgs=( $(find "${builderDir}/"*/ -type d) ) - if [ ! -z "$DOCKER_BUILD_PKGS" ]; then - pkgs=() - for p in $DOCKER_BUILD_PKGS; do - pkgs+=( "$builderDir/$p" ) - done - fi - for dir in "${pkgs[@]}"; do - [ -d "$dir" ] || { echo >&2 "skipping nonexistent $dir"; continue; } - version="$(basename "$dir")" - suite="${version##*-}" - - image="dockercore/builder-deb:$version" - if ! docker inspect "$image" &> /dev/null; then - ( - # Add the APT_MIRROR args only if the consuming Dockerfile uses it - # Otherwise this will cause the build to fail - if [ "$(grep 'ARG APT_MIRROR=' $dir/Dockerfile)" ] && [ "$BUILD_APT_MIRROR" ]; then - DOCKER_BUILD_ARGS="$DOCKER_BUILD_ARGS $BUILD_APT_MIRROR" - fi - set -x && docker build ${DOCKER_BUILD_ARGS} -t "$image" "$dir" - ) - fi - - mkdir -p "$DEST/$version" - cat > "$DEST/$version/Dockerfile.build" <<-EOF - FROM $image - WORKDIR /usr/src/docker - COPY . /usr/src/docker - ENV DOCKER_GITCOMMIT $GITCOMMIT - RUN mkdir -p /go/src/github.com/docker && mkdir -p /go/src/github.com/opencontainers \ - && ln -snf /usr/src/docker /go/src/github.com/docker/docker - EOF - - cat >> "$DEST/$version/Dockerfile.build" <<-EOF - # Install runc, containerd, proxy and tini - RUN ./hack/dockerfile/install-binaries.sh runc-dynamic containerd-dynamic proxy-dynamic tini - EOF - cat >> "$DEST/$version/Dockerfile.build" <<-EOF - RUN cp -aL hack/make/.build-deb debian - RUN { echo '$debSource (${debVersion}-0~${version}) $suite; urgency=low'; echo; echo ' * Version: $VERSION'; echo; echo " -- $debMaintainer $debDate"; } > debian/changelog && cat >&2 debian/changelog - RUN dpkg-buildpackage -uc -us -I.git - EOF - tempImage="docker-temp/build-deb:$version" - ( set -x && docker build ${DOCKER_BUILD_ARGS} -t "$tempImage" -f "$DEST/$version/Dockerfile.build" . ) - docker run --rm "$tempImage" bash -c 'cd .. && tar -c *_*' | tar -xvC "$DEST/$version" - docker rmi "$tempImage" - done - - bundle .integration-daemon-stop -) 2>&1 | tee -a "$DEST/test.log" diff --git a/components/engine/hack/make/build-rpm b/components/engine/hack/make/build-rpm deleted file mode 100644 index 1e89a78d5f..0000000000 --- a/components/engine/hack/make/build-rpm +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env bash -set -e - -# subshell so that we can export PATH and TZ without breaking other things -( - export TZ=UTC # make sure our "date" variables are UTC-based - - source "$(dirname "$BASH_SOURCE")/.integration-daemon-start" - source "$(dirname "$BASH_SOURCE")/.detect-daemon-osarch" - - # TODO consider using frozen images for the dockercore/builder-rpm tags - - rpmName=docker-engine - rpmVersion="$VERSION" - rpmRelease=1 - - # rpmRelease versioning is as follows - # Docker 1.7.0: version=1.7.0, release=1 - # Docker 1.7.0-rc1: version=1.7.0, release=0.1.rc1 - # Docker 1.7.0-cs1: version=1.7.0.cs1, release=1 - # Docker 1.7.0-cs1-rc1: version=1.7.0.cs1, release=0.1.rc1 - # Docker 1.7.0-dev nightly: version=1.7.0, release=0.0.YYYYMMDD.HHMMSS.gitHASH - - # if we have a "-rc*" suffix, set appropriate release - if [[ "$rpmVersion" =~ .*-rc[0-9]+$ ]] ; then - rcVersion=${rpmVersion#*-rc} - rpmVersion=${rpmVersion%-rc*} - rpmRelease="0.${rcVersion}.rc${rcVersion}" - fi - - DOCKER_GITCOMMIT=$(git rev-parse --short HEAD) - if [ -n "$(git status --porcelain --untracked-files=no)" ]; then - DOCKER_GITCOMMIT="$DOCKER_GITCOMMIT-unsupported" - fi - - # if we have a "-dev" suffix or have change in Git, let's make this package version more complex so it works better - if [[ "$rpmVersion" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then - gitUnix="$(git log -1 --pretty='%at')" - gitDate="$(date --date "@$gitUnix" +'%Y%m%d.%H%M%S')" - gitCommit="$(git log -1 --pretty='%h')" - gitVersion="${gitDate}.git${gitCommit}" - # gitVersion is now something like '20150128.112847.17e840a' - rpmVersion="${rpmVersion%-dev}" - rpmRelease="0.0.$gitVersion" - fi - - # Replace any other dashes with periods - rpmVersion="${rpmVersion/-/.}" - - rpmPackager="$(awk -F ': ' '$1 == "Packager" { print $2; exit }' hack/make/.build-rpm/${rpmName}.spec)" - rpmDate="$(date +'%a %b %d %Y')" - - # if go-md2man is available, pre-generate the man pages - make manpages - - # Convert the CHANGELOG.md file into RPM changelog format - rm -f contrib/builder/rpm/${PACKAGE_ARCH}/changelog - VERSION_REGEX="^\W\W (.*) \((.*)\)$" - ENTRY_REGEX="^[-+*] (.*)$" - while read -r line || [[ -n "$line" ]]; do - if [ -z "$line" ]; then continue; fi - if [[ "$line" =~ $VERSION_REGEX ]]; then - echo >> contrib/builder/rpm/${PACKAGE_ARCH}/changelog - echo "* `date -d ${BASH_REMATCH[2]} '+%a %b %d %Y'` ${rpmPackager} - ${BASH_REMATCH[1]}" >> contrib/builder/rpm/${PACKAGE_ARCH}/changelog - fi - if [[ "$line" =~ $ENTRY_REGEX ]]; then - echo "- ${BASH_REMATCH[1]//\`}" >> contrib/builder/rpm/${PACKAGE_ARCH}/changelog - fi - done < CHANGELOG.md - - builderDir="contrib/builder/rpm/${PACKAGE_ARCH}" - pkgs=( $(find "${builderDir}/"*/ -type d) ) - if [ ! -z "$DOCKER_BUILD_PKGS" ]; then - pkgs=() - for p in $DOCKER_BUILD_PKGS; do - pkgs+=( "$builderDir/$p" ) - done - fi - for dir in "${pkgs[@]}"; do - [ -d "$dir" ] || { echo >&2 "skipping nonexistent $dir"; continue; } - version="$(basename "$dir")" - suite="${version##*-}" - - image="dockercore/builder-rpm:$version" - if ! docker inspect "$image" &> /dev/null; then - ( set -x && docker build ${DOCKER_BUILD_ARGS} -t "$image" "$dir" ) - fi - - mkdir -p "$DEST/$version" - cat > "$DEST/$version/Dockerfile.build" <<-EOF - FROM $image - COPY . /usr/src/${rpmName} - WORKDIR /usr/src/${rpmName} - RUN mkdir -p /go/src/github.com/docker && mkdir -p /go/src/github.com/opencontainers - EOF - - cat >> "$DEST/$version/Dockerfile.build" <<-EOF - # Install runc, containerd, proxy and tini - RUN TMP_GOPATH="/go" ./hack/dockerfile/install-binaries.sh runc-dynamic containerd-dynamic proxy-dynamic tini - EOF - if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then - echo 'ENV DOCKER_EXPERIMENTAL 1' >> "$DEST/$version/Dockerfile.build" - fi - cat >> "$DEST/$version/Dockerfile.build" <<-EOF - RUN mkdir -p /root/rpmbuild/SOURCES \ - && echo '%_topdir /root/rpmbuild' > /root/.rpmmacros - WORKDIR /root/rpmbuild - RUN ln -sfv /usr/src/${rpmName}/hack/make/.build-rpm SPECS - WORKDIR /root/rpmbuild/SPECS - RUN tar --exclude .git -r -C /usr/src -f /root/rpmbuild/SOURCES/${rpmName}.tar ${rpmName} - RUN tar --exclude .git -r -C /go/src/github.com/docker -f /root/rpmbuild/SOURCES/${rpmName}.tar containerd - RUN tar --exclude .git -r -C /go/src/github.com/docker/libnetwork/cmd -f /root/rpmbuild/SOURCES/${rpmName}.tar proxy - RUN tar --exclude .git -r -C /go/src/github.com/opencontainers -f /root/rpmbuild/SOURCES/${rpmName}.tar runc - RUN tar --exclude .git -r -C /go/ -f /root/rpmbuild/SOURCES/${rpmName}.tar tini - RUN gzip /root/rpmbuild/SOURCES/${rpmName}.tar - RUN { cat /usr/src/${rpmName}/contrib/builder/rpm/${PACKAGE_ARCH}/changelog; } >> ${rpmName}.spec && tail >&2 ${rpmName}.spec - RUN rpmbuild -ba \ - --define '_gitcommit $DOCKER_GITCOMMIT' \ - --define '_release $rpmRelease' \ - --define '_version $rpmVersion' \ - --define '_origversion $VERSION' \ - --define '_experimental ${DOCKER_EXPERIMENTAL:-0}' \ - ${rpmName}.spec - EOF - # selinux policy referencing systemd things won't work on non-systemd versions - # of centos or rhel, which we don't support anyways - if [ "${suite%.*}" -gt 6 ] && [[ "$version" != opensuse* ]]; then - if [ -d "./contrib/selinux-$version" ]; then - selinuxDir="selinux-${version}" - cat >> "$DEST/$version/Dockerfile.build" <<-EOF - RUN tar -cz -C /usr/src/${rpmName}/contrib/${selinuxDir} -f /root/rpmbuild/SOURCES/${rpmName}-selinux.tar.gz ${rpmName}-selinux - RUN rpmbuild -ba \ - --define '_gitcommit $DOCKER_GITCOMMIT' \ - --define '_release $rpmRelease' \ - --define '_version $rpmVersion' \ - --define '_origversion $VERSION' \ - ${rpmName}-selinux.spec - EOF - fi - fi - tempImage="docker-temp/build-rpm:$version" - ( set -x && docker build ${DOCKER_BUILD_ARGS} -t "$tempImage" -f $DEST/$version/Dockerfile.build . ) - docker run --rm "$tempImage" bash -c 'cd /root/rpmbuild && tar -c *RPMS' | tar -xvC "$DEST/$version" - docker rmi "$tempImage" - done - - source "$(dirname "$BASH_SOURCE")/.integration-daemon-stop" -) 2>&1 | tee -a $DEST/test.log diff --git a/components/engine/hack/make/clean-apt-repo b/components/engine/hack/make/clean-apt-repo deleted file mode 100755 index e823cb537f..0000000000 --- a/components/engine/hack/make/clean-apt-repo +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -set -e - -# This script cleans the experimental pool for the apt repo. -# This is useful when there are a lot of old experimental debs and you only want to keep the most recent. -# - -: ${DOCKER_RELEASE_DIR:=$DEST} -APTDIR=$DOCKER_RELEASE_DIR/apt/repo/pool/experimental -: ${DOCKER_ARCHIVE_DIR:=$DEST/archive} -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -latest_versions=$(dpkg-scanpackages "$APTDIR" /dev/null 2>/dev/null | awk -F ': ' '$1 == "Filename" { print $2 }') - -# get the latest version -latest_docker_engine_file=$(echo "$latest_versions" | grep docker-engine) -latest_docker_engine_version=$(basename ${latest_docker_engine_file%~*}) - -echo "latest docker-engine version: $latest_docker_engine_version" - -# remove all the files that are not that version in experimental -pool_dir=$(dirname "$latest_docker_engine_file") -old_pkgs=( $(ls "$pool_dir" | grep -v "^${latest_docker_engine_version}" | grep "${latest_docker_engine_version%%~git*}") ) - -echo "${old_pkgs[@]}" - -mkdir -p "$DOCKER_ARCHIVE_DIR" -for old_pkg in "${old_pkgs[@]}"; do - echo "moving ${pool_dir}/${old_pkg} to $DOCKER_ARCHIVE_DIR" - mv "${pool_dir}/${old_pkg}" "$DOCKER_ARCHIVE_DIR" -done - -echo -echo "$pool_dir now has contents:" -ls "$pool_dir" - -# now regenerate release files for experimental -export COMPONENT=experimental -source "${DIR}/update-apt-repo" - -echo "You will now want to: " -echo " - re-sign the repo with hack/make/sign-repo" -echo " - re-generate index files with hack/make/generate-index-listing" diff --git a/components/engine/hack/make/clean-yum-repo b/components/engine/hack/make/clean-yum-repo deleted file mode 100755 index 012689a965..0000000000 --- a/components/engine/hack/make/clean-yum-repo +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -set -e - -# This script cleans the experimental pool for the yum repo. -# This is useful when there are a lot of old experimental rpms and you only want to keep the most recent. -# - -: ${DOCKER_RELEASE_DIR:=$DEST} -YUMDIR=$DOCKER_RELEASE_DIR/yum/repo/experimental - -suites=( $(find "$YUMDIR" -mindepth 1 -maxdepth 1 -type d) ) - -for suite in "${suites[@]}"; do - echo "cleanup in: $suite" - ( set -x; repomanage -k2 --old "$suite" | xargs rm -f ) -done - -echo "You will now want to: " -echo " - re-sign the repo with hack/make/sign-repo" -echo " - re-generate index files with hack/make/generate-index-listing" diff --git a/components/engine/hack/make/cover b/components/engine/hack/make/cover deleted file mode 100644 index 4a37995f69..0000000000 --- a/components/engine/hack/make/cover +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -set -e - -bundle_cover() { - coverprofiles=( "$DEST/../"*"/coverprofiles/"* ) - for p in "${coverprofiles[@]}"; do - echo - ( - set -x - go tool cover -func="$p" - ) - done -} - -bundle_cover 2>&1 | tee "$DEST/report.log" diff --git a/components/engine/hack/make/generate-index-listing b/components/engine/hack/make/generate-index-listing deleted file mode 100755 index 9f1208403f..0000000000 --- a/components/engine/hack/make/generate-index-listing +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash -set -e - -# This script generates index files for the directory structure -# of the apt and yum repos - -: ${DOCKER_RELEASE_DIR:=$DEST} -APTDIR=$DOCKER_RELEASE_DIR/apt -YUMDIR=$DOCKER_RELEASE_DIR/yum - -if [ ! -d $APTDIR ] && [ ! -d $YUMDIR ]; then - echo >&2 'release-rpm or release-deb must be run before generate-index-listing' - exit 1 -fi - -create_index() { - local directory=$1 - local original=$2 - local cleaned=${directory#$original} - - # the index file to create - local index_file="${directory}/index" - - # cd into dir & touch the index file - cd $directory - touch $index_file - - # print the html header - cat <<-EOF > "$index_file" - - - Index of ${cleaned}/ - -

Index of ${cleaned}/


-
../
-	EOF
-
-	# start of content output
-	(
-	# change IFS locally within subshell so the for loop saves line correctly to L var
-	IFS=$'\n';
-
-	# pretty sweet, will mimic the normal apache output. skipping "index" and hidden files
-	for L in $(find -L . -mount -depth -maxdepth 1 -type f ! -name 'index' ! -name '.*' -prune -printf "%f|@_@%Td-%Tb-%TY %Tk:%TM  @%f@\n"|sort|column -t -s '|' | sed 's,\([\ ]\+\)@_@,\1,g');
-	do
-		# file
-		F=$(sed -e 's,^.*@\([^@]\+\)@.*$,\1,g'<<<"$L");
-
-		# file with file size
-		F=$(du -bh $F | cut -f1);
-
-		# output with correct format
-		sed -e 's,\ @.*$, '"$F"',g'<<<"$L";
-	done;
-	) >> $index_file;
-
-	# now output a list of all directories in this dir (maxdepth 1) other than '.' outputting in a sorted manner exactly like apache
-	find -L . -mount -depth -maxdepth 1 -type d ! -name '.' -printf "%-43f@_@%Td-%Tb-%TY %Tk:%TM  -\n"|sort -d|sed 's,\([\ ]\+\)@_@,/\1,g' >> $index_file
-
-	# print the footer html
-	echo "

" >> $index_file - -} - -get_dirs() { - local directory=$1 - - for d in `find ${directory} -type d`; do - create_index $d $directory - done -} - -get_dirs $APTDIR -get_dirs $YUMDIR diff --git a/components/engine/hack/make/install-binary b/components/engine/hack/make/install-binary old mode 100755 new mode 100644 index 57aa1a28c1..f6a4361fdb --- a/components/engine/hack/make/install-binary +++ b/components/engine/hack/make/install-binary @@ -3,6 +3,27 @@ set -e rm -rf "$DEST" +install_binary() { + local file="$1" + local target="${DOCKER_MAKE_INSTALL_PREFIX:=/usr/local}/bin/" + if [ "$(go env GOOS)" == "linux" ]; then + echo "Installing $(basename $file) to ${target}" + mkdir -p "$target" + cp -f -L "$file" "$target" + else + echo "Install is only supported on linux" + return 1 + fi +} + ( - source "${MAKEDIR}/install-binary-daemon" + DEST="$(dirname $DEST)/binary-daemon" + source "${MAKEDIR}/.binary-setup" + install_binary "${DEST}/${DOCKER_DAEMON_BINARY_NAME}" + install_binary "${DEST}/${DOCKER_RUNC_BINARY_NAME}" + install_binary "${DEST}/${DOCKER_CONTAINERD_BINARY_NAME}" + install_binary "${DEST}/${DOCKER_CONTAINERD_CTR_BINARY_NAME}" + install_binary "${DEST}/${DOCKER_CONTAINERD_SHIM_BINARY_NAME}" + install_binary "${DEST}/${DOCKER_PROXY_BINARY_NAME}" + install_binary "${DEST}/${DOCKER_INIT_BINARY_NAME}" ) diff --git a/components/engine/hack/make/install-binary-daemon b/components/engine/hack/make/install-binary-daemon deleted file mode 100644 index f6a4361fdb..0000000000 --- a/components/engine/hack/make/install-binary-daemon +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -set -e -rm -rf "$DEST" - -install_binary() { - local file="$1" - local target="${DOCKER_MAKE_INSTALL_PREFIX:=/usr/local}/bin/" - if [ "$(go env GOOS)" == "linux" ]; then - echo "Installing $(basename $file) to ${target}" - mkdir -p "$target" - cp -f -L "$file" "$target" - else - echo "Install is only supported on linux" - return 1 - fi -} - -( - DEST="$(dirname $DEST)/binary-daemon" - source "${MAKEDIR}/.binary-setup" - install_binary "${DEST}/${DOCKER_DAEMON_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_RUNC_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_CONTAINERD_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_CONTAINERD_CTR_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_CONTAINERD_SHIM_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_PROXY_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_INIT_BINARY_NAME}" -) diff --git a/components/engine/hack/make/release-deb b/components/engine/hack/make/release-deb deleted file mode 100755 index acf4901d6e..0000000000 --- a/components/engine/hack/make/release-deb +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env bash -set -e - -# This script creates the apt repos for the .deb files generated by hack/make/build-deb -# -# The following can then be used as apt sources: -# deb http://apt.dockerproject.org/repo $distro-$release $version -# -# For example: -# deb http://apt.dockerproject.org/repo ubuntu-trusty main -# deb http://apt.dockerproject.org/repo ubuntu-trusty testing -# deb http://apt.dockerproject.org/repo debian-wheezy experimental -# deb http://apt.dockerproject.org/repo debian-jessie main -# -# ... and so on and so forth for the builds created by hack/make/build-deb - -: ${DOCKER_RELEASE_DIR:=$DEST} -: ${GPG_KEYID:=releasedocker} -APTDIR=$DOCKER_RELEASE_DIR/apt/repo - -# setup the apt repo (if it does not exist) -mkdir -p "$APTDIR/conf" "$APTDIR/db" "$APTDIR/dists" - -# supported arches/sections -arches=( amd64 i386 armhf ppc64le s390x ) - -# Preserve existing components but don't add any non-existing ones -for component in main testing experimental ; do - exists=$(find "$APTDIR/dists" -mindepth 2 -maxdepth 2 -type d -name "$component" -print -quit) - if [ -n "$exists" ] ; then - components+=( $component ) - fi -done - -# set the component for the version being released -component="main" - -if [[ "$VERSION" == *-rc* ]]; then - component="testing" -fi - -if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then - component="experimental" -fi - -# Make sure our component is in the list of components -if [[ ! "${components[*]}" =~ $component ]] ; then - components+=( $component ) -fi - -# create apt-ftparchive file on every run. This is essential to avoid -# using stale versions of the config file that could cause unnecessary -# refreshing of bits for EOL-ed releases. -cat <<-EOF > "$APTDIR/conf/apt-ftparchive.conf" -Dir { - ArchiveDir "${APTDIR}"; - CacheDir "${APTDIR}/db"; -}; - -Default { - Packages::Compress ". gzip bzip2"; - Sources::Compress ". gzip bzip2"; - Contents::Compress ". gzip bzip2"; -}; - -TreeDefault { - BinCacheDB "packages-\$(SECTION)-\$(ARCH).db"; - Directory "pool/\$(SECTION)"; - Packages "\$(DIST)/\$(SECTION)/binary-\$(ARCH)/Packages"; - SrcDirectory "pool/\$(SECTION)"; - Sources "\$(DIST)/\$(SECTION)/source/Sources"; - Contents "\$(DIST)/\$(SECTION)/Contents-\$(ARCH)"; - FileList "$APTDIR/\$(DIST)/\$(SECTION)/filelist"; -}; -EOF - -for dir in bundles/$VERSION/build-deb/*/; do - version="$(basename "$dir")" - suite="${version//debootstrap-}" - - cat <<-EOF - Tree "dists/${suite}" { - Sections "${components[*]}"; - Architectures "${arches[*]}"; - } - - EOF -done >> "$APTDIR/conf/apt-ftparchive.conf" - -cat <<-EOF > "$APTDIR/conf/docker-engine-release.conf" -APT::FTPArchive::Release::Origin "Docker"; -APT::FTPArchive::Release::Components "${components[*]}"; -APT::FTPArchive::Release::Label "Docker APT Repository"; -APT::FTPArchive::Release::Architectures "${arches[*]}"; -EOF - -# release the debs -for dir in bundles/$VERSION/build-deb/*/; do - version="$(basename "$dir")" - codename="${version//debootstrap-}" - - tempdir="$(mktemp -d /tmp/tmp-docker-release-deb.XXXXXXXX)" - DEBFILE=( "$dir/docker-engine"*.deb ) - - # add the deb for each component for the distro version into the - # pool (if it is not there already) - mkdir -p "$APTDIR/pool/$component/d/docker-engine/" - for deb in ${DEBFILE[@]}; do - d=$(basename "$deb") - # We do not want to generate a new deb if it has already been - # copied into the APTDIR - if [ ! -f "$APTDIR/pool/$component/d/docker-engine/$d" ]; then - cp "$deb" "$tempdir/" - # if we have a $GPG_PASSPHRASE we may as well - # dpkg-sign before copying the deb into the pool - if [ ! -z "$GPG_PASSPHRASE" ]; then - dpkg-sig -g "--no-tty --digest-algo 'sha512' --passphrase '$GPG_PASSPHRASE'" \ - -k "$GPG_KEYID" --sign builder "$tempdir/$d" - fi - mv "$tempdir/$d" "$APTDIR/pool/$component/d/docker-engine/" - fi - done - - rm -rf "$tempdir" - - # build the right directory structure, needed for apt-ftparchive - for arch in "${arches[@]}"; do - for c in "${components[@]}"; do - mkdir -p "$APTDIR/dists/$codename/$c/binary-$arch" - done - done - - # update the filelist for this codename/component - find "$APTDIR/pool/$component" \ - -name *~${codename}*.deb -o \ - -name *~${codename#*-}*.deb > "$APTDIR/dists/$codename/$component/filelist" -done - -# run the apt-ftparchive commands so we can have pinning -apt-ftparchive generate "$APTDIR/conf/apt-ftparchive.conf" - -for dir in bundles/$VERSION/build-deb/*/; do - version="$(basename "$dir")" - codename="${version//debootstrap-}" - - apt-ftparchive \ - -c "$APTDIR/conf/docker-engine-release.conf" \ - -o "APT::FTPArchive::Release::Codename=$codename" \ - -o "APT::FTPArchive::Release::Suite=$codename" \ - release \ - "$APTDIR/dists/$codename" > "$APTDIR/dists/$codename/Release" - - for arch in "${arches[@]}"; do - apt-ftparchive \ - -c "$APTDIR/conf/docker-engine-release.conf" \ - -o "APT::FTPArchive::Release::Codename=$codename" \ - -o "APT::FTPArchive::Release::Suite=$codename" \ - -o "APT::FTPArchive::Release::Components=$component" \ - -o "APT::FTPArchive::Release::Architecture=$arch" \ - release \ - "$APTDIR/dists/$codename/$component/binary-$arch" > "$APTDIR/dists/$codename/$component/binary-$arch/Release" - done -done diff --git a/components/engine/hack/make/release-rpm b/components/engine/hack/make/release-rpm deleted file mode 100755 index 477d15bee9..0000000000 --- a/components/engine/hack/make/release-rpm +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -set -e - -# This script creates the yum repos for the .rpm files generated by hack/make/build-rpm -# -# The following can then be used as a yum repo: -# http://yum.dockerproject.org/repo/$release/$distro/$distro-version -# -# For example: -# http://yum.dockerproject.org/repo/main/fedora/23 -# http://yum.dockerproject.org/repo/testing/centos/7 -# http://yum.dockerproject.org/repo/experimental/fedora/23 -# http://yum.dockerproject.org/repo/main/centos/7 -# -# ... and so on and so forth for the builds created by hack/make/build-rpm - -: ${DOCKER_RELEASE_DIR:=$DEST} -YUMDIR=$DOCKER_RELEASE_DIR/yum/repo -: ${GPG_KEYID:=releasedocker} - -# get the release -release="main" - -if [[ "$VERSION" == *-rc* ]]; then - release="testing" -fi - -if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then - release="experimental" -fi - -# Setup the yum repo -for dir in bundles/$VERSION/build-rpm/*/; do - version="$(basename "$dir")" - suite="${version##*-}" - distro="${version%-*}" - - REPO=$YUMDIR/$release/$distro - - # if the directory does not exist, initialize the yum repo - if [[ ! -d $REPO/$suite/Packages ]]; then - mkdir -p "$REPO/$suite/Packages" - - createrepo --pretty "$REPO/$suite" - fi - - # path to rpms - RPMFILE=( "bundles/$VERSION/build-rpm/$version/RPMS/"*"/docker-engine"*.rpm "bundles/$VERSION/build-rpm/$version/SRPMS/docker-engine"*.rpm ) - - # if we have a $GPG_PASSPHRASE we may as well - # sign the rpms before adding to repo - if [ ! -z $GPG_PASSPHRASE ]; then - # export our key to rpm import - gpg --armor --export "$GPG_KEYID" > /tmp/gpg - rpm --import /tmp/gpg - - # sign the rpms - echo "yes" | setsid rpm \ - --define "_gpg_name $GPG_KEYID" \ - --define "_signature gpg" \ - --define "__gpg_check_password_cmd /bin/true" \ - --define "__gpg_sign_cmd %{__gpg} gpg --batch --no-armor --digest-algo 'sha512' --passphrase '$GPG_PASSPHRASE' --no-secmem-warning -u '%{_gpg_name}' --sign --detach-sign --output %{__signature_filename} %{__plaintext_filename}" \ - --resign "${RPMFILE[@]}" - fi - - # copy the rpms to the packages folder - cp "${RPMFILE[@]}" "$REPO/$suite/Packages" - - # update the repo - createrepo --pretty --update "$REPO/$suite" -done diff --git a/components/engine/hack/make/sign-repos b/components/engine/hack/make/sign-repos deleted file mode 100755 index 61dbd7acce..0000000000 --- a/components/engine/hack/make/sign-repos +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env bash - -# This script signs the deliverables from release-deb and release-rpm -# with a designated GPG key. - -: ${DOCKER_RELEASE_DIR:=$DEST} -: ${GPG_KEYID:=releasedocker} -APTDIR=$DOCKER_RELEASE_DIR/apt/repo -YUMDIR=$DOCKER_RELEASE_DIR/yum/repo - -if [ -z "$GPG_PASSPHRASE" ]; then - echo >&2 'you need to set GPG_PASSPHRASE in order to sign artifacts' - exit 1 -fi - -if [ ! -d $APTDIR ] && [ ! -d $YUMDIR ]; then - echo >&2 'release-rpm or release-deb must be run before sign-repos' - exit 1 -fi - -sign_packages(){ - # sign apt repo metadata - if [ -d $APTDIR ]; then - # create file with public key - gpg --armor --export "$GPG_KEYID" > "$DOCKER_RELEASE_DIR/apt/gpg" - - # sign the repo metadata - for F in $(find $APTDIR -name Release); do - if test "$F" -nt "$F.gpg" ; then - gpg -u "$GPG_KEYID" --passphrase "$GPG_PASSPHRASE" \ - --digest-algo "sha512" \ - --armor --sign --detach-sign \ - --batch --yes \ - --output "$F.gpg" "$F" - fi - inRelease="$(dirname "$F")/InRelease" - if test "$F" -nt "$inRelease" ; then - gpg -u "$GPG_KEYID" --passphrase "$GPG_PASSPHRASE" \ - --digest-algo "sha512" \ - --clearsign \ - --batch --yes \ - --output "$inRelease" "$F" - fi - done - fi - - # sign yum repo metadata - if [ -d $YUMDIR ]; then - # create file with public key - gpg --armor --export "$GPG_KEYID" > "$DOCKER_RELEASE_DIR/yum/gpg" - - # sign the repo metadata - for F in $(find $YUMDIR -name repomd.xml); do - if test "$F" -nt "$F.asc" ; then - gpg -u "$GPG_KEYID" --passphrase "$GPG_PASSPHRASE" \ - --digest-algo "sha512" \ - --armor --sign --detach-sign \ - --batch --yes \ - --output "$F.asc" "$F" - fi - done - fi -} - -sign_packages diff --git a/components/engine/hack/make/test-old-apt-repo b/components/engine/hack/make/test-old-apt-repo deleted file mode 100755 index e92b20ef06..0000000000 --- a/components/engine/hack/make/test-old-apt-repo +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -set -e - -versions=( 1.3.3 1.4.1 1.5.0 1.6.2 ) - -install() { - local version=$1 - local tmpdir=$(mktemp -d /tmp/XXXXXXXXXX) - local dockerfile="${tmpdir}/Dockerfile" - cat <<-EOF > "$dockerfile" - FROM debian:jessie - ENV VERSION ${version} - RUN apt-get update && apt-get install -y \ - apt-transport-https \ - ca-certificates \ - --no-install-recommends - RUN echo "deb https://get.docker.com/ubuntu docker main" > /etc/apt/sources.list.d/docker.list - RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 \ - --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9 - RUN apt-get update && apt-get install -y \ - lxc-docker-\${VERSION} - EOF - - docker build --rm --force-rm --no-cache -t docker-old-repo:${version} -f $dockerfile $tmpdir -} - -for v in "${versions[@]}"; do - install "$v" -done diff --git a/components/engine/hack/make/ubuntu b/components/engine/hack/make/ubuntu deleted file mode 100644 index ad3f1d78bb..0000000000 --- a/components/engine/hack/make/ubuntu +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env bash - -PKGVERSION="${VERSION//-/'~'}" -# if we have a "-dev" suffix or have change in Git, let's make this package version more complex so it works better -if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then - GIT_UNIX="$(git log -1 --pretty='%at')" - GIT_DATE="$(date --date "@$GIT_UNIX" +'%Y%m%d.%H%M%S')" - GIT_COMMIT="$(git log -1 --pretty='%h')" - GIT_VERSION="git${GIT_DATE}.0.${GIT_COMMIT}" - # GIT_VERSION is now something like 'git20150128.112847.0.17e840a' - PKGVERSION="$PKGVERSION~$GIT_VERSION" -fi - -# $ dpkg --compare-versions 1.5.0 gt 1.5.0~rc1 && echo true || echo false -# true -# $ dpkg --compare-versions 1.5.0~rc1 gt 1.5.0~git20150128.112847.17e840a && echo true || echo false -# true -# $ dpkg --compare-versions 1.5.0~git20150128.112847.17e840a gt 1.5.0~dev~git20150128.112847.17e840a && echo true || echo false -# true - -# ie, 1.5.0 > 1.5.0~rc1 > 1.5.0~git20150128.112847.17e840a > 1.5.0~dev~git20150128.112847.17e840a - -PACKAGE_ARCHITECTURE="$(dpkg-architecture -qDEB_HOST_ARCH)" -PACKAGE_URL="https://www.docker.com/" -PACKAGE_MAINTAINER="support@docker.com" -PACKAGE_DESCRIPTION="Linux container runtime -Docker complements LXC with a high-level API which operates at the process -level. It runs unix processes with strong guarantees of isolation and -repeatability across servers. -Docker is a great building block for automating distributed systems: -large-scale web deployments, database clusters, continuous deployment systems, -private PaaS, service-oriented architectures, etc." -PACKAGE_LICENSE="Apache-2.0" - -# Build docker as an ubuntu package using FPM and REPREPRO (sue me). -# bundle_binary must be called first. -bundle_ubuntu() { - DIR="$ABS_DEST/build" - - # Include our udev rules - mkdir -p "$DIR/etc/udev/rules.d" - cp contrib/udev/80-docker.rules "$DIR/etc/udev/rules.d/" - - # Include our init scripts - mkdir -p "$DIR/etc/init" - cp contrib/init/upstart/docker.conf "$DIR/etc/init/" - mkdir -p "$DIR/etc/init.d" - cp contrib/init/sysvinit-debian/docker "$DIR/etc/init.d/" - mkdir -p "$DIR/etc/default" - cp contrib/init/sysvinit-debian/docker.default "$DIR/etc/default/docker" - mkdir -p "$DIR/lib/systemd/system" - cp contrib/init/systemd/docker.{service,socket} "$DIR/lib/systemd/system/" - - # Include contributed completions - mkdir -p "$DIR/etc/bash_completion.d" - cp contrib/completion/bash/docker "$DIR/etc/bash_completion.d/" - mkdir -p "$DIR/usr/share/zsh/vendor-completions" - cp contrib/completion/zsh/_docker "$DIR/usr/share/zsh/vendor-completions/" - mkdir -p "$DIR/etc/fish/completions" - cp contrib/completion/fish/docker.fish "$DIR/etc/fish/completions/" - - # Include man pages - make manpages - manRoot="$DIR/usr/share/man" - mkdir -p "$manRoot" - for manDir in man/man?; do - manBase="$(basename "$manDir")" # "man1" - for manFile in "$manDir"/*; do - manName="$(basename "$manFile")" # "docker-build.1" - mkdir -p "$manRoot/$manBase" - gzip -c "$manFile" > "$manRoot/$manBase/$manName.gz" - done - done - - # Copy the binary - # This will fail if the binary bundle hasn't been built - mkdir -p "$DIR/usr/bin" - cp "$DEST/../binary/docker-$VERSION" "$DIR/usr/bin/docker" - - # Generate postinst/prerm/postrm scripts - cat > "$DEST/postinst" <<'EOF' -#!/bin/sh -set -e -set -u - -if [ "$1" = 'configure' ] && [ -z "$2" ]; then - if ! getent group docker > /dev/null; then - groupadd --system docker - fi -fi - -if ! { [ -x /sbin/initctl ] && /sbin/initctl version 2>/dev/null | grep -q upstart; }; then - # we only need to do this if upstart isn't in charge - update-rc.d docker defaults > /dev/null || true -fi -if [ -n "$2" ]; then - _dh_action=restart -else - _dh_action=start -fi -service docker $_dh_action 2>/dev/null || true - -#DEBHELPER# -EOF - cat > "$DEST/prerm" <<'EOF' -#!/bin/sh -set -e -set -u - -service docker stop 2>/dev/null || true - -#DEBHELPER# -EOF - cat > "$DEST/postrm" <<'EOF' -#!/bin/sh -set -e -set -u - -if [ "$1" = "purge" ] ; then - update-rc.d docker remove > /dev/null || true -fi - -# In case this system is running systemd, we make systemd reload the unit files -# to pick up changes. -if [ -d /run/systemd/system ] ; then - systemctl --system daemon-reload > /dev/null || true -fi - -#DEBHELPER# -EOF - # TODO swaths of these were borrowed from debhelper's auto-inserted stuff, because we're still using fpm - we need to use debhelper instead, and somehow reconcile Ubuntu that way - chmod +x "$DEST/postinst" "$DEST/prerm" "$DEST/postrm" - - ( - # switch directories so we create *.deb in the right folder - cd "$DEST" - - # create lxc-docker-VERSION package - fpm -s dir -C "$DIR" \ - --name "lxc-docker-$VERSION" --version "$PKGVERSION" \ - --after-install "$ABS_DEST/postinst" \ - --before-remove "$ABS_DEST/prerm" \ - --after-remove "$ABS_DEST/postrm" \ - --architecture "$PACKAGE_ARCHITECTURE" \ - --prefix / \ - --depends iptables \ - --deb-recommends aufs-tools \ - --deb-recommends ca-certificates \ - --deb-recommends git \ - --deb-recommends xz-utils \ - --deb-recommends 'cgroupfs-mount | cgroup-lite' \ - --deb-suggests apparmor \ - --description "$PACKAGE_DESCRIPTION" \ - --maintainer "$PACKAGE_MAINTAINER" \ - --conflicts docker \ - --conflicts docker.io \ - --conflicts lxc-docker-virtual-package \ - --provides lxc-docker \ - --provides lxc-docker-virtual-package \ - --replaces lxc-docker \ - --replaces lxc-docker-virtual-package \ - --url "$PACKAGE_URL" \ - --license "$PACKAGE_LICENSE" \ - --config-files /etc/udev/rules.d/80-docker.rules \ - --config-files /etc/init/docker.conf \ - --config-files /etc/init.d/docker \ - --config-files /etc/default/docker \ - --deb-compression gz \ - -t deb . - # TODO replace "Suggests: cgroup-lite" with "Recommends: cgroupfs-mount | cgroup-lite" once cgroupfs-mount is available - - # create empty lxc-docker wrapper package - fpm -s empty \ - --name lxc-docker --version "$PKGVERSION" \ - --architecture "$PACKAGE_ARCHITECTURE" \ - --depends lxc-docker-$VERSION \ - --description "$PACKAGE_DESCRIPTION" \ - --maintainer "$PACKAGE_MAINTAINER" \ - --url "$PACKAGE_URL" \ - --license "$PACKAGE_LICENSE" \ - --deb-compression gz \ - -t deb - ) - - # clean up after ourselves so we have a clean output directory - rm "$DEST/postinst" "$DEST/prerm" "$DEST/postrm" - rm -r "$DIR" -} - -bundle_ubuntu diff --git a/components/engine/hack/make/update-apt-repo b/components/engine/hack/make/update-apt-repo deleted file mode 100755 index 3a80c94ca3..0000000000 --- a/components/engine/hack/make/update-apt-repo +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash -set -e - -# This script updates the apt repo in $DOCKER_RELEASE_DIR/apt/repo. -# This script is a "fix all" for any sort of problems that might have occurred with -# the Release or Package files in the repo. -# It should only be used in the rare case of extreme emergencies to regenerate -# Release and Package files for the apt repo. -# -# NOTE: Always be sure to re-sign the repo with hack/make/sign-repos after running -# this script. - -: ${DOCKER_RELEASE_DIR:=$DEST} -APTDIR=$DOCKER_RELEASE_DIR/apt/repo - -# supported arches/sections -arches=( amd64 i386 ) - -# Preserve existing components but don't add any non-existing ones -for component in main testing experimental ; do - if ls "$APTDIR/dists/*/$component" >/dev/null 2>&1 ; then - components+=( $component ) - fi -done - -dists=( $(find "${APTDIR}/dists" -maxdepth 1 -mindepth 1 -type d) ) - -# override component if it is set -if [ "$COMPONENT" ]; then - components=( $COMPONENT ) -fi - -# release the debs -for version in "${dists[@]}"; do - for component in "${components[@]}"; do - codename="${version//debootstrap-}" - - # update the filelist for this codename/component - find "$APTDIR/pool/$component" \ - -name *~${codename#*-}*.deb > "$APTDIR/dists/$codename/$component/filelist" - done -done - -# run the apt-ftparchive commands so we can have pinning -apt-ftparchive generate "$APTDIR/conf/apt-ftparchive.conf" - -for dist in "${dists[@]}"; do - version=$(basename "$dist") - for component in "${components[@]}"; do - codename="${version//debootstrap-}" - - apt-ftparchive \ - -o "APT::FTPArchive::Release::Codename=$codename" \ - -o "APT::FTPArchive::Release::Suite=$codename" \ - -c "$APTDIR/conf/docker-engine-release.conf" \ - release \ - "$APTDIR/dists/$codename" > "$APTDIR/dists/$codename/Release" - - for arch in "${arches[@]}"; do - apt-ftparchive \ - -o "APT::FTPArchive::Release::Codename=$codename" \ - -o "APT::FTPArchive::Release::Suite=$codename" \ - -o "APT::FTPArchive::Release::Component=$component" \ - -o "APT::FTPArchive::Release::Architecture=$arch" \ - -c "$APTDIR/conf/docker-engine-release.conf" \ - release \ - "$APTDIR/dists/$codename/$component/binary-$arch" > "$APTDIR/dists/$codename/$component/binary-$arch/Release" - done - done -done diff --git a/components/engine/hack/make/win b/components/engine/hack/make/win deleted file mode 100644 index bc6d5108b8..0000000000 --- a/components/engine/hack/make/win +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -set -e - -# explicit list of os/arch combos that support being a daemon -declare -A daemonSupporting -daemonSupporting=( - [linux/amd64]=1 - [windows/amd64]=1 -) -platform="windows/amd64" -export DEST="$DEST/$platform" # bundles/VERSION/cross/GOOS/GOARCH/docker-VERSION -mkdir -p "$DEST" -ABS_DEST="$(cd "$DEST" && pwd -P)" -export GOOS=${platform%/*} -export GOARCH=${platform##*/} -if [ -z "${daemonSupporting[$platform]}" ]; then - export LDFLAGS_STATIC_DOCKER="" # we just need a simple client for these platforms - export BUILDFLAGS=( "${ORIG_BUILDFLAGS[@]/ daemon/}" ) # remove the "daemon" build tag from platforms that aren't supported -fi -source "${MAKEDIR}/binary" diff --git a/components/engine/hack/release.sh b/components/engine/hack/release.sh deleted file mode 100755 index 4a4f402f5a..0000000000 --- a/components/engine/hack/release.sh +++ /dev/null @@ -1,313 +0,0 @@ -#!/usr/bin/env bash -set -e - -# This script looks for bundles built by make.sh, and releases them on a -# public S3 bucket. -# -# Bundles should be available for the VERSION string passed as argument. -# -# The correct way to call this script is inside a container built by the -# official Dockerfile at the root of the Docker source code. The Dockerfile, -# make.sh and release.sh should all be from the same source code revision. - -set -o pipefail - -# Print a usage message and exit. -usage() { - cat >&2 <<'EOF' -To run, I need: -- to be in a container generated by the Dockerfile at the top of the Docker - repository; -- to be provided with the location of an S3 bucket and path, in - environment variables AWS_S3_BUCKET and AWS_S3_BUCKET_PATH (default: ''); -- to be provided with AWS credentials for this S3 bucket, in environment - variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY; -- a generous amount of good will and nice manners. -The canonical way to run me is to run the image produced by the Dockerfile: e.g.:" - -docker run -e AWS_S3_BUCKET=test.docker.com \ - -e AWS_ACCESS_KEY_ID \ - -e AWS_SECRET_ACCESS_KEY \ - -e AWS_DEFAULT_REGION \ - -it --privileged \ - docker ./hack/release.sh -EOF - exit 1 -} - -[ "$AWS_S3_BUCKET" ] || usage -[ "$AWS_ACCESS_KEY_ID" ] || usage -[ "$AWS_SECRET_ACCESS_KEY" ] || usage -[ -d /go/src/github.com/docker/docker ] || usage -cd /go/src/github.com/docker/docker -[ -x hack/make.sh ] || usage - -export AWS_DEFAULT_REGION -: ${AWS_DEFAULT_REGION:=us-west-1} - -AWS_CLI=${AWS_CLI:-'aws'} - -RELEASE_BUNDLES=( - binary - cross - tgz -) - -if [ "$1" != '--release-regardless-of-test-failure' ]; then - RELEASE_BUNDLES=( - test-unit - "${RELEASE_BUNDLES[@]}" - test-integration - ) -fi - -VERSION=$(< VERSION) -BUCKET=$AWS_S3_BUCKET -BUCKET_PATH=$BUCKET -[[ -n "$AWS_S3_BUCKET_PATH" ]] && BUCKET_PATH+=/$AWS_S3_BUCKET_PATH - -if command -v git &> /dev/null && git rev-parse &> /dev/null; then - if [ -n "$(git status --porcelain --untracked-files=no)" ]; then - echo "You cannot run the release script on a repo with uncommitted changes" - usage - fi -fi - -# These are the 2 keys we've used to sign the deb's -# release (get.docker.com) -# GPG_KEY="36A1D7869245C8950F966E92D8576A8BA88D21E9" -# test (test.docker.com) -# GPG_KEY="740B314AE3941731B942C66ADF4FD13717AAD7D6" - -setup_s3() { - echo "Setting up S3" - # Try creating the bucket. Ignore errors (it might already exist). - $AWS_CLI s3 mb "s3://$BUCKET" 2>/dev/null || true - # Check access to the bucket. - $AWS_CLI s3 ls "s3://$BUCKET" >/dev/null - # Make the bucket accessible through website endpoints. - $AWS_CLI s3 website --index-document index --error-document error "s3://$BUCKET" -} - -# write_to_s3 uploads the contents of standard input to the specified S3 url. -write_to_s3() { - DEST=$1 - F=`mktemp` - cat > "$F" - $AWS_CLI s3 cp --acl public-read --content-type 'text/plain' "$F" "$DEST" - rm -f "$F" -} - -s3_url() { - case "$BUCKET" in - get.docker.com|test.docker.com|experimental.docker.com) - echo "https://$BUCKET_PATH" - ;; - *) - BASE_URL="http://${BUCKET}.s3-website-${AWS_DEFAULT_REGION}.amazonaws.com" - if [[ -n "$AWS_S3_BUCKET_PATH" ]] ; then - echo "$BASE_URL/$AWS_S3_BUCKET_PATH" - else - echo "$BASE_URL" - fi - ;; - esac -} - -build_all() { - echo "Building release" - if ! ./hack/make.sh "${RELEASE_BUNDLES[@]}"; then - echo >&2 - echo >&2 'The build or tests appear to have failed.' - echo >&2 - echo >&2 'You, as the release maintainer, now have a couple options:' - echo >&2 '- delay release and fix issues' - echo >&2 '- delay release and fix issues' - echo >&2 '- did we mention how important this is? issues need fixing :)' - echo >&2 - echo >&2 'As a final LAST RESORT, you (because only you, the release maintainer,' - echo >&2 ' really knows all the hairy problems at hand with the current release' - echo >&2 ' issues) may bypass this checking by running this script again with the' - echo >&2 ' single argument of "--release-regardless-of-test-failure", which will skip' - echo >&2 ' running the test suite, and will only build the binaries and packages. Please' - echo >&2 ' avoid using this if at all possible.' - echo >&2 - echo >&2 'Regardless, we cannot stress enough the scarcity with which this bypass' - echo >&2 ' should be used. If there are release issues, we should always err on the' - echo >&2 ' side of caution.' - echo >&2 - exit 1 - fi -} - -upload_release_build() { - src="$1" - dst="$2" - latest="$3" - - echo - echo "Uploading $src" - echo " to $dst" - echo - $AWS_CLI s3 cp --follow-symlinks --acl public-read "$src" "$dst" - if [ "$latest" ]; then - echo - echo "Copying to $latest" - echo - $AWS_CLI s3 cp --acl public-read "$dst" "$latest" - fi - - # get hash files too (see hash_files() in hack/make.sh) - for hashAlgo in md5 sha256; do - if [ -e "$src.$hashAlgo" ]; then - echo - echo "Uploading $src.$hashAlgo" - echo " to $dst.$hashAlgo" - echo - $AWS_CLI s3 cp --follow-symlinks --acl public-read --content-type='text/plain' "$src.$hashAlgo" "$dst.$hashAlgo" - if [ "$latest" ]; then - echo - echo "Copying to $latest.$hashAlgo" - echo - $AWS_CLI s3 cp --acl public-read "$dst.$hashAlgo" "$latest.$hashAlgo" - fi - fi - done -} - -release_build() { - echo "Releasing binaries" - GOOS=$1 - GOARCH=$2 - - binDir=bundles/$VERSION/cross/$GOOS/$GOARCH - tgzDir=bundles/$VERSION/tgz/$GOOS/$GOARCH - binary=docker-$VERSION - zipExt=".tgz" - binaryExt="" - tgz=$binary$zipExt - - latestBase= - if [ -z "$NOLATEST" ]; then - latestBase=docker-latest - fi - - # we need to map our GOOS and GOARCH to uname values - # see https://en.wikipedia.org/wiki/Uname - # ie, GOOS=linux -> "uname -s"=Linux - - s3Os=$GOOS - case "$s3Os" in - darwin) - s3Os=Darwin - ;; - freebsd) - s3Os=FreeBSD - ;; - linux) - s3Os=Linux - ;; - windows) - # this is windows use the .zip and .exe extensions for the files. - s3Os=Windows - zipExt=".zip" - binaryExt=".exe" - tgz=$binary$zipExt - binary+=$binaryExt - ;; - *) - echo >&2 "error: can't convert $s3Os to an appropriate value for 'uname -s'" - exit 1 - ;; - esac - - s3Arch=$GOARCH - case "$s3Arch" in - amd64) - s3Arch=x86_64 - ;; - 386) - s3Arch=i386 - ;; - arm) - s3Arch=armel - # someday, we might potentially support multiple GOARM values, in which case we might get armhf here too - ;; - *) - echo >&2 "error: can't convert $s3Arch to an appropriate value for 'uname -m'" - exit 1 - ;; - esac - - s3Dir="s3://$BUCKET_PATH/builds/$s3Os/$s3Arch" - # latest= - latestTgz= - if [ "$latestBase" ]; then - # commented out since we aren't uploading binaries right now. - # latest="$s3Dir/$latestBase$binaryExt" - # we don't include the $binaryExt because we don't want docker.exe.zip - latestTgz="$s3Dir/$latestBase$zipExt" - fi - - if [ ! -f "$tgzDir/$tgz" ]; then - echo >&2 "error: can't find $tgzDir/$tgz - was it packaged properly?" - exit 1 - fi - # disable binary uploads for now. Only providing tgz downloads - # upload_release_build "$binDir/$binary" "$s3Dir/$binary" "$latest" - upload_release_build "$tgzDir/$tgz" "$s3Dir/$tgz" "$latestTgz" -} - -# Upload binaries and tgz files to S3 -release_binaries() { - [ "$(find bundles/$VERSION -path "bundles/$VERSION/cross/*/*/docker-$VERSION")" != "" ] || { - echo >&2 './hack/make.sh must be run before release_binaries' - exit 1 - } - - for d in bundles/$VERSION/cross/*/*; do - GOARCH="$(basename "$d")" - GOOS="$(basename "$(dirname "$d")")" - release_build "$GOOS" "$GOARCH" - done - - # TODO create redirect from builds/*/i686 to builds/*/i386 - - cat < Date: Fri, 28 Apr 2017 13:53:00 +0200 Subject: [PATCH 15/94] Add --until flag for docker logs; closes #32807 Signed-off-by: Jamie Hannaford Upstream-commit: e8d9a61f4c9e1f3cfdf1c889c541c9dc72a4bb40 Component: engine --- .../router/container/container_routes.go | 1 + components/engine/api/swagger.yaml | 5 + components/engine/api/types/client.go | 1 + components/engine/client/container_logs.go | 8 ++ .../engine/client/container_logs_test.go | 20 ++- components/engine/daemon/logger/adapter.go | 3 + .../engine/daemon/logger/journald/read.go | 42 ++++-- .../engine/daemon/logger/jsonfilelog/read.go | 22 ++- components/engine/daemon/logger/logger.go | 1 + components/engine/daemon/logs.go | 10 ++ components/engine/docs/api/version-history.md | 3 +- .../integration-cli/docker_api_logs_test.go | 127 ++++++++++++++++++ 12 files changed, 223 insertions(+), 20 deletions(-) diff --git a/components/engine/api/server/router/container/container_routes.go b/components/engine/api/server/router/container/container_routes.go index 106a7087cd..d845fdd00f 100644 --- a/components/engine/api/server/router/container/container_routes.go +++ b/components/engine/api/server/router/container/container_routes.go @@ -96,6 +96,7 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response Follow: httputils.BoolValue(r, "follow"), Timestamps: httputils.BoolValue(r, "timestamps"), Since: r.Form.Get("since"), + Until: r.Form.Get("until"), Tail: r.Form.Get("tail"), ShowStdout: stdout, ShowStderr: stderr, diff --git a/components/engine/api/swagger.yaml b/components/engine/api/swagger.yaml index b0c0575fc0..0e10b7a42a 100644 --- a/components/engine/api/swagger.yaml +++ b/components/engine/api/swagger.yaml @@ -4955,6 +4955,11 @@ paths: description: "Only return logs since this time, as a UNIX timestamp" type: "integer" default: 0 + - name: "until" + in: "query" + description: "Only return logs before this time, as a UNIX timestamp" + type: "integer" + default: 0 - name: "timestamps" in: "query" description: "Add timestamps to every log line" diff --git a/components/engine/api/types/client.go b/components/engine/api/types/client.go index db37f1fe4e..93ca428540 100644 --- a/components/engine/api/types/client.go +++ b/components/engine/api/types/client.go @@ -74,6 +74,7 @@ type ContainerLogsOptions struct { ShowStdout bool ShowStderr bool Since string + Until string Timestamps bool Follow bool Tail string diff --git a/components/engine/client/container_logs.go b/components/engine/client/container_logs.go index 0f32e9f12b..35c297c5fb 100644 --- a/components/engine/client/container_logs.go +++ b/components/engine/client/container_logs.go @@ -51,6 +51,14 @@ func (cli *Client) ContainerLogs(ctx context.Context, container string, options query.Set("since", ts) } + if options.Until != "" { + ts, err := timetypes.GetTimestamp(options.Until, time.Now()) + if err != nil { + return nil, err + } + query.Set("until", ts) + } + if options.Timestamps { query.Set("timestamps", "1") } diff --git a/components/engine/client/container_logs_test.go b/components/engine/client/container_logs_test.go index 99e31842c9..8cb7635120 100644 --- a/components/engine/client/container_logs_test.go +++ b/components/engine/client/container_logs_test.go @@ -13,6 +13,7 @@ import ( "time" "github.com/docker/docker/api/types" + "github.com/docker/docker/internal/testutil" "golang.org/x/net/context" ) @@ -28,9 +29,11 @@ func TestContainerLogsError(t *testing.T) { _, err = client.ContainerLogs(context.Background(), "container_id", types.ContainerLogsOptions{ Since: "2006-01-02TZ", }) - if err == nil || !strings.Contains(err.Error(), `parsing time "2006-01-02TZ"`) { - t.Fatalf("expected a 'parsing time' error, got %v", err) - } + testutil.ErrorContains(t, err, `parsing time "2006-01-02TZ"`) + _, err = client.ContainerLogs(context.Background(), "container_id", types.ContainerLogsOptions{ + Until: "2006-01-02TZ", + }) + testutil.ErrorContains(t, err, `parsing time "2006-01-02TZ"`) } func TestContainerLogs(t *testing.T) { @@ -80,6 +83,17 @@ func TestContainerLogs(t *testing.T) { "since": "invalid but valid", }, }, + { + options: types.ContainerLogsOptions{ + // An complete invalid date, timestamp or go duration will be + // passed as is + Until: "invalid but valid", + }, + expectedQueryParams: map[string]string{ + "tail": "", + "until": "invalid but valid", + }, + }, } for _, logCase := range cases { client := &Client{ diff --git a/components/engine/daemon/logger/adapter.go b/components/engine/daemon/logger/adapter.go index 98852e89c1..5817913cbc 100644 --- a/components/engine/daemon/logger/adapter.go +++ b/components/engine/daemon/logger/adapter.go @@ -122,6 +122,9 @@ func (a *pluginAdapterWithRead) ReadLogs(config ReadConfig) *LogWatcher { if !config.Since.IsZero() && msg.Timestamp.Before(config.Since) { continue } + if !config.Until.IsZero() && msg.Timestamp.After(config.Until) { + return + } select { case watcher.Msg <- msg: diff --git a/components/engine/daemon/logger/journald/read.go b/components/engine/daemon/logger/journald/read.go index 4d9b999a50..6aff21f441 100644 --- a/components/engine/daemon/logger/journald/read.go +++ b/components/engine/daemon/logger/journald/read.go @@ -171,13 +171,15 @@ func (s *journald) Close() error { return nil } -func (s *journald) drainJournal(logWatcher *logger.LogWatcher, config logger.ReadConfig, j *C.sd_journal, oldCursor *C.char) *C.char { +func (s *journald) drainJournal(logWatcher *logger.LogWatcher, j *C.sd_journal, oldCursor *C.char, untilUnixMicro uint64) (*C.char, bool) { var msg, data, cursor *C.char var length C.size_t var stamp C.uint64_t var priority, partial C.int + var done bool - // Walk the journal from here forward until we run out of new entries. + // Walk the journal from here forward until we run out of new entries + // or we reach the until value (if provided). drain: for { // Try not to send a given entry twice. @@ -195,6 +197,12 @@ drain: if C.sd_journal_get_realtime_usec(j, &stamp) != 0 { break } + // Break if the timestamp exceeds any provided until flag. + if untilUnixMicro != 0 && untilUnixMicro < uint64(stamp) { + done = true + break + } + // Set up the time and text of the entry. timestamp := time.Unix(int64(stamp)/1000000, (int64(stamp)%1000000)*1000) line := C.GoBytes(unsafe.Pointer(msg), C.int(length)) @@ -240,10 +248,10 @@ drain: // ensure that we won't be freeing an address that's invalid cursor = nil } - return cursor + return cursor, done } -func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.ReadConfig, j *C.sd_journal, pfd [2]C.int, cursor *C.char) *C.char { +func (s *journald) followJournal(logWatcher *logger.LogWatcher, j *C.sd_journal, pfd [2]C.int, cursor *C.char, untilUnixMicro uint64) *C.char { s.mu.Lock() s.readers.readers[logWatcher] = logWatcher if s.closed { @@ -270,9 +278,10 @@ func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.Re break } - cursor = s.drainJournal(logWatcher, config, j, cursor) + var done bool + cursor, done = s.drainJournal(logWatcher, j, cursor, untilUnixMicro) - if status != 1 { + if status != 1 || done { // We were notified to stop break } @@ -304,6 +313,7 @@ func (s *journald) readLogs(logWatcher *logger.LogWatcher, config logger.ReadCon var cmatch, cursor *C.char var stamp C.uint64_t var sinceUnixMicro uint64 + var untilUnixMicro uint64 var pipes [2]C.int // Get a handle to the journal. @@ -343,10 +353,19 @@ func (s *journald) readLogs(logWatcher *logger.LogWatcher, config logger.ReadCon nano := config.Since.UnixNano() sinceUnixMicro = uint64(nano / 1000) } + // If we have an until value, convert it too + if !config.Until.IsZero() { + nano := config.Until.UnixNano() + untilUnixMicro = uint64(nano / 1000) + } if config.Tail > 0 { lines := config.Tail - // Start at the end of the journal. - if C.sd_journal_seek_tail(j) < 0 { + // If until time provided, start from there. + // Otherwise start at the end of the journal. + if untilUnixMicro != 0 && C.sd_journal_seek_realtime_usec(j, C.uint64_t(untilUnixMicro)) < 0 { + logWatcher.Err <- fmt.Errorf("error seeking provided until value") + return + } else if C.sd_journal_seek_tail(j) < 0 { logWatcher.Err <- fmt.Errorf("error seeking to end of journal") return } @@ -362,8 +381,7 @@ func (s *journald) readLogs(logWatcher *logger.LogWatcher, config logger.ReadCon if C.sd_journal_get_realtime_usec(j, &stamp) != 0 { break } else { - // Compare the timestamp on the entry - // to our threshold value. + // Compare the timestamp on the entry to our threshold value. if sinceUnixMicro != 0 && sinceUnixMicro > uint64(stamp) { break } @@ -392,7 +410,7 @@ func (s *journald) readLogs(logWatcher *logger.LogWatcher, config logger.ReadCon return } } - cursor = s.drainJournal(logWatcher, config, j, nil) + cursor, _ = s.drainJournal(logWatcher, j, nil, untilUnixMicro) if config.Follow { // Allocate a descriptor for following the journal, if we'll // need one. Do it here so that we can report if it fails. @@ -404,7 +422,7 @@ func (s *journald) readLogs(logWatcher *logger.LogWatcher, config logger.ReadCon if C.pipe(&pipes[0]) == C.int(-1) { logWatcher.Err <- fmt.Errorf("error opening journald close notification pipe") } else { - cursor = s.followJournal(logWatcher, config, j, pipes, cursor) + cursor = s.followJournal(logWatcher, j, pipes, cursor, untilUnixMicro) // Let followJournal handle freeing the journal context // object and closing the channel. following = true diff --git a/components/engine/daemon/logger/jsonfilelog/read.go b/components/engine/daemon/logger/jsonfilelog/read.go index 2586c7d7f7..09eaaf00de 100644 --- a/components/engine/daemon/logger/jsonfilelog/read.go +++ b/components/engine/daemon/logger/jsonfilelog/read.go @@ -98,7 +98,7 @@ func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.R if config.Tail != 0 { tailer := multireader.MultiReadSeeker(append(files, latestChunk)...) - tailFile(tailer, logWatcher, config.Tail, config.Since) + tailFile(tailer, logWatcher, config.Tail, config.Since, config.Until) } // close all the rotated files @@ -119,7 +119,7 @@ func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.R l.readers[logWatcher] = struct{}{} l.mu.Unlock() - followLogs(latestFile, logWatcher, notifyRotate, config.Since) + followLogs(latestFile, logWatcher, notifyRotate, config) l.mu.Lock() delete(l.readers, logWatcher) @@ -136,7 +136,7 @@ func newSectionReader(f *os.File) (*io.SectionReader, error) { return io.NewSectionReader(f, 0, size), nil } -func tailFile(f io.ReadSeeker, logWatcher *logger.LogWatcher, tail int, since time.Time) { +func tailFile(f io.ReadSeeker, logWatcher *logger.LogWatcher, tail int, since, until time.Time) { rdr := io.Reader(f) if tail > 0 { ls, err := tailfile.TailFile(f, tail) @@ -158,6 +158,9 @@ func tailFile(f io.ReadSeeker, logWatcher *logger.LogWatcher, tail int, since ti if !since.IsZero() && msg.Timestamp.Before(since) { continue } + if !until.IsZero() && msg.Timestamp.After(until) { + return + } select { case <-logWatcher.WatchClose(): return @@ -186,7 +189,7 @@ func watchFile(name string) (filenotify.FileWatcher, error) { return fileWatcher, nil } -func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan interface{}, since time.Time) { +func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan interface{}, config logger.ReadConfig) { dec := json.NewDecoder(f) l := &jsonlog.JSONLog{} @@ -324,14 +327,22 @@ func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan int continue } + since := config.Since + until := config.Until + retries = 0 // reset retries since we've succeeded if !since.IsZero() && msg.Timestamp.Before(since) { continue } + if !until.IsZero() && msg.Timestamp.After(until) { + return + } select { case logWatcher.Msg <- msg: case <-ctx.Done(): logWatcher.Msg <- msg + // This for loop is used when the logger is closed (ie, container + // stopped) but the consumer is still waiting for logs. for { msg, err := decodeLogLine(dec, l) if err != nil { @@ -340,6 +351,9 @@ func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan int if !since.IsZero() && msg.Timestamp.Before(since) { continue } + if !until.IsZero() && msg.Timestamp.After(until) { + return + } logWatcher.Msg <- msg } } diff --git a/components/engine/daemon/logger/logger.go b/components/engine/daemon/logger/logger.go index ee91b79c98..3ea7794221 100644 --- a/components/engine/daemon/logger/logger.go +++ b/components/engine/daemon/logger/logger.go @@ -81,6 +81,7 @@ type Logger interface { // ReadConfig is the configuration passed into ReadLogs. type ReadConfig struct { Since time.Time + Until time.Time Tail int Follow bool } diff --git a/components/engine/daemon/logs.go b/components/engine/daemon/logs.go index 68c5e5aa47..babf07e36d 100644 --- a/components/engine/daemon/logs.go +++ b/components/engine/daemon/logs.go @@ -77,8 +77,18 @@ func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, c since = time.Unix(s, n) } + var until time.Time + if config.Until != "" && config.Until != "0" { + s, n, err := timetypes.ParseTimestamps(config.Until, 0) + if err != nil { + return nil, false, err + } + until = time.Unix(s, n) + } + readConfig := logger.ReadConfig{ Since: since, + Until: until, Tail: tailLines, Follow: follow, } diff --git a/components/engine/docs/api/version-history.md b/components/engine/docs/api/version-history.md index 77b8545bcc..a306e71609 100644 --- a/components/engine/docs/api/version-history.md +++ b/components/engine/docs/api/version-history.md @@ -23,6 +23,7 @@ keywords: "API, Docker, rcli, REST, documentation" If `Error` is `null`, container removal has succeeded, otherwise the test of an error message indicating why container removal has failed is available from `Error.Message` field. +* `GET /containers/(name)/logs` now supports an additional query parameter: `until`, which returns log lines that occurred before the specified timestamp. ## v1.33 API changes @@ -93,7 +94,7 @@ keywords: "API, Docker, rcli, REST, documentation" * `POST /containers/(name)/wait` now accepts a `condition` query parameter to indicate which state change condition to wait for. Also, response headers are now returned immediately to acknowledge that the server has registered a wait callback for the client. * `POST /swarm/init` now accepts a `DataPathAddr` property to set the IP-address or network interface to use for data traffic * `POST /swarm/join` now accepts a `DataPathAddr` property to set the IP-address or network interface to use for data traffic -* `GET /events` now supports service, node and secret events which are emitted when users create, update and remove service, node and secret +* `GET /events` now supports service, node and secret events which are emitted when users create, update and remove service, node and secret * `GET /events` now supports network remove event which is emitted when users remove a swarm scoped network * `GET /events` now supports a filter type `scope` in which supported value could be swarm and local diff --git a/components/engine/integration-cli/docker_api_logs_test.go b/components/engine/integration-cli/docker_api_logs_test.go index 1f2a30a929..0672e328db 100644 --- a/components/engine/integration-cli/docker_api_logs_test.go +++ b/components/engine/integration-cli/docker_api_logs_test.go @@ -2,8 +2,12 @@ package main import ( "bufio" + "bytes" "fmt" + "io" + "io/ioutil" "net/http" + "strconv" "strings" "time" @@ -11,6 +15,7 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/request" + "github.com/docker/docker/pkg/stdcopy" "github.com/go-check/check" "golang.org/x/net/context" ) @@ -85,3 +90,125 @@ func (s *DockerSuite) TestLogsAPIContainerNotFound(c *check.C) { c.Assert(err, checker.IsNil) c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound) } + +func (s *DockerSuite) TestLogsAPIUntilFutureFollow(c *check.C) { + testRequires(c, DaemonIsLinux) + + name := "logsuntilfuturefollow" + dockerCmd(c, "run", "-d", "--name", name, "busybox", "/bin/sh", "-c", "while true; do date +%s; sleep 1; done") + c.Assert(waitRun(name), checker.IsNil) + + untilSecs := 5 + untilDur, err := time.ParseDuration(fmt.Sprintf("%ds", untilSecs)) + c.Assert(err, checker.IsNil) + until := daemonTime(c).Add(untilDur) + + client, err := request.NewClient() + if err != nil { + c.Fatal(err) + } + + cfg := types.ContainerLogsOptions{Until: until.Format(time.RFC3339Nano), Follow: true, ShowStdout: true, Timestamps: true} + reader, err := client.ContainerLogs(context.Background(), name, cfg) + c.Assert(err, checker.IsNil) + + type logOut struct { + out string + err error + } + + chLog := make(chan logOut) + + go func() { + bufReader := bufio.NewReader(reader) + defer reader.Close() + for i := 0; i < untilSecs; i++ { + out, _, err := bufReader.ReadLine() + if err != nil { + if err == io.EOF { + return + } + chLog <- logOut{"", err} + return + } + + chLog <- logOut{strings.TrimSpace(string(out)), err} + } + }() + + for i := 0; i < untilSecs; i++ { + select { + case l := <-chLog: + c.Assert(l.err, checker.IsNil) + i, err := strconv.ParseInt(strings.Split(l.out, " ")[1], 10, 64) + c.Assert(err, checker.IsNil) + c.Assert(time.Unix(i, 0).UnixNano(), checker.LessOrEqualThan, until.UnixNano()) + case <-time.After(20 * time.Second): + c.Fatal("timeout waiting for logs to exit") + } + } +} + +func (s *DockerSuite) TestLogsAPIUntil(c *check.C) { + name := "logsuntil" + dockerCmd(c, "run", "--name", name, "busybox", "/bin/sh", "-c", "for i in $(seq 1 3); do echo log$i; sleep 0.5; done") + + client, err := request.NewClient() + if err != nil { + c.Fatal(err) + } + + extractBody := func(c *check.C, cfg types.ContainerLogsOptions) []string { + reader, err := client.ContainerLogs(context.Background(), name, cfg) + c.Assert(err, checker.IsNil) + + actualStdout := new(bytes.Buffer) + actualStderr := ioutil.Discard + _, err = stdcopy.StdCopy(actualStdout, actualStderr, reader) + c.Assert(err, checker.IsNil) + + return strings.Split(actualStdout.String(), "\n") + } + + // Get timestamp of second log line + allLogs := extractBody(c, types.ContainerLogsOptions{Timestamps: true, ShowStdout: true}) + t, err := time.Parse(time.RFC3339Nano, strings.Split(allLogs[1], " ")[0]) + c.Assert(err, checker.IsNil) + until := t.Format(time.RFC3339Nano) + + // Get logs until the timestamp of second line, i.e. first two lines + logs := extractBody(c, types.ContainerLogsOptions{Timestamps: true, ShowStdout: true, Until: until}) + + // Ensure log lines after cut-off are excluded + logsString := strings.Join(logs, "\n") + c.Assert(logsString, checker.Not(checker.Contains), "log3", check.Commentf("unexpected log message returned, until=%v", until)) +} + +func (s *DockerSuite) TestLogsAPIUntilDefaultValue(c *check.C) { + name := "logsuntildefaultval" + dockerCmd(c, "run", "--name", name, "busybox", "/bin/sh", "-c", "for i in $(seq 1 3); do echo log$i; done") + + client, err := request.NewClient() + if err != nil { + c.Fatal(err) + } + + extractBody := func(c *check.C, cfg types.ContainerLogsOptions) []string { + reader, err := client.ContainerLogs(context.Background(), name, cfg) + c.Assert(err, checker.IsNil) + + actualStdout := new(bytes.Buffer) + actualStderr := ioutil.Discard + _, err = stdcopy.StdCopy(actualStdout, actualStderr, reader) + c.Assert(err, checker.IsNil) + + return strings.Split(actualStdout.String(), "\n") + } + + // Get timestamp of second log line + allLogs := extractBody(c, types.ContainerLogsOptions{Timestamps: true, ShowStdout: true}) + + // Test with default value specified and parameter omitted + defaultLogs := extractBody(c, types.ContainerLogsOptions{Timestamps: true, ShowStdout: true, Until: "0"}) + c.Assert(defaultLogs, checker.DeepEquals, allLogs) +} From 0bcfa850805d3981fce1a55108572814d1763daa Mon Sep 17 00:00:00 2001 From: Boaz Shuster Date: Wed, 1 Nov 2017 11:29:37 +0200 Subject: [PATCH 16/94] Nitpick plugin/manager.go: use loop to create directories Instead of duplicating the same if condition per plugin manager directory, use one if condition and a for-loop. Signed-off-by: Boaz Shuster Upstream-commit: d75f1d848721f04fdc703a2e88a98600f29933a6 Component: engine --- components/engine/plugin/manager.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/components/engine/plugin/manager.go b/components/engine/plugin/manager.go index e0ac6e85fb..f144e8208b 100644 --- a/components/engine/plugin/manager.go +++ b/components/engine/plugin/manager.go @@ -107,14 +107,10 @@ func NewManager(config ManagerConfig) (*Manager, error) { manager := &Manager{ config: config, } - if err := os.MkdirAll(manager.config.Root, 0700); err != nil { - return nil, errors.Wrapf(err, "failed to mkdir %v", manager.config.Root) - } - if err := os.MkdirAll(manager.config.ExecRoot, 0700); err != nil { - return nil, errors.Wrapf(err, "failed to mkdir %v", manager.config.ExecRoot) - } - if err := os.MkdirAll(manager.tmpDir(), 0700); err != nil { - return nil, errors.Wrapf(err, "failed to mkdir %v", manager.tmpDir()) + for _, dirName := range []string{manager.config.Root, manager.config.ExecRoot, manager.tmpDir()} { + if err := os.MkdirAll(dirName, 0700); err != nil { + return nil, errors.Wrapf(err, "failed to mkdir %v", dirName) + } } if err := setupRoot(manager.config.Root); err != nil { From a92483bbdf467eb99021cca9d7e97a997cadfae9 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 1 Nov 2017 11:12:27 +0100 Subject: [PATCH 17/94] Fix .go-autogen warnings The `.integration-test-helpers` script was sourced by `/etc/bash/bash.rc`. However, the `$SCRIPTDIR` environment variable is set through `hack/make.sh`, so will not be set when calling the `.integration-test-helpers` script directly. Before this patch; make BIND_DIR=. shell ... bash: /make/.go-autogen: No such file or directory After this patch, the warning is no longer printed Also removed sourcing `.go-autogen` from test-integration and build-integration-test-binary, as they already sourced `.integration-test-helpers` (which sources `.go-autogen`). Signed-off-by: Sebastiaan van Stijn Upstream-commit: 6d5e468db1f07c12140af0cf36ba0d1a09bb4ca9 Component: engine --- components/engine/hack/make/.integration-test-helpers | 6 ++++-- components/engine/hack/make/build-integration-test-binary | 1 - components/engine/hack/make/test-integration | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/engine/hack/make/.integration-test-helpers b/components/engine/hack/make/.integration-test-helpers index 23780396e0..abd1d0f305 100644 --- a/components/engine/hack/make/.integration-test-helpers +++ b/components/engine/hack/make/.integration-test-helpers @@ -5,8 +5,10 @@ # # TESTFLAGS='-check.f DockerSuite.TestBuild*' ./hack/make.sh binary test-integration # - -source "$SCRIPTDIR/make/.go-autogen" +if [ -z $MAKEDIR ]; then + export MAKEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +fi +source "$MAKEDIR/.go-autogen" # Set defaults : ${TEST_REPEAT:=1} diff --git a/components/engine/hack/make/build-integration-test-binary b/components/engine/hack/make/build-integration-test-binary index ad3e8c2123..bbd5a22bcc 100755 --- a/components/engine/hack/make/build-integration-test-binary +++ b/components/engine/hack/make/build-integration-test-binary @@ -2,7 +2,6 @@ # required by `make build-integration-cli-on-swarm` set -e -source "${MAKEDIR}/.go-autogen" source hack/make/.integration-test-helpers build_test_suite_binaries diff --git a/components/engine/hack/make/test-integration b/components/engine/hack/make/test-integration index 0100ac9cc7..c807cd4978 100755 --- a/components/engine/hack/make/test-integration +++ b/components/engine/hack/make/test-integration @@ -1,7 +1,6 @@ #!/usr/bin/env bash set -e -o pipefail -source "${MAKEDIR}/.go-autogen" source hack/make/.integration-test-helpers ( From 86be218c459d943fc184a25df2cda97322808de2 Mon Sep 17 00:00:00 2001 From: Phil Estes Date: Sat, 22 Jul 2017 22:11:09 -0400 Subject: [PATCH 18/94] Only chown network files within container metadata If the user specifies a mountpath from the host, we should not be attempting to chown files outside the daemon's metadata directory (represented by `daemon.repository` at init time). This forces users who want to use user namespaces to handle the ownership needs of any external files mounted as network files (/etc/resolv.conf, /etc/hosts, /etc/hostname) separately from the daemon. In all other volume/bind mount situations we have taken this same line--we don't chown host file content. Docker-DCO-1.1-Signed-off-by: Phil Estes Upstream-commit: 42716dcf5c986e4cbb51f480f2782c05e5bd0b41 Component: engine --- components/engine/daemon/volumes_unix.go | 9 ++- .../docker_api_containers_unix_test.go | 77 +++++++++++++++++++ .../integration-cli/docker_cli_run_test.go | 15 ++++ 3 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 components/engine/integration-cli/docker_api_containers_unix_test.go diff --git a/components/engine/daemon/volumes_unix.go b/components/engine/daemon/volumes_unix.go index 0a4cbf8493..a88e0f1530 100644 --- a/components/engine/daemon/volumes_unix.go +++ b/components/engine/daemon/volumes_unix.go @@ -86,8 +86,13 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er // remapped root (user namespaces) rootIDs := daemon.idMappings.RootPair() for _, mount := range netMounts { - if err := os.Chown(mount.Source, rootIDs.UID, rootIDs.GID); err != nil { - return nil, err + // we should only modify ownership of network files within our own container + // metadata repository. If the user specifies a mount path external, it is + // up to the user to make sure the file has proper ownership for userns + if strings.Index(mount.Source, daemon.repository) == 0 { + if err := os.Chown(mount.Source, rootIDs.UID, rootIDs.GID); err != nil { + return nil, err + } } } return append(mounts, netMounts...), nil diff --git a/components/engine/integration-cli/docker_api_containers_unix_test.go b/components/engine/integration-cli/docker_api_containers_unix_test.go new file mode 100644 index 0000000000..4964f52644 --- /dev/null +++ b/components/engine/integration-cli/docker_api_containers_unix_test.go @@ -0,0 +1,77 @@ +// +build !windows + +package main + +import ( + "io/ioutil" + "os" + "path/filepath" + + "github.com/docker/docker/api/types" + containertypes "github.com/docker/docker/api/types/container" + mounttypes "github.com/docker/docker/api/types/mount" + networktypes "github.com/docker/docker/api/types/network" + "github.com/docker/docker/client" + "github.com/docker/docker/integration-cli/checker" + "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/pkg/system" + "github.com/go-check/check" + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +func (s *DockerSuite) TestContainersAPINetworkMountsNoChown(c *check.C) { + // chown only applies to Linux bind mounted volumes; must be same host to verify + testRequires(c, DaemonIsLinux, SameHostDaemon) + + tmpDir, err := ioutils.TempDir("", "test-network-mounts") + c.Assert(err, checker.IsNil) + defer os.RemoveAll(tmpDir) + + // make tmp dir readable by anyone to allow userns process to mount from + err = os.Chmod(tmpDir, 0755) + c.Assert(err, checker.IsNil) + // create temp files to use as network mounts + tmpNWFileMount := filepath.Join(tmpDir, "nwfile") + + err = ioutil.WriteFile(tmpNWFileMount, []byte("network file bind mount"), 0644) + c.Assert(err, checker.IsNil) + + config := containertypes.Config{ + Image: "busybox", + } + hostConfig := containertypes.HostConfig{ + Mounts: []mounttypes.Mount{ + { + Type: "bind", + Source: tmpNWFileMount, + Target: "/etc/resolv.conf", + }, + { + Type: "bind", + Source: tmpNWFileMount, + Target: "/etc/hostname", + }, + { + Type: "bind", + Source: tmpNWFileMount, + Target: "/etc/hosts", + }, + }, + } + + cli, err := client.NewEnvClient() + c.Assert(err, checker.IsNil) + defer cli.Close() + + ctrCreate, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") + c.Assert(err, checker.IsNil) + // container will exit immediately because of no tty, but we only need the start sequence to test the condition + err = cli.ContainerStart(context.Background(), ctrCreate.ID, types.ContainerStartOptions{}) + c.Assert(err, checker.IsNil) + + // check that host-located bind mount network file did not change ownership when the container was started + statT, err := system.Stat(tmpNWFileMount) + c.Assert(err, checker.IsNil) + assert.Equal(c, uint32(0), statT.UID(), "bind mounted network file should not change ownership from root") +} diff --git a/components/engine/integration-cli/docker_cli_run_test.go b/components/engine/integration-cli/docker_cli_run_test.go index 6ac10e70e9..67225235b2 100644 --- a/components/engine/integration-cli/docker_cli_run_test.go +++ b/components/engine/integration-cli/docker_cli_run_test.go @@ -3115,6 +3115,11 @@ func (s *DockerSuite) TestRunNetworkFilesBindMount(c *check.C) { filename := createTmpFile(c, expected) defer os.Remove(filename) + // for user namespaced test runs, the temp file must be accessible to unprivileged root + if err := os.Chmod(filename, 0646); err != nil { + c.Fatalf("error modifying permissions of %s: %v", filename, err) + } + nwfiles := []string{"/etc/resolv.conf", "/etc/hosts", "/etc/hostname"} for i := range nwfiles { @@ -3132,6 +3137,11 @@ func (s *DockerSuite) TestRunNetworkFilesBindMountRO(c *check.C) { filename := createTmpFile(c, "test123") defer os.Remove(filename) + // for user namespaced test runs, the temp file must be accessible to unprivileged root + if err := os.Chmod(filename, 0646); err != nil { + c.Fatalf("error modifying permissions of %s: %v", filename, err) + } + nwfiles := []string{"/etc/resolv.conf", "/etc/hosts", "/etc/hostname"} for i := range nwfiles { @@ -3149,6 +3159,11 @@ func (s *DockerSuite) TestRunNetworkFilesBindMountROFilesystem(c *check.C) { filename := createTmpFile(c, "test123") defer os.Remove(filename) + // for user namespaced test runs, the temp file must be accessible to unprivileged root + if err := os.Chmod(filename, 0646); err != nil { + c.Fatalf("error modifying permissions of %s: %v", filename, err) + } + nwfiles := []string{"/etc/resolv.conf", "/etc/hosts", "/etc/hostname"} for i := range nwfiles { From 4d815597635333ae0ac7b65bc884329643272517 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 1 Nov 2017 16:26:29 +0000 Subject: [PATCH 19/94] Remove Dockerfile.solaris reference Remove Dockerfile.solaris reference in `hack/make/.detect-daemon-osarch` as `Dockerfile.solaris` has been removed. Signed-off-by: Yong Tang Upstream-commit: d157d98a5fbfc98f4adec20ce510b99c795b0d9c Component: engine --- components/engine/hack/make/.detect-daemon-osarch | 3 --- 1 file changed, 3 deletions(-) diff --git a/components/engine/hack/make/.detect-daemon-osarch b/components/engine/hack/make/.detect-daemon-osarch index ac16055fcf..26cb30f4cf 100644 --- a/components/engine/hack/make/.detect-daemon-osarch +++ b/components/engine/hack/make/.detect-daemon-osarch @@ -60,9 +60,6 @@ case "$PACKAGE_ARCH" in windows) DOCKERFILE='Dockerfile.windows' ;; - solaris) - DOCKERFILE='Dockerfile.solaris' - ;; esac ;; *) From 814650081156590df54456dced798608d14d1154 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Wed, 1 Nov 2017 13:07:30 -0400 Subject: [PATCH 20/94] Remove W2L scripts Signed-off-by: Daniel Nephin Upstream-commit: 6d87d178131429217dec0621263d43c848dc7589 Component: engine --- .../engine/hack/Jenkins/W2L/postbuild.sh | 35 -- components/engine/hack/Jenkins/W2L/setup.sh | 309 ------------------ components/engine/hack/Jenkins/readme.md | 3 - 3 files changed, 347 deletions(-) delete mode 100644 components/engine/hack/Jenkins/W2L/postbuild.sh delete mode 100644 components/engine/hack/Jenkins/W2L/setup.sh delete mode 100644 components/engine/hack/Jenkins/readme.md diff --git a/components/engine/hack/Jenkins/W2L/postbuild.sh b/components/engine/hack/Jenkins/W2L/postbuild.sh deleted file mode 100644 index 662e2dcc37..0000000000 --- a/components/engine/hack/Jenkins/W2L/postbuild.sh +++ /dev/null @@ -1,35 +0,0 @@ -set +x -set +e - -echo "" -echo "" -echo "---" -echo "Now starting POST-BUILD steps" -echo "---" -echo "" - -echo INFO: Pointing to $DOCKER_HOST - -if [ ! $(docker ps -aq | wc -l) -eq 0 ]; then - echo INFO: Removing containers... - ! docker rm -vf $(docker ps -aq) -fi - -# Remove all images which don't have docker or debian in the name -if [ ! $(docker images | sed -n '1!p' | grep -v 'docker' | grep -v 'debian' | awk '{ print $3 }' | wc -l) -eq 0 ]; then - echo INFO: Removing images... - ! docker rmi -f $(docker images | sed -n '1!p' | grep -v 'docker' | grep -v 'debian' | awk '{ print $3 }') -fi - -# Kill off any instances of git, go and docker, just in case -! taskkill -F -IM git.exe -T >& /dev/null -! taskkill -F -IM go.exe -T >& /dev/null -! taskkill -F -IM docker.exe -T >& /dev/null - -# Remove everything -! cd /c/jenkins/gopath/src/github.com/docker/docker -! rm -rfd * >& /dev/null -! rm -rfd .* >& /dev/null - -echo INFO: Cleanup complete -exit 0 \ No newline at end of file diff --git a/components/engine/hack/Jenkins/W2L/setup.sh b/components/engine/hack/Jenkins/W2L/setup.sh deleted file mode 100644 index a3d86b857a..0000000000 --- a/components/engine/hack/Jenkins/W2L/setup.sh +++ /dev/null @@ -1,309 +0,0 @@ -# Jenkins CI script for Windows to Linux CI. -# Heavily modified by John Howard (@jhowardmsft) December 2015 to try to make it more reliable. -set +xe -SCRIPT_VER="Wed Apr 20 18:30:19 UTC 2016" - -# TODO to make (even) more resilient: -# - Wait for daemon to be running before executing docker commands -# - Check if jq is installed -# - Make sure bash is v4.3 or later. Can't do until all Azure nodes on the latest version -# - Make sure we are not running as local system. Can't do until all Azure nodes are updated. -# - Error if docker versions are not equal. Can't do until all Azure nodes are updated -# - Error if go versions are not equal. Can't do until all Azure nodes are updated. -# - Error if running 32-bit posix tools. Probably can take from bash --version and check contains "x86_64" -# - Warn if the CI directory cannot be deleted afterwards. Otherwise turdlets are left behind -# - Use %systemdrive% ($SYSTEMDRIVE) rather than hard code to c: for TEMP -# - Consider cross building the Windows binary and copy across. That's a bit of a heavy lift. Only reason -# for doing that is that it mirrors the actual release process for docker.exe which is cross-built. -# However, should absolutely not be a problem if built natively, so nit-picking. -# - Tidy up of images and containers. Either here, or in the teardown script. - -ec=0 -uniques=1 -echo INFO: Started at `date`. Script version $SCRIPT_VER - - -# !README! -# There are two daemons running on the remote Linux host: -# - outer: specified by DOCKER_HOST, this is the daemon that will build and run the inner docker daemon -# from the sources matching the PR. -# - inner: runs on the host network, on a port number similar to that of DOCKER_HOST but the last two digits are inverted -# (2357 if DOCKER_HOST had port 2375; and 2367 if DOCKER_HOST had port 2376). -# The windows integration tests are run against this inner daemon. - -# get the ip, inner and outer ports. -ip="${DOCKER_HOST#*://}" -port_outer="${ip#*:}" -# inner port is like outer port with last two digits inverted. -port_inner=$(echo "$port_outer" | sed -E 's/(.)(.)$/\2\1/') -ip="${ip%%:*}" - -echo "INFO: IP=$ip PORT_OUTER=$port_outer PORT_INNER=$port_inner" - -# If TLS is enabled -if [ -n "$DOCKER_TLS_VERIFY" ]; then - protocol=https - if [ -z "$DOCKER_MACHINE_NAME" ]; then - ec=1 - echo "ERROR: DOCKER_MACHINE_NAME is undefined" - fi - certs=$(echo ~/.docker/machine/machines/$DOCKER_MACHINE_NAME) - curlopts="--cacert $certs/ca.pem --cert $certs/cert.pem --key $certs/key.pem" - run_extra_args="-v tlscerts:/etc/docker" - daemon_extra_args="--tlsverify --tlscacert /etc/docker/ca.pem --tlscert /etc/docker/server.pem --tlskey /etc/docker/server-key.pem" -else - protocol=http -fi - -# Save for use by make.sh and scripts it invokes -export MAIN_DOCKER_HOST="tcp://$ip:$port_inner" - -# Verify we can get the remote node to respond to _ping -if [ $ec -eq 0 ]; then - reply=`curl -s $curlopts $protocol://$ip:$port_outer/_ping` - if [ "$reply" != "OK" ]; then - ec=1 - echo "ERROR: Failed to get an 'OK' response from the docker daemon on the Linux node" - echo " at $ip:$port_outer when called with an http request for '_ping'. This implies that" - echo " either the daemon has crashed/is not running, or the Linux node is unavailable." - echo - echo " A regular ping to the remote Linux node is below. It should reply. If not, the" - echo " machine cannot be reached at all and may have crashed. If it does reply, it is" - echo " likely a case of the Linux daemon not running or having crashed, which requires" - echo " further investigation." - echo - echo " Try re-running this CI job, or ask on #docker-dev or #docker-maintainers" - echo " for someone to perform further diagnostics, or take this node out of rotation." - echo - ping $ip - else - echo "INFO: The Linux nodes outer daemon replied to a ping. Good!" - fi -fi - -# Get the version from the remote node. Note this may fail if jq is not installed. -# That's probably worth checking to make sure, just in case. -if [ $ec -eq 0 ]; then - remoteVersion=`curl -s $curlopts $protocol://$ip:$port_outer/version | jq -c '.Version'` - echo "INFO: Remote daemon is running docker version $remoteVersion" -fi - -# Compare versions. We should really fail if result is no 1. Output at end of script. -if [ $ec -eq 0 ]; then - uniques=`docker version | grep Version | /usr/bin/sort -u | wc -l` -fi - -# Make sure we are in repo -if [ $ec -eq 0 ]; then - if [ ! -d hack ]; then - echo "ERROR: Are you sure this is being launched from a the root of docker repository?" - echo " If this is a Windows CI machine, it should be c:\jenkins\gopath\src\github.com\docker\docker." - echo " Current directory is `pwd`" - ec=1 - fi -fi - -# Are we in split binary mode? -if [ `grep DOCKER_CLIENTONLY Makefile | wc -l` -gt 0 ]; then - splitBinary=0 - echo "INFO: Running in single binary mode" -else - splitBinary=1 - echo "INFO: Running in split binary mode" -fi - - -# Get the commit has and verify we have something -if [ $ec -eq 0 ]; then - export COMMITHASH=$(git rev-parse --short HEAD) - echo INFO: Commit hash is $COMMITHASH - if [ -z $COMMITHASH ]; then - echo "ERROR: Failed to get commit hash. Are you sure this is a docker repository?" - ec=1 - fi -fi - -# Redirect to a temporary location. Check is here for local runs from Jenkins machines just in case not -# in the right directory where the repo is cloned. We also redirect TEMP to not use the environment -# TEMP as when running as a standard user (not local system), it otherwise exposes a bug in posix tar which -# will cause CI to fail from Windows to Linux. Obviously it's not best practice to ever run as local system... -if [ $ec -eq 0 ]; then - export TEMP=/c/CI/CI-$COMMITHASH - export TMP=$TEMP - /usr/bin/mkdir -p $TEMP # Make sure Linux mkdir for -p -fi - -# Tidy up time -if [ $ec -eq 0 ]; then - echo INFO: Deleting pre-existing containers and images... - - # Force remove all containers based on a previously built image with this commit - ! docker rm -f $(docker ps -aq --filter "ancestor=docker:$COMMITHASH") &>/dev/null - - # Force remove any container with this commithash as a name - ! docker rm -f $(docker ps -aq --filter "name=docker-$COMMITHASH") &>/dev/null - - # This SHOULD never happen, but just in case, also blow away any containers - # that might be around. - ! if [ ! $(docker ps -aq | wc -l) -eq 0 ]; then - echo WARN: There were some leftover containers. Cleaning them up. - ! docker rm -f $(docker ps -aq) - fi - - # Force remove the image if it exists - ! docker rmi -f "docker-$COMMITHASH" &>/dev/null -fi - -# Provide the docker version for debugging purposes. If these fail, game over. -# as the Linux box isn't responding for some reason. -if [ $ec -eq 0 ]; then - echo INFO: Docker version and info of the outer daemon on the Linux node - echo - docker version - ec=$? - if [ 0 -ne $ec ]; then - echo "ERROR: The main linux daemon does not appear to be running. Has the Linux node crashed?" - fi - echo -fi - -# Same as above, but docker info -if [ $ec -eq 0 ]; then - echo - docker info - ec=$? - if [ 0 -ne $ec ]; then - echo "ERROR: The main linux daemon does not appear to be running. Has the Linux node crashed?" - fi - echo -fi - -# build the daemon image -if [ $ec -eq 0 ]; then - echo "INFO: Running docker build on Linux host at $DOCKER_HOST" - if [ $splitBinary -eq 0 ]; then - set -x - docker build --rm --force-rm --build-arg APT_MIRROR=cdn-fastly.deb.debian.org -t "docker:$COMMITHASH" . - cat < Date: Wed, 1 Nov 2017 11:01:43 -0700 Subject: [PATCH 21/94] Remove dupl setting of OOMScoreAdj in OCI spec Signed-off-by: John Howard Upstream-commit: f0b44881b5d43b18871dee2ecc2bb313046038f6 Component: engine --- components/engine/daemon/oci_linux.go | 1 - 1 file changed, 1 deletion(-) diff --git a/components/engine/daemon/oci_linux.go b/components/engine/daemon/oci_linux.go index b4a6bf60d2..4af14cbd9b 100644 --- a/components/engine/daemon/oci_linux.go +++ b/components/engine/daemon/oci_linux.go @@ -755,7 +755,6 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { if err := setResources(&s, c.HostConfig.Resources); err != nil { return nil, fmt.Errorf("linux runtime spec resources: %v", err) } - s.Process.OOMScoreAdj = &c.HostConfig.OomScoreAdj s.Linux.Sysctl = c.HostConfig.Sysctls p := s.Linux.CgroupsPath From 9a9cb243850de4b2dcc22a6496333e3bf20c42f7 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Wed, 1 Nov 2017 15:46:46 -0400 Subject: [PATCH 22/94] Remove version file Signed-off-by: Daniel Nephin Upstream-commit: 1e1ad008db1af4f415b9ef9b8d6d225a09ab42e1 Component: engine --- components/engine/VERSION | 1 - components/engine/hack/make.ps1 | 8 +------- components/engine/hack/make.sh | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 components/engine/VERSION diff --git a/components/engine/VERSION b/components/engine/VERSION deleted file mode 100644 index 2d736aaa18..0000000000 --- a/components/engine/VERSION +++ /dev/null @@ -1 +0,0 @@ -17.06.0-dev diff --git a/components/engine/hack/make.ps1 b/components/engine/hack/make.ps1 index 73c9577a0c..3380a5b693 100644 --- a/components/engine/hack/make.ps1 +++ b/components/engine/hack/make.ps1 @@ -114,12 +114,6 @@ Function Get-GitCommit() { return $gitCommit } -# Utility function to get get the current build version of docker -Function Get-DockerVersion() { - if (-not (Test-Path ".\VERSION")) { Throw "VERSION file not found. Is this running from the root of a docker repository?" } - return $(Get-Content ".\VERSION" -raw).ToString().Replace("`n","").Trim() -} - # Utility function to determine if we are running in a container or not. # In Windows, we get this through an environment variable set in `Dockerfile.Windows` Function Check-InContainer() { @@ -356,7 +350,7 @@ Try { if ($CommitSuffix -ne "") { $gitCommit += "-"+$CommitSuffix -Replace ' ', '' } # Get the version of docker (eg 17.04.0-dev) - $dockerVersion=Get-DockerVersion + $dockerVersion="0.0.0-dev" # Give a warning if we are not running in a container and are building binaries or running unit tests. # Not relevant for validation tests as these are fine to run outside of a container. diff --git a/components/engine/hack/make.sh b/components/engine/hack/make.sh index d6cf3de83f..99c51a7b62 100755 --- a/components/engine/hack/make.sh +++ b/components/engine/hack/make.sh @@ -65,7 +65,7 @@ DEFAULT_BUNDLES=( cross ) -VERSION=${VERSION:-$(< ./VERSION)} +VERSION=${VERSION:-dev} ! BUILDTIME=$(date -u -d "@${SOURCE_DATE_EPOCH:-$(date +%s)}" --rfc-3339 ns 2> /dev/null | sed -e 's/ /T/') if [ "$DOCKER_GITCOMMIT" ]; then GITCOMMIT="$DOCKER_GITCOMMIT" From a1f2fd42a74ed01fe81241db59bf4faedc075fb1 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 1 Nov 2017 22:13:10 +0100 Subject: [PATCH 23/94] Move Isolation API changes to the correct version Commit d91c5f42eb37c6f88cec4021c10c0a1ded1785c3 added support for "Isolation" mode for services, but didn't get merged before API 1.34. This patch moves the description in the API version history to the correct API version (1.35), and does a slight rewording of the functionality. Signed-off-by: Sebastiaan van Stijn Upstream-commit: 4533a16fa98785c8c18bf9b9a32da5390fc74757 Component: engine --- components/engine/docs/api/version-history.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/engine/docs/api/version-history.md b/components/engine/docs/api/version-history.md index ca4b82df35..3541a5a712 100644 --- a/components/engine/docs/api/version-history.md +++ b/components/engine/docs/api/version-history.md @@ -15,6 +15,13 @@ keywords: "API, Docker, rcli, REST, documentation" ## v1.35 API changes +[Docker Engine API v1.35](https://docs.docker.com/engine/api/v1.35/) documentation + +* `POST /services/create` and `POST /services/(id)/update` now accepts an + `Isolation` field on container spec to set the Isolation technology of the + containers running the service (`default`, `process`, or `hyperv`). This + configuration is only used for Windows containers. + ## v1.34 API changes @@ -26,7 +33,6 @@ keywords: "API, Docker, rcli, REST, documentation" If `Error` is `null`, container removal has succeeded, otherwise the test of an error message indicating why container removal has failed is available from `Error.Message` field. -* `POST /services/create` and `POST /services/(id)/update` now accept an `Isolation` field on container spec ## v1.33 API changes From bd820b2e313c1b6ce799f66f1cf5e1eb4245321d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 1 Nov 2017 22:29:26 +0100 Subject: [PATCH 24/94] Remove Docker/API version matrix The Swagger file contained a version matrix to find which API version is used by which version of Docker. Given that Docker is a downstream of the Moby project, we should not be maintaining such a matrix in this repository. Signed-off-by: Sebastiaan van Stijn Upstream-commit: 3fb228ba50ccb921148e0174e5e5e45ab650c3e5 Component: engine --- components/engine/api/swagger.yaml | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/components/engine/api/swagger.yaml b/components/engine/api/swagger.yaml index cd64b918c4..87ba9c0a06 100644 --- a/components/engine/api/swagger.yaml +++ b/components/engine/api/swagger.yaml @@ -62,28 +62,6 @@ info: properties in responses to ensure they do not break when talking to newer daemons. - This documentation is for version v1.35 of the API. Use this table to find - documentation for previous versions of the API: - - Docker version | API version | Changes - ----------------|-------------|--------- - 17.11.x | [1.34](https://docs.docker.com/engine/api/v1.34/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-34-api-changes) - 17.10.x | [1.33](https://docs.docker.com/engine/api/v1.33/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-33-api-changes) - 17.09.x | [1.32](https://docs.docker.com/engine/api/v1.32/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-32-api-changes) - 17.07.x | [1.31](https://docs.docker.com/engine/api/v1.31/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-31-api-changes) - 17.06.x | [1.30](https://docs.docker.com/engine/api/v1.30/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-30-api-changes) - 17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes) - 17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes) - 17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes) - 1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes) - 1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes) - 1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes) - 1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes) - 1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes) - 1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes) - 1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes) - 1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes) - 1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes) # Authentication From eefbd135ae8d69f0cc402ec8a4bfb7290e77a044 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Wed, 1 Nov 2017 23:37:53 +0000 Subject: [PATCH 25/94] Remove solaris build tag and `contrib/mkimage/solaris Signed-off-by: Yong Tang Upstream-commit: 4785f1a7ab7ec857dc3ca849ee6ecadf519ef30e Component: engine --- components/engine/client/client_unix.go | 2 +- .../engine/cmd/dockerd/config_common_unix.go | 2 +- components/engine/cmd/dockerd/config_unix.go | 2 +- .../engine/cmd/dockerd/config_unix_test.go | 2 +- components/engine/cmd/dockerd/daemon_unix.go | 2 +- .../engine/cmd/dockerd/daemon_unix_test.go | 5 +- .../engine/container/container_notlinux.go | 4 +- components/engine/container/container_unix.go | 2 +- .../contrib/docker-device-tool/device_tool.go | 2 +- components/engine/contrib/mkimage.sh | 8 -- components/engine/contrib/mkimage/solaris | 89 ------------------- .../daemon/cluster/listen_addr_others.go | 2 +- .../daemon/config/config_common_unix.go | 2 +- components/engine/daemon/daemon_test.go | 2 - components/engine/daemon/daemon_unix_test.go | 2 +- .../engine/daemon/daemon_unsupported.go | 2 +- .../engine/daemon/debugtrap_unsupported.go | 2 +- components/engine/daemon/getsize_unix.go | 2 +- .../daemon/graphdriver/driver_unsupported.go | 2 +- .../graphdriver/graphtest/graphtest_unix.go | 2 +- .../graphdriver/register/register_zfs.go | 2 +- .../engine/daemon/graphdriver/zfs/zfs.go | 2 +- .../daemon/graphdriver/zfs/zfs_unsupported.go | 2 +- components/engine/daemon/inspect_unix.go | 2 +- components/engine/daemon/list_unix.go | 2 +- .../engine/daemon/listeners/listeners_unix.go | 2 +- .../engine/daemon/logger/plugin_unix.go | 2 +- .../daemon/logger/plugin_unsupported.go | 2 +- components/engine/daemon/reload_test.go | 2 - components/engine/daemon/stats/collector.go | 2 - .../engine/daemon/stats/collector_unix.go | 2 +- components/engine/daemon/volumes_unix.go | 2 - components/engine/layer/layer_unix.go | 2 +- ..._daemon_unix.go => remote_daemon_linux.go} | 2 - ...unix.go => remote_daemon_options_linux.go} | 2 - ...unix.go => remote_daemon_process_linux.go} | 2 - .../engine/pkg/directory/directory_unix.go | 2 +- .../engine/pkg/mount/flags_unsupported.go | 2 +- components/engine/pkg/mount/mount.go | 2 +- .../engine/pkg/mount/mount_unix_test.go | 2 +- .../engine/pkg/mount/mounter_unsupported.go | 2 +- .../engine/pkg/mount/mountinfo_unsupported.go | 2 +- .../engine/pkg/parsers/kernel/kernel_unix.go | 2 +- .../pkg/parsers/kernel/uname_unsupported.go | 2 +- .../engine/pkg/platform/architecture_unix.go | 2 +- components/engine/pkg/reexec/command_unix.go | 2 +- .../engine/pkg/reexec/command_unsupported.go | 4 +- .../engine/pkg/signal/signal_linux_test.go | 2 +- .../engine/pkg/signal/signal_unsupported.go | 2 +- components/engine/pkg/sysinfo/sysinfo_unix.go | 4 +- .../engine/pkg/system/meminfo_unsupported.go | 2 +- components/engine/pkg/system/process_unix.go | 2 +- components/engine/pkg/term/tc.go | 1 - components/engine/pkg/term/winsize.go | 2 +- components/engine/registry/auth_test.go | 2 - .../engine/registry/registry_mock_test.go | 2 - components/engine/registry/registry_test.go | 2 - .../engine/runconfig/hostconfig_unix.go | 2 +- components/engine/volume/local/local_unix.go | 2 +- components/engine/volume/store/store_unix.go | 2 +- components/engine/volume/volume_unix.go | 2 +- 61 files changed, 51 insertions(+), 172 deletions(-) delete mode 100755 components/engine/contrib/mkimage/solaris rename components/engine/libcontainerd/{remote_daemon_unix.go => remote_daemon_linux.go} (97%) rename components/engine/libcontainerd/{remote_daemon_options_unix.go => remote_daemon_options_linux.go} (96%) rename components/engine/libcontainerd/{remote_daemon_process_unix.go => remote_daemon_process_linux.go} (97%) diff --git a/components/engine/client/client_unix.go b/components/engine/client/client_unix.go index 89de892c85..eba8d909a9 100644 --- a/components/engine/client/client_unix.go +++ b/components/engine/client/client_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris openbsd darwin +// +build linux freebsd openbsd darwin package client diff --git a/components/engine/cmd/dockerd/config_common_unix.go b/components/engine/cmd/dockerd/config_common_unix.go index b29307b596..febf30ae9f 100644 --- a/components/engine/cmd/dockerd/config_common_unix.go +++ b/components/engine/cmd/dockerd/config_common_unix.go @@ -1,4 +1,4 @@ -// +build solaris linux freebsd +// +build linux freebsd package main diff --git a/components/engine/cmd/dockerd/config_unix.go b/components/engine/cmd/dockerd/config_unix.go index dcc7dc5e81..b3bd741c95 100644 --- a/components/engine/cmd/dockerd/config_unix.go +++ b/components/engine/cmd/dockerd/config_unix.go @@ -1,4 +1,4 @@ -// +build linux,!solaris freebsd,!solaris +// +build linux freebsd package main diff --git a/components/engine/cmd/dockerd/config_unix_test.go b/components/engine/cmd/dockerd/config_unix_test.go index 588ac19fbd..2705d671ba 100644 --- a/components/engine/cmd/dockerd/config_unix_test.go +++ b/components/engine/cmd/dockerd/config_unix_test.go @@ -1,4 +1,4 @@ -// +build linux,!solaris freebsd,!solaris +// +build linux freebsd package main diff --git a/components/engine/cmd/dockerd/daemon_unix.go b/components/engine/cmd/dockerd/daemon_unix.go index 324b299e18..41e6b61ffa 100644 --- a/components/engine/cmd/dockerd/daemon_unix.go +++ b/components/engine/cmd/dockerd/daemon_unix.go @@ -1,4 +1,4 @@ -// +build !windows,!solaris +// +build !windows package main diff --git a/components/engine/cmd/dockerd/daemon_unix_test.go b/components/engine/cmd/dockerd/daemon_unix_test.go index 5d99e51053..475ff9efa7 100644 --- a/components/engine/cmd/dockerd/daemon_unix_test.go +++ b/components/engine/cmd/dockerd/daemon_unix_test.go @@ -1,7 +1,4 @@ -// +build !windows,!solaris - -// TODO: Create new file for Solaris which tests config parameters -// as described in daemon/config_solaris.go +// +build !windows package main diff --git a/components/engine/container/container_notlinux.go b/components/engine/container/container_notlinux.go index 768c762d2f..246a146f0f 100644 --- a/components/engine/container/container_notlinux.go +++ b/components/engine/container/container_notlinux.go @@ -1,4 +1,4 @@ -// +build solaris freebsd +// +build freebsd package container @@ -7,7 +7,7 @@ import ( ) func detachMounted(path string) error { - //Solaris and FreeBSD do not support the lazy unmount or MNT_DETACH feature. + // FreeBSD do not support the lazy unmount or MNT_DETACH feature. // Therefore there are separate definitions for this. return unix.Unmount(path, 0) } diff --git a/components/engine/container/container_unix.go b/components/engine/container/container_unix.go index 98042f1308..77851946f3 100644 --- a/components/engine/container/container_unix.go +++ b/components/engine/container/container_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris +// +build linux freebsd package container diff --git a/components/engine/contrib/docker-device-tool/device_tool.go b/components/engine/contrib/docker-device-tool/device_tool.go index 905b689581..d3ec46a8b4 100644 --- a/components/engine/contrib/docker-device-tool/device_tool.go +++ b/components/engine/contrib/docker-device-tool/device_tool.go @@ -1,4 +1,4 @@ -// +build !windows,!solaris +// +build !windows package main diff --git a/components/engine/contrib/mkimage.sh b/components/engine/contrib/mkimage.sh index 13298c8036..ae05d139c3 100755 --- a/components/engine/contrib/mkimage.sh +++ b/components/engine/contrib/mkimage.sh @@ -11,7 +11,6 @@ usage() { echo >&2 " $mkimg -t someuser/centos:5 rinse --distribution centos-5" echo >&2 " $mkimg -t someuser/mageia:4 mageia-urpmi --version=4" echo >&2 " $mkimg -t someuser/mageia:4 mageia-urpmi --version=4 --mirror=http://somemirror/" - echo >&2 " $mkimg -t someuser/solaris solaris" exit 1 } @@ -20,13 +19,6 @@ scriptDir="$(dirname "$(readlink -f "$BASH_SOURCE")")/mkimage" os= os=$(uname -o) -# set up path to gnu tools if solaris -[[ $os == "Solaris" ]] && export PATH=/usr/gnu/bin:$PATH -# TODO check for gnu-tar, gnu-getopt - -# TODO requires root/sudo due to some pkg operations. sigh. -[[ $os == "Solaris" && $EUID != "0" ]] && echo >&2 "image create on Solaris requires superuser privilege" - optTemp=$(getopt --options '+d:t:c:hC' --longoptions 'dir:,tag:,compression:,no-compression,help' --name "$mkimg" -- "$@") eval set -- "$optTemp" unset optTemp diff --git a/components/engine/contrib/mkimage/solaris b/components/engine/contrib/mkimage/solaris deleted file mode 100755 index 158970e69e..0000000000 --- a/components/engine/contrib/mkimage/solaris +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env bash -# -# Solaris 12 base image build script. -# -set -e - -# TODO add optional package publisher origin - -rootfsDir="$1" -shift - -# base install -( - set -x - - pkg image-create --full --zone \ - --facet facet.locale.*=false \ - --facet facet.locale.POSIX=true \ - --facet facet.doc=false \ - --facet facet.doc.*=false \ - "$rootfsDir" - - pkg -R "$rootfsDir" set-property use-system-repo true - - pkg -R "$rootfsDir" set-property flush-content-cache-on-success true - - pkg -R "$rootfsDir" install core-os -) - -# Lay in stock configuration, set up milestone -# XXX This all may become optional in a base image -( - # faster to build repository database on tmpfs - REPO_DB=/system/volatile/repository.$$ - export SVCCFG_REPOSITORY=${REPO_DB} - export SVCCFG_DOOR_PATH=$rootfsDir/system/volatile/tmp_repo_door - - # Import base manifests. NOTE These are a combination of basic requirement - # and gleaned from container milestone manifest. They may change. - for m in $rootfsDir/lib/svc/manifest/system/environment.xml \ - $rootfsDir/lib/svc/manifest/system/svc/global.xml \ - $rootfsDir/lib/svc/manifest/system/svc/restarter.xml \ - $rootfsDir/lib/svc/manifest/network/dns/client.xml \ - $rootfsDir/lib/svc/manifest/system/name-service/switch.xml \ - $rootfsDir/lib/svc/manifest/system/name-service/cache.xml \ - $rootfsDir/lib/svc/manifest/milestone/container.xml ; do - svccfg import $m - done - - # Apply system layer profile, deleting unnecessary dependencies - svccfg apply $rootfsDir/etc/svc/profile/generic_container.xml - - # XXX Even if we keep a repo in the base image, this is definitely optional - svccfg apply $rootfsDir/etc/svc/profile/sysconfig/container_sc.xml - - for s in svc:/system/svc/restarter \ - svc:/system/environment \ - svc:/network/dns/client \ - svc:/system/name-service/switch \ - svc:/system/name-service/cache \ - svc:/system/svc/global \ - svc:/milestone/container ;do - svccfg -s $s refresh - done - - # now copy the built up repository into the base rootfs - mv $REPO_DB $rootfsDir/etc/svc/repository.db -) - -# pkg(1) needs the zoneproxy-client running in the container. -# use a simple wrapper to run it as needed. -# XXX maybe we go back to running this in SMF? -mv "$rootfsDir/usr/bin/pkg" "$rootfsDir/usr/bin/wrapped_pkg" -cat > "$rootfsDir/usr/bin/pkg" <<-'EOF' -#!/bin/sh -# -# THIS FILE CREATED DURING DOCKER BASE IMAGE CREATION -# -# The Solaris base image uses the sysrepo proxy mechanism. The -# IPS client pkg(1) requires the zoneproxy-client to reach the -# remote publisher origins through the host. This wrapper script -# enables and disables the proxy client as needed. This is a -# temporary solution. - -/usr/lib/zones/zoneproxy-client -s localhost:1008 -PKG_SYSREPO_URL=http://localhost:1008 /usr/bin/wrapped_pkg "$@" -pkill -9 zoneproxy-client -EOF -chmod +x "$rootfsDir/usr/bin/pkg" diff --git a/components/engine/daemon/cluster/listen_addr_others.go b/components/engine/daemon/cluster/listen_addr_others.go index 4e845f5c8f..ebf7daea24 100644 --- a/components/engine/daemon/cluster/listen_addr_others.go +++ b/components/engine/daemon/cluster/listen_addr_others.go @@ -1,4 +1,4 @@ -// +build !linux,!solaris +// +build !linux package cluster diff --git a/components/engine/daemon/config/config_common_unix.go b/components/engine/daemon/config/config_common_unix.go index cea3fffdda..d2fa2e035a 100644 --- a/components/engine/daemon/config/config_common_unix.go +++ b/components/engine/daemon/config/config_common_unix.go @@ -1,4 +1,4 @@ -// +build solaris linux freebsd +// +build linux freebsd package config diff --git a/components/engine/daemon/daemon_test.go b/components/engine/daemon/daemon_test.go index 13d1059c1c..4044fad836 100644 --- a/components/engine/daemon/daemon_test.go +++ b/components/engine/daemon/daemon_test.go @@ -1,5 +1,3 @@ -// +build !solaris - package daemon import ( diff --git a/components/engine/daemon/daemon_unix_test.go b/components/engine/daemon/daemon_unix_test.go index 2bdbd23290..a4db4733d4 100644 --- a/components/engine/daemon/daemon_unix_test.go +++ b/components/engine/daemon/daemon_unix_test.go @@ -1,4 +1,4 @@ -// +build !windows,!solaris +// +build !windows package daemon diff --git a/components/engine/daemon/daemon_unsupported.go b/components/engine/daemon/daemon_unsupported.go index cb1acf63d6..987528f476 100644 --- a/components/engine/daemon/daemon_unsupported.go +++ b/components/engine/daemon/daemon_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!freebsd,!windows,!solaris +// +build !linux,!freebsd,!windows package daemon diff --git a/components/engine/daemon/debugtrap_unsupported.go b/components/engine/daemon/debugtrap_unsupported.go index f5b9170907..6ae9ebfde9 100644 --- a/components/engine/daemon/debugtrap_unsupported.go +++ b/components/engine/daemon/debugtrap_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!darwin,!freebsd,!windows,!solaris +// +build !linux,!darwin,!freebsd,!windows package daemon diff --git a/components/engine/daemon/getsize_unix.go b/components/engine/daemon/getsize_unix.go index e47e646df3..fff90f2756 100644 --- a/components/engine/daemon/getsize_unix.go +++ b/components/engine/daemon/getsize_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris +// +build linux freebsd package daemon diff --git a/components/engine/daemon/graphdriver/driver_unsupported.go b/components/engine/daemon/graphdriver/driver_unsupported.go index 4a875608b0..b3f6857309 100644 --- a/components/engine/daemon/graphdriver/driver_unsupported.go +++ b/components/engine/daemon/graphdriver/driver_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!windows,!freebsd,!solaris +// +build !linux,!windows,!freebsd package graphdriver diff --git a/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go b/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go index 11dff48896..6b352ba69a 100644 --- a/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go +++ b/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris +// +build linux freebsd package graphtest diff --git a/components/engine/daemon/graphdriver/register/register_zfs.go b/components/engine/daemon/graphdriver/register/register_zfs.go index 8f34e35537..8c31c415f4 100644 --- a/components/engine/daemon/graphdriver/register/register_zfs.go +++ b/components/engine/daemon/graphdriver/register/register_zfs.go @@ -1,4 +1,4 @@ -// +build !exclude_graphdriver_zfs,linux !exclude_graphdriver_zfs,freebsd, solaris +// +build !exclude_graphdriver_zfs,linux !exclude_graphdriver_zfs,freebsd package register diff --git a/components/engine/daemon/graphdriver/zfs/zfs.go b/components/engine/daemon/graphdriver/zfs/zfs.go index 4caedef0ee..52e2aa1d26 100644 --- a/components/engine/daemon/graphdriver/zfs/zfs.go +++ b/components/engine/daemon/graphdriver/zfs/zfs.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris +// +build linux freebsd package zfs diff --git a/components/engine/daemon/graphdriver/zfs/zfs_unsupported.go b/components/engine/daemon/graphdriver/zfs/zfs_unsupported.go index ce8daadaf6..643b169bc5 100644 --- a/components/engine/daemon/graphdriver/zfs/zfs_unsupported.go +++ b/components/engine/daemon/graphdriver/zfs/zfs_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!freebsd,!solaris +// +build !linux,!freebsd package zfs diff --git a/components/engine/daemon/inspect_unix.go b/components/engine/daemon/inspect_unix.go index bd28481e6a..f073695e33 100644 --- a/components/engine/daemon/inspect_unix.go +++ b/components/engine/daemon/inspect_unix.go @@ -1,4 +1,4 @@ -// +build !windows,!solaris +// +build !windows package daemon diff --git a/components/engine/daemon/list_unix.go b/components/engine/daemon/list_unix.go index ebaae4560c..7b92c7c491 100644 --- a/components/engine/daemon/list_unix.go +++ b/components/engine/daemon/list_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris +// +build linux freebsd package daemon diff --git a/components/engine/daemon/listeners/listeners_unix.go b/components/engine/daemon/listeners/listeners_unix.go index 0a4e5e4e31..3a7c0f85b0 100644 --- a/components/engine/daemon/listeners/listeners_unix.go +++ b/components/engine/daemon/listeners/listeners_unix.go @@ -1,4 +1,4 @@ -// +build !windows,!solaris +// +build !windows package listeners diff --git a/components/engine/daemon/logger/plugin_unix.go b/components/engine/daemon/logger/plugin_unix.go index f93d7af0ee..edf11af15e 100644 --- a/components/engine/daemon/logger/plugin_unix.go +++ b/components/engine/daemon/logger/plugin_unix.go @@ -1,4 +1,4 @@ -// +build linux solaris freebsd +// +build linux freebsd package logger diff --git a/components/engine/daemon/logger/plugin_unsupported.go b/components/engine/daemon/logger/plugin_unsupported.go index 0a2036c838..b649b0644e 100644 --- a/components/engine/daemon/logger/plugin_unsupported.go +++ b/components/engine/daemon/logger/plugin_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!solaris,!freebsd +// +build !linux,!freebsd package logger diff --git a/components/engine/daemon/reload_test.go b/components/engine/daemon/reload_test.go index 3ff6b57735..96b1a2452d 100644 --- a/components/engine/daemon/reload_test.go +++ b/components/engine/daemon/reload_test.go @@ -1,5 +1,3 @@ -// +build !solaris - package daemon import ( diff --git a/components/engine/daemon/stats/collector.go b/components/engine/daemon/stats/collector.go index c930bc756c..f13a8045d4 100644 --- a/components/engine/daemon/stats/collector.go +++ b/components/engine/daemon/stats/collector.go @@ -1,5 +1,3 @@ -// +build !solaris - package stats import ( diff --git a/components/engine/daemon/stats/collector_unix.go b/components/engine/daemon/stats/collector_unix.go index cd522e07ce..6b1318a1bd 100644 --- a/components/engine/daemon/stats/collector_unix.go +++ b/components/engine/daemon/stats/collector_unix.go @@ -1,4 +1,4 @@ -// +build !windows,!solaris +// +build !windows package stats diff --git a/components/engine/daemon/volumes_unix.go b/components/engine/daemon/volumes_unix.go index 0a4cbf8493..0ab0c6d8a6 100644 --- a/components/engine/daemon/volumes_unix.go +++ b/components/engine/daemon/volumes_unix.go @@ -1,7 +1,5 @@ // +build !windows -// TODO(amitkris): We need to split this file for solaris. - package daemon import ( diff --git a/components/engine/layer/layer_unix.go b/components/engine/layer/layer_unix.go index 776b78ac02..d77e2fc66e 100644 --- a/components/engine/layer/layer_unix.go +++ b/components/engine/layer/layer_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd darwin openbsd solaris +// +build linux freebsd darwin openbsd package layer diff --git a/components/engine/libcontainerd/remote_daemon_unix.go b/components/engine/libcontainerd/remote_daemon_linux.go similarity index 97% rename from components/engine/libcontainerd/remote_daemon_unix.go rename to components/engine/libcontainerd/remote_daemon_linux.go index e0c56e83aa..e99a4fd355 100644 --- a/components/engine/libcontainerd/remote_daemon_unix.go +++ b/components/engine/libcontainerd/remote_daemon_linux.go @@ -1,5 +1,3 @@ -// +build linux solaris - package libcontainerd import ( diff --git a/components/engine/libcontainerd/remote_daemon_options_unix.go b/components/engine/libcontainerd/remote_daemon_options_linux.go similarity index 96% rename from components/engine/libcontainerd/remote_daemon_options_unix.go rename to components/engine/libcontainerd/remote_daemon_options_linux.go index e97789c4e5..1e5a98124a 100644 --- a/components/engine/libcontainerd/remote_daemon_options_unix.go +++ b/components/engine/libcontainerd/remote_daemon_options_linux.go @@ -1,5 +1,3 @@ -// +build linux solaris - package libcontainerd import "fmt" diff --git a/components/engine/libcontainerd/remote_daemon_process_unix.go b/components/engine/libcontainerd/remote_daemon_process_linux.go similarity index 97% rename from components/engine/libcontainerd/remote_daemon_process_unix.go rename to components/engine/libcontainerd/remote_daemon_process_linux.go index 38533df35f..fd54d01981 100644 --- a/components/engine/libcontainerd/remote_daemon_process_unix.go +++ b/components/engine/libcontainerd/remote_daemon_process_linux.go @@ -1,5 +1,3 @@ -// +build linux solaris - package libcontainerd import ( diff --git a/components/engine/pkg/directory/directory_unix.go b/components/engine/pkg/directory/directory_unix.go index d4f2970a64..25da197fc0 100644 --- a/components/engine/pkg/directory/directory_unix.go +++ b/components/engine/pkg/directory/directory_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris +// +build linux freebsd package directory diff --git a/components/engine/pkg/mount/flags_unsupported.go b/components/engine/pkg/mount/flags_unsupported.go index 9ed741e3ff..43d5e339f0 100644 --- a/components/engine/pkg/mount/flags_unsupported.go +++ b/components/engine/pkg/mount/flags_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!freebsd freebsd,!cgo solaris,!cgo +// +build !linux,!freebsd freebsd,!cgo package mount diff --git a/components/engine/pkg/mount/mount.go b/components/engine/pkg/mount/mount.go index eced0219fd..ee5833c49d 100644 --- a/components/engine/pkg/mount/mount.go +++ b/components/engine/pkg/mount/mount.go @@ -13,7 +13,7 @@ func GetMounts() ([]*Info, error) { } // Mounted determines if a specified mountpoint has been mounted. -// On Linux it looks at /proc/self/mountinfo and on Solaris at mnttab. +// On Linux it looks at /proc/self/mountinfo. func Mounted(mountpoint string) (bool, error) { entries, err := parseMountTable() if err != nil { diff --git a/components/engine/pkg/mount/mount_unix_test.go b/components/engine/pkg/mount/mount_unix_test.go index 253aff3b8e..90fa348b22 100644 --- a/components/engine/pkg/mount/mount_unix_test.go +++ b/components/engine/pkg/mount/mount_unix_test.go @@ -1,4 +1,4 @@ -// +build !windows,!solaris +// +build !windows package mount diff --git a/components/engine/pkg/mount/mounter_unsupported.go b/components/engine/pkg/mount/mounter_unsupported.go index a2a3bb457f..eb93365eb7 100644 --- a/components/engine/pkg/mount/mounter_unsupported.go +++ b/components/engine/pkg/mount/mounter_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo +// +build !linux,!freebsd freebsd,!cgo package mount diff --git a/components/engine/pkg/mount/mountinfo_unsupported.go b/components/engine/pkg/mount/mountinfo_unsupported.go index 7fbcf19214..b8d9aa5c73 100644 --- a/components/engine/pkg/mount/mountinfo_unsupported.go +++ b/components/engine/pkg/mount/mountinfo_unsupported.go @@ -1,4 +1,4 @@ -// +build !windows,!linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo +// +build !windows,!linux,!freebsd freebsd,!cgo package mount diff --git a/components/engine/pkg/parsers/kernel/kernel_unix.go b/components/engine/pkg/parsers/kernel/kernel_unix.go index 46ef7a622f..767ede2887 100644 --- a/components/engine/pkg/parsers/kernel/kernel_unix.go +++ b/components/engine/pkg/parsers/kernel/kernel_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris openbsd +// +build linux freebsd openbsd // Package kernel provides helper function to get, parse and compare kernel // versions for different platforms. diff --git a/components/engine/pkg/parsers/kernel/uname_unsupported.go b/components/engine/pkg/parsers/kernel/uname_unsupported.go index 1da3f239fa..79c66b3228 100644 --- a/components/engine/pkg/parsers/kernel/uname_unsupported.go +++ b/components/engine/pkg/parsers/kernel/uname_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!solaris +// +build !linux package kernel diff --git a/components/engine/pkg/platform/architecture_unix.go b/components/engine/pkg/platform/architecture_unix.go index 45bbcf1535..8db908682d 100644 --- a/components/engine/pkg/platform/architecture_unix.go +++ b/components/engine/pkg/platform/architecture_unix.go @@ -1,4 +1,4 @@ -// +build freebsd solaris darwin +// +build freebsd darwin // Package platform provides helper function to get the runtime architecture // for different platforms. diff --git a/components/engine/pkg/reexec/command_unix.go b/components/engine/pkg/reexec/command_unix.go index 778a720e3b..55c0c97d96 100644 --- a/components/engine/pkg/reexec/command_unix.go +++ b/components/engine/pkg/reexec/command_unix.go @@ -1,4 +1,4 @@ -// +build freebsd solaris darwin +// +build freebsd darwin package reexec diff --git a/components/engine/pkg/reexec/command_unsupported.go b/components/engine/pkg/reexec/command_unsupported.go index 76edd82427..6f5e55d001 100644 --- a/components/engine/pkg/reexec/command_unsupported.go +++ b/components/engine/pkg/reexec/command_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!windows,!freebsd,!solaris,!darwin +// +build !linux,!windows,!freebsd,!darwin package reexec @@ -6,7 +6,7 @@ import ( "os/exec" ) -// Command is unsupported on operating systems apart from Linux, Windows, Solaris and Darwin. +// Command is unsupported on operating systems apart from Linux, Windows, and Darwin. func Command(args ...string) *exec.Cmd { return nil } diff --git a/components/engine/pkg/signal/signal_linux_test.go b/components/engine/pkg/signal/signal_linux_test.go index da0e010545..8dc913b4ed 100644 --- a/components/engine/pkg/signal/signal_linux_test.go +++ b/components/engine/pkg/signal/signal_linux_test.go @@ -1,4 +1,4 @@ -// +build darwin linux solaris +// +build darwin linux package signal diff --git a/components/engine/pkg/signal/signal_unsupported.go b/components/engine/pkg/signal/signal_unsupported.go index c592d37dfe..161ba27397 100644 --- a/components/engine/pkg/signal/signal_unsupported.go +++ b/components/engine/pkg/signal/signal_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!darwin,!freebsd,!windows,!solaris +// +build !linux,!darwin,!freebsd,!windows package signal diff --git a/components/engine/pkg/sysinfo/sysinfo_unix.go b/components/engine/pkg/sysinfo/sysinfo_unix.go index 45f3ef1c65..beac32840c 100644 --- a/components/engine/pkg/sysinfo/sysinfo_unix.go +++ b/components/engine/pkg/sysinfo/sysinfo_unix.go @@ -1,8 +1,8 @@ -// +build !linux,!solaris,!windows +// +build !linux,!windows package sysinfo -// New returns an empty SysInfo for non linux nor solaris for now. +// New returns an empty SysInfo for non linux for now. func New(quiet bool) *SysInfo { sysInfo := &SysInfo{} return sysInfo diff --git a/components/engine/pkg/system/meminfo_unsupported.go b/components/engine/pkg/system/meminfo_unsupported.go index 3ce019dffd..82ddd30c1b 100644 --- a/components/engine/pkg/system/meminfo_unsupported.go +++ b/components/engine/pkg/system/meminfo_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!windows,!solaris +// +build !linux,!windows package system diff --git a/components/engine/pkg/system/process_unix.go b/components/engine/pkg/system/process_unix.go index 26c8b42c17..02c138235a 100644 --- a/components/engine/pkg/system/process_unix.go +++ b/components/engine/pkg/system/process_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris darwin +// +build linux freebsd darwin package system diff --git a/components/engine/pkg/term/tc.go b/components/engine/pkg/term/tc.go index 6d2dfd3a8a..19dbb1cb11 100644 --- a/components/engine/pkg/term/tc.go +++ b/components/engine/pkg/term/tc.go @@ -1,5 +1,4 @@ // +build !windows -// +build !solaris !cgo package term diff --git a/components/engine/pkg/term/winsize.go b/components/engine/pkg/term/winsize.go index 85c4d9d67e..1ef98d5996 100644 --- a/components/engine/pkg/term/winsize.go +++ b/components/engine/pkg/term/winsize.go @@ -1,4 +1,4 @@ -// +build !solaris,!windows +// +build !windows package term diff --git a/components/engine/registry/auth_test.go b/components/engine/registry/auth_test.go index 34f0c5564f..f5f213bf94 100644 --- a/components/engine/registry/auth_test.go +++ b/components/engine/registry/auth_test.go @@ -1,5 +1,3 @@ -// +build !solaris - package registry import ( diff --git a/components/engine/registry/registry_mock_test.go b/components/engine/registry/registry_mock_test.go index cf1cd19c1c..f814273d0f 100644 --- a/components/engine/registry/registry_mock_test.go +++ b/components/engine/registry/registry_mock_test.go @@ -1,5 +1,3 @@ -// +build !solaris - package registry import ( diff --git a/components/engine/registry/registry_test.go b/components/engine/registry/registry_test.go index 4cbfb110e3..56e362f8f9 100644 --- a/components/engine/registry/registry_test.go +++ b/components/engine/registry/registry_test.go @@ -1,5 +1,3 @@ -// +build !solaris - package registry import ( diff --git a/components/engine/runconfig/hostconfig_unix.go b/components/engine/runconfig/hostconfig_unix.go index 55df5da3ff..3527d29058 100644 --- a/components/engine/runconfig/hostconfig_unix.go +++ b/components/engine/runconfig/hostconfig_unix.go @@ -1,4 +1,4 @@ -// +build !windows,!solaris +// +build !windows package runconfig diff --git a/components/engine/volume/local/local_unix.go b/components/engine/volume/local/local_unix.go index 5bba5b7068..6226955717 100644 --- a/components/engine/volume/local/local_unix.go +++ b/components/engine/volume/local/local_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris +// +build linux freebsd // Package local provides the default implementation for volumes. It // is used to mount data volume containers and directories local to diff --git a/components/engine/volume/store/store_unix.go b/components/engine/volume/store/store_unix.go index c024abbf9a..065cb28eb8 100644 --- a/components/engine/volume/store/store_unix.go +++ b/components/engine/volume/store/store_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris +// +build linux freebsd package store diff --git a/components/engine/volume/volume_unix.go b/components/engine/volume/volume_unix.go index 0968fe37e1..1cb9317e7a 100644 --- a/components/engine/volume/volume_unix.go +++ b/components/engine/volume/volume_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd darwin solaris +// +build linux freebsd darwin package volume From b878f3dd1f238ebe68d0116566045a71b141a86b Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 2 Nov 2017 13:03:50 +0100 Subject: [PATCH 26/94] Fix API version Commit 3ba1dda1914fa7d380d9d3220c3b158a41f90cba bumped the API version, but forgot to actually bump the version in code. This patch fixes the version to match those changes :-) Signed-off-by: Sebastiaan van Stijn Upstream-commit: 7f8dbe4a86650b02ffb490098b9f3bf0912c575d Component: engine --- components/engine/api/common.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/api/common.go b/components/engine/api/common.go index d0229e0389..af34d0b354 100644 --- a/components/engine/api/common.go +++ b/components/engine/api/common.go @@ -3,7 +3,7 @@ package api // Common constants for daemon and client. const ( // DefaultVersion of Current REST API - DefaultVersion string = "1.34" + DefaultVersion string = "1.35" // NoBaseImageSpecifier is the symbol used by the FROM // command to specify that no base image is to be used. From 3e0fab4d2ebbcfff69ef27d7733b0fc49e0f1f75 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Tue, 31 Oct 2017 09:32:20 -0400 Subject: [PATCH 27/94] /dev should not be readonly with --readonly flag /dev is mounted on a tmpfs inside of a container. Processes inside of containers some times need to create devices nodes, or to setup a socket that listens on /dev/log Allowing these containers to run with the --readonly flag makes sense. Making a tmpfs readonly does not add any security to the container, since there is plenty of places where the container can write tmpfs content. I have no idea why /dev was excluded. Signed-off-by: Daniel J Walsh Upstream-commit: 5f3bd2473ee2a1b9f37ba0130e934133d0e01f89 Component: engine --- components/engine/daemon/oci_linux.go | 2 +- components/engine/integration-cli/docker_cli_run_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/daemon/oci_linux.go b/components/engine/daemon/oci_linux.go index b4a6bf60d2..905e20cd90 100644 --- a/components/engine/daemon/oci_linux.go +++ b/components/engine/daemon/oci_linux.go @@ -628,7 +628,7 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c if s.Root.Readonly { for i, m := range s.Mounts { switch m.Destination { - case "/proc", "/dev/pts", "/dev/mqueue": // /dev is remounted by runc + case "/proc", "/dev/pts", "/dev/mqueue", "/dev": continue } if _, ok := userMounts[m.Destination]; !ok { diff --git a/components/engine/integration-cli/docker_cli_run_test.go b/components/engine/integration-cli/docker_cli_run_test.go index 6ac10e70e9..ddb3ae95f4 100644 --- a/components/engine/integration-cli/docker_cli_run_test.go +++ b/components/engine/integration-cli/docker_cli_run_test.go @@ -2729,7 +2729,7 @@ func (s *DockerSuite) TestRunContainerWithReadonlyRootfs(c *check.C) { if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" { testPriv = false } - testReadOnlyFile(c, testPriv, "/file", "/etc/hosts", "/etc/resolv.conf", "/etc/hostname", "/sys/kernel", "/dev/.dont.touch.me") + testReadOnlyFile(c, testPriv, "/file", "/etc/hosts", "/etc/resolv.conf", "/etc/hostname", "/sys/kernel") } func (s *DockerSuite) TestPermissionsPtsReadonlyRootfs(c *check.C) { From 9f6163c9a9889983a31d2591926672969a6f36eb Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 2 Nov 2017 16:46:37 +0100 Subject: [PATCH 28/94] Update Misty's GitHub handle Signed-off-by: Sebastiaan van Stijn Upstream-commit: 30b4b5640e7117fc38755a7d5aae0d9a872efaaf Component: engine --- components/engine/MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/MAINTAINERS b/components/engine/MAINTAINERS index b3dfd4933f..d81caba80a 100644 --- a/components/engine/MAINTAINERS +++ b/components/engine/MAINTAINERS @@ -364,7 +364,7 @@ [people.misty] Name = "Misty Stanley-Jones" Email = "misty@docker.com" - GitHub = "mstanleyjones" + GitHub = "mistyhacks" [people.mlaventure] Name = "Kenfe-Mickaël Laventure" From cbad1c68f7910f27f632fd26441038a89ad187b6 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Wed, 1 Nov 2017 21:28:37 -0400 Subject: [PATCH 29/94] Update docker-py commit Signed-off-by: Brian Goff Upstream-commit: 16d83571f5f8f833aca123874cbb34ff023df995 Component: engine --- components/engine/Dockerfile | 2 +- components/engine/Dockerfile.aarch64 | 2 +- components/engine/Dockerfile.armhf | 2 +- components/engine/Dockerfile.ppc64le | 2 +- components/engine/Dockerfile.s390x | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/engine/Dockerfile b/components/engine/Dockerfile index bf86bdb4a9..25da31313e 100644 --- a/components/engine/Dockerfile +++ b/components/engine/Dockerfile @@ -133,7 +133,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT a962578e515185cf06506050b2200c0b81aa84ef +ENV DOCKER_PY_COMMIT ca7a6132a418c32df6bb11ba9b2a8b9b2727227a # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.aarch64 b/components/engine/Dockerfile.aarch64 index 4a4c635fde..97dff4654f 100644 --- a/components/engine/Dockerfile.aarch64 +++ b/components/engine/Dockerfile.aarch64 @@ -105,7 +105,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT a962578e515185cf06506050b2200c0b81aa84ef +ENV DOCKER_PY_COMMIT ca7a6132a418c32df6bb11ba9b2a8b9b2727227a # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.armhf b/components/engine/Dockerfile.armhf index e64bfd7d20..30ddf8c040 100644 --- a/components/engine/Dockerfile.armhf +++ b/components/engine/Dockerfile.armhf @@ -103,7 +103,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT a962578e515185cf06506050b2200c0b81aa84ef +ENV DOCKER_PY_COMMIT ca7a6132a418c32df6bb11ba9b2a8b9b2727227a # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.ppc64le b/components/engine/Dockerfile.ppc64le index c95b68b225..709a6e6241 100644 --- a/components/engine/Dockerfile.ppc64le +++ b/components/engine/Dockerfile.ppc64le @@ -101,7 +101,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT a962578e515185cf06506050b2200c0b81aa84ef +ENV DOCKER_PY_COMMIT ca7a6132a418c32df6bb11ba9b2a8b9b2727227a # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.s390x b/components/engine/Dockerfile.s390x index b438a5d2bb..752d052c62 100644 --- a/components/engine/Dockerfile.s390x +++ b/components/engine/Dockerfile.s390x @@ -95,7 +95,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT a962578e515185cf06506050b2200c0b81aa84ef +ENV DOCKER_PY_COMMIT ca7a6132a418c32df6bb11ba9b2a8b9b2727227a # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ From 9136cbe3998c2ffd8542c5b826d870e47780bb0d Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Thu, 2 Nov 2017 18:05:38 -0400 Subject: [PATCH 30/94] temporarily move docker-py tests for faster feedback Signed-off-by: Brian Goff Upstream-commit: 073dd7ab39da342793c30c08192a8005498e0ffd Component: engine --- components/engine/hack/ci/janky | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/hack/ci/janky b/components/engine/hack/ci/janky index fe04908cbc..180f8c602d 100755 --- a/components/engine/hack/ci/janky +++ b/components/engine/hack/ci/janky @@ -8,6 +8,6 @@ hack/test/unit hack/make.sh \ binary-daemon \ dynbinary \ - test-integration \ test-docker-py \ + test-integration \ cross From 304c69a1ec352895b41c4298ed56513a7f85002d Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Thu, 2 Nov 2017 16:34:00 -0700 Subject: [PATCH 31/94] cluster: avoid recursive readlock on swarm info Signed-off-by: Tonis Tiigi Upstream-commit: ee1be71e9dc52db8a7ac951ba96f18a5c894806a Component: engine --- components/engine/daemon/cluster/swarm.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/components/engine/daemon/cluster/swarm.go b/components/engine/daemon/cluster/swarm.go index e3fffe983d..61223691bd 100644 --- a/components/engine/daemon/cluster/swarm.go +++ b/components/engine/daemon/cluster/swarm.go @@ -198,9 +198,9 @@ func (c *Cluster) Join(req types.JoinRequest) error { // Inspect retrieves the configuration properties of a managed swarm cluster. func (c *Cluster) Inspect() (types.Swarm, error) { - var swarm *swarmapi.Cluster + var swarm types.Swarm if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error { - s, err := getSwarm(ctx, state.controlClient) + s, err := c.inspect(ctx, state) if err != nil { return err } @@ -209,7 +209,15 @@ func (c *Cluster) Inspect() (types.Swarm, error) { }); err != nil { return types.Swarm{}, err } - return convert.SwarmFromGRPC(*swarm), nil + return swarm, nil +} + +func (c *Cluster) inspect(ctx context.Context, state nodeState) (types.Swarm, error) { + s, err := getSwarm(ctx, state.controlClient) + if err != nil { + return types.Swarm{}, err + } + return convert.SwarmFromGRPC(*s), nil } // Update updates configuration of a managed swarm cluster. @@ -413,7 +421,7 @@ func (c *Cluster) Info() types.Info { if state.IsActiveManager() { info.ControlAvailable = true - swarm, err := c.Inspect() + swarm, err := c.inspect(ctx, state) if err != nil { info.Error = err.Error() } From 4a715bb927ca90e6be56ed7034c8d9cd681fe7a5 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 3 Nov 2017 01:21:18 +0100 Subject: [PATCH 32/94] Use containerd API to get version The `docker info` code was shelling out to obtain the version of containerd (using the `--version` flag). Parsing the output of this version string is error-prone, and not needed, as the containerd API can return the version. This patch adds a `Version()` method to the containerd Client interface, and uses this to get the containerd version. Signed-off-by: Sebastiaan van Stijn Upstream-commit: fec2b144feaaa18998ec2ed34c9bc843c4c29abd Component: engine --- components/engine/daemon/info_unix.go | 17 ++++------------- .../engine/libcontainerd/client_daemon.go | 4 ++++ .../libcontainerd/client_local_windows.go | 5 +++++ components/engine/libcontainerd/types.go | 2 ++ 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/components/engine/daemon/info_unix.go b/components/engine/daemon/info_unix.go index fd2bbb45c3..9433434bcb 100644 --- a/components/engine/daemon/info_unix.go +++ b/components/engine/daemon/info_unix.go @@ -3,6 +3,7 @@ package daemon import ( + "context" "os/exec" "strings" @@ -48,20 +49,10 @@ func (daemon *Daemon) FillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) } v.ContainerdCommit.Expected = dockerversion.ContainerdCommitID - if rv, err := exec.Command("docker-containerd", "--version").Output(); err == nil { - parts := strings.Split(strings.TrimSpace(string(rv)), " ") - if len(parts) == 3 { - v.ContainerdCommit.ID = parts[2] - } - switch { - case v.ContainerdCommit.ID == "": - logrus.Warnf("failed to retrieve docker-containerd version: unknown format", string(rv)) - v.ContainerdCommit.ID = "N/A" - case strings.HasSuffix(v.ContainerdCommit.ID, "-g"+v.ContainerdCommit.ID[len(v.ContainerdCommit.ID)-7:]): - v.ContainerdCommit.ID = v.ContainerdCommit.Expected - } + if rv, err := daemon.containerd.Version(context.Background()); err == nil { + v.ContainerdCommit.ID = rv.Revision } else { - logrus.Warnf("failed to retrieve docker-containerd version: %v", err) + logrus.Warnf("failed to retrieve containerd version: %v", err) v.ContainerdCommit.ID = "N/A" } diff --git a/components/engine/libcontainerd/client_daemon.go b/components/engine/libcontainerd/client_daemon.go index e6514374ce..b0cdcfcfd9 100644 --- a/components/engine/libcontainerd/client_daemon.go +++ b/components/engine/libcontainerd/client_daemon.go @@ -60,6 +60,10 @@ type client struct { containers map[string]*container } +func (c *client) Version(ctx context.Context) (containerd.Version, error) { + return c.remote.Version(ctx) +} + func (c *client) Restore(ctx context.Context, id string, attachStdio StdioCallback) (alive bool, pid int, err error) { c.Lock() defer c.Unlock() diff --git a/components/engine/libcontainerd/client_local_windows.go b/components/engine/libcontainerd/client_local_windows.go index c33e346a7a..8ce9dfedab 100644 --- a/components/engine/libcontainerd/client_local_windows.go +++ b/components/engine/libcontainerd/client_local_windows.go @@ -17,6 +17,7 @@ import ( "github.com/Microsoft/hcsshim" opengcs "github.com/Microsoft/opengcs/client" + "github.com/containerd/containerd" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" specs "github.com/opencontainers/runtime-spec/specs-go" @@ -70,6 +71,10 @@ const ( // of docker. const defaultOwner = "docker" +func (c *client) Version(ctx context.Context) (containerd.Version, error) { + return containerd.Version{}, errors.New("not implemented on Windows") +} + // Create is the entrypoint to create a container from a spec. // Table below shows the fields required for HCS JSON calling parameters, // where if not populated, is omitted. diff --git a/components/engine/libcontainerd/types.go b/components/engine/libcontainerd/types.go index 9e05c16bf8..9eede43a49 100644 --- a/components/engine/libcontainerd/types.go +++ b/components/engine/libcontainerd/types.go @@ -82,6 +82,8 @@ type Backend interface { // Client provides access to containerd features. type Client interface { + Version(ctx context.Context) (containerd.Version, error) + Restore(ctx context.Context, containerID string, attachStdio StdioCallback) (alive bool, pid int, err error) Create(ctx context.Context, containerID string, spec *specs.Spec, runtimeOptions interface{}) error From fc26552dd299a99a1e451a174ebd0a4947e6b930 Mon Sep 17 00:00:00 2001 From: Christopher Crone Date: Fri, 3 Nov 2017 10:09:06 +0100 Subject: [PATCH 33/94] e2e: Add missing headers for build Signed-off-by: Christopher Crone Upstream-commit: 37553a6963455fc0d5cea1ae218575cc28d3d251 Component: engine --- components/engine/Dockerfile.e2e | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/Dockerfile.e2e b/components/engine/Dockerfile.e2e index dcbecb7787..c57d7d3217 100644 --- a/components/engine/Dockerfile.e2e +++ b/components/engine/Dockerfile.e2e @@ -3,6 +3,7 @@ FROM golang:1.8.5-alpine3.6 as builder RUN apk add --update \ bash \ + btrfs-progs-dev \ build-base \ curl \ lvm2-dev \ From 4aac83aaac74f6a0b011ed9690fd5e28cf6055c2 Mon Sep 17 00:00:00 2001 From: Justin Cormack Date: Wed, 11 Oct 2017 15:06:02 +0100 Subject: [PATCH 34/94] First pass updating CONTRIBUTING This largely removes references to Docker where possible. The HOWTO guides are still Docker docs and refer to Docker not moby, so the next step is to rework these as Moby docs and put them in `docs/` in this repo. Signed-off-by: Justin Cormack Upstream-commit: cecd0de3a9f783b34f75ca5e6394b9a0c123999e Component: engine --- components/engine/CONTRIBUTING.md | 75 ++-- components/engine/docs/contributing/README.md | 8 + .../docs/contributing/images/branch-sig.png | Bin 0 -> 56537 bytes .../contributing/images/contributor-edit.png | Bin 0 -> 17933 bytes .../docs/contributing/images/copy_url.png | Bin 0 -> 69486 bytes .../docs/contributing/images/fork_docker.png | Bin 0 -> 52190 bytes .../docs/contributing/images/git_bash.png | Bin 0 -> 26097 bytes .../docs/contributing/images/list_example.png | Bin 0 -> 51194 bytes .../docs/contributing/set-up-dev-env.md | 321 ++++++++++++++++++ .../engine/docs/contributing/set-up-git.md | 280 +++++++++++++++ .../docs/contributing/software-req-win.md | 177 ++++++++++ .../docs/contributing/software-required.md | 94 +++++ components/engine/docs/contributing/test.md | 234 +++++++++++++ .../docs/contributing/who-written-for.md | 49 +++ 14 files changed, 1185 insertions(+), 53 deletions(-) create mode 100644 components/engine/docs/contributing/README.md create mode 100644 components/engine/docs/contributing/images/branch-sig.png create mode 100644 components/engine/docs/contributing/images/contributor-edit.png create mode 100644 components/engine/docs/contributing/images/copy_url.png create mode 100644 components/engine/docs/contributing/images/fork_docker.png create mode 100644 components/engine/docs/contributing/images/git_bash.png create mode 100644 components/engine/docs/contributing/images/list_example.png create mode 100644 components/engine/docs/contributing/set-up-dev-env.md create mode 100644 components/engine/docs/contributing/set-up-git.md create mode 100644 components/engine/docs/contributing/software-req-win.md create mode 100644 components/engine/docs/contributing/software-required.md create mode 100644 components/engine/docs/contributing/test.md create mode 100644 components/engine/docs/contributing/who-written-for.md diff --git a/components/engine/CONTRIBUTING.md b/components/engine/CONTRIBUTING.md index c55e13bbcf..b7961e14e4 100644 --- a/components/engine/CONTRIBUTING.md +++ b/components/engine/CONTRIBUTING.md @@ -1,8 +1,8 @@ -# Contributing to Docker +# Contribute to the Moby Project -Want to hack on Docker? Awesome! We have a contributor's guide that explains -[setting up a Docker development environment and the contribution -process](https://docs.docker.com/opensource/project/who-written-for/). +Want to hack on the Moby Project? Awesome! We have a contributor's guide that explains +[setting up a development environment and the contribution +process](docs/contributing/). [![Contributors guide](docs/static_files/contributors.png)](https://docs.docker.com/opensource/project/who-written-for/) @@ -21,14 +21,14 @@ start participating. ## Reporting security issues -The Docker maintainers take security seriously. If you discover a security +The Moby maintainers take security seriously. If you discover a security issue, please bring it to their attention right away! Please **DO NOT** file a public issue, instead send your report privately to [security@docker.com](mailto:security@docker.com). Security reports are greatly appreciated and we will publicly thank you for it. -We also like to send gifts—if you're into Docker schwag, make sure to let +We also like to send gifts—if you're into schwag, make sure to let us know. We currently do not offer a paid security bounty program, but are not ruling it out in the future. @@ -83,11 +83,7 @@ contributions, see [the advanced contribution section](https://docs.docker.com/opensource/workflow/advanced-contributing/) in the contributors guide. -We try hard to keep Docker lean and focused. Docker can't do everything for -everybody. This means that we might decide against incorporating a new feature. -However, there might be a way to implement that feature *on top of* Docker. - -### Talking to other Docker users and contributors +### Connect with other Moby Project contributors @@ -96,52 +92,29 @@ However, there might be a way to implement that feature *on top of* Docker. - + - - - - - - - -
Forums A public forum for users to discuss questions and explore current design patterns and - best practices about Docker and related projects in the Docker Ecosystem. To participate, - just log in with your Docker Hub account on https://forums.docker.com. + best practices about all the Moby projects. To participate, log in with your Github + account or create an account at https://forums.mobyproject.org.
Internet Relay Chat (IRC)Slack

- IRC a direct line to our most knowledgeable Docker users; we have - both the #docker and #docker-dev group on - irc.freenode.net. - IRC is a rich chat protocol but it can overwhelm new users. You can search - our chat archives. + Register for the Docker Community Slack at + https://community.docker.com/registrations/groups/4316. + We use the #moby-project channel for general discussion, and there are seperate channels for other Moby projects such as #containerd. + Archives are available at https://dockercommunity.slackarchive.io/.

-

- Read our IRC quickstart guide - for an easy way to get started. -

-
Google Group - The docker-dev - group is for contributors and other people contributing to the Docker project. - You can join them without a google account by sending an email to - docker-dev+subscribe@googlegroups.com. - After receiving the join-request message, you can simply reply to that to confirm the subscription.
Twitter - You can follow Docker's Twitter feed + You can follow Moby Project Twitter feed to get updates on our products. You can also tweet us questions or just share blogs or stories.
Stack Overflow - Stack Overflow has thousands of Docker questions listed. We regularly - monitor Docker questions - and so do many other knowledgeable Docker users. -
@@ -159,7 +132,7 @@ Submit tests for your changes. See [TESTING.md](./TESTING.md) for details. If your changes need integration tests, write them against the API. The `cli` integration tests are slowly either migrated to API tests or moved away as unit -tests in `docker/cli` and end-to-end tests for docker. +tests in `docker/cli` and end-to-end tests for Docker. Update the documentation when creating or modifying features. Test your documentation changes for clarity, concision, and correctness, as well as a @@ -266,15 +239,11 @@ Please see the [Coding Style](#coding-style) for further guidelines. ### Merge approval -Docker maintainers use LGTM (Looks Good To Me) in comments on the code review to -indicate acceptance. +Moby maintainers use LGTM (Looks Good To Me) in comments on the code review to +indicate acceptance, or use the Github review approval feature. -A change requires LGTMs from an absolute majority of the maintainers of each -component affected. For example, if a change affects `docs/` and `registry/`, it -needs an absolute majority from the maintainers of `docs/` AND, separately, an -absolute majority of the maintainers of `registry/`. - -For more details, see the [MAINTAINERS](MAINTAINERS) page. +For an explanation of the review and approval process see the +[REVIEWING](project/REVIEWING.md) page. ### Sign your work @@ -342,9 +311,9 @@ Don't forget: being a maintainer is a time investment. Make sure you will have time to make yourself available. You don't have to be a maintainer to make a difference on the project! -## Docker community guidelines +## Moby community guidelines -We want to keep the Docker community awesome, growing and collaborative. We need +We want to keep the Moby community awesome, growing and collaborative. We need your help to keep it that way. To help with this we've come up with some general guidelines for the community as a whole: diff --git a/components/engine/docs/contributing/README.md b/components/engine/docs/contributing/README.md new file mode 100644 index 0000000000..915c0cff1e --- /dev/null +++ b/components/engine/docs/contributing/README.md @@ -0,0 +1,8 @@ +### Get set up for Moby development + + * [README first](who-written-for.md) + * [Get the required software](software-required.md) + * [Set up for development on Windows](software-req-win.md) + * [Configure Git for contributing](set-up-git.md) + * [Work with a development container](set-up-dev-env.md) + * [Run tests and test documentation](test.md) diff --git a/components/engine/docs/contributing/images/branch-sig.png b/components/engine/docs/contributing/images/branch-sig.png new file mode 100644 index 0000000000000000000000000000000000000000..b069319eeeb7efa7c3f00b5b9d080ce83ffc6654 GIT binary patch literal 56537 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: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 TmoUm+00000NkvXXu0mjf*nsb{ literal 0 HcmV?d00001 diff --git a/components/engine/docs/contributing/images/contributor-edit.png b/components/engine/docs/contributing/images/contributor-edit.png new file mode 100644 index 0000000000000000000000000000000000000000..d847e224a5596d1211627295f068f19d5c0f38e6 GIT binary patch literal 17933 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

vP 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�_G*w{>1Y|S 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 literal 0 HcmV?d00001 diff --git a/components/engine/docs/contributing/images/list_example.png b/components/engine/docs/contributing/images/list_example.png new file mode 100644 index 0000000000000000000000000000000000000000..2e3b59a29e7377d89e8d4b4d8d8943e24c4e7721 GIT binary patch literal 51194 zcmZU31yoyIvv!a|C>E?hac_$j3IumAP@pYPC{8KvF2UWkc(G!|A-KDg;I75p3C%WA$gBqyEK@&?uDeJ=+{vq&@=Tswl@atP~aQAW$G~B2DTR#1x>gv{_v%yblxKo zd#2VjzU6*CUik2x2tc-zC5r>)Ga9uTHJ%iz%lO(~f^9IVHvmi48<~U!s7y2(O9@PuTA_8}tJ`5+EU{3p-K4KTbQMya;Ft zT7}AOD%!*8nUv3{)c92Jgnp}^_BnPY#|Fy zM>h~QI5d>9?&Pl+_n3!sv~0#WXkMuy$fpeBWN2d7Wf{^dxf7{fgD`VR@Sl<0^wH*p zBGW}H8ydli#;bfcSY&lbcsf0XKRyuV%cJC&{9wJ5`=O6B$m=lL0Uut)J^YMDjRJQ_o$@YB&i1hM&ns^O6~o0!B`; z0J}l6Kiaj{>-JGsEO@ov3?xS?Qfhf4N&ZCr=z}(bgw{r=fvmLx)b?RIMiHaNN~Wh| zd1hgh@!lwQLyvm~a4JSKgK_E2X#*lhn_NK!KT8oMs{@pJll>$+_YD$73v5&1crNU1 z3-q-Ck!Fj(jAk=J)f9z-Q0#*4eu7>hhX%z`Q__3qj+lKX_y{BtGajKSK)ZQn=EEjB z{gZNA_#GfZ)H;iu<2hq{Q8|wV7A%OgoR}O-rd>ggG7WpH!}Zvyj4cfS8}K|aJkf=`DWxF2(Qm(yV1{D`hJE(9>OGwmbt3*=a8gKA@DU*);W<$n(Tmr|L{rT1Z_+;26>

ED#DEzu(DCH{~r`>9&h=#3rW^y@@s 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 z%0D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 Hxck2V))LuH literal 0 HcmV?d00001 diff --git a/components/engine/docs/contributing/set-up-dev-env.md b/components/engine/docs/contributing/set-up-dev-env.md new file mode 100644 index 0000000000..acd6888cd4 --- /dev/null +++ b/components/engine/docs/contributing/set-up-dev-env.md @@ -0,0 +1,321 @@ +### Work with a development container + +In this section, you learn to develop like the Moby Engine core team. +The `moby/moby` repository includes a `Dockerfile` at its root. This file defines +Moby's development environment. The `Dockerfile` lists the environment's +dependencies: system libraries and binaries, Go environment, Go dependencies, +etc. + +Moby's development environment is itself, ultimately a Docker container. +You use the `moby/moby` repository and its `Dockerfile` to create a Docker image, +run a Docker container, and develop code in the container. + +If you followed the procedures that +set up Git for contributing, you should have a fork of the `moby/moby` +repository. You also created a branch called `dry-run-test`. In this section, +you continue working with your fork on this branch. + +## Task 1. Remove images and containers + +Moby developers run the latest stable release of the Docker software. They clean their local hosts of +unnecessary Docker artifacts such as stopped containers or unused images. +Cleaning unnecessary artifacts isn't strictly necessary, but it is good +practice, so it is included here. + +To remove unnecessary artifacts: + +1. Verify that you have no unnecessary containers running on your host. + + ```none + $ docker ps -a + ``` + + You should see something similar to the following: + + ```none + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + ``` + + There are no running or stopped containers on this host. A fast way to + remove old containers is the following: + + You can now use the `docker system prune` command to achieve this: + + ```none + $ docker system prune -a + ``` + + Older versions of the Docker Engine should reference the command below: + + ```none + $ docker rm $(docker ps -a -q) + ``` + + This command uses `docker ps` to list all containers (`-a` flag) by numeric + IDs (`-q` flag). Then, the `docker rm` command removes the resulting list. + If you have running but unused containers, stop and then remove them with + the `docker stop` and `docker rm` commands. + +2. Verify that your host has no dangling images. + + ```none + $ docker images + ``` + + You should see something similar to the following: + + ```none + REPOSITORY TAG IMAGE ID CREATED SIZE + ``` + + This host has no images. You may have one or more _dangling_ images. A + dangling image is not used by a running container and is not an ancestor of + another image on your system. A fast way to remove dangling image is + the following: + + ```none + $ docker rmi -f $(docker images -q -a -f dangling=true) + ``` + + This command uses `docker images` to list all images (`-a` flag) by numeric + IDs (`-q` flag) and filter them to find dangling images (`-f dangling=true`). + Then, the `docker rmi` command forcibly (`-f` flag) removes + the resulting list. If you get a "docker: "rmi" requires a minimum of 1 argument." + message, that means there were no dangling images. To remove just one image, use the + `docker rmi ID` command. + +## Task 2. Start a development container + +If you followed the last procedure, your host is clean of unnecessary images and +containers. In this section, you build an image from the Engine development +environment and run it in the container. Both steps are automated for you by the +Makefile in the Engine code repository. The first time you build an image, it +can take over 15 minutes to complete. + +1. Open a terminal. + + For [Docker Toolbox](../../toolbox/overview.md) users, use `docker-machine status your_vm_name` to make sure your VM is running. You + may need to run `eval "$(docker-machine env your_vm_name)"` to initialize your + shell environment. If you use Docker for Mac or Docker for Windows, you do not need + to use Docker Machine. + +2. Change into the root of the `moby-fork` repository. + + ```none + $ cd ~/repos/moby-fork + ``` + + If you are following along with this guide, you created a `dry-run-test` + branch when you + set up Git for contributing. + +3. Ensure you are on your `dry-run-test` branch. + + ```none + $ git checkout dry-run-test + ``` + + If you get a message that the branch doesn't exist, add the `-b` flag (`git checkout -b dry-run-test`) so the + command both creates the branch and checks it out. + +4. Use `make` to build a development environment image and run it in a container. + + ```none + $ make BIND_DIR=. shell + ``` + + Using the instructions in the + `Dockerfile`, the build may need to download and / or configure source and other images. On first build this process may take between 5 - 15 minutes to create an image. The command returns informational messages as it runs. A + successful build returns a final message and opens a Bash shell into the + container. + + ```none + Successfully built 3d872560918e + docker run --rm -i --privileged -e BUILDFLAGS -e KEEPBUNDLE -e DOCKER_BUILD_GOGC -e DOCKER_BUILD_PKGS -e DOCKER_CLIENTONLY -e DOCKER_DEBUG -e DOCKER_EXPERIMENTAL -e DOCKER_GITCOMMIT -e DOCKER_GRAPHDRIVER=devicemapper -e DOCKER_INCREMENTAL_BINARY -e DOCKER_REMAP_ROOT -e DOCKER_STORAGE_OPTS -e DOCKER_USERLANDPROXY -e TESTDIRS -e TESTFLAGS -e TIMEOUT -v "home/ubuntu/repos/docker/bundles:/go/src/github.com/moby/moby/bundles" -t "docker-dev:dry-run-test" bash + root@f31fa223770f:/go/src/github.com/moby/moby# + ``` + + At this point, your prompt reflects the container's BASH shell. + +5. List the contents of the current directory (`/go/src/github.com/moby/moby`). + + You should see the image's source from the `/go/src/github.com/moby/moby` + directory. + + ![List example](images/list_example.png) + +6. Make a `dockerd` binary. + + ```none + root@a8b2885ab900:/go/src/github.com/moby/moby# hack/make.sh binary + Removing bundles/ + + ---> Making bundle: binary (in bundles/binary) + Building: bundles/binary-daemon/dockerd-17.06.0-dev + Created binary: bundles/binary-daemon/dockerd-17.06.0-dev + Copying nested executables into bundles/binary-daemon + + ``` + +7. Run `make install`, which copies the binary to the container's + `/usr/local/bin/` directory. + + ```none + root@a8b2885ab900:/go/src/github.com/moby/moby# make install + ``` + +8. Start the Engine daemon running in the background. + + ```none + root@a8b2885ab900:/go/src/github.com/docker/docker# dockerd -D & + ...output snipped... + DEBU[0001] Registering POST, /networks/{id:.*}/connect + DEBU[0001] Registering POST, /networks/{id:.*}/disconnect + DEBU[0001] Registering DELETE, /networks/{id:.*} + INFO[0001] API listen on /var/run/docker.sock + DEBU[0003] containerd connection state change: READY + ``` + + The `-D` flag starts the daemon in debug mode. The `&` starts it as a + background process. You'll find these options useful when debugging code + development. You will need to hit `return` in order to get back to your shell prompt. + + > **Note**: The following command automates the `build`, + > `install`, and `run` steps above. Once the command below completes, hit `ctrl-z` to suspend the process, then run `bg 1` and hit `enter` to resume the daemon process in the background and get back to your shell prompt. + + ```none + hack/make.sh binary install-binary run + ``` + +9. Inside your container, check your Docker version. + + ```none + root@5f8630b873fe:/go/src/github.com/moby/moby# docker --version + Docker version 1.12.0-dev, build 6e728fb + ``` + + Inside the container you are running a development version. This is the version + on the current branch. It reflects the value of the `VERSION` file at the + root of your `docker-fork` repository. + +10. Run the `hello-world` image. + + ```none + root@5f8630b873fe:/go/src/github.com/moby/moby# docker run hello-world + ``` + +11. List the image you just downloaded. + + ```none + root@5f8630b873fe:/go/src/github.com/moby/moby# docker images + REPOSITORY TAG IMAGE ID CREATED SIZE + hello-world latest c54a2cc56cbb 3 months ago 1.85 kB + ``` + +12. Open another terminal on your local host. + +13. List the container running your development container. + + ```none + ubuntu@ubuntu1404:~$ docker ps + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + a8b2885ab900 docker-dev:dry-run-test "hack/dind bash" 43 minutes ago Up 43 minutes hungry_payne + ``` + + Notice that the tag on the container is marked with the `dry-run-test` branch name. + + +## Task 3. Make a code change + +At this point, you have experienced the "Moby inception" technique. That is, +you have: + +* forked and cloned the Moby Engine code repository +* created a feature branch for development +* created and started an Engine development container from your branch +* built a binary inside of your development container +* launched a `docker` daemon using your newly compiled binary +* called the `docker` client to run a `hello-world` container inside + your development container + +Running the `make BIND_DIR=. shell` command mounted your local Docker repository source into +your Docker container. + + > **Note**: Inspecting the `Dockerfile` shows a `COPY . /go/src/github.com/docker/docker` instruction, suggesting that dynamic code changes will _not_ be reflected in the container. However inspecting the `Makefile` shows that the current working directory _will_ be mounted via a `-v` volume mount. + +When you start to develop code though, you'll +want to iterate code changes and builds inside the container. If you have +followed this guide exactly, you have a bash shell running a development +container. + +Try a simple code change and see it reflected in your container. For this +example, you'll edit the help for the `attach` subcommand. + +1. If you don't have one, open a terminal in your local host. + +2. Make sure you are in your `moby-fork` repository. + + ```none + $ pwd + /Users/mary/go/src/github.com/moxiegirl/moby-fork + ``` + + Your location should be different because, at least, your username is + different. + +3. Open the `cmd/dockerd/docker.go` file. + +4. Edit the command's help message. + + For example, you can edit this line: + + ```go + Short: "A self-sufficient runtime for containers.", + ``` + + And change it to this: + + ```go + Short: "A self-sufficient and really fun runtime for containers.", + ``` + +5. Save and close the `cmd/dockerd/docker.go` file. + +6. Go to your running docker development container shell. + +7. Rebuild the binary by using the command `hack/make.sh binary` in the docker development container shell. + +8. Stop Docker if it is running. + +9. Copy the binaries to **/usr/bin** by entering the following commands in the docker development container shell. + + ``` + hack/make.sh binary install-binary + ``` + +10. To view your change, run the `dockerd --help` command in the docker development container shell. + + ```bash + root@b0cb4f22715d:/go/src/github.com/moby/moby# dockerd --help + + Usage: dockerd COMMAND + + A self-sufficient and really fun runtime for containers. + + Options: + ... + + ``` + +You've just done the basic workflow for changing the Engine code base. You made +your code changes in your feature branch. Then, you updated the binary in your +development container and tried your change out. If you were making a bigger +change, you might repeat or iterate through this flow several times. + +## Where to go next + +Congratulations, you have successfully achieved Docker inception. You've had a +small experience of the development process. You've set up your development +environment and verified almost all the essential processes you need to +contribute. Of course, before you start contributing, [you'll need to learn one +more piece of the development process, the test framework](test.md). diff --git a/components/engine/docs/contributing/set-up-git.md b/components/engine/docs/contributing/set-up-git.md new file mode 100644 index 0000000000..f320c2716c --- /dev/null +++ b/components/engine/docs/contributing/set-up-git.md @@ -0,0 +1,280 @@ +### Configure Git for contributing + +Work through this page to configure Git and a repository you'll use throughout +the Contributor Guide. The work you do further in the guide, depends on the work +you do here. + +## Task 1. Fork and clone the Moby code + +Before contributing, you first fork the Moby code repository. A fork copies +a repository at a particular point in time. GitHub tracks for you where a fork +originates. + +As you make contributions, you change your fork's code. When you are ready, +you make a pull request back to the original Docker repository. If you aren't +familiar with this workflow, don't worry, this guide walks you through all the +steps. + +To fork and clone Moby: + +1. Open a browser and log into GitHub with your account. + +2. Go to the moby/moby repository. + +3. Click the "Fork" button in the upper right corner of the GitHub interface. + + ![Branch Signature](images/fork_docker.png) + + GitHub forks the repository to your GitHub account. The original + `moby/moby` repository becomes a new fork `YOUR_ACCOUNT/moby` under + your account. + +4. Copy your fork's clone URL from GitHub. + + GitHub allows you to use HTTPS or SSH protocols for clones. You can use the + `git` command line or clients like Subversion to clone a repository. + + ![Copy clone URL](images/copy_url.png) + + This guide assume you are using the HTTPS protocol and the `git` command + line. If you are comfortable with SSH and some other tool, feel free to use + that instead. You'll need to convert what you see in the guide to what is + appropriate to your tool. + +5. Open a terminal window on your local host and change to your home directory. + + ```bash + $ cd ~ + ``` + + In Windows, you'll work in your Docker Quickstart Terminal window instead of + Powershell or a `cmd` window. + +6. Create a `repos` directory. + + ```bash + $ mkdir repos + ``` + +7. Change into your `repos` directory. + + ```bash + $ cd repos + ``` + +8. Clone the fork to your local host into a repository called `moby-fork`. + + ```bash + $ git clone https://github.com/moxiegirl/moby.git moby-fork + ``` + + Naming your local repo `moby-fork` should help make these instructions + easier to follow; experienced coders don't typically change the name. + +9. Change directory into your new `moby-fork` directory. + + ```bash + $ cd moby-fork + ``` + + Take a moment to familiarize yourself with the repository's contents. List + the contents. + +## Task 2. Set your signature and an upstream remote + +When you contribute to Docker, you must certify you agree with the +Developer Certificate of Origin. +You indicate your agreement by signing your `git` commits like this: + +``` +Signed-off-by: Pat Smith +``` + +To create a signature, you configure your username and email address in Git. +You can set these globally or locally on just your `moby-fork` repository. +You must sign with your real name. You can sign your git commit automatically +with `git commit -s`. Moby does not accept anonymous contributions or contributions +through pseudonyms. + +As you change code in your fork, you'll want to keep it in sync with the changes +others make in the `moby/moby` repository. To make syncing easier, you'll +also add a _remote_ called `upstream` that points to `moby/moby`. A remote +is just another project version hosted on the internet or network. + +To configure your username, email, and add a remote: + +1. Change to the root of your `moby-fork` repository. + + ```bash + $ cd moby-fork + ``` + +2. Set your `user.name` for the repository. + + ```bash + $ git config --local user.name "FirstName LastName" + ``` + +3. Set your `user.email` for the repository. + + ```bash + $ git config --local user.email "emailname@mycompany.com" + ``` + +4. Set your local repo to track changes upstream, on the `moby/moby` repository. + + ```bash + $ git remote add upstream https://github.com/moby/moby.git + ``` + +5. Check the result in your `git` configuration. + + ```bash + $ git config --local -l + core.repositoryformatversion=0 + core.filemode=true + core.bare=false + core.logallrefupdates=true + remote.origin.url=https://github.com/moxiegirl/moby.git + remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* + branch.master.remote=origin + branch.master.merge=refs/heads/master + user.name=Mary Anthony + user.email=mary@docker.com + remote.upstream.url=https://github.com/moby/moby.git + remote.upstream.fetch=+refs/heads/*:refs/remotes/upstream/* + ``` + + To list just the remotes use: + + ```bash + $ git remote -v + origin https://github.com/moxiegirl/moby.git (fetch) + origin https://github.com/moxiegirl/moby.git (push) + upstream https://github.com/moby/moby.git (fetch) + upstream https://github.com/moby/moby.git (push) + ``` + +## Task 3. Create and push a branch + +As you change code in your fork, make your changes on a repository branch. +The branch name should reflect what you are working on. In this section, you +create a branch, make a change, and push it up to your fork. + +This branch is just for testing your config for this guide. The changes are part +of a dry run, so the branch name will be dry-run-test. To create and push +the branch to your fork on GitHub: + +1. Open a terminal and go to the root of your `moby-fork`. + + ```bash + $ cd moby-fork + ``` + +2. Create a `dry-run-test` branch. + + ```bash + $ git checkout -b dry-run-test + ``` + + This command creates the branch and switches the repository to it. + +3. Verify you are in your new branch. + + ```bash + $ git branch + * dry-run-test + master + ``` + + The current branch has an * (asterisk) marker. So, these results show you + are on the right branch. + +4. Create a `TEST.md` file in the repository's root. + + ```bash + $ touch TEST.md + ``` + +5. Edit the file and add your email and location. + + ![Add your information](images/contributor-edit.png) + + You can use any text editor you are comfortable with. + +6. Save and close the file. + +7. Check the status of your branch. + + ```bash + $ git status + On branch dry-run-test + Untracked files: + (use "git add ..." to include in what will be committed) + + TEST.md + + nothing added to commit but untracked files present (use "git add" to track) + ``` + + You've only changed the one file. It is untracked so far by git. + +8. Add your file. + + ```bash + $ git add TEST.md + ``` + + That is the only _staged_ file. Stage is fancy word for work that Git is + tracking. + +9. Sign and commit your change. + + ```bash + $ git commit -s -m "Making a dry run test." + [dry-run-test 6e728fb] Making a dry run test + 1 file changed, 1 insertion(+) + create mode 100644 TEST.md + ``` + + Commit messages should have a short summary sentence of no more than 50 + characters. Optionally, you can also include a more detailed explanation + after the summary. Separate the summary from any explanation with an empty + line. + +10. Push your changes to GitHub. + + ```bash + $ git push --set-upstream origin dry-run-test + Username for 'https://github.com': moxiegirl + Password for 'https://moxiegirl@github.com': + ``` + + Git prompts you for your GitHub username and password. Then, the command + returns a result. + + ```bash + Counting objects: 13, done. + Compressing objects: 100% (2/2), done. + Writing objects: 100% (3/3), 320 bytes | 0 bytes/s, done. + Total 3 (delta 1), reused 0 (delta 0) + To https://github.com/moxiegirl/moby.git + * [new branch] dry-run-test -> dry-run-test + Branch dry-run-test set up to track remote branch dry-run-test from origin. + ``` + +11. Open your browser to GitHub. + +12. Navigate to your Moby fork. + +13. Make sure the `dry-run-test` branch exists, that it has your commit, and the +commit is signed. + + ![Branch Signature](images/branch-sig.png) + +## Where to go next + +Congratulations, you have finished configuring both your local host environment +and Git for contributing. In the next section you'll [learn how to set up and +work in a Moby development container](set-up-dev-env.md). diff --git a/components/engine/docs/contributing/software-req-win.md b/components/engine/docs/contributing/software-req-win.md new file mode 100644 index 0000000000..3be4327933 --- /dev/null +++ b/components/engine/docs/contributing/software-req-win.md @@ -0,0 +1,177 @@ +### Build and test Moby on Windows + +This page explains how to get the software you need to build, test, and run the +Moby source code for Windows and setup the required software and services: + +- Windows containers +- GitHub account +- Git + +## Prerequisites + +### 1. Windows Server 2016 or Windows 10 with all Windows updates applied + +The major build number must be at least 14393. This can be confirmed, for example, +by running the following from an elevated PowerShell prompt - this sample output +is from a fully up to date machine as at mid-November 2016: + + + PS C:\> $(gin).WindowsBuildLabEx + 14393.447.amd64fre.rs1_release_inmarket.161102-0100 + +### 2. Git for Windows (or another git client) must be installed + +https://git-scm.com/download/win. + +### 3. The machine must be configured to run containers + +For example, by following the quick start guidance at +https://msdn.microsoft.com/en-us/virtualization/windowscontainers/quick_start/quick_start or https://github.com/docker/labs/blob/master/windows/windows-containers/Setup.md + +### 4. If building in a Hyper-V VM + +For Windows Server 2016 using Windows Server containers as the default option, +it is recommended you have at least 1GB of memory assigned; +For Windows 10 where Hyper-V Containers are employed, you should have at least +4GB of memory assigned. +Note also, to run Hyper-V containers in a VM, it is necessary to configure the VM +for nested virtualization. + +## Usage + +The following steps should be run from an elevated Windows PowerShell prompt. + +>**Note**: In a default installation of containers on Windows following the quick-start guidance at https://msdn.microsoft.com/en-us/virtualization/windowscontainers/quick_start/quick_start, +the `docker.exe` client must run elevated to be able to connect to the daemon). + +### 1. Windows containers + +To test and run the Windows Moby engine, you need a system that supports Windows Containers: + +- Windows 10 Anniversary Edition +- Windows Server 2016 running in a VM, on bare metal or in the cloud + +Check out the [getting started documentation](https://github.com/docker/labs/blob/master/windows/windows-containers/Setup.md) for details. + +### 2. GitHub account + +To contribute to the Docker project, you need a GitHub account. +A free account is fine. All the Moby project repositories are public and visible to everyone. + +This guide assumes that you have basic familiarity with Git and Github terminology +and usage. +Refer to [GitHub For Beginners: Don’t Get Scared, Get Started](http://readwrite.com/2013/09/30/understanding-github-a-journey-for-beginners-part-1/) +to get up to speed on Github. + +### 3. Git + +In PowerShell, run: + + Invoke-Webrequest "https://github.com/git-for-windows/git/releases/download/v2.7.2.windows.1/Git-2.7.2-64-bit.exe" -OutFile git.exe -UseBasicParsing + Start-Process git.exe -ArgumentList '/VERYSILENT /SUPPRESSMSGBOXES /CLOSEAPPLICATIONS /DIR=c:\git\' -Wait + setx /M PATH "$env:Path;c:\git\cmd" + +You are now ready clone and build the Moby source code. + +### 4. Clone Moby + +In a new (to pick up the path change) PowerShell prompt, run: + + git clone https://github.com/moby/moby + cd moby + +This clones the main Moby repository. Check out [Moby Project](https://mobyproject.org) +to learn about the other software that powers the Moby platform. + +### 5. Build and run + +Create a builder-container with the Moby source code. You can change the source +code on your system and rebuild any time: + + docker build -t nativebuildimage -f .\Dockerfile.windows . + docker build -t nativebuildimage -f Dockerfile.windows -m 2GB . # (if using Hyper-V containers) + +To build Moby, run: + + $DOCKER_GITCOMMIT=(git rev-parse --short HEAD) + docker run --name binaries -e DOCKER_GITCOMMIT=$DOCKER_GITCOMMIT nativebuildimage hack\make.ps1 -Binary + docker run --name binaries -e DOCKER_GITCOMMIT=$DOCKER_GITCOMMIT -m 2GB nativebuildimage hack\make.ps1 -Binary # (if using Hyper-V containers) + +Copy out the resulting Windows Moby Engine binary to `dockerd.exe` in the +current directory: + + docker cp binaries:C:\go\src\github.com\moby\moby\bundles\docker.exe docker.exe + docker cp binaries:C:\go\src\github.com\moby\moby\bundles\dockerd.exe dockerd.exe + +To test it, stop the system Docker daemon and start the one you just built: + + Stop-Service Docker + .\dockerd.exe -D + +The other make targets work too, to run unit tests try: +`docker run --rm docker-builder sh -c 'cd /c/go/src/github.com/moby/moby; hack/make.sh test-unit'`. + +### 6. Remove the interim binaries container + +_(Optional)_ + + docker rm binaries + +### 7. Remove the image + +_(Optional)_ + +It may be useful to keep this image around if you need to build multiple times. +Then you can take advantage of the builder cache to have an image which has all +the components required to build the binaries already installed. + + docker rmi nativebuildimage + +## Validation + +The validation tests can only run directly on the host. +This is because they calculate information from the git repo, but the .git directory +is not passed into the image as it is excluded via `.dockerignore`. +Run the following from a Windows PowerShell prompt (elevation is not required): +(Note Go must be installed to run these tests) + + hack\make.ps1 -DCO -PkgImports -GoFormat + +## Unit tests + +To run unit tests, ensure you have created the nativebuildimage above. +Then run one of the following from an (elevated) Windows PowerShell prompt: + + docker run --rm nativebuildimage hack\make.ps1 -TestUnit + docker run --rm -m 2GB nativebuildimage hack\make.ps1 -TestUnit # (if using Hyper-V containers) + +To run unit tests and binary build, ensure you have created the nativebuildimage above. +Then run one of the following from an (elevated) Windows PowerShell prompt: + + docker run nativebuildimage hack\make.ps1 -All + docker run -m 2GB nativebuildimage hack\make.ps1 -All # (if using Hyper-V containers) + +## Windows limitations + +Don't attempt to use a bind mount to pass a local directory as the bundles +target directory. +It does not work (golang attempts for follow a mapped folder incorrectly). +Instead, use docker cp as per the example. + +`go.zip` is not removed from the image as it is used by the Windows CI servers +to ensure the host and image are running consistent versions of go. + +Nanoserver support is a work in progress. Although the image will build if the +`FROM` statement is updated, it will not work when running autogen through `hack\make.ps1`. +It is suspected that the required GCC utilities (eg gcc, windres, windmc) silently +quit due to the use of console hooks which are not available. + +The docker integration tests do not currently run in a container on Windows, +predominantly due to Windows not supporting privileged mode, so anything using a volume would fail. +They (along with the rest of the docker CI suite) can be run using +https://github.com/jhowardmsft/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1. + +## Where to go next + +In the next section, you'll [learn how to set up and configure Git for +contributing to Moby](set-up-git.md). diff --git a/components/engine/docs/contributing/software-required.md b/components/engine/docs/contributing/software-required.md new file mode 100644 index 0000000000..b14c6f9050 --- /dev/null +++ b/components/engine/docs/contributing/software-required.md @@ -0,0 +1,94 @@ +### Get the required software for Linux or macOS + +This page explains how to get the software you need to use a Linux or macOS +machine for Moby development. Before you begin contributing you must have: + +* a GitHub account +* `git` +* `make` +* `docker` + +You'll notice that `go`, the language that Moby is written in, is not listed. +That's because you don't need it installed; Moby's development environment +provides it for you. You'll learn more about the development environment later. + +## Task 1. Get a GitHub account + +To contribute to the Moby project, you will need a GitHub account. A free account is +fine. All the Moby project repositories are public and visible to everyone. + +You should also have some experience using both the GitHub application and `git` +on the command line. + +## Task 2. Install git + +Install `git` on your local system. You can check if `git` is on already on your +system and properly installed with the following command: + +```bash +$ git --version +``` + +This documentation is written using `git` version 2.2.2. Your version may be +different depending on your OS. + +## Task 3. Install make + +Install `make`. You can check if `make` is on your system with the following +command: + +```bash +$ make -v +``` + +This documentation is written using GNU Make 3.81. Your version may be different +depending on your OS. + +## Task 4. Install or upgrade Docker + +If you haven't already, install the Docker software using the +instructions for your operating system. +If you have an existing installation, check your version and make sure you have +the latest Docker. + +To check if `docker` is already installed on Linux: + +```bash +docker --version +Docker version 17.10.0-ce, build f4ffd25 +``` + +On macOS or Windows, you should have installed Docker for Mac or +Docker for Windows. + +```bash +$ docker --version +Docker version 17.10.0-ce, build f4ffd25 +``` + +## Tip for Linux users + +This guide assumes you have added your user to the `docker` group on your system. +To check, list the group's contents: + +``` +$ getent group docker +docker:x:999:ubuntu +``` + +If the command returns no matches, you have two choices. You can preface this +guide's `docker` commands with `sudo` as you work. Alternatively, you can add +your user to the `docker` group as follows: + +```bash +$ sudo usermod -aG docker ubuntu +``` + +You must log out and log back in for this modification to take effect. + + +## Where to go next + +In the next section, you'll [learn how to set up and configure Git for +contributing to Moby](set-up-git.md). diff --git a/components/engine/docs/contributing/test.md b/components/engine/docs/contributing/test.md new file mode 100644 index 0000000000..9a63a12b86 --- /dev/null +++ b/components/engine/docs/contributing/test.md @@ -0,0 +1,234 @@ +### Run tests + +Contributing includes testing your changes. If you change the Moby code, you +may need to add a new test or modify an existing test. Your contribution could +even be adding tests to Moby. For this reason, you need to know a little +about Moby's test infrastructure. + +This section describes tests you can run in the `dry-run-test` branch of your Docker +fork. If you have followed along in this guide, you already have this branch. +If you don't have this branch, you can create it or simply use another of your +branches. + +## Understand how to test Moby + +Moby tests use the Go language's test framework. In this framework, files +whose names end in `_test.go` contain test code; you'll find test files like +this throughout the Moby repo. Use these files for inspiration when writing +your own tests. For information on Go's test framework, see Go's testing package +documentation and the go test help. + +You are responsible for _unit testing_ your contribution when you add new or +change existing Moby code. A unit test is a piece of code that invokes a +single, small piece of code (_unit of work_) to verify the unit works as +expected. + +Depending on your contribution, you may need to add _integration tests_. These +are tests that combine two or more work units into one component. These work +units each have unit tests and then, together, integration tests that test the +interface between the components. The `integration` and `integration-cli` +directories in the Docker repository contain integration test code. + +Testing is its own specialty. If you aren't familiar with testing techniques, +there is a lot of information available to you on the Web. For now, you should +understand that, the Docker maintainers may ask you to write a new test or +change an existing one. + +## Run tests on your local host + +Before submitting a pull request with a code change, you should run the entire +Moby Engine test suite. The `Makefile` contains a target for the entire test +suite, named `test`. Also, it contains several targets for +testing: + +| Target | What this target does | +| ---------------------- | ---------------------------------------------- | +| `test` | Run the unit, integration, and docker-py tests | +| `test-unit` | Run just the unit tests | +| `test-integration-cli` | Run the integration tests for the CLI | +| `test-docker-py` | Run the tests for the Docker API client | + +Running the entire test suite on your current repository can take over half an +hour. To run the test suite, do the following: + +1. Open a terminal on your local host. + +2. Change to the root of your Docker repository. + + ```bash + $ cd moby-fork + ``` + +3. Make sure you are in your development branch. + + ```bash + $ git checkout dry-run-test + ``` + +4. Run the `make test` command. + + ```bash + $ make test + ``` + + This command does several things, it creates a container temporarily for + testing. Inside that container, the `make`: + + * creates a new binary + * cross-compiles all the binaries for the various operating systems + * runs all the tests in the system + + It can take approximate one hour to run all the tests. The time depends + on your host performance. The default timeout is 60 minutes, which is + defined in `hack/make.sh` (`${TIMEOUT:=60m}`). You can modify the timeout + value on the basis of your host performance. When they complete + successfully, you see the output concludes with something like this: + + ```none + Ran 68 tests in 79.135s + ``` + +## Run targets inside a development container + +If you are working inside a development container, you use the +`hack/make.sh` script to run tests. The `hack/make.sh` script doesn't +have a single target that runs all the tests. Instead, you provide a single +command line with multiple targets that does the same thing. + +Try this now. + +1. Open a terminal and change to the `moby-fork` root. + +2. Start a Moby development image. + + If you are following along with this guide, you should have a + `dry-run-test` image. + + ```bash + $ docker run --privileged --rm -ti -v `pwd`:/go/src/github.com/moby/moby dry-run-test /bin/bash + ``` + +3. Run the tests using the `hack/make.sh` script. + + ```bash + root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit test-integration-cli test-docker-py + ``` + + The tests run just as they did within your local host. + + Of course, you can also run a subset of these targets too. For example, to run + just the unit tests: + + ```bash + root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit + ``` + + Most test targets require that you build these precursor targets first: + `dynbinary binary cross` + + +## Run unit tests + +We use golang standard [testing](https://golang.org/pkg/testing/) +package or [gocheck](https://labix.org/gocheck) for our unit tests. + +You can use the `TESTDIRS` environment variable to run unit tests for +a single package. + +```bash +$ TESTDIRS='opts' make test-unit +``` + +You can also use the `TESTFLAGS` environment variable to run a single test. The +flag's value is passed as arguments to the `go test` command. For example, from +your local host you can run the `TestBuild` test with this command: + +```bash +$ TESTFLAGS='-test.run ^TestValidateIPAddress$' make test-unit +``` + +On unit tests, it's better to use `TESTFLAGS` in combination with +`TESTDIRS` to make it quicker to run a specific test. + +```bash +$ TESTDIRS='opts' TESTFLAGS='-test.run ^TestValidateIPAddress$' make test-unit +``` + +## Run integration tests + +We use [gocheck](https://labix.org/gocheck) for our integration-cli tests. +You can use the `TESTFLAGS` environment variable to run a single test. The +flag's value is passed as arguments to the `go test` command. For example, from +your local host you can run the `TestBuild` test with this command: + +```bash +$ TESTFLAGS='-check.f DockerSuite.TestBuild*' make test-integration-cli +``` + +To run the same test inside your Docker development container, you do this: + +```bash +root@5f8630b873fe:/go/src/github.com/moby/moby# TESTFLAGS='-check.f TestBuild*' hack/make.sh binary test-integration-cli +``` + +## Test the Windows binary against a Linux daemon + +This explains how to test the Windows binary on a Windows machine set up as a +development environment. The tests will be run against a daemon +running on a remote Linux machine. You'll use **Git Bash** that came with the +Git for Windows installation. **Git Bash**, just as it sounds, allows you to +run a Bash terminal on Windows. + +1. If you don't have one open already, start a Git Bash terminal. + + ![Git Bash](images/git_bash.png) + +2. Change to the `moby` source directory. + + ```bash + $ cd /c/gopath/src/github.com/moby/moby + ``` + +3. Set `DOCKER_REMOTE_DAEMON` as follows: + + ```bash + $ export DOCKER_REMOTE_DAEMON=1 + ``` + +4. Set `DOCKER_TEST_HOST` to the `tcp://IP_ADDRESS:2376` value; substitute your + Linux machines actual IP address. For example: + + ```bash + $ export DOCKER_TEST_HOST=tcp://213.124.23.200:2376 + ``` + +5. Make the binary and run the tests: + + ```bash + $ hack/make.sh binary test-integration-cli + ``` + Some tests are skipped on Windows for various reasons. You can see which + tests were skipped by re-running the make and passing in the + `TESTFLAGS='-test.v'` value. For example + + ```bash + $ TESTFLAGS='-test.v' hack/make.sh binary test-integration-cli + ``` + + Should you wish to run a single test such as one with the name + 'TestExample', you can pass in `TESTFLAGS='-check.f TestExample'`. For + example + + ```bash + $ TESTFLAGS='-check.f TestExample' hack/make.sh binary test-integration-cli + ``` + +You can now choose to make changes to the Moby source or the tests. If you +make any changes, just run these commands again. + +## Where to go next + +Congratulations, you have successfully completed the basics you need to +understand the Moby test framework. diff --git a/components/engine/docs/contributing/who-written-for.md b/components/engine/docs/contributing/who-written-for.md new file mode 100644 index 0000000000..1431f42c50 --- /dev/null +++ b/components/engine/docs/contributing/who-written-for.md @@ -0,0 +1,49 @@ +### README first + +This section of the documentation contains a guide for Moby project users who want to +contribute code or documentation to the Moby Engine project. As a community, we +share rules of behavior and interaction. Make sure you are familiar with the community guidelines before continuing. + +## Where and what you can contribute + +The Moby project consists of not just one but several repositories on GitHub. +So, in addition to the `moby/moby` repository, there is the +`containerd/containerd` repo, the `moby/buildkit` repo, and several more. +Contribute to any of these and you contribute to the Moby project. + +Not all Moby repositories use the Go language. Also, each repository has its +own focus area. So, if you are an experienced contributor, think about +contributing to a Moby project repository that has a language or a focus area you are +familiar with. + +If you are new to the open source community, to Moby, or to formal +programming, you should start out contributing to the `moby/moby` +repository. Why? Because this guide is written for that repository specifically. + +Finally, code or documentation isn't the only way to contribute. You can report +an issue, add to discussions in our community channel, write a blog post, or +take a usability test. You can even propose your own type of contribution. +Right now we don't have a lot written about this yet, but feel free to open an issue +to discuss other contributions. + +## How to use this guide + +This is written for the distracted, the overworked, the sloppy reader with fair +`git` skills and a failing memory for the GitHub GUI. The guide attempts to +explain how to use the Moby Engine development environment as precisely, +predictably, and procedurally as possible. + +Users who are new to Engine development should start by setting up their +environment. Then, they should try a simple code change. After that, you should +find something to work on or propose a totally new change. + +If you are a programming prodigy, you still may find this documentation useful. +Please feel free to skim past information you find obvious or boring. + +## How to get started + +Start by getting the software you require. If you are on Mac or Linux, go to +[get the required software for Linux or macOS](software-required.md). If you are +on Windows, see [get the required software for Windows](software-req-win.md). From 7264e2410cf4f1af206516bfb3966c3481ce2974 Mon Sep 17 00:00:00 2001 From: Renaud Gaubert Date: Sat, 28 Oct 2017 16:23:03 +0200 Subject: [PATCH 35/94] Revendored swarmkit Signed-off-by: Renaud Gaubert Upstream-commit: b3960fc1c450961ef3f31d6db21ac237d1b4d99d Component: engine --- components/engine/vendor.conf | 2 +- .../swarmkit/api/genericresource/parse.go | 90 +++++- .../docker/swarmkit/api/specs.pb.go | 302 ++++++++++-------- .../docker/swarmkit/api/specs.proto | 4 + .../swarmkit/connectionbroker/broker.go | 9 +- .../swarmkit/manager/controlapi/node.go | 27 ++ .../manager/state/raft/transport/transport.go | 8 + 7 files changed, 293 insertions(+), 149 deletions(-) diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index 4e2fde5a88..bec25b7e59 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -113,7 +113,7 @@ github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788 github.com/dmcgowan/go-tar 2e2c51242e8993c50445dab7c03c8e7febddd0cf # cluster -github.com/docker/swarmkit 28f91d87bd3f75fd039dbb9be49bfd2381019261 +github.com/docker/swarmkit de950a7ed842c7b7e47e9451cde9bf8f96031894 github.com/gogo/protobuf v0.4 github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e diff --git a/components/engine/vendor/github.com/docker/swarmkit/api/genericresource/parse.go b/components/engine/vendor/github.com/docker/swarmkit/api/genericresource/parse.go index de30908104..f39a7077a8 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/api/genericresource/parse.go +++ b/components/engine/vendor/github.com/docker/swarmkit/api/genericresource/parse.go @@ -1,6 +1,7 @@ package genericresource import ( + "encoding/csv" "fmt" "strconv" "strings" @@ -12,36 +13,99 @@ func newParseError(format string, args ...interface{}) error { return fmt.Errorf("could not parse GenericResource: "+format, args...) } -// Parse parses the GenericResource resources given by the arguments -func Parse(cmd string) ([]*api.GenericResource, error) { - var rs []*api.GenericResource +// discreteResourceVal returns an int64 if the string is a discreteResource +// and an error if it isn't +func discreteResourceVal(res string) (int64, error) { + return strconv.ParseInt(res, 10, 64) +} - for _, term := range strings.Split(cmd, ";") { +// allNamedResources returns true if the array of resources are all namedResources +// e.g: res = [red, orange, green] +func allNamedResources(res []string) bool { + for _, v := range res { + if _, err := discreteResourceVal(v); err == nil { + return false + } + } + + return true +} + +// ParseCmd parses the Generic Resource command line argument +// and returns a list of *api.GenericResource +func ParseCmd(cmd string) ([]*api.GenericResource, error) { + if strings.Contains(cmd, "\n") { + return nil, newParseError("unexpected '\\n' character") + } + + r := csv.NewReader(strings.NewReader(cmd)) + records, err := r.ReadAll() + + if err != nil { + return nil, newParseError("%v", err) + } + + if len(records) != 1 { + return nil, newParseError("found multiple records while parsing cmd %v", records) + } + + return Parse(records[0]) +} + +// Parse parses a table of GenericResource resources +func Parse(cmds []string) ([]*api.GenericResource, error) { + tokens := make(map[string][]string) + + for _, term := range cmds { kva := strings.Split(term, "=") if len(kva) != 2 { - return nil, newParseError("incorrect term %s, missing '=' or malformed expr", term) + return nil, newParseError("incorrect term %s, missing"+ + " '=' or malformed expression", term) } key := strings.TrimSpace(kva[0]) val := strings.TrimSpace(kva[1]) - u, err := strconv.ParseInt(val, 10, 64) - if err == nil { + tokens[key] = append(tokens[key], val) + } + + var rs []*api.GenericResource + for k, v := range tokens { + if u, ok := isDiscreteResource(v); ok { if u < 0 { - return nil, newParseError("cannot ask for negative resource %s", key) + return nil, newParseError("cannot ask for"+ + " negative resource %s", k) } - rs = append(rs, NewDiscrete(key, u)) + + rs = append(rs, NewDiscrete(k, u)) continue } - if len(val) > 2 && val[0] == '{' && val[len(val)-1] == '}' { - val = val[1 : len(val)-1] - rs = append(rs, NewSet(key, strings.Split(val, ",")...)...) + if allNamedResources(v) { + rs = append(rs, NewSet(k, v...)...) continue } - return nil, newParseError("could not parse expression '%s'", term) + return nil, newParseError("mixed discrete and named resources"+ + " in expression '%s=%s'", k, v) } return rs, nil } + +// isDiscreteResource returns true if the array of resources is a +// Discrete Resource. +// e.g: res = [1] +func isDiscreteResource(values []string) (int64, bool) { + if len(values) != 1 { + return int64(0), false + } + + u, err := discreteResourceVal(values[0]) + if err != nil { + return int64(0), false + } + + return u, true + +} diff --git a/components/engine/vendor/github.com/docker/swarmkit/api/specs.pb.go b/components/engine/vendor/github.com/docker/swarmkit/api/specs.pb.go index b150d6f81e..dfd18a6d78 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/api/specs.pb.go +++ b/components/engine/vendor/github.com/docker/swarmkit/api/specs.pb.go @@ -620,6 +620,9 @@ type ContainerSpec struct { // Isolation defines the isolation level for windows containers (default, process, hyperv). // Runtimes that don't support it ignore that field Isolation ContainerSpec_Isolation `protobuf:"varint,24,opt,name=isolation,proto3,enum=docker.swarmkit.v1.ContainerSpec_Isolation" json:"isolation,omitempty"` + // PidsLimit prevents from OS resource damage by applications inside the container + // using fork bomb attack. + PidsLimit int64 `protobuf:"varint,25,opt,name=pidsLimit,proto3" json:"pidsLimit,omitempty"` } func (m *ContainerSpec) Reset() { *m = ContainerSpec{} } @@ -2055,6 +2058,13 @@ func (m *ContainerSpec) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintSpecs(dAtA, i, uint64(m.Isolation)) } + if m.PidsLimit != 0 { + dAtA[i] = 0xc8 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintSpecs(dAtA, i, uint64(m.PidsLimit)) + } return i, nil } @@ -2787,6 +2797,9 @@ func (m *ContainerSpec) Size() (n int) { if m.Isolation != 0 { n += 2 + sovSpecs(uint64(m.Isolation)) } + if m.PidsLimit != 0 { + n += 2 + sovSpecs(uint64(m.PidsLimit)) + } return n } @@ -3134,6 +3147,7 @@ func (this *ContainerSpec) String() string { `Privileges:` + strings.Replace(fmt.Sprintf("%v", this.Privileges), "Privileges", "Privileges", 1) + `,`, `Init:` + strings.Replace(fmt.Sprintf("%v", this.Init), "BoolValue", "google_protobuf4.BoolValue", 1) + `,`, `Isolation:` + fmt.Sprintf("%v", this.Isolation) + `,`, + `PidsLimit:` + fmt.Sprintf("%v", this.PidsLimit) + `,`, `}`, }, "") return s @@ -5261,6 +5275,25 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { break } } + case 25: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PidsLimit", wireType) + } + m.PidsLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PidsLimit |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipSpecs(dAtA[iNdEx:]) @@ -6572,138 +6605,139 @@ var ( func init() { proto.RegisterFile("github.com/docker/swarmkit/api/specs.proto", fileDescriptorSpecs) } var fileDescriptorSpecs = []byte{ - // 2114 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x6f, 0xdb, 0xc8, - 0x15, 0xb7, 0x6c, 0x59, 0x96, 0x1e, 0xe5, 0x44, 0x9e, 0x4d, 0xb2, 0xb4, 0xb2, 0xb1, 0x15, 0x6d, - 0x36, 0xf5, 0xee, 0xa2, 0x32, 0xea, 0x2e, 0xb6, 0xd9, 0x4d, 0xb7, 0xad, 0x64, 0x69, 0x1d, 0x35, - 0x89, 0x2d, 0x8c, 0x1c, 0xb7, 0x01, 0x0a, 0x08, 0x63, 0x72, 0x2c, 0x11, 0xa6, 0x38, 0xec, 0x70, - 0xe8, 0x40, 0xb7, 0x1e, 0x17, 0xee, 0x67, 0x30, 0x7a, 0x28, 0x7a, 0x6f, 0xbf, 0x42, 0x4f, 0x39, - 0xf6, 0xd8, 0x5e, 0x8c, 0xae, 0xbf, 0x42, 0x6f, 0xbd, 0xb4, 0x98, 0xe1, 0x90, 0xa2, 0x1c, 0x3a, - 0x0e, 0xd0, 0x1c, 0x7a, 0x9b, 0x79, 0xfc, 0xfd, 0xde, 0xfc, 0xfb, 0xbd, 0x37, 0x6f, 0x08, 0x9f, - 0x0d, 0x1d, 0x31, 0x0a, 0x0f, 0x1b, 0x16, 0x1b, 0x6f, 0xda, 0xcc, 0x3a, 0xa6, 0x7c, 0x33, 0x78, - 0x45, 0xf8, 0xf8, 0xd8, 0x11, 0x9b, 0xc4, 0x77, 0x36, 0x03, 0x9f, 0x5a, 0x41, 0xc3, 0xe7, 0x4c, - 0x30, 0x84, 0x22, 0x40, 0x23, 0x06, 0x34, 0x4e, 0x7e, 0x54, 0xbd, 0x8e, 0x2f, 0x26, 0x3e, 0xd5, - 0xfc, 0xea, 0xad, 0x21, 0x1b, 0x32, 0xd5, 0xdc, 0x94, 0x2d, 0x6d, 0x5d, 0x1b, 0x32, 0x36, 0x74, - 0xe9, 0xa6, 0xea, 0x1d, 0x86, 0x47, 0x9b, 0x76, 0xc8, 0x89, 0x70, 0x98, 0xa7, 0xbf, 0xaf, 0x5e, - 0xfe, 0x4e, 0xbc, 0xc9, 0x55, 0xd4, 0x57, 0x9c, 0xf8, 0x3e, 0xe5, 0x7a, 0xc0, 0xfa, 0x59, 0x1e, - 0x8a, 0xbb, 0xcc, 0xa6, 0x7d, 0x9f, 0x5a, 0x68, 0x07, 0x0c, 0xe2, 0x79, 0x4c, 0x28, 0xdf, 0x81, - 0x99, 0xab, 0xe5, 0x36, 0x8c, 0xad, 0xf5, 0xc6, 0x9b, 0x6b, 0x6a, 0x34, 0xa7, 0xb0, 0x56, 0xfe, - 0xf5, 0xf9, 0xfa, 0x1c, 0x4e, 0x33, 0xd1, 0xcf, 0xa1, 0x6c, 0xd3, 0xc0, 0xe1, 0xd4, 0x1e, 0x70, - 0xe6, 0x52, 0x73, 0xbe, 0x96, 0xdb, 0xb8, 0xb1, 0xf5, 0x51, 0x96, 0x27, 0x39, 0x38, 0x66, 0x2e, - 0xc5, 0x86, 0x66, 0xc8, 0x0e, 0xda, 0x01, 0x18, 0xd3, 0xf1, 0x21, 0xe5, 0xc1, 0xc8, 0xf1, 0xcd, - 0x05, 0x45, 0xff, 0xc1, 0x55, 0x74, 0x39, 0xf7, 0xc6, 0xf3, 0x04, 0x8e, 0x53, 0x54, 0xf4, 0x1c, - 0xca, 0xe4, 0x84, 0x38, 0x2e, 0x39, 0x74, 0x5c, 0x47, 0x4c, 0xcc, 0xbc, 0x72, 0xf5, 0xe9, 0x5b, - 0x5d, 0x35, 0x53, 0x04, 0x3c, 0x43, 0xaf, 0xdb, 0x00, 0xd3, 0x81, 0xd0, 0x43, 0x58, 0xea, 0x75, - 0x76, 0xdb, 0xdd, 0xdd, 0x9d, 0xca, 0x5c, 0x75, 0xf5, 0xf4, 0xac, 0x76, 0x5b, 0xfa, 0x98, 0x02, - 0x7a, 0xd4, 0xb3, 0x1d, 0x6f, 0x88, 0x36, 0xa0, 0xd8, 0xdc, 0xde, 0xee, 0xf4, 0xf6, 0x3b, 0xed, - 0x4a, 0xae, 0x5a, 0x3d, 0x3d, 0xab, 0xdd, 0x99, 0x05, 0x36, 0x2d, 0x8b, 0xfa, 0x82, 0xda, 0xd5, - 0xfc, 0x77, 0x7f, 0x5c, 0x9b, 0xab, 0x7f, 0x97, 0x83, 0x72, 0x7a, 0x12, 0xe8, 0x21, 0x14, 0x9a, - 0xdb, 0xfb, 0xdd, 0x83, 0x4e, 0x65, 0x6e, 0x4a, 0x4f, 0x23, 0x9a, 0x96, 0x70, 0x4e, 0x28, 0x7a, - 0x00, 0x8b, 0xbd, 0xe6, 0x8b, 0x7e, 0xa7, 0x92, 0x9b, 0x4e, 0x27, 0x0d, 0xeb, 0x91, 0x30, 0x50, - 0xa8, 0x36, 0x6e, 0x76, 0x77, 0x2b, 0xf3, 0xd9, 0xa8, 0x36, 0x27, 0x8e, 0xa7, 0xa7, 0xf2, 0x87, - 0x3c, 0x18, 0x7d, 0xca, 0x4f, 0x1c, 0xeb, 0x3d, 0x4b, 0xe4, 0x4b, 0xc8, 0x0b, 0x12, 0x1c, 0x2b, - 0x69, 0x18, 0xd9, 0xd2, 0xd8, 0x27, 0xc1, 0xb1, 0x1c, 0x54, 0xd3, 0x15, 0x5e, 0x2a, 0x83, 0x53, - 0xdf, 0x75, 0x2c, 0x22, 0xa8, 0xad, 0x94, 0x61, 0x6c, 0x7d, 0x92, 0xc5, 0xc6, 0x09, 0x4a, 0xcf, - 0xff, 0xc9, 0x1c, 0x4e, 0x51, 0xd1, 0x63, 0x28, 0x0c, 0x5d, 0x76, 0x48, 0x5c, 0xa5, 0x09, 0x63, - 0xeb, 0x7e, 0x96, 0x93, 0x1d, 0x85, 0x98, 0x3a, 0xd0, 0x14, 0xf4, 0x08, 0x0a, 0xa1, 0x6f, 0x13, - 0x41, 0xcd, 0x82, 0x22, 0xd7, 0xb2, 0xc8, 0x2f, 0x14, 0x62, 0x9b, 0x79, 0x47, 0xce, 0x10, 0x6b, - 0x3c, 0x7a, 0x0a, 0x45, 0x8f, 0x8a, 0x57, 0x8c, 0x1f, 0x07, 0xe6, 0x52, 0x6d, 0x61, 0xc3, 0xd8, - 0xfa, 0x3c, 0x53, 0x8c, 0x11, 0xa6, 0x29, 0x04, 0xb1, 0x46, 0x63, 0xea, 0x89, 0xc8, 0x4d, 0x6b, - 0xde, 0xcc, 0xe1, 0xc4, 0x01, 0xfa, 0x29, 0x14, 0xa9, 0x67, 0xfb, 0xcc, 0xf1, 0x84, 0x59, 0xbc, - 0x7a, 0x22, 0x1d, 0x8d, 0x91, 0x9b, 0x89, 0x13, 0x86, 0x64, 0x73, 0xe6, 0xba, 0x87, 0xc4, 0x3a, - 0x36, 0x4b, 0xef, 0xb8, 0x8c, 0x84, 0xd1, 0x2a, 0x40, 0x7e, 0xcc, 0x6c, 0x5a, 0xdf, 0x84, 0x95, - 0x37, 0xb6, 0x1a, 0x55, 0xa1, 0xa8, 0xb7, 0x3a, 0xd2, 0x48, 0x1e, 0x27, 0xfd, 0xfa, 0x4d, 0x58, - 0x9e, 0xd9, 0xd6, 0xfa, 0x9f, 0x17, 0xa1, 0x18, 0x9f, 0x35, 0x6a, 0x42, 0xc9, 0x62, 0x9e, 0x20, - 0x8e, 0x47, 0xb9, 0x96, 0x57, 0xe6, 0xc9, 0x6c, 0xc7, 0x20, 0xc9, 0x7a, 0x32, 0x87, 0xa7, 0x2c, - 0xf4, 0x2d, 0x94, 0x38, 0x0d, 0x58, 0xc8, 0x2d, 0x1a, 0x68, 0x7d, 0x6d, 0x64, 0x2b, 0x24, 0x02, - 0x61, 0xfa, 0xdb, 0xd0, 0xe1, 0x54, 0xee, 0x72, 0x80, 0xa7, 0x54, 0xf4, 0x18, 0x96, 0x38, 0x0d, - 0x04, 0xe1, 0xe2, 0x6d, 0x12, 0xc1, 0x11, 0xa4, 0xc7, 0x5c, 0xc7, 0x9a, 0xe0, 0x98, 0x81, 0x1e, - 0x43, 0xc9, 0x77, 0x89, 0xa5, 0xbc, 0x9a, 0x8b, 0x8a, 0x7e, 0x2f, 0x8b, 0xde, 0x8b, 0x41, 0x78, - 0x8a, 0x47, 0x5f, 0x01, 0xb8, 0x6c, 0x38, 0xb0, 0xb9, 0x73, 0x42, 0xb9, 0x96, 0x58, 0x35, 0x8b, - 0xdd, 0x56, 0x08, 0x5c, 0x72, 0xd9, 0x30, 0x6a, 0xa2, 0x9d, 0xff, 0x49, 0x5f, 0x29, 0x6d, 0x3d, - 0x05, 0x20, 0xc9, 0x57, 0xad, 0xae, 0x4f, 0xdf, 0xc9, 0x95, 0x3e, 0x91, 0x14, 0x1d, 0xdd, 0x87, - 0xf2, 0x11, 0xe3, 0x16, 0x1d, 0xe8, 0xa8, 0x29, 0x29, 0x4d, 0x18, 0xca, 0x16, 0xe9, 0x0b, 0xb5, - 0x60, 0x69, 0x48, 0x3d, 0xca, 0x1d, 0xcb, 0x04, 0x35, 0xd8, 0xc3, 0xcc, 0x80, 0x8c, 0x20, 0x38, - 0xf4, 0x84, 0x33, 0xa6, 0x7a, 0xa4, 0x98, 0x88, 0x7e, 0x03, 0x1f, 0xc4, 0xc7, 0x37, 0xe0, 0xf4, - 0x88, 0x72, 0xea, 0x49, 0x0d, 0x18, 0x6a, 0x1f, 0x3e, 0x79, 0xbb, 0x06, 0x34, 0x5a, 0x27, 0x1b, - 0xc4, 0x2f, 0x7f, 0x08, 0x5a, 0x25, 0x58, 0xe2, 0xd1, 0xb8, 0xf5, 0xdf, 0xe7, 0xa4, 0xea, 0x2f, - 0x21, 0xd0, 0x26, 0x18, 0xc9, 0xf0, 0x8e, 0xad, 0xd4, 0x5b, 0x6a, 0xdd, 0xb8, 0x38, 0x5f, 0x87, - 0x18, 0xdb, 0x6d, 0xcb, 0x1c, 0xa4, 0xdb, 0x36, 0xea, 0xc0, 0x72, 0x42, 0x90, 0x65, 0x80, 0xbe, - 0x28, 0x6b, 0x6f, 0x9b, 0xe9, 0xfe, 0xc4, 0xa7, 0xb8, 0xcc, 0x53, 0xbd, 0xfa, 0xaf, 0x01, 0xbd, - 0xb9, 0x2f, 0x08, 0x41, 0xfe, 0xd8, 0xf1, 0xf4, 0x34, 0xb0, 0x6a, 0xa3, 0x06, 0x2c, 0xf9, 0x64, - 0xe2, 0x32, 0x62, 0xeb, 0xc0, 0xb8, 0xd5, 0x88, 0x0a, 0x84, 0x46, 0x5c, 0x20, 0x34, 0x9a, 0xde, - 0x04, 0xc7, 0xa0, 0xfa, 0x53, 0xb8, 0x9d, 0x79, 0xbc, 0x68, 0x0b, 0xca, 0x49, 0xc0, 0x4d, 0xd7, - 0x7a, 0xf3, 0xe2, 0x7c, 0xdd, 0x48, 0x22, 0xb3, 0xdb, 0xc6, 0x46, 0x02, 0xea, 0xda, 0xf5, 0xbf, - 0x1a, 0xb0, 0x3c, 0x13, 0xb6, 0xe8, 0x16, 0x2c, 0x3a, 0x63, 0x32, 0xa4, 0x7a, 0x8e, 0x51, 0x07, - 0x75, 0xa0, 0xe0, 0x92, 0x43, 0xea, 0xca, 0xe0, 0x95, 0x07, 0xf7, 0xc3, 0x6b, 0xe3, 0xbf, 0xf1, - 0x4c, 0xe1, 0x3b, 0x9e, 0xe0, 0x13, 0xac, 0xc9, 0xc8, 0x84, 0x25, 0x8b, 0x8d, 0xc7, 0xc4, 0x93, - 0xd7, 0xc4, 0xc2, 0x46, 0x09, 0xc7, 0x5d, 0xb9, 0x33, 0x84, 0x0f, 0x03, 0x33, 0xaf, 0xcc, 0xaa, - 0x8d, 0x2a, 0xb0, 0x40, 0xbd, 0x13, 0x73, 0x51, 0x99, 0x64, 0x53, 0x5a, 0x6c, 0x27, 0x8a, 0xbe, - 0x12, 0x96, 0x4d, 0xc9, 0x0b, 0x03, 0xca, 0xcd, 0xa5, 0x68, 0x47, 0x65, 0x1b, 0xfd, 0x04, 0x0a, - 0x63, 0x16, 0x7a, 0x22, 0x30, 0x8b, 0x6a, 0xb2, 0xab, 0x59, 0x93, 0x7d, 0x2e, 0x11, 0x5a, 0x59, - 0x1a, 0x8e, 0x3a, 0xb0, 0x12, 0x08, 0xe6, 0x0f, 0x86, 0x9c, 0x58, 0x74, 0xe0, 0x53, 0xee, 0x30, - 0x5b, 0xa7, 0xe1, 0xd5, 0x37, 0x0e, 0xa5, 0xad, 0x0b, 0x3e, 0x7c, 0x53, 0x72, 0x76, 0x24, 0xa5, - 0xa7, 0x18, 0xa8, 0x07, 0x65, 0x3f, 0x74, 0xdd, 0x01, 0xf3, 0xa3, 0x1b, 0x39, 0x8a, 0x9d, 0x77, - 0xd8, 0xb2, 0x5e, 0xe8, 0xba, 0x7b, 0x11, 0x09, 0x1b, 0xfe, 0xb4, 0x83, 0xee, 0x40, 0x61, 0xc8, - 0x59, 0xe8, 0x47, 0x71, 0x53, 0xc2, 0xba, 0x87, 0xbe, 0x81, 0xa5, 0x80, 0x5a, 0x9c, 0x8a, 0xc0, - 0x2c, 0xab, 0xa5, 0x7e, 0x9c, 0x35, 0x48, 0x5f, 0x41, 0x92, 0x98, 0xc0, 0x31, 0x07, 0xad, 0xc2, - 0x82, 0x10, 0x13, 0x73, 0xb9, 0x96, 0xdb, 0x28, 0xb6, 0x96, 0x2e, 0xce, 0xd7, 0x17, 0xf6, 0xf7, - 0x5f, 0x62, 0x69, 0x93, 0xb7, 0xc5, 0x88, 0x05, 0xc2, 0x23, 0x63, 0x6a, 0xde, 0x50, 0x7b, 0x9b, - 0xf4, 0xd1, 0x4b, 0x00, 0xdb, 0x0b, 0x06, 0x96, 0x4a, 0x4f, 0xe6, 0x4d, 0xb5, 0xba, 0xcf, 0xaf, - 0x5f, 0x5d, 0x7b, 0xb7, 0xaf, 0x6f, 0xcc, 0xe5, 0x8b, 0xf3, 0xf5, 0x52, 0xd2, 0xc5, 0x25, 0xdb, - 0x0b, 0xa2, 0x26, 0x6a, 0x81, 0x31, 0xa2, 0xc4, 0x15, 0x23, 0x6b, 0x44, 0xad, 0x63, 0xb3, 0x72, - 0xf5, 0x15, 0xf8, 0x44, 0xc1, 0xb4, 0x87, 0x34, 0x49, 0x2a, 0x58, 0x4e, 0x35, 0x30, 0x57, 0xd4, - 0x5e, 0x45, 0x1d, 0x74, 0x0f, 0x80, 0xf9, 0xd4, 0x1b, 0x04, 0xc2, 0x76, 0x3c, 0x13, 0xc9, 0x25, - 0xe3, 0x92, 0xb4, 0xf4, 0xa5, 0x01, 0xdd, 0x95, 0x17, 0x14, 0xb1, 0x07, 0xcc, 0x73, 0x27, 0xe6, - 0x07, 0xea, 0x6b, 0x51, 0x1a, 0xf6, 0x3c, 0x77, 0x82, 0xd6, 0xc1, 0x50, 0xba, 0x08, 0x9c, 0xa1, - 0x47, 0x5c, 0xf3, 0x96, 0xda, 0x0f, 0x90, 0xa6, 0xbe, 0xb2, 0xc8, 0x73, 0x88, 0x76, 0x23, 0x30, - 0x6f, 0x5f, 0x7d, 0x0e, 0x7a, 0xb2, 0xd3, 0x73, 0xd0, 0x1c, 0xf4, 0x33, 0x00, 0x9f, 0x3b, 0x27, - 0x8e, 0x4b, 0x87, 0x34, 0x30, 0xef, 0xa8, 0x45, 0xaf, 0x65, 0xde, 0x4c, 0x09, 0x0a, 0xa7, 0x18, - 0xa8, 0x01, 0x79, 0xc7, 0x73, 0x84, 0xf9, 0xa1, 0xbe, 0x95, 0x2e, 0x4b, 0xb5, 0xc5, 0x98, 0x7b, - 0x40, 0xdc, 0x90, 0x62, 0x85, 0x43, 0x5d, 0x28, 0x39, 0x01, 0x73, 0x95, 0x7c, 0x4d, 0x53, 0xe5, - 0xb7, 0x77, 0x38, 0xbf, 0x6e, 0x4c, 0xc1, 0x53, 0x76, 0xf5, 0x2b, 0x30, 0x52, 0x81, 0x2e, 0x03, - 0xf4, 0x98, 0x4e, 0x74, 0xee, 0x90, 0x4d, 0x79, 0x1a, 0x27, 0x72, 0x68, 0x95, 0xdc, 0x4a, 0x38, - 0xea, 0x7c, 0x3d, 0xff, 0x28, 0x57, 0xdd, 0x02, 0x23, 0x25, 0x78, 0xf4, 0xb1, 0x4c, 0xbc, 0x43, - 0x27, 0x10, 0x7c, 0x32, 0x20, 0xa1, 0x18, 0x99, 0xbf, 0x50, 0x84, 0x72, 0x6c, 0x6c, 0x86, 0x62, - 0x54, 0x1d, 0xc0, 0x54, 0x37, 0xa8, 0x06, 0x86, 0xd4, 0x63, 0x40, 0xf9, 0x09, 0xe5, 0xb2, 0xa8, - 0x91, 0xc7, 0x9d, 0x36, 0xc9, 0xb8, 0x09, 0x28, 0xe1, 0xd6, 0x48, 0xa5, 0xad, 0x12, 0xd6, 0x3d, - 0x99, 0x87, 0xe2, 0xe0, 0xd4, 0x79, 0x48, 0x77, 0xeb, 0x7f, 0xc9, 0x41, 0x29, 0x59, 0x28, 0xfa, - 0x02, 0x56, 0xba, 0xfd, 0xbd, 0x67, 0xcd, 0xfd, 0xee, 0xde, 0xee, 0xa0, 0xdd, 0xf9, 0xb6, 0xf9, - 0xe2, 0xd9, 0x7e, 0x65, 0xae, 0x7a, 0xef, 0xf4, 0xac, 0xb6, 0x3a, 0xcd, 0xa9, 0x31, 0xbc, 0x4d, - 0x8f, 0x48, 0xe8, 0x8a, 0x59, 0x56, 0x0f, 0xef, 0x6d, 0x77, 0xfa, 0xfd, 0x4a, 0xee, 0x2a, 0x56, - 0x8f, 0x33, 0x8b, 0x06, 0x01, 0xda, 0x82, 0xca, 0x94, 0xf5, 0xe4, 0x65, 0xaf, 0x83, 0x0f, 0x2a, - 0xf3, 0xd5, 0x8f, 0x4e, 0xcf, 0x6a, 0xe6, 0x9b, 0xa4, 0x27, 0x13, 0x9f, 0xf2, 0x03, 0xfd, 0x20, - 0xf8, 0x57, 0x0e, 0xca, 0xe9, 0x7a, 0x12, 0x6d, 0x47, 0x75, 0xa0, 0x3a, 0x86, 0x1b, 0x5b, 0x9b, - 0xd7, 0xd5, 0x9f, 0xea, 0x1e, 0x73, 0x43, 0xe9, 0xf7, 0xb9, 0x7c, 0xfa, 0x29, 0x32, 0xfa, 0x02, - 0x16, 0x7d, 0xc6, 0x45, 0x9c, 0xf1, 0xb3, 0xf5, 0xc8, 0x78, 0x5c, 0xa5, 0x44, 0xe0, 0xfa, 0x08, - 0x6e, 0xcc, 0x7a, 0x43, 0x0f, 0x60, 0xe1, 0xa0, 0xdb, 0xab, 0xcc, 0x55, 0xef, 0x9e, 0x9e, 0xd5, - 0x3e, 0x9c, 0xfd, 0x78, 0xe0, 0x70, 0x11, 0x12, 0xb7, 0xdb, 0x43, 0x9f, 0xc1, 0x62, 0x7b, 0xb7, - 0x8f, 0x71, 0x25, 0x57, 0x5d, 0x3f, 0x3d, 0xab, 0xdd, 0x9d, 0xc5, 0xc9, 0x4f, 0x2c, 0xf4, 0x6c, - 0xcc, 0x0e, 0x93, 0x67, 0xd0, 0xbf, 0xe7, 0xc1, 0xd0, 0x17, 0xe1, 0xfb, 0x7e, 0x29, 0x2f, 0x47, - 0x55, 0x5e, 0x9c, 0xe1, 0xe6, 0xaf, 0x2d, 0xf6, 0xca, 0x11, 0x41, 0xeb, 0xf2, 0x3e, 0x94, 0x1d, - 0xff, 0xe4, 0xcb, 0x01, 0xf5, 0xc8, 0xa1, 0xab, 0x5f, 0x44, 0x45, 0x6c, 0x48, 0x5b, 0x27, 0x32, - 0xc9, 0xf4, 0xea, 0x78, 0x82, 0x72, 0x4f, 0xbf, 0x75, 0x8a, 0x38, 0xe9, 0xa3, 0x6f, 0x20, 0xef, - 0xf8, 0x64, 0xac, 0x2b, 0xd4, 0xcc, 0x15, 0x74, 0x7b, 0xcd, 0xe7, 0x3a, 0x6e, 0x5a, 0xc5, 0x8b, - 0xf3, 0xf5, 0xbc, 0x34, 0x60, 0x45, 0x43, 0x6b, 0x71, 0x91, 0x28, 0x47, 0x52, 0x57, 0x65, 0x11, - 0xa7, 0x2c, 0x52, 0xfb, 0x8e, 0x37, 0xe4, 0x34, 0x08, 0xd4, 0xa5, 0x59, 0xc4, 0x71, 0x17, 0x55, - 0x61, 0x49, 0x97, 0x9a, 0xaa, 0xb6, 0x2c, 0xc9, 0x32, 0x4e, 0x1b, 0x5a, 0xcb, 0x60, 0x44, 0xbb, - 0x31, 0x38, 0xe2, 0x6c, 0x5c, 0xff, 0x4f, 0x1e, 0x8c, 0x6d, 0x37, 0x0c, 0x84, 0xae, 0x1a, 0xde, - 0xdb, 0xe6, 0xbf, 0x84, 0x15, 0xa2, 0x5e, 0xde, 0xc4, 0x93, 0x57, 0xb0, 0xaa, 0xe0, 0xf5, 0x01, - 0x3c, 0xc8, 0x74, 0x97, 0x80, 0xa3, 0x6a, 0xbf, 0x55, 0x90, 0x3e, 0xcd, 0x1c, 0xae, 0x90, 0x4b, - 0x5f, 0x50, 0x1f, 0x96, 0x19, 0xb7, 0x46, 0x34, 0x10, 0xd1, 0xc5, 0xad, 0x5f, 0xaa, 0x99, 0xff, - 0x30, 0xf6, 0xd2, 0x40, 0x7d, 0x6b, 0x45, 0xb3, 0x9d, 0xf5, 0x81, 0x1e, 0x41, 0x9e, 0x93, 0xa3, - 0xf8, 0x35, 0x92, 0x19, 0x24, 0x98, 0x1c, 0x89, 0x19, 0x17, 0x8a, 0x81, 0x7e, 0x09, 0x60, 0x3b, - 0x81, 0x4f, 0x84, 0x35, 0xa2, 0x5c, 0x1f, 0x76, 0xe6, 0x12, 0xdb, 0x09, 0x6a, 0xc6, 0x4b, 0x8a, - 0x8d, 0x9e, 0x42, 0xc9, 0x22, 0xb1, 0x5c, 0x0b, 0x57, 0x3f, 0xdf, 0xb7, 0x9b, 0xda, 0x45, 0x45, - 0xba, 0xb8, 0x38, 0x5f, 0x2f, 0xc6, 0x16, 0x5c, 0xb4, 0x88, 0x96, 0xef, 0x53, 0x58, 0x96, 0xcf, - 0xfa, 0x81, 0x1d, 0xa5, 0xb3, 0x48, 0x26, 0x57, 0xdc, 0xc2, 0xf2, 0x8d, 0xa8, 0xd3, 0x5e, 0x7c, - 0x9c, 0x65, 0x91, 0xb2, 0xa1, 0x5f, 0xc1, 0x0a, 0xf5, 0x2c, 0x3e, 0x51, 0x62, 0x8d, 0x67, 0x58, - 0xbc, 0x7a, 0xb1, 0x9d, 0x04, 0x3c, 0xb3, 0xd8, 0x0a, 0xbd, 0x64, 0xaf, 0xff, 0x23, 0x07, 0x10, - 0x15, 0x36, 0xef, 0x57, 0x80, 0x08, 0xf2, 0x36, 0x11, 0x44, 0x69, 0xae, 0x8c, 0x55, 0x1b, 0x7d, - 0x0d, 0x20, 0xe8, 0xd8, 0x97, 0xa9, 0xd7, 0x1b, 0x6a, 0xd9, 0xbc, 0x2d, 0x1d, 0xa4, 0xd0, 0x68, - 0x0b, 0x0a, 0xfa, 0xcd, 0x98, 0xbf, 0x96, 0xa7, 0x91, 0xf5, 0x3f, 0xe5, 0x00, 0xa2, 0x65, 0xfe, - 0x5f, 0xaf, 0xad, 0x65, 0xbe, 0xfe, 0x7e, 0x6d, 0xee, 0xef, 0xdf, 0xaf, 0xcd, 0xfd, 0xee, 0x62, - 0x2d, 0xf7, 0xfa, 0x62, 0x2d, 0xf7, 0xb7, 0x8b, 0xb5, 0xdc, 0x3f, 0x2f, 0xd6, 0x72, 0x87, 0x05, - 0x55, 0x7b, 0xfc, 0xf8, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x1a, 0xbd, 0x13, 0xac, 0xa9, 0x15, - 0x00, 0x00, + // 2131 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x6f, 0x1b, 0xc7, + 0x15, 0x17, 0x25, 0x8a, 0x22, 0xdf, 0x52, 0x36, 0x35, 0x71, 0x9c, 0x15, 0x6d, 0x4b, 0x34, 0xe3, + 0xb8, 0x4a, 0x82, 0x52, 0xa8, 0x1a, 0xa4, 0x4e, 0xdc, 0xb4, 0x25, 0x45, 0x46, 0x66, 0x6d, 0x4b, + 0xc4, 0x50, 0x56, 0x6b, 0xa0, 0x00, 0x31, 0xda, 0x1d, 0x91, 0x03, 0x2d, 0x77, 0xb6, 0xb3, 0x43, + 0x19, 0xbc, 0xf5, 0x18, 0xa8, 0x9f, 0x41, 0xe8, 0xa1, 0xe8, 0xbd, 0xfd, 0x16, 0x3e, 0xf6, 0xd8, + 0x5e, 0x84, 0x44, 0x5f, 0xa1, 0xb7, 0x5e, 0x5a, 0xcc, 0xec, 0xec, 0x92, 0x94, 0x57, 0x96, 0x81, + 0xfa, 0xd0, 0xdb, 0xcc, 0xdb, 0xdf, 0xef, 0xcd, 0xbf, 0xdf, 0xbc, 0xf7, 0x66, 0xe1, 0xb3, 0x3e, + 0x93, 0x83, 0xd1, 0x61, 0xcd, 0xe1, 0xc3, 0x4d, 0x97, 0x3b, 0xc7, 0x54, 0x6c, 0x86, 0xaf, 0x88, + 0x18, 0x1e, 0x33, 0xb9, 0x49, 0x02, 0xb6, 0x19, 0x06, 0xd4, 0x09, 0x6b, 0x81, 0xe0, 0x92, 0x23, + 0x14, 0x01, 0x6a, 0x31, 0xa0, 0x76, 0xf2, 0x93, 0xf2, 0x75, 0x7c, 0x39, 0x0e, 0xa8, 0xe1, 0x97, + 0x6f, 0xf5, 0x79, 0x9f, 0xeb, 0xe6, 0xa6, 0x6a, 0x19, 0xeb, 0x5a, 0x9f, 0xf3, 0xbe, 0x47, 0x37, + 0x75, 0xef, 0x70, 0x74, 0xb4, 0xe9, 0x8e, 0x04, 0x91, 0x8c, 0xfb, 0xe6, 0xfb, 0xea, 0xe5, 0xef, + 0xc4, 0x1f, 0x5f, 0x45, 0x7d, 0x25, 0x48, 0x10, 0x50, 0x61, 0x06, 0xac, 0x9e, 0x65, 0x21, 0xbf, + 0xcb, 0x5d, 0xda, 0x0d, 0xa8, 0x83, 0x76, 0xc0, 0x22, 0xbe, 0xcf, 0xa5, 0xf6, 0x1d, 0xda, 0x99, + 0x4a, 0x66, 0xc3, 0xda, 0x5a, 0xaf, 0xbd, 0xb9, 0xa6, 0x5a, 0x7d, 0x02, 0x6b, 0x64, 0x5f, 0x9f, + 0xaf, 0xcf, 0xe1, 0x69, 0x26, 0xfa, 0x25, 0x14, 0x5d, 0x1a, 0x32, 0x41, 0xdd, 0x9e, 0xe0, 0x1e, + 0xb5, 0xe7, 0x2b, 0x99, 0x8d, 0x1b, 0x5b, 0x77, 0xd3, 0x3c, 0xa9, 0xc1, 0x31, 0xf7, 0x28, 0xb6, + 0x0c, 0x43, 0x75, 0xd0, 0x0e, 0xc0, 0x90, 0x0e, 0x0f, 0xa9, 0x08, 0x07, 0x2c, 0xb0, 0x17, 0x34, + 0xfd, 0x47, 0x57, 0xd1, 0xd5, 0xdc, 0x6b, 0xcf, 0x13, 0x38, 0x9e, 0xa2, 0xa2, 0xe7, 0x50, 0x24, + 0x27, 0x84, 0x79, 0xe4, 0x90, 0x79, 0x4c, 0x8e, 0xed, 0xac, 0x76, 0xf5, 0xe9, 0x5b, 0x5d, 0xd5, + 0xa7, 0x08, 0x78, 0x86, 0x5e, 0x75, 0x01, 0x26, 0x03, 0xa1, 0x87, 0xb0, 0xd4, 0x69, 0xed, 0x36, + 0xdb, 0xbb, 0x3b, 0xa5, 0xb9, 0xf2, 0xea, 0xe9, 0x59, 0xe5, 0x43, 0xe5, 0x63, 0x02, 0xe8, 0x50, + 0xdf, 0x65, 0x7e, 0x1f, 0x6d, 0x40, 0xbe, 0xbe, 0xbd, 0xdd, 0xea, 0xec, 0xb7, 0x9a, 0xa5, 0x4c, + 0xb9, 0x7c, 0x7a, 0x56, 0xb9, 0x3d, 0x0b, 0xac, 0x3b, 0x0e, 0x0d, 0x24, 0x75, 0xcb, 0xd9, 0xef, + 0xfe, 0xbc, 0x36, 0x57, 0xfd, 0x2e, 0x03, 0xc5, 0xe9, 0x49, 0xa0, 0x87, 0x90, 0xab, 0x6f, 0xef, + 0xb7, 0x0f, 0x5a, 0xa5, 0xb9, 0x09, 0x7d, 0x1a, 0x51, 0x77, 0x24, 0x3b, 0xa1, 0xe8, 0x01, 0x2c, + 0x76, 0xea, 0x2f, 0xba, 0xad, 0x52, 0x66, 0x32, 0x9d, 0x69, 0x58, 0x87, 0x8c, 0x42, 0x8d, 0x6a, + 0xe2, 0x7a, 0x7b, 0xb7, 0x34, 0x9f, 0x8e, 0x6a, 0x0a, 0xc2, 0x7c, 0x33, 0x95, 0x3f, 0x65, 0xc1, + 0xea, 0x52, 0x71, 0xc2, 0x9c, 0xf7, 0x2c, 0x91, 0x2f, 0x21, 0x2b, 0x49, 0x78, 0xac, 0xa5, 0x61, + 0xa5, 0x4b, 0x63, 0x9f, 0x84, 0xc7, 0x6a, 0x50, 0x43, 0xd7, 0x78, 0xa5, 0x0c, 0x41, 0x03, 0x8f, + 0x39, 0x44, 0x52, 0x57, 0x2b, 0xc3, 0xda, 0xfa, 0x24, 0x8d, 0x8d, 0x13, 0x94, 0x99, 0xff, 0x93, + 0x39, 0x3c, 0x45, 0x45, 0x8f, 0x21, 0xd7, 0xf7, 0xf8, 0x21, 0xf1, 0xb4, 0x26, 0xac, 0xad, 0xfb, + 0x69, 0x4e, 0x76, 0x34, 0x62, 0xe2, 0xc0, 0x50, 0xd0, 0x23, 0xc8, 0x8d, 0x02, 0x97, 0x48, 0x6a, + 0xe7, 0x34, 0xb9, 0x92, 0x46, 0x7e, 0xa1, 0x11, 0xdb, 0xdc, 0x3f, 0x62, 0x7d, 0x6c, 0xf0, 0xe8, + 0x29, 0xe4, 0x7d, 0x2a, 0x5f, 0x71, 0x71, 0x1c, 0xda, 0x4b, 0x95, 0x85, 0x0d, 0x6b, 0xeb, 0xf3, + 0x54, 0x31, 0x46, 0x98, 0xba, 0x94, 0xc4, 0x19, 0x0c, 0xa9, 0x2f, 0x23, 0x37, 0x8d, 0x79, 0x3b, + 0x83, 0x13, 0x07, 0xe8, 0xe7, 0x90, 0xa7, 0xbe, 0x1b, 0x70, 0xe6, 0x4b, 0x3b, 0x7f, 0xf5, 0x44, + 0x5a, 0x06, 0xa3, 0x36, 0x13, 0x27, 0x0c, 0xc5, 0x16, 0xdc, 0xf3, 0x0e, 0x89, 0x73, 0x6c, 0x17, + 0xde, 0x71, 0x19, 0x09, 0xa3, 0x91, 0x83, 0xec, 0x90, 0xbb, 0xb4, 0xba, 0x09, 0x2b, 0x6f, 0x6c, + 0x35, 0x2a, 0x43, 0xde, 0x6c, 0x75, 0xa4, 0x91, 0x2c, 0x4e, 0xfa, 0xd5, 0x9b, 0xb0, 0x3c, 0xb3, + 0xad, 0xd5, 0xbf, 0x2e, 0x42, 0x3e, 0x3e, 0x6b, 0x54, 0x87, 0x82, 0xc3, 0x7d, 0x49, 0x98, 0x4f, + 0x85, 0x91, 0x57, 0xea, 0xc9, 0x6c, 0xc7, 0x20, 0xc5, 0x7a, 0x32, 0x87, 0x27, 0x2c, 0xf4, 0x2d, + 0x14, 0x04, 0x0d, 0xf9, 0x48, 0x38, 0x34, 0x34, 0xfa, 0xda, 0x48, 0x57, 0x48, 0x04, 0xc2, 0xf4, + 0xf7, 0x23, 0x26, 0xa8, 0xda, 0xe5, 0x10, 0x4f, 0xa8, 0xe8, 0x31, 0x2c, 0x09, 0x1a, 0x4a, 0x22, + 0xe4, 0xdb, 0x24, 0x82, 0x23, 0x48, 0x87, 0x7b, 0xcc, 0x19, 0xe3, 0x98, 0x81, 0x1e, 0x43, 0x21, + 0xf0, 0x88, 0xa3, 0xbd, 0xda, 0x8b, 0x9a, 0x7e, 0x2f, 0x8d, 0xde, 0x89, 0x41, 0x78, 0x82, 0x47, + 0x5f, 0x01, 0x78, 0xbc, 0xdf, 0x73, 0x05, 0x3b, 0xa1, 0xc2, 0x48, 0xac, 0x9c, 0xc6, 0x6e, 0x6a, + 0x04, 0x2e, 0x78, 0xbc, 0x1f, 0x35, 0xd1, 0xce, 0xff, 0xa4, 0xaf, 0x29, 0x6d, 0x3d, 0x05, 0x20, + 0xc9, 0x57, 0xa3, 0xae, 0x4f, 0xdf, 0xc9, 0x95, 0x39, 0x91, 0x29, 0x3a, 0xba, 0x0f, 0xc5, 0x23, + 0x2e, 0x1c, 0xda, 0x33, 0xb7, 0xa6, 0xa0, 0x35, 0x61, 0x69, 0x5b, 0xa4, 0x2f, 0xd4, 0x80, 0xa5, + 0x3e, 0xf5, 0xa9, 0x60, 0x8e, 0x0d, 0x7a, 0xb0, 0x87, 0xa9, 0x17, 0x32, 0x82, 0xe0, 0x91, 0x2f, + 0xd9, 0x90, 0x9a, 0x91, 0x62, 0x22, 0xfa, 0x1d, 0x7c, 0x10, 0x1f, 0x5f, 0x4f, 0xd0, 0x23, 0x2a, + 0xa8, 0xaf, 0x34, 0x60, 0xe9, 0x7d, 0xf8, 0xe4, 0xed, 0x1a, 0x30, 0x68, 0x13, 0x6c, 0x90, 0xb8, + 0xfc, 0x21, 0x6c, 0x14, 0x60, 0x49, 0x44, 0xe3, 0x56, 0xff, 0x98, 0x51, 0xaa, 0xbf, 0x84, 0x40, + 0x9b, 0x60, 0x25, 0xc3, 0x33, 0x57, 0xab, 0xb7, 0xd0, 0xb8, 0x71, 0x71, 0xbe, 0x0e, 0x31, 0xb6, + 0xdd, 0x54, 0x31, 0xc8, 0xb4, 0x5d, 0xd4, 0x82, 0xe5, 0x84, 0xa0, 0xca, 0x00, 0x93, 0x28, 0x2b, + 0x6f, 0x9b, 0xe9, 0xfe, 0x38, 0xa0, 0xb8, 0x28, 0xa6, 0x7a, 0xd5, 0xdf, 0x02, 0x7a, 0x73, 0x5f, + 0x10, 0x82, 0xec, 0x31, 0xf3, 0xcd, 0x34, 0xb0, 0x6e, 0xa3, 0x1a, 0x2c, 0x05, 0x64, 0xec, 0x71, + 0xe2, 0x9a, 0x8b, 0x71, 0xab, 0x16, 0x15, 0x08, 0xb5, 0xb8, 0x40, 0xa8, 0xd5, 0xfd, 0x31, 0x8e, + 0x41, 0xd5, 0xa7, 0xf0, 0x61, 0xea, 0xf1, 0xa2, 0x2d, 0x28, 0x26, 0x17, 0x6e, 0xb2, 0xd6, 0x9b, + 0x17, 0xe7, 0xeb, 0x56, 0x72, 0x33, 0xdb, 0x4d, 0x6c, 0x25, 0xa0, 0xb6, 0x5b, 0xfd, 0xde, 0x82, + 0xe5, 0x99, 0x6b, 0x8b, 0x6e, 0xc1, 0x22, 0x1b, 0x92, 0x3e, 0x35, 0x73, 0x8c, 0x3a, 0xa8, 0x05, + 0x39, 0x8f, 0x1c, 0x52, 0x4f, 0x5d, 0x5e, 0x75, 0x70, 0x3f, 0xbe, 0xf6, 0xfe, 0xd7, 0x9e, 0x69, + 0x7c, 0xcb, 0x97, 0x62, 0x8c, 0x0d, 0x19, 0xd9, 0xb0, 0xe4, 0xf0, 0xe1, 0x90, 0xf8, 0x2a, 0x4d, + 0x2c, 0x6c, 0x14, 0x70, 0xdc, 0x55, 0x3b, 0x43, 0x44, 0x3f, 0xb4, 0xb3, 0xda, 0xac, 0xdb, 0xa8, + 0x04, 0x0b, 0xd4, 0x3f, 0xb1, 0x17, 0xb5, 0x49, 0x35, 0x95, 0xc5, 0x65, 0xd1, 0xed, 0x2b, 0x60, + 0xd5, 0x54, 0xbc, 0x51, 0x48, 0x85, 0xbd, 0x14, 0xed, 0xa8, 0x6a, 0xa3, 0x9f, 0x41, 0x6e, 0xc8, + 0x47, 0xbe, 0x0c, 0xed, 0xbc, 0x9e, 0xec, 0x6a, 0xda, 0x64, 0x9f, 0x2b, 0x84, 0x51, 0x96, 0x81, + 0xa3, 0x16, 0xac, 0x84, 0x92, 0x07, 0xbd, 0xbe, 0x20, 0x0e, 0xed, 0x05, 0x54, 0x30, 0xee, 0x9a, + 0x30, 0xbc, 0xfa, 0xc6, 0xa1, 0x34, 0x4d, 0xc1, 0x87, 0x6f, 0x2a, 0xce, 0x8e, 0xa2, 0x74, 0x34, + 0x03, 0x75, 0xa0, 0x18, 0x8c, 0x3c, 0xaf, 0xc7, 0x83, 0x28, 0x23, 0x47, 0x77, 0xe7, 0x1d, 0xb6, + 0xac, 0x33, 0xf2, 0xbc, 0xbd, 0x88, 0x84, 0xad, 0x60, 0xd2, 0x41, 0xb7, 0x21, 0xd7, 0x17, 0x7c, + 0x14, 0x44, 0xf7, 0xa6, 0x80, 0x4d, 0x0f, 0x7d, 0x03, 0x4b, 0x21, 0x75, 0x04, 0x95, 0xa1, 0x5d, + 0xd4, 0x4b, 0xfd, 0x38, 0x6d, 0x90, 0xae, 0x86, 0x24, 0x77, 0x02, 0xc7, 0x1c, 0xb4, 0x0a, 0x0b, + 0x52, 0x8e, 0xed, 0xe5, 0x4a, 0x66, 0x23, 0xdf, 0x58, 0xba, 0x38, 0x5f, 0x5f, 0xd8, 0xdf, 0x7f, + 0x89, 0x95, 0x4d, 0x65, 0x8b, 0x01, 0x0f, 0xa5, 0x4f, 0x86, 0xd4, 0xbe, 0xa1, 0xf7, 0x36, 0xe9, + 0xa3, 0x97, 0x00, 0xae, 0x1f, 0xf6, 0x1c, 0x1d, 0x9e, 0xec, 0x9b, 0x7a, 0x75, 0x9f, 0x5f, 0xbf, + 0xba, 0xe6, 0x6e, 0xd7, 0x64, 0xcc, 0xe5, 0x8b, 0xf3, 0xf5, 0x42, 0xd2, 0xc5, 0x05, 0xd7, 0x0f, + 0xa3, 0x26, 0x6a, 0x80, 0x35, 0xa0, 0xc4, 0x93, 0x03, 0x67, 0x40, 0x9d, 0x63, 0xbb, 0x74, 0x75, + 0x0a, 0x7c, 0xa2, 0x61, 0xc6, 0xc3, 0x34, 0x49, 0x29, 0x58, 0x4d, 0x35, 0xb4, 0x57, 0xf4, 0x5e, + 0x45, 0x1d, 0x74, 0x0f, 0x80, 0x07, 0xd4, 0xef, 0x85, 0xd2, 0x65, 0xbe, 0x8d, 0xd4, 0x92, 0x71, + 0x41, 0x59, 0xba, 0xca, 0x80, 0xee, 0xa8, 0x04, 0x45, 0xdc, 0x1e, 0xf7, 0xbd, 0xb1, 0xfd, 0x81, + 0xfe, 0x9a, 0x57, 0x86, 0x3d, 0xdf, 0x1b, 0xa3, 0x75, 0xb0, 0xb4, 0x2e, 0x42, 0xd6, 0xf7, 0x89, + 0x67, 0xdf, 0xd2, 0xfb, 0x01, 0xca, 0xd4, 0xd5, 0x16, 0x75, 0x0e, 0xd1, 0x6e, 0x84, 0xf6, 0x87, + 0x57, 0x9f, 0x83, 0x99, 0xec, 0xe4, 0x1c, 0x0c, 0x07, 0xfd, 0x02, 0x20, 0x10, 0xec, 0x84, 0x79, + 0xb4, 0x4f, 0x43, 0xfb, 0xb6, 0x5e, 0xf4, 0x5a, 0x6a, 0x66, 0x4a, 0x50, 0x78, 0x8a, 0x81, 0x6a, + 0x90, 0x65, 0x3e, 0x93, 0xf6, 0x47, 0x26, 0x2b, 0x5d, 0x96, 0x6a, 0x83, 0x73, 0xef, 0x80, 0x78, + 0x23, 0x8a, 0x35, 0x0e, 0xb5, 0xa1, 0xc0, 0x42, 0xee, 0x69, 0xf9, 0xda, 0xb6, 0x8e, 0x6f, 0xef, + 0x70, 0x7e, 0xed, 0x98, 0x82, 0x27, 0x6c, 0x74, 0x17, 0x0a, 0x01, 0x73, 0xc3, 0x67, 0x6c, 0xc8, + 0xa4, 0xbd, 0x5a, 0xc9, 0x6c, 0x2c, 0xe0, 0x89, 0xa1, 0xfc, 0x15, 0x58, 0x53, 0x61, 0x40, 0x5d, + 0xdf, 0x63, 0x3a, 0x36, 0x91, 0x45, 0x35, 0xd5, 0x59, 0x9d, 0xa8, 0x89, 0xe9, 0xd0, 0x57, 0xc0, + 0x51, 0xe7, 0xeb, 0xf9, 0x47, 0x99, 0xf2, 0x16, 0x58, 0x53, 0xd7, 0x01, 0x7d, 0xac, 0xc2, 0x72, + 0x9f, 0x85, 0x52, 0x8c, 0x7b, 0x64, 0x24, 0x07, 0xf6, 0xaf, 0x34, 0xa1, 0x18, 0x1b, 0xeb, 0x23, + 0x39, 0x28, 0xf7, 0x60, 0xa2, 0x2a, 0x54, 0x01, 0x4b, 0xa9, 0x35, 0xa4, 0xe2, 0x84, 0x0a, 0x55, + 0xf2, 0x28, 0x31, 0x4c, 0x9b, 0xd4, 0xad, 0x0a, 0x29, 0x11, 0xce, 0x40, 0x07, 0xb5, 0x02, 0x36, + 0x3d, 0x15, 0xa5, 0xe2, 0xab, 0x6b, 0xa2, 0x94, 0xe9, 0x56, 0xff, 0x96, 0x81, 0x42, 0xb2, 0x0d, + 0xe8, 0x0b, 0x58, 0x69, 0x77, 0xf7, 0x9e, 0xd5, 0xf7, 0xdb, 0x7b, 0xbb, 0xbd, 0x66, 0xeb, 0xdb, + 0xfa, 0x8b, 0x67, 0xfb, 0xa5, 0xb9, 0xf2, 0xbd, 0xd3, 0xb3, 0xca, 0xea, 0x24, 0xe2, 0xc6, 0xf0, + 0x26, 0x3d, 0x22, 0x23, 0x4f, 0xce, 0xb2, 0x3a, 0x78, 0x6f, 0xbb, 0xd5, 0xed, 0x96, 0x32, 0x57, + 0xb1, 0x3a, 0x82, 0x3b, 0x34, 0x0c, 0xd1, 0x16, 0x94, 0x26, 0xac, 0x27, 0x2f, 0x3b, 0x2d, 0x7c, + 0x50, 0x9a, 0x2f, 0xdf, 0x3d, 0x3d, 0xab, 0xd8, 0x6f, 0x92, 0x9e, 0x8c, 0x03, 0x2a, 0x0e, 0xcc, + 0x73, 0xe1, 0x5f, 0x19, 0x28, 0x4e, 0x57, 0x9b, 0x68, 0x3b, 0xaa, 0x12, 0xf5, 0x31, 0xdc, 0xd8, + 0xda, 0xbc, 0xae, 0x3a, 0xd5, 0x59, 0xce, 0x1b, 0x29, 0xbf, 0xcf, 0xd5, 0xc3, 0x50, 0x93, 0xd1, + 0x17, 0xb0, 0x18, 0x70, 0x21, 0xe3, 0x7c, 0x90, 0xae, 0x56, 0x2e, 0xe2, 0x1a, 0x26, 0x02, 0x57, + 0x07, 0x70, 0x63, 0xd6, 0x1b, 0x7a, 0x00, 0x0b, 0x07, 0xed, 0x4e, 0x69, 0xae, 0x7c, 0xe7, 0xf4, + 0xac, 0xf2, 0xd1, 0xec, 0xc7, 0x03, 0x26, 0xe4, 0x88, 0x78, 0xed, 0x0e, 0xfa, 0x0c, 0x16, 0x9b, + 0xbb, 0x5d, 0x8c, 0x4b, 0x99, 0xf2, 0xfa, 0xe9, 0x59, 0xe5, 0xce, 0x2c, 0x4e, 0x7d, 0xe2, 0x23, + 0xdf, 0xc5, 0xfc, 0x30, 0x79, 0x24, 0xfd, 0x7b, 0x1e, 0x2c, 0x93, 0x26, 0xdf, 0xf7, 0x3b, 0x7a, + 0x39, 0xaa, 0x01, 0xe3, 0xf8, 0x37, 0x7f, 0x6d, 0x29, 0x58, 0x8c, 0x08, 0x46, 0x97, 0xf7, 0xa1, + 0xc8, 0x82, 0x93, 0x2f, 0x7b, 0xd4, 0x27, 0x87, 0x9e, 0x79, 0x2f, 0xe5, 0xb1, 0xa5, 0x6c, 0xad, + 0xc8, 0xa4, 0x82, 0x2f, 0xf3, 0x25, 0x15, 0xbe, 0x79, 0x09, 0xe5, 0x71, 0xd2, 0x47, 0xdf, 0x40, + 0x96, 0x05, 0x64, 0x68, 0xea, 0xd7, 0xd4, 0x15, 0xb4, 0x3b, 0xf5, 0xe7, 0xe6, 0xde, 0x34, 0xf2, + 0x17, 0xe7, 0xeb, 0x59, 0x65, 0xc0, 0x9a, 0x86, 0xd6, 0xe2, 0x12, 0x52, 0x8d, 0xa4, 0x13, 0x69, + 0x1e, 0x4f, 0x59, 0x94, 0xf6, 0x99, 0xdf, 0x17, 0x34, 0x0c, 0x75, 0x4a, 0xcd, 0xe3, 0xb8, 0x8b, + 0xca, 0xb0, 0x64, 0x0a, 0x51, 0x5d, 0x79, 0x16, 0x54, 0x91, 0x67, 0x0c, 0x8d, 0x65, 0xb0, 0xa2, + 0xdd, 0xe8, 0x1d, 0x09, 0x3e, 0xac, 0xfe, 0x27, 0x0b, 0xd6, 0xb6, 0x37, 0x0a, 0xa5, 0xa9, 0x29, + 0xde, 0xdb, 0xe6, 0xbf, 0x84, 0x15, 0xa2, 0xdf, 0xe5, 0xc4, 0x57, 0x09, 0x5a, 0xd7, 0xf7, 0xe6, + 0x00, 0x1e, 0xa4, 0xba, 0x4b, 0xc0, 0xd1, 0x5b, 0xa0, 0x91, 0x53, 0x3e, 0xed, 0x0c, 0x2e, 0x91, + 0x4b, 0x5f, 0x50, 0x17, 0x96, 0xb9, 0x70, 0x06, 0x34, 0x94, 0x51, 0x5a, 0x37, 0xef, 0xd8, 0xd4, + 0x3f, 0x1c, 0x7b, 0xd3, 0x40, 0x93, 0xd3, 0xa2, 0xd9, 0xce, 0xfa, 0x40, 0x8f, 0x20, 0x2b, 0xc8, + 0x51, 0xfc, 0x56, 0x49, 0xbd, 0x24, 0x98, 0x1c, 0xc9, 0x19, 0x17, 0x9a, 0x81, 0x7e, 0x0d, 0xe0, + 0xb2, 0x30, 0x20, 0xd2, 0x19, 0x50, 0x61, 0x0e, 0x3b, 0x75, 0x89, 0xcd, 0x04, 0x35, 0xe3, 0x65, + 0x8a, 0x8d, 0x9e, 0x42, 0xc1, 0x21, 0xb1, 0x5c, 0x73, 0x57, 0x3f, 0xee, 0xb7, 0xeb, 0xc6, 0x45, + 0x49, 0xb9, 0xb8, 0x38, 0x5f, 0xcf, 0xc7, 0x16, 0x9c, 0x77, 0x88, 0x91, 0xef, 0x53, 0x58, 0x56, + 0x8f, 0xfe, 0x9e, 0x1b, 0x85, 0xb3, 0x48, 0x26, 0x57, 0xe4, 0x68, 0xf5, 0x82, 0x34, 0x61, 0x2f, + 0x3e, 0xce, 0xa2, 0x9c, 0xb2, 0xa1, 0xdf, 0xc0, 0x0a, 0xf5, 0x1d, 0x31, 0xd6, 0x62, 0x8d, 0x67, + 0x98, 0xbf, 0x7a, 0xb1, 0xad, 0x04, 0x3c, 0xb3, 0xd8, 0x12, 0xbd, 0x64, 0xaf, 0xfe, 0x33, 0x03, + 0x10, 0x95, 0x3d, 0xef, 0x57, 0x80, 0x08, 0xb2, 0x2e, 0x91, 0x44, 0x6b, 0xae, 0x88, 0x75, 0x1b, + 0x7d, 0x0d, 0x20, 0xe9, 0x30, 0x50, 0xa1, 0xd7, 0xef, 0x1b, 0xd9, 0xbc, 0x2d, 0x1c, 0x4c, 0xa1, + 0xd1, 0x16, 0xe4, 0xcc, 0x8b, 0x32, 0x7b, 0x2d, 0xcf, 0x20, 0xab, 0x7f, 0xc9, 0x00, 0x44, 0xcb, + 0xfc, 0xbf, 0x5e, 0x5b, 0xc3, 0x7e, 0xfd, 0xc3, 0xda, 0xdc, 0x3f, 0x7e, 0x58, 0x9b, 0xfb, 0xc3, + 0xc5, 0x5a, 0xe6, 0xf5, 0xc5, 0x5a, 0xe6, 0xef, 0x17, 0x6b, 0x99, 0xef, 0x2f, 0xd6, 0x32, 0x87, + 0x39, 0x5d, 0x99, 0xfc, 0xf4, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb8, 0xa3, 0x85, 0xdc, 0xc7, + 0x15, 0x00, 0x00, } diff --git a/components/engine/vendor/github.com/docker/swarmkit/api/specs.proto b/components/engine/vendor/github.com/docker/swarmkit/api/specs.proto index 2b002c54f2..14448d0409 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/api/specs.proto +++ b/components/engine/vendor/github.com/docker/swarmkit/api/specs.proto @@ -314,6 +314,10 @@ message ContainerSpec { // Isolation defines the isolation level for windows containers (default, process, hyperv). // Runtimes that don't support it ignore that field Isolation isolation = 24; + + // PidsLimit prevents from OS resource damage by applications inside the container + // using fork bomb attack. + int64 pidsLimit = 25; } // EndpointSpec defines the properties that can be configured to diff --git a/components/engine/vendor/github.com/docker/swarmkit/connectionbroker/broker.go b/components/engine/vendor/github.com/docker/swarmkit/connectionbroker/broker.go index a0ba7cf0a8..43b384ab2a 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/connectionbroker/broker.go +++ b/components/engine/vendor/github.com/docker/swarmkit/connectionbroker/broker.go @@ -4,7 +4,9 @@ package connectionbroker import ( + "net" "sync" + "time" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/remotes" @@ -60,9 +62,14 @@ func (b *Broker) SelectRemote(dialOpts ...grpc.DialOption) (*Conn, error) { return nil, err } + // gRPC dialer connects to proxy first. Provide a custom dialer here avoid that. + // TODO(anshul) Add an option to configure this. dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(grpc_prometheus.UnaryClientInterceptor), - grpc.WithStreamInterceptor(grpc_prometheus.StreamClientInterceptor)) + grpc.WithStreamInterceptor(grpc_prometheus.StreamClientInterceptor), + grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout("tcp", addr, timeout) + })) cc, err := grpc.Dial(peer.Addr, dialOpts...) if err != nil { diff --git a/components/engine/vendor/github.com/docker/swarmkit/manager/controlapi/node.go b/components/engine/vendor/github.com/docker/swarmkit/manager/controlapi/node.go index f3ee9e45df..bac6b8073d 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/manager/controlapi/node.go +++ b/components/engine/vendor/github.com/docker/swarmkit/manager/controlapi/node.go @@ -248,6 +248,29 @@ func (s *Server) UpdateNode(ctx context.Context, request *api.UpdateNodeRequest) }, nil } +func removeNodeAttachments(tx store.Tx, nodeID string) error { + // orphan the node's attached containers. if we don't do this, the + // network these attachments are connected to will never be removeable + tasks, err := store.FindTasks(tx, store.ByNodeID(nodeID)) + if err != nil { + return err + } + for _, task := range tasks { + // if the task is an attachment, then we just delete it. the allocator + // will do the heavy lifting. basically, GetAttachment will return the + // attachment if that's the kind of runtime, or nil if it's not. + if task.Spec.GetAttachment() != nil { + // don't delete the task. instead, update it to `ORPHANED` so that + // the taskreaper will clean it up. + task.Status.State = api.TaskStateOrphaned + if err := store.UpdateTask(tx, task); err != nil { + return err + } + } + } + return nil +} + // RemoveNode removes a Node referenced by NodeID with the given NodeSpec. // - Returns NotFound if the Node is not found. // - Returns FailedPrecondition if the Node has manager role (and is part of the memberlist) or is not shut down. @@ -313,6 +336,10 @@ func (s *Server) RemoveNode(ctx context.Context, request *api.RemoveNodeRequest) return err } + if err := removeNodeAttachments(tx, request.NodeID); err != nil { + return err + } + return store.DeleteNode(tx, request.NodeID) }) if err != nil { diff --git a/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go b/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go index bd5a04eec7..6f096ef9b2 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go +++ b/components/engine/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go @@ -3,6 +3,7 @@ package transport import ( + "net" "sync" "time" @@ -348,6 +349,13 @@ func (t *Transport) dial(addr string) (*grpc.ClientConn, error) { grpcOptions = append(grpcOptions, grpc.WithTimeout(t.config.SendTimeout)) } + // gRPC dialer connects to proxy first. Provide a custom dialer here avoid that. + // TODO(anshul) Add an option to configure this. + grpcOptions = append(grpcOptions, + grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout("tcp", addr, timeout) + })) + cc, err := grpc.Dial(addr, grpcOptions...) if err != nil { return nil, err From 84f5991ee5b4b9b8be124664d3900ae95746531e Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 3 Nov 2017 16:11:07 +0100 Subject: [PATCH 36/94] Move "until" option to correct API version Signed-off-by: Sebastiaan van Stijn Upstream-commit: 6f34bd6f968be243659ad62976822036f4f4eb17 Component: engine --- components/engine/docs/api/version-history.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/docs/api/version-history.md b/components/engine/docs/api/version-history.md index 8ae30fc270..5056c0ddba 100644 --- a/components/engine/docs/api/version-history.md +++ b/components/engine/docs/api/version-history.md @@ -21,7 +21,8 @@ keywords: "API, Docker, rcli, REST, documentation" `Isolation` field on container spec to set the Isolation technology of the containers running the service (`default`, `process`, or `hyperv`). This configuration is only used for Windows containers. - +* `GET /containers/(name)/logs` now supports an additional query parameter: `until`, + which returns log lines that occurred before the specified timestamp. ## v1.34 API changes @@ -33,7 +34,6 @@ keywords: "API, Docker, rcli, REST, documentation" If `Error` is `null`, container removal has succeeded, otherwise the test of an error message indicating why container removal has failed is available from `Error.Message` field. -* `GET /containers/(name)/logs` now supports an additional query parameter: `until`, which returns log lines that occurred before the specified timestamp. ## v1.33 API changes From 61e2a38de517a4ccaf71f47aa967a87f7cf70e98 Mon Sep 17 00:00:00 2001 From: Justin Cormack Date: Fri, 3 Nov 2017 15:12:22 +0000 Subject: [PATCH 37/94] Add /proc/scsi to masked paths This is writeable, and can be used to remove devices. Containers do not need to know about scsi devices. Signed-off-by: Justin Cormack Upstream-commit: a21ecdf3c8a343a7c94e4c4d01b178c87ca7aaa1 Component: engine --- components/engine/oci/defaults.go | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/oci/defaults.go b/components/engine/oci/defaults.go index 0cc07ffa13..41880710bd 100644 --- a/components/engine/oci/defaults.go +++ b/components/engine/oci/defaults.go @@ -119,6 +119,7 @@ func DefaultLinuxSpec() specs.Spec { "/proc/timer_list", "/proc/timer_stats", "/proc/sched_debug", + "/proc/scsi", }, ReadonlyPaths: []string{ "/proc/asound", From 6edab5bbbd61f5a9e96f3dd0bf9d1cb51a2f3484 Mon Sep 17 00:00:00 2001 From: Joyce Date: Mon, 16 Oct 2017 14:42:37 -0700 Subject: [PATCH 38/94] fix todo for printing error messages Signed-off-by: Joyce Upstream-commit: 883ab41ce8f5163ba8ce0450ffff1e63c266f23b Component: engine --- components/engine/builder/remotecontext/git.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/components/engine/builder/remotecontext/git.go b/components/engine/builder/remotecontext/git.go index 158bb5ad4d..f6fc0bc3fb 100644 --- a/components/engine/builder/remotecontext/git.go +++ b/components/engine/builder/remotecontext/git.go @@ -6,6 +6,7 @@ import ( "github.com/docker/docker/builder" "github.com/docker/docker/builder/remotecontext/git" "github.com/docker/docker/pkg/archive" + "github.com/sirupsen/logrus" ) // MakeGitContext returns a Context from gitURL that is cloned in a temporary directory. @@ -21,9 +22,14 @@ func MakeGitContext(gitURL string) (builder.Source, error) { } defer func() { - // TODO: print errors? - c.Close() - os.RemoveAll(root) + err := c.Close() + if err != nil { + logrus.WithField("action", "MakeGitContext").WithField("module", "builder").WithField("url", gitURL).WithError(err).Error("error while closing git context") + } + err = os.RemoveAll(root) + if err != nil { + logrus.WithField("action", "MakeGitContext").WithField("module", "builder").WithField("url", gitURL).WithError(err).Error("error while removing path and children of root") + } }() return FromArchive(c) } From b9b2e53e405c8971f15e22c213f80f64707aa38a Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Fri, 3 Nov 2017 10:36:12 -0700 Subject: [PATCH 39/94] builder: fix long stream sync Signed-off-by: Tonis Tiigi Upstream-commit: c6703b722e1c0914342d61ca6af77aea93150873 Component: engine --- components/engine/builder/dockerfile/builder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/builder/dockerfile/builder.go b/components/engine/builder/dockerfile/builder.go index 2b7b43827c..b62d6fc024 100644 --- a/components/engine/builder/dockerfile/builder.go +++ b/components/engine/builder/dockerfile/builder.go @@ -131,10 +131,10 @@ func (bm *BuildManager) initializeClientSession(ctx context.Context, cancel func } logrus.Debug("client is session enabled") - ctx, cancelCtx := context.WithTimeout(ctx, sessionConnectTimeout) + connectCtx, cancelCtx := context.WithTimeout(ctx, sessionConnectTimeout) defer cancelCtx() - c, err := bm.sg.Get(ctx, options.SessionID) + c, err := bm.sg.Get(connectCtx, options.SessionID) if err != nil { return nil, err } From 70ca1da8d357ab3a391c86e1ecadeec54229f9f8 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Mon, 17 Jul 2017 16:29:11 -0400 Subject: [PATCH 40/94] Refactor log file writer Make the `*RotateFileWriter` specifically about writing `logger.Message`'s, which is what it's used for. This allows for future changes where the log writer can cache details about log entries such as (e.g.) the timestamps included in a particular log file, which can be used to optimize reads. Signed-off-by: Brian Goff Upstream-commit: 52d82b4fbc9f0fe00f63e2df9a3d2a49d4095bda Component: engine --- .../daemon/logger/jsonfilelog/jsonfilelog.go | 38 +++++++----------- components/engine/daemon/logger/logger.go | 3 ++ .../logger/loggerutils/rotatefilewriter.go | 40 ++++++++++++------- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go b/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go index 177c070394..65f19e72a2 100644 --- a/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go +++ b/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go @@ -7,7 +7,6 @@ import ( "bytes" "encoding/json" "fmt" - "io" "strconv" "sync" @@ -24,10 +23,7 @@ const Name = "json-file" // JSONFileLogger is Logger implementation for default Docker logging. type JSONFileLogger struct { - extra []byte // json-encoded extra attributes - mu sync.RWMutex - buf *bytes.Buffer // avoids allocating a new buffer on each call to `Log()` closed bool writer *loggerutils.RotateFileWriter readers map[*logger.LogWatcher]struct{} // stores the active log followers @@ -65,11 +61,6 @@ func New(info logger.Info) (logger.Logger, error) { } } - writer, err := loggerutils.NewRotateFileWriter(info.LogPath, capval, maxFiles) - if err != nil { - return nil, err - } - var extra []byte attrs, err := info.ExtraAttributes(nil) if err != nil { @@ -83,33 +74,34 @@ func New(info logger.Info) (logger.Logger, error) { } } + buf := bytes.NewBuffer(nil) + marshalFunc := func(msg *logger.Message) ([]byte, error) { + if err := marshalMessage(msg, extra, buf); err != nil { + return nil, err + } + b := buf.Bytes() + buf.Reset() + return b, nil + } + writer, err := loggerutils.NewRotateFileWriter(info.LogPath, capval, maxFiles, marshalFunc) + if err != nil { + return nil, err + } + return &JSONFileLogger{ - buf: bytes.NewBuffer(nil), writer: writer, readers: make(map[*logger.LogWatcher]struct{}), - extra: extra, }, nil } // Log converts logger.Message to jsonlog.JSONLog and serializes it to file. func (l *JSONFileLogger) Log(msg *logger.Message) error { l.mu.Lock() - err := writeMessageBuf(l.writer, msg, l.extra, l.buf) - l.buf.Reset() + err := l.writer.WriteLogEntry(msg) l.mu.Unlock() return err } -func writeMessageBuf(w io.Writer, m *logger.Message, extra json.RawMessage, buf *bytes.Buffer) error { - if err := marshalMessage(m, extra, buf); err != nil { - logger.PutMessage(m) - return err - } - logger.PutMessage(m) - _, err := w.Write(buf.Bytes()) - return errors.Wrap(err, "error writing log entry") -} - func marshalMessage(msg *logger.Message, extra json.RawMessage, buf *bytes.Buffer) error { logLine := msg.Line if !msg.Partial { diff --git a/components/engine/daemon/logger/logger.go b/components/engine/daemon/logger/logger.go index 6edbf734c1..dc25bebfc7 100644 --- a/components/engine/daemon/logger/logger.go +++ b/components/engine/daemon/logger/logger.go @@ -140,3 +140,6 @@ type Capability struct { // Determines if a log driver can read back logs ReadLogs bool } + +// MarshalFunc is a func that marshals a message into an arbitrary format +type MarshalFunc func(*Message) ([]byte, error) diff --git a/components/engine/daemon/logger/loggerutils/rotatefilewriter.go b/components/engine/daemon/logger/loggerutils/rotatefilewriter.go index 457a39b5a3..54c5688003 100644 --- a/components/engine/daemon/logger/loggerutils/rotatefilewriter.go +++ b/components/engine/daemon/logger/loggerutils/rotatefilewriter.go @@ -1,12 +1,13 @@ package loggerutils import ( - "errors" "os" "strconv" "sync" + "github.com/docker/docker/daemon/logger" "github.com/docker/docker/pkg/pubsub" + "github.com/pkg/errors" ) // RotateFileWriter is Logger implementation for default Docker logging. @@ -18,10 +19,11 @@ type RotateFileWriter struct { currentSize int64 // current size of the latest file maxFiles int //maximum number of files notifyRotate *pubsub.Publisher + marshal logger.MarshalFunc } //NewRotateFileWriter creates new RotateFileWriter -func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateFileWriter, error) { +func NewRotateFileWriter(logPath string, capacity int64, maxFiles int, marshaller logger.MarshalFunc) (*RotateFileWriter, error) { log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640) if err != nil { return nil, err @@ -38,27 +40,37 @@ func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateF currentSize: size, maxFiles: maxFiles, notifyRotate: pubsub.NewPublisher(0, 1), + marshal: marshaller, }, nil } -//WriteLog write log message to File -func (w *RotateFileWriter) Write(message []byte) (int, error) { +// WriteLogEntry writes the provided log message to the current log file. +// This may trigger a rotation event if the max file/capacity limits are hit. +func (w *RotateFileWriter) WriteLogEntry(msg *logger.Message) error { + b, err := w.marshal(msg) + if err != nil { + return errors.Wrap(err, "error marshalling log message") + } + + logger.PutMessage(msg) + w.mu.Lock() if w.closed { w.mu.Unlock() - return -1, errors.New("cannot write because the output file was closed") - } - if err := w.checkCapacityAndRotate(); err != nil { - w.mu.Unlock() - return -1, err + return errors.New("cannot write because the output file was closed") } - n, err := w.f.Write(message) + if err := w.checkCapacityAndRotate(); err != nil { + w.mu.Unlock() + return err + } + + n, err := w.f.Write(b) if err == nil { w.currentSize += int64(n) } w.mu.Unlock() - return n, err + return err } func (w *RotateFileWriter) checkCapacityAndRotate() error { @@ -69,7 +81,7 @@ func (w *RotateFileWriter) checkCapacityAndRotate() error { if w.currentSize >= w.capacity { name := w.f.Name() if err := w.f.Close(); err != nil { - return err + return errors.Wrap(err, "error closing file") } if err := rotate(name, w.maxFiles); err != nil { return err @@ -94,12 +106,12 @@ func rotate(name string, maxFiles int) error { toPath := name + "." + strconv.Itoa(i) fromPath := name + "." + strconv.Itoa(i-1) if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) { - return err + return errors.Wrap(err, "error rotating old log entries") } } if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) { - return err + return errors.Wrap(err, "error rotating current log") } return nil } From dff2ac1e173964fe9da974f33fe67ee488e0af9d Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Tue, 18 Jul 2017 11:54:03 -0400 Subject: [PATCH 41/94] Move json log reading into log file object This allows much of the read logic to be shared for other things, especially for the new log driver proposed in https://github.com/moby/moby/issues/33475 The only logic for reads in the json logger is around decoding log messages, which gets passed into the log file object. This also helps with implementing compression as it allows us to simplify locking strategies. Signed-off-by: Brian Goff Upstream-commit: 16f7cd674902b69b97692de2a83915a1a6be2cdb Component: engine --- .../daemon/logger/jsonfilelog/jsonfilelog.go | 7 +- .../logger/jsonfilelog/jsonfilelog_test.go | 2 +- .../engine/daemon/logger/jsonfilelog/read.go | 360 ++------------ .../daemon/logger/jsonfilelog/read_test.go | 2 +- .../daemon/logger/loggerutils/logfile.go | 454 ++++++++++++++++++ .../multireader/multireader.go | 0 .../multireader/multireader_test.go | 0 .../logger/loggerutils/rotatefilewriter.go | 153 ------ components/engine/daemon/logs.go | 2 +- 9 files changed, 505 insertions(+), 475 deletions(-) create mode 100644 components/engine/daemon/logger/loggerutils/logfile.go rename components/engine/daemon/logger/{jsonfilelog => loggerutils}/multireader/multireader.go (100%) rename components/engine/daemon/logger/{jsonfilelog => loggerutils}/multireader/multireader_test.go (100%) delete mode 100644 components/engine/daemon/logger/loggerutils/rotatefilewriter.go diff --git a/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go b/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go index 65f19e72a2..7aa92f3d37 100644 --- a/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go +++ b/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go @@ -23,9 +23,9 @@ const Name = "json-file" // JSONFileLogger is Logger implementation for default Docker logging. type JSONFileLogger struct { - mu sync.RWMutex + mu sync.Mutex closed bool - writer *loggerutils.RotateFileWriter + writer *loggerutils.LogFile readers map[*logger.LogWatcher]struct{} // stores the active log followers } @@ -83,7 +83,8 @@ func New(info logger.Info) (logger.Logger, error) { buf.Reset() return b, nil } - writer, err := loggerutils.NewRotateFileWriter(info.LogPath, capval, maxFiles, marshalFunc) + + writer, err := loggerutils.NewLogFile(info.LogPath, capval, maxFiles, marshalFunc, decodeFunc) if err != nil { return nil, err } diff --git a/components/engine/daemon/logger/jsonfilelog/jsonfilelog_test.go b/components/engine/daemon/logger/jsonfilelog/jsonfilelog_test.go index 2b2b2b5229..893c054669 100644 --- a/components/engine/daemon/logger/jsonfilelog/jsonfilelog_test.go +++ b/components/engine/daemon/logger/jsonfilelog/jsonfilelog_test.go @@ -82,7 +82,7 @@ func BenchmarkJSONFileLoggerLog(b *testing.B) { } buf := bytes.NewBuffer(nil) - require.NoError(b, marshalMessage(msg, jsonlogger.(*JSONFileLogger).extra, buf)) + require.NoError(b, marshalMessage(msg, nil, buf)) b.SetBytes(int64(buf.Len())) b.ResetTimer() diff --git a/components/engine/daemon/logger/jsonfilelog/read.go b/components/engine/daemon/logger/jsonfilelog/read.go index 09eaaf00de..f190e01a56 100644 --- a/components/engine/daemon/logger/jsonfilelog/read.go +++ b/components/engine/daemon/logger/jsonfilelog/read.go @@ -1,33 +1,45 @@ package jsonfilelog import ( - "bytes" "encoding/json" - "fmt" "io" - "os" - "time" - - "github.com/fsnotify/fsnotify" - "golang.org/x/net/context" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/jsonfilelog/jsonlog" - "github.com/docker/docker/daemon/logger/jsonfilelog/multireader" - "github.com/docker/docker/pkg/filenotify" - "github.com/docker/docker/pkg/tailfile" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) const maxJSONDecodeRetry = 20000 +// ReadLogs implements the logger's LogReader interface for the logs +// created by this driver. +func (l *JSONFileLogger) ReadLogs(config logger.ReadConfig) *logger.LogWatcher { + logWatcher := logger.NewLogWatcher() + + go l.readLogs(logWatcher, config) + return logWatcher +} + +func (l *JSONFileLogger) readLogs(watcher *logger.LogWatcher, config logger.ReadConfig) { + defer close(watcher.Msg) + + l.mu.Lock() + l.readers[watcher] = struct{}{} + l.mu.Unlock() + + l.writer.ReadLogs(config, watcher) + + l.mu.Lock() + delete(l.readers, watcher) + l.mu.Unlock() +} + func decodeLogLine(dec *json.Decoder, l *jsonlog.JSONLog) (*logger.Message, error) { l.Reset() if err := dec.Decode(l); err != nil { return nil, err } + var attrs []backend.LogAttr if len(l.Attrs) != 0 { attrs = make([]backend.LogAttr, 0, len(l.Attrs)) @@ -44,318 +56,34 @@ func decodeLogLine(dec *json.Decoder, l *jsonlog.JSONLog) (*logger.Message, erro return msg, nil } -// ReadLogs implements the logger's LogReader interface for the logs -// created by this driver. -func (l *JSONFileLogger) ReadLogs(config logger.ReadConfig) *logger.LogWatcher { - logWatcher := logger.NewLogWatcher() - - go l.readLogs(logWatcher, config) - return logWatcher -} - -func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.ReadConfig) { - defer close(logWatcher.Msg) - - // lock so the read stream doesn't get corrupted due to rotations or other log data written while we open these files - // This will block writes!!! - l.mu.RLock() - - // TODO it would be nice to move a lot of this reader implementation to the rotate logger object - pth := l.writer.LogPath() - var files []io.ReadSeeker - for i := l.writer.MaxFiles(); i > 1; i-- { - f, err := os.Open(fmt.Sprintf("%s.%d", pth, i-1)) - if err != nil { - if !os.IsNotExist(err) { - logWatcher.Err <- err - l.mu.RUnlock() - return - } - continue - } - defer f.Close() - files = append(files, f) - } - - latestFile, err := os.Open(pth) - if err != nil { - logWatcher.Err <- errors.Wrap(err, "error opening latest log file") - l.mu.RUnlock() - return - } - defer latestFile.Close() - - latestChunk, err := newSectionReader(latestFile) - - // Now we have the reader sectioned, all fd's opened, we can unlock. - // New writes/rotates will not affect seeking through these files - l.mu.RUnlock() - - if err != nil { - logWatcher.Err <- err - return - } - - if config.Tail != 0 { - tailer := multireader.MultiReadSeeker(append(files, latestChunk)...) - tailFile(tailer, logWatcher, config.Tail, config.Since, config.Until) - } - - // close all the rotated files - for _, f := range files { - if err := f.(io.Closer).Close(); err != nil { - logrus.WithField("logger", "json-file").Warnf("error closing tailed log file: %v", err) - } - } - - if !config.Follow || l.closed { - return - } - - notifyRotate := l.writer.NotifyRotate() - defer l.writer.NotifyRotateEvict(notifyRotate) - - l.mu.Lock() - l.readers[logWatcher] = struct{}{} - l.mu.Unlock() - - followLogs(latestFile, logWatcher, notifyRotate, config) - - l.mu.Lock() - delete(l.readers, logWatcher) - l.mu.Unlock() -} - -func newSectionReader(f *os.File) (*io.SectionReader, error) { - // seek to the end to get the size - // we'll leave this at the end of the file since section reader does not advance the reader - size, err := f.Seek(0, os.SEEK_END) - if err != nil { - return nil, errors.Wrap(err, "error getting current file size") - } - return io.NewSectionReader(f, 0, size), nil -} - -func tailFile(f io.ReadSeeker, logWatcher *logger.LogWatcher, tail int, since, until time.Time) { - rdr := io.Reader(f) - if tail > 0 { - ls, err := tailfile.TailFile(f, tail) - if err != nil { - logWatcher.Err <- err - return - } - rdr = bytes.NewBuffer(bytes.Join(ls, []byte("\n"))) - } - dec := json.NewDecoder(rdr) - for { - msg, err := decodeLogLine(dec, &jsonlog.JSONLog{}) - if err != nil { - if err != io.EOF { - logWatcher.Err <- err - } - return - } - if !since.IsZero() && msg.Timestamp.Before(since) { - continue - } - if !until.IsZero() && msg.Timestamp.After(until) { - return - } - select { - case <-logWatcher.WatchClose(): - return - case logWatcher.Msg <- msg: - } - } -} - -func watchFile(name string) (filenotify.FileWatcher, error) { - fileWatcher, err := filenotify.New() - if err != nil { - return nil, err - } - - if err := fileWatcher.Add(name); err != nil { - logrus.WithField("logger", "json-file").Warnf("falling back to file poller due to error: %v", err) - fileWatcher.Close() - fileWatcher = filenotify.NewPollingWatcher() - - if err := fileWatcher.Add(name); err != nil { - fileWatcher.Close() - logrus.Debugf("error watching log file for modifications: %v", err) - return nil, err - } - } - return fileWatcher, nil -} - -func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan interface{}, config logger.ReadConfig) { - dec := json.NewDecoder(f) +// decodeFunc is used to create a decoder for the log file reader +func decodeFunc(rdr io.Reader) func() (*logger.Message, error) { l := &jsonlog.JSONLog{} - - name := f.Name() - fileWatcher, err := watchFile(name) - if err != nil { - logWatcher.Err <- err - return - } - defer func() { - f.Close() - fileWatcher.Remove(name) - fileWatcher.Close() - }() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - go func() { - select { - case <-logWatcher.WatchClose(): - fileWatcher.Remove(name) - cancel() - case <-ctx.Done(): - return - } - }() - - var retries int - handleRotate := func() error { - f.Close() - fileWatcher.Remove(name) - - // retry when the file doesn't exist - for retries := 0; retries <= 5; retries++ { - f, err = os.Open(name) - if err == nil || !os.IsNotExist(err) { + dec := json.NewDecoder(rdr) + return func() (msg *logger.Message, err error) { + for retries := 0; retries < maxJSONDecodeRetry; retries++ { + msg, err = decodeLogLine(dec, l) + if err == nil { break } - } - if err != nil { - return err - } - if err := fileWatcher.Add(name); err != nil { - return err - } - dec = json.NewDecoder(f) - return nil - } - errRetry := errors.New("retry") - errDone := errors.New("done") - waitRead := func() error { - select { - case e := <-fileWatcher.Events(): - switch e.Op { - case fsnotify.Write: - dec = json.NewDecoder(f) - return nil - case fsnotify.Rename, fsnotify.Remove: - select { - case <-notifyRotate: - case <-ctx.Done(): - return errDone - } - if err := handleRotate(); err != nil { - return err - } - return nil - } - return errRetry - case err := <-fileWatcher.Errors(): - logrus.Debug("logger got error watching file: %v", err) - // Something happened, let's try and stay alive and create a new watcher - if retries <= 5 { - fileWatcher.Close() - fileWatcher, err = watchFile(name) - if err != nil { - return err - } + // try again, could be due to a an incomplete json object as we read + if _, ok := err.(*json.SyntaxError); ok { + dec = json.NewDecoder(rdr) retries++ - return errRetry + continue } - return err - case <-ctx.Done(): - return errDone - } - } - handleDecodeErr := func(err error) error { - if err == io.EOF { - for { - err := waitRead() - if err == nil { - break - } - if err == errRetry { - continue - } - return err - } - return nil - } - // try again because this shouldn't happen - if _, ok := err.(*json.SyntaxError); ok && retries <= maxJSONDecodeRetry { - dec = json.NewDecoder(f) - retries++ - return nil - } - // io.ErrUnexpectedEOF is returned from json.Decoder when there is - // remaining data in the parser's buffer while an io.EOF occurs. - // If the json logger writes a partial json log entry to the disk - // while at the same time the decoder tries to decode it, the race condition happens. - if err == io.ErrUnexpectedEOF && retries <= maxJSONDecodeRetry { - reader := io.MultiReader(dec.Buffered(), f) - dec = json.NewDecoder(reader) - retries++ - return nil - } - return err - } - - // main loop - for { - msg, err := decodeLogLine(dec, l) - if err != nil { - if err := handleDecodeErr(err); err != nil { - if err == errDone { - return - } - // we got an unrecoverable error, so return - logWatcher.Err <- err - return - } - // ready to try again - continue - } - - since := config.Since - until := config.Until - - retries = 0 // reset retries since we've succeeded - if !since.IsZero() && msg.Timestamp.Before(since) { - continue - } - if !until.IsZero() && msg.Timestamp.After(until) { - return - } - select { - case logWatcher.Msg <- msg: - case <-ctx.Done(): - logWatcher.Msg <- msg - // This for loop is used when the logger is closed (ie, container - // stopped) but the consumer is still waiting for logs. - for { - msg, err := decodeLogLine(dec, l) - if err != nil { - return - } - if !since.IsZero() && msg.Timestamp.Before(since) { - continue - } - if !until.IsZero() && msg.Timestamp.After(until) { - return - } - logWatcher.Msg <- msg + // io.ErrUnexpectedEOF is returned from json.Decoder when there is + // remaining data in the parser's buffer while an io.EOF occurs. + // If the json logger writes a partial json log entry to the disk + // while at the same time the decoder tries to decode it, the race condition happens. + if err == io.ErrUnexpectedEOF { + reader := io.MultiReader(dec.Buffered(), rdr) + dec = json.NewDecoder(reader) + retries++ } } + return msg, err } } diff --git a/components/engine/daemon/logger/jsonfilelog/read_test.go b/components/engine/daemon/logger/jsonfilelog/read_test.go index 01a05c4b78..599fdf9336 100644 --- a/components/engine/daemon/logger/jsonfilelog/read_test.go +++ b/components/engine/daemon/logger/jsonfilelog/read_test.go @@ -35,7 +35,7 @@ func BenchmarkJSONFileLoggerReadLogs(b *testing.B) { } buf := bytes.NewBuffer(nil) - require.NoError(b, marshalMessage(msg, jsonlogger.(*JSONFileLogger).extra, buf)) + require.NoError(b, marshalMessage(msg, nil, buf)) b.SetBytes(int64(buf.Len())) b.ResetTimer() diff --git a/components/engine/daemon/logger/loggerutils/logfile.go b/components/engine/daemon/logger/loggerutils/logfile.go new file mode 100644 index 0000000000..6a7a68909e --- /dev/null +++ b/components/engine/daemon/logger/loggerutils/logfile.go @@ -0,0 +1,454 @@ +package loggerutils + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + "strconv" + "sync" + "time" + + "github.com/docker/docker/daemon/logger" + "github.com/docker/docker/daemon/logger/loggerutils/multireader" + "github.com/docker/docker/pkg/filenotify" + "github.com/docker/docker/pkg/pubsub" + "github.com/docker/docker/pkg/tailfile" + "github.com/fsnotify/fsnotify" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// LogFile is Logger implementation for default Docker logging. +type LogFile struct { + f *os.File // store for closing + closed bool + mu sync.RWMutex + capacity int64 //maximum size of each file + currentSize int64 // current size of the latest file + maxFiles int //maximum number of files + notifyRotate *pubsub.Publisher + marshal logger.MarshalFunc + createDecoder makeDecoderFunc +} + +type makeDecoderFunc func(rdr io.Reader) func() (*logger.Message, error) + +//NewLogFile creates new LogFile +func NewLogFile(logPath string, capacity int64, maxFiles int, marshaller logger.MarshalFunc, decodeFunc makeDecoderFunc) (*LogFile, error) { + log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640) + if err != nil { + return nil, err + } + + size, err := log.Seek(0, os.SEEK_END) + if err != nil { + return nil, err + } + + return &LogFile{ + f: log, + capacity: capacity, + currentSize: size, + maxFiles: maxFiles, + notifyRotate: pubsub.NewPublisher(0, 1), + marshal: marshaller, + createDecoder: decodeFunc, + }, nil +} + +// WriteLogEntry writes the provided log message to the current log file. +// This may trigger a rotation event if the max file/capacity limits are hit. +func (w *LogFile) WriteLogEntry(msg *logger.Message) error { + b, err := w.marshal(msg) + if err != nil { + return errors.Wrap(err, "error marshalling log message") + } + + logger.PutMessage(msg) + + w.mu.Lock() + if w.closed { + w.mu.Unlock() + return errors.New("cannot write because the output file was closed") + } + + if err := w.checkCapacityAndRotate(); err != nil { + w.mu.Unlock() + return err + } + + n, err := w.f.Write(b) + if err == nil { + w.currentSize += int64(n) + } + w.mu.Unlock() + return err +} + +func (w *LogFile) checkCapacityAndRotate() error { + if w.capacity == -1 { + return nil + } + + if w.currentSize >= w.capacity { + name := w.f.Name() + if err := w.f.Close(); err != nil { + return errors.Wrap(err, "error closing file") + } + if err := rotate(name, w.maxFiles); err != nil { + return err + } + file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0640) + if err != nil { + return err + } + w.f = file + w.currentSize = 0 + w.notifyRotate.Publish(struct{}{}) + } + + return nil +} + +func rotate(name string, maxFiles int) error { + if maxFiles < 2 { + return nil + } + for i := maxFiles - 1; i > 1; i-- { + toPath := name + "." + strconv.Itoa(i) + fromPath := name + "." + strconv.Itoa(i-1) + if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) { + return errors.Wrap(err, "error rotating old log entries") + } + } + + if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) { + return errors.Wrap(err, "error rotating current log") + } + return nil +} + +// LogPath returns the location the given writer logs to. +func (w *LogFile) LogPath() string { + w.mu.Lock() + defer w.mu.Unlock() + return w.f.Name() +} + +// MaxFiles return maximum number of files +func (w *LogFile) MaxFiles() int { + return w.maxFiles +} + +// Close closes underlying file and signals all readers to stop. +func (w *LogFile) Close() error { + w.mu.Lock() + defer w.mu.Unlock() + if w.closed { + return nil + } + if err := w.f.Close(); err != nil { + return err + } + w.closed = true + return nil +} + +// ReadLogs decodes entries from log files and sends them the passed in watcher +func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) { + w.mu.RLock() + files, err := w.openRotatedFiles() + if err != nil { + w.mu.RUnlock() + watcher.Err <- err + return + } + defer func() { + for _, f := range files { + f.Close() + } + }() + + currentFile, err := os.Open(w.f.Name()) + if err != nil { + w.mu.RUnlock() + watcher.Err <- err + return + } + defer currentFile.Close() + + currentChunk, err := newSectionReader(currentFile) + w.mu.RUnlock() + + if err != nil { + watcher.Err <- err + return + } + + if config.Tail != 0 { + seekers := make([]io.ReadSeeker, 0, len(files)+1) + for _, f := range files { + seekers = append(seekers, f) + } + seekers = append(seekers, currentChunk) + tailFile(multireader.MultiReadSeeker(seekers...), watcher, w.createDecoder, config) + } + + w.mu.RLock() + if !config.Follow || w.closed { + w.mu.RUnlock() + return + } + w.mu.RUnlock() + + notifyRotate := w.notifyRotate.Subscribe() + defer w.notifyRotate.Evict(notifyRotate) + followLogs(currentFile, watcher, notifyRotate, w.createDecoder, config.Since, config.Until) +} + +func (w *LogFile) openRotatedFiles() (files []*os.File, err error) { + defer func() { + if err == nil { + return + } + for _, f := range files { + f.Close() + } + }() + + for i := w.maxFiles; i > 1; i-- { + f, err := os.Open(fmt.Sprintf("%s.%d", w.f.Name(), i-1)) + if err != nil { + if !os.IsNotExist(err) { + return nil, err + } + continue + } + files = append(files, f) + } + + return files, nil +} + +func newSectionReader(f *os.File) (*io.SectionReader, error) { + // seek to the end to get the size + // we'll leave this at the end of the file since section reader does not advance the reader + size, err := f.Seek(0, os.SEEK_END) + if err != nil { + return nil, errors.Wrap(err, "error getting current file size") + } + return io.NewSectionReader(f, 0, size), nil +} + +type decodeFunc func() (*logger.Message, error) + +func tailFile(f io.ReadSeeker, watcher *logger.LogWatcher, createDecoder makeDecoderFunc, config logger.ReadConfig) { + var rdr io.Reader = f + if config.Tail > 0 { + ls, err := tailfile.TailFile(f, config.Tail) + if err != nil { + watcher.Err <- err + return + } + rdr = bytes.NewBuffer(bytes.Join(ls, []byte("\n"))) + } + + decodeLogLine := createDecoder(rdr) + for { + msg, err := decodeLogLine() + if err != nil { + if err != io.EOF { + watcher.Err <- err + } + return + } + if !config.Since.IsZero() && msg.Timestamp.Before(config.Since) { + continue + } + if !config.Until.IsZero() && msg.Timestamp.After(config.Until) { + return + } + select { + case <-watcher.WatchClose(): + return + case watcher.Msg <- msg: + } + } +} + +func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan interface{}, createDecoder makeDecoderFunc, since, until time.Time) { + decodeLogLine := createDecoder(f) + + name := f.Name() + fileWatcher, err := watchFile(name) + if err != nil { + logWatcher.Err <- err + return + } + defer func() { + f.Close() + fileWatcher.Remove(name) + fileWatcher.Close() + }() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func() { + select { + case <-logWatcher.WatchClose(): + fileWatcher.Remove(name) + cancel() + case <-ctx.Done(): + return + } + }() + + var retries int + handleRotate := func() error { + f.Close() + fileWatcher.Remove(name) + + // retry when the file doesn't exist + for retries := 0; retries <= 5; retries++ { + f, err = os.Open(name) + if err == nil || !os.IsNotExist(err) { + break + } + } + if err != nil { + return err + } + if err := fileWatcher.Add(name); err != nil { + return err + } + decodeLogLine = createDecoder(f) + return nil + } + + errRetry := errors.New("retry") + errDone := errors.New("done") + waitRead := func() error { + select { + case e := <-fileWatcher.Events(): + switch e.Op { + case fsnotify.Write: + decodeLogLine = createDecoder(f) + return nil + case fsnotify.Rename, fsnotify.Remove: + select { + case <-notifyRotate: + case <-ctx.Done(): + return errDone + } + if err := handleRotate(); err != nil { + return err + } + return nil + } + return errRetry + case err := <-fileWatcher.Errors(): + logrus.Debug("logger got error watching file: %v", err) + // Something happened, let's try and stay alive and create a new watcher + if retries <= 5 { + fileWatcher.Close() + fileWatcher, err = watchFile(name) + if err != nil { + return err + } + retries++ + return errRetry + } + return err + case <-ctx.Done(): + return errDone + } + } + + handleDecodeErr := func(err error) error { + if err != io.EOF { + return err + } + + for { + err := waitRead() + if err == nil { + break + } + if err == errRetry { + continue + } + return err + } + return nil + } + + // main loop + for { + msg, err := decodeLogLine() + if err != nil { + if err := handleDecodeErr(err); err != nil { + if err == errDone { + return + } + // we got an unrecoverable error, so return + logWatcher.Err <- err + return + } + // ready to try again + continue + } + + retries = 0 // reset retries since we've succeeded + if !since.IsZero() && msg.Timestamp.Before(since) { + continue + } + if !until.IsZero() && msg.Timestamp.After(until) { + return + } + select { + case logWatcher.Msg <- msg: + case <-ctx.Done(): + logWatcher.Msg <- msg + for { + msg, err := decodeLogLine() + if err != nil { + return + } + if !since.IsZero() && msg.Timestamp.Before(since) { + continue + } + if !until.IsZero() && msg.Timestamp.After(until) { + return + } + logWatcher.Msg <- msg + } + } + } +} + +func watchFile(name string) (filenotify.FileWatcher, error) { + fileWatcher, err := filenotify.New() + if err != nil { + return nil, err + } + + logger := logrus.WithFields(logrus.Fields{ + "module": "logger", + "fille": name, + }) + + if err := fileWatcher.Add(name); err != nil { + logger.WithError(err).Warnf("falling back to file poller") + fileWatcher.Close() + fileWatcher = filenotify.NewPollingWatcher() + + if err := fileWatcher.Add(name); err != nil { + fileWatcher.Close() + logger.WithError(err).Debugf("error watching log file for modifications") + return nil, err + } + } + return fileWatcher, nil +} diff --git a/components/engine/daemon/logger/jsonfilelog/multireader/multireader.go b/components/engine/daemon/logger/loggerutils/multireader/multireader.go similarity index 100% rename from components/engine/daemon/logger/jsonfilelog/multireader/multireader.go rename to components/engine/daemon/logger/loggerutils/multireader/multireader.go diff --git a/components/engine/daemon/logger/jsonfilelog/multireader/multireader_test.go b/components/engine/daemon/logger/loggerutils/multireader/multireader_test.go similarity index 100% rename from components/engine/daemon/logger/jsonfilelog/multireader/multireader_test.go rename to components/engine/daemon/logger/loggerutils/multireader/multireader_test.go diff --git a/components/engine/daemon/logger/loggerutils/rotatefilewriter.go b/components/engine/daemon/logger/loggerutils/rotatefilewriter.go deleted file mode 100644 index 54c5688003..0000000000 --- a/components/engine/daemon/logger/loggerutils/rotatefilewriter.go +++ /dev/null @@ -1,153 +0,0 @@ -package loggerutils - -import ( - "os" - "strconv" - "sync" - - "github.com/docker/docker/daemon/logger" - "github.com/docker/docker/pkg/pubsub" - "github.com/pkg/errors" -) - -// RotateFileWriter is Logger implementation for default Docker logging. -type RotateFileWriter struct { - f *os.File // store for closing - closed bool - mu sync.Mutex - capacity int64 //maximum size of each file - currentSize int64 // current size of the latest file - maxFiles int //maximum number of files - notifyRotate *pubsub.Publisher - marshal logger.MarshalFunc -} - -//NewRotateFileWriter creates new RotateFileWriter -func NewRotateFileWriter(logPath string, capacity int64, maxFiles int, marshaller logger.MarshalFunc) (*RotateFileWriter, error) { - log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640) - if err != nil { - return nil, err - } - - size, err := log.Seek(0, os.SEEK_END) - if err != nil { - return nil, err - } - - return &RotateFileWriter{ - f: log, - capacity: capacity, - currentSize: size, - maxFiles: maxFiles, - notifyRotate: pubsub.NewPublisher(0, 1), - marshal: marshaller, - }, nil -} - -// WriteLogEntry writes the provided log message to the current log file. -// This may trigger a rotation event if the max file/capacity limits are hit. -func (w *RotateFileWriter) WriteLogEntry(msg *logger.Message) error { - b, err := w.marshal(msg) - if err != nil { - return errors.Wrap(err, "error marshalling log message") - } - - logger.PutMessage(msg) - - w.mu.Lock() - if w.closed { - w.mu.Unlock() - return errors.New("cannot write because the output file was closed") - } - - if err := w.checkCapacityAndRotate(); err != nil { - w.mu.Unlock() - return err - } - - n, err := w.f.Write(b) - if err == nil { - w.currentSize += int64(n) - } - w.mu.Unlock() - return err -} - -func (w *RotateFileWriter) checkCapacityAndRotate() error { - if w.capacity == -1 { - return nil - } - - if w.currentSize >= w.capacity { - name := w.f.Name() - if err := w.f.Close(); err != nil { - return errors.Wrap(err, "error closing file") - } - if err := rotate(name, w.maxFiles); err != nil { - return err - } - file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0640) - if err != nil { - return err - } - w.f = file - w.currentSize = 0 - w.notifyRotate.Publish(struct{}{}) - } - - return nil -} - -func rotate(name string, maxFiles int) error { - if maxFiles < 2 { - return nil - } - for i := maxFiles - 1; i > 1; i-- { - toPath := name + "." + strconv.Itoa(i) - fromPath := name + "." + strconv.Itoa(i-1) - if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) { - return errors.Wrap(err, "error rotating old log entries") - } - } - - if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) { - return errors.Wrap(err, "error rotating current log") - } - return nil -} - -// LogPath returns the location the given writer logs to. -func (w *RotateFileWriter) LogPath() string { - w.mu.Lock() - defer w.mu.Unlock() - return w.f.Name() -} - -// MaxFiles return maximum number of files -func (w *RotateFileWriter) MaxFiles() int { - return w.maxFiles -} - -//NotifyRotate returns the new subscriber -func (w *RotateFileWriter) NotifyRotate() chan interface{} { - return w.notifyRotate.Subscribe() -} - -//NotifyRotateEvict removes the specified subscriber from receiving any more messages. -func (w *RotateFileWriter) NotifyRotateEvict(sub chan interface{}) { - w.notifyRotate.Evict(sub) -} - -// Close closes underlying file and signals all readers to stop. -func (w *RotateFileWriter) Close() error { - w.mu.Lock() - defer w.mu.Unlock() - if w.closed { - return nil - } - if err := w.f.Close(); err != nil { - return err - } - w.closed = true - return nil -} diff --git a/components/engine/daemon/logs.go b/components/engine/daemon/logs.go index babf07e36d..131360b7b4 100644 --- a/components/engine/daemon/logs.go +++ b/components/engine/daemon/logs.go @@ -123,7 +123,7 @@ func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, c } return case <-ctx.Done(): - lg.Debug("logs: end stream, ctx is done: %v", ctx.Err()) + lg.Debugf("logs: end stream, ctx is done: %v", ctx.Err()) return case msg, ok := <-logs.Msg: // there is some kind of pool or ring buffer in the logger that From e5124fd7ef3f1f3647d1fe27da77792ce1db490b Mon Sep 17 00:00:00 2001 From: Renaud Gaubert Date: Sun, 29 Oct 2017 20:30:31 +0100 Subject: [PATCH 42/94] Updated GenericResource CLI Signed-off-by: Renaud Gaubert Upstream-commit: ebe14310b7728cca6d092ff66236577a3f3016d5 Component: engine --- components/engine/cmd/dockerd/config.go | 3 ++- components/engine/daemon/config/config.go | 3 ++- components/engine/daemon/config/opts.go | 4 ++-- components/engine/opts/opts.go | 10 ++++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/components/engine/cmd/dockerd/config.go b/components/engine/cmd/dockerd/config.go index f142b7538c..b9d586a4ba 100644 --- a/components/engine/cmd/dockerd/config.go +++ b/components/engine/cmd/dockerd/config.go @@ -65,7 +65,8 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) { flags.StringVar(&conf.MetricsAddress, "metrics-addr", "", "Set default address and port to serve the metrics api on") - flags.StringVar(&conf.NodeGenericResources, "node-generic-resources", "", "user defined resources (e.g. fpga=2;gpu={UUID1,UUID2,UUID3})") + flags.Var(opts.NewListOptsRef(&conf.NodeGenericResources, opts.ValidateSingleGenericResource), "node-generic-resource", "Advertise user-defined resource") + flags.IntVar(&conf.NetworkControlPlaneMTU, "network-control-plane-mtu", config.DefaultNetworkMtu, "Network Control plane MTU") // "--deprecated-key-path" is to allow configuration of the key used diff --git a/components/engine/daemon/config/config.go b/components/engine/daemon/config/config.go index a340997073..f7a0df58ed 100644 --- a/components/engine/daemon/config/config.go +++ b/components/engine/daemon/config/config.go @@ -171,7 +171,8 @@ type CommonConfig struct { Experimental bool `json:"experimental"` // Experimental indicates whether experimental features should be exposed or not // Exposed node Generic Resources - NodeGenericResources string `json:"node-generic-resources,omitempty"` + // e.g: ["orange=red", "orange=green", "orange=blue", "apple=3"] + NodeGenericResources []string `json:"node-generic-resources,omitempty"` // NetworkControlPlaneMTU allows to specify the control plane MTU, this will allow to optimize the network use in some components NetworkControlPlaneMTU int `json:"network-control-plane-mtu,omitempty"` diff --git a/components/engine/daemon/config/opts.go b/components/engine/daemon/config/opts.go index 00f32e43bb..cdf264911f 100644 --- a/components/engine/daemon/config/opts.go +++ b/components/engine/daemon/config/opts.go @@ -7,8 +7,8 @@ import ( ) // ParseGenericResources parses and validates the specified string as a list of GenericResource -func ParseGenericResources(value string) ([]swarm.GenericResource, error) { - if value == "" { +func ParseGenericResources(value []string) ([]swarm.GenericResource, error) { + if len(value) == 0 { return nil, nil } diff --git a/components/engine/opts/opts.go b/components/engine/opts/opts.go index a86d74d60a..a2cc5e33b1 100644 --- a/components/engine/opts/opts.go +++ b/components/engine/opts/opts.go @@ -263,6 +263,16 @@ func ValidateLabel(val string) (string, error) { return val, nil } +// ValidateSingleGenericResource validates that a single entry in the +// generic resource list is valid. +// i.e 'GPU=UID1' is valid however 'GPU:UID1' or 'UID1' isn't +func ValidateSingleGenericResource(val string) (string, error) { + if strings.Count(val, "=") < 1 { + return "", fmt.Errorf("invalid node-generic-resource format `%s` expected `name=value`", val) + } + return val, nil +} + // ParseLink parses and validates the specified string as a link format (name:alias) func ParseLink(val string) (string, string, error) { if val == "" { From aee7705a5fff5067fd64f7d2d8b675a8353d4ce0 Mon Sep 17 00:00:00 2001 From: Renaud Gaubert Date: Mon, 30 Oct 2017 23:23:43 +0100 Subject: [PATCH 43/94] Added NodeGenericResource config tests Signed-off-by: Renaud Gaubert Upstream-commit: 734346a37e0cd5d2576f759d302beed5033ff14e Component: engine --- .../engine/daemon/config/config_test.go | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/components/engine/daemon/config/config_test.go b/components/engine/daemon/config/config_test.go index 30a93126d3..bdd046bcdc 100644 --- a/components/engine/daemon/config/config_test.go +++ b/components/engine/daemon/config/config_test.go @@ -259,6 +259,20 @@ func TestValidateConfigurationErrors(t *testing.T) { }, }, }, + { + config: &Config{ + CommonConfig: CommonConfig{ + NodeGenericResources: []string{"foo"}, + }, + }, + }, + { + config: &Config{ + CommonConfig: CommonConfig{ + NodeGenericResources: []string{"foo=bar", "foo=1"}, + }, + }, + }, } for _, tc := range testCases { err := Validate(tc.config) @@ -316,6 +330,20 @@ func TestValidateConfiguration(t *testing.T) { }, }, }, + { + config: &Config{ + CommonConfig: CommonConfig{ + NodeGenericResources: []string{"foo=bar", "foo=baz"}, + }, + }, + }, + { + config: &Config{ + CommonConfig: CommonConfig{ + NodeGenericResources: []string{"foo=1"}, + }, + }, + }, } for _, tc := range testCases { err := Validate(tc.config) From bab1ef9f351145b00042d44951fc4866dbcd6808 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Mon, 30 Oct 2017 13:18:14 -0700 Subject: [PATCH 44/94] Add quota support to VFS graphdriver This patch adds the capability for the VFS graphdriver to use XFS project quotas. It reuses the existing quota management code that was created by overlay2 on XFS. It doesn't rely on a filesystem whitelist, but instead the quota-capability detection code. Signed-off-by: Sargun Dhillon Upstream-commit: 7a1618ced359a3ac921d8a05903d62f544ff17d0 Component: engine --- .../graphdriver/graphtest/graphtest_unix.go | 28 ++++++++++--- .../engine/daemon/graphdriver/quota/errors.go | 19 +++++++++ .../daemon/graphdriver/quota/projectquota.go | 5 --- .../engine/daemon/graphdriver/vfs/driver.go | 42 ++++++++++++++++++- .../daemon/graphdriver/vfs/quota_linux.go | 27 ++++++++++++ .../graphdriver/vfs/quota_unsupported.go | 20 +++++++++ .../engine/daemon/graphdriver/vfs/vfs_test.go | 4 ++ .../engine/daemon/graphdriver/zfs/zfs_test.go | 2 +- 8 files changed, 133 insertions(+), 14 deletions(-) create mode 100644 components/engine/daemon/graphdriver/quota/errors.go create mode 100644 components/engine/daemon/graphdriver/vfs/quota_linux.go create mode 100644 components/engine/daemon/graphdriver/vfs/quota_unsupported.go diff --git a/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go b/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go index 6b352ba69a..c25d4826fc 100644 --- a/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go +++ b/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go @@ -13,6 +13,7 @@ import ( "unsafe" "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/docker/daemon/graphdriver/quota" "github.com/docker/docker/pkg/stringid" "github.com/docker/go-units" "github.com/stretchr/testify/assert" @@ -310,7 +311,7 @@ func writeRandomFile(path string, size uint64) error { } // DriverTestSetQuota Create a driver and test setting quota. -func DriverTestSetQuota(t *testing.T, drivername string) { +func DriverTestSetQuota(t *testing.T, drivername string, required bool) { driver := GetDriver(t, drivername) defer PutDriver(t) @@ -318,19 +319,34 @@ func DriverTestSetQuota(t *testing.T, drivername string) { createOpts := &graphdriver.CreateOpts{} createOpts.StorageOpt = make(map[string]string, 1) createOpts.StorageOpt["size"] = "50M" - if err := driver.Create("zfsTest", "Base", createOpts); err != nil { + layerName := drivername + "Test" + if err := driver.CreateReadWrite(layerName, "Base", createOpts); err == quota.ErrQuotaNotSupported && !required { + t.Skipf("Quota not supported on underlying filesystem: %v", err) + } else if err != nil { t.Fatal(err) } - mountPath, err := driver.Get("zfsTest", "") + mountPath, err := driver.Get(layerName, "") if err != nil { t.Fatal(err) } quota := uint64(50 * units.MiB) - err = writeRandomFile(path.Join(mountPath.Path(), "file"), quota*2) - if pathError, ok := err.(*os.PathError); ok && pathError.Err != unix.EDQUOT { - t.Fatalf("expect write() to fail with %v, got %v", unix.EDQUOT, err) + // Try to write a file smaller than quota, and ensure it works + err = writeRandomFile(path.Join(mountPath.Path(), "smallfile"), quota/2) + if err != nil { + t.Fatal(err) + } + defer os.Remove(path.Join(mountPath.Path(), "smallfile")) + + // Try to write a file bigger than quota. We've already filled up half the quota, so hitting the limit should be easy + err = writeRandomFile(path.Join(mountPath.Path(), "bigfile"), quota) + if err == nil { + t.Fatalf("expected write to fail(), instead had success") + } + if pathError, ok := err.(*os.PathError); ok && pathError.Err != unix.EDQUOT && pathError.Err != unix.ENOSPC { + os.Remove(path.Join(mountPath.Path(), "bigfile")) + t.Fatalf("expect write() to fail with %v or %v, got %v", unix.EDQUOT, unix.ENOSPC, pathError.Err) } } diff --git a/components/engine/daemon/graphdriver/quota/errors.go b/components/engine/daemon/graphdriver/quota/errors.go new file mode 100644 index 0000000000..1741f2f5db --- /dev/null +++ b/components/engine/daemon/graphdriver/quota/errors.go @@ -0,0 +1,19 @@ +package quota + +import "github.com/docker/docker/api/errdefs" + +var ( + _ errdefs.ErrNotImplemented = (*errQuotaNotSupported)(nil) +) + +// ErrQuotaNotSupported indicates if were found the FS didn't have projects quotas available +var ErrQuotaNotSupported = errQuotaNotSupported{} + +type errQuotaNotSupported struct { +} + +func (e errQuotaNotSupported) NotImplemented() {} + +func (e errQuotaNotSupported) Error() string { + return "Filesystem does not support, or has not enabled quotas" +} diff --git a/components/engine/daemon/graphdriver/quota/projectquota.go b/components/engine/daemon/graphdriver/quota/projectquota.go index 84e391aa89..9709588b6b 100644 --- a/components/engine/daemon/graphdriver/quota/projectquota.go +++ b/components/engine/daemon/graphdriver/quota/projectquota.go @@ -58,15 +58,10 @@ import ( "path/filepath" "unsafe" - "errors" - "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) -// ErrQuotaNotSupported indicates if were found the FS does not have projects quotas available -var ErrQuotaNotSupported = errors.New("Filesystem does not support or has not enabled quotas") - // Quota limit params - currently we only control blocks hard limit type Quota struct { Size uint64 diff --git a/components/engine/daemon/graphdriver/vfs/driver.go b/components/engine/daemon/graphdriver/vfs/driver.go index 0482dccb87..610476fd88 100644 --- a/components/engine/daemon/graphdriver/vfs/driver.go +++ b/components/engine/daemon/graphdriver/vfs/driver.go @@ -6,10 +6,12 @@ import ( "path/filepath" "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/docker/daemon/graphdriver/quota" "github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/system" + units "github.com/docker/go-units" "github.com/opencontainers/selinux/go-selinux/label" ) @@ -33,6 +35,11 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil { return nil, err } + + if err := setupDriverQuota(d); err != nil { + return nil, err + } + return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil } @@ -41,6 +48,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap // In order to support layering, files are copied from the parent layer into the new layer. There is no copy-on-write support. // Driver must be wrapped in NaiveDiffDriver to be used as a graphdriver.Driver type Driver struct { + driverQuota home string idMappings *idtools.IDMappings } @@ -67,15 +75,38 @@ func (d *Driver) Cleanup() error { // CreateReadWrite creates a layer that is writable for use as a container // file system. func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { - return d.Create(id, parent, opts) + var err error + var size int64 + + if opts != nil { + for key, val := range opts.StorageOpt { + switch key { + case "size": + if !d.quotaSupported() { + return quota.ErrQuotaNotSupported + } + if size, err = units.RAMInBytes(val); err != nil { + return err + } + default: + return fmt.Errorf("Storage opt %s not supported", key) + } + } + } + + return d.create(id, parent, uint64(size)) } // Create prepares the filesystem for the VFS driver and copies the directory for the given id under the parent. func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { if opts != nil && len(opts.StorageOpt) != 0 { - return fmt.Errorf("--storage-opt is not supported for vfs") + return fmt.Errorf("--storage-opt is not supported for vfs on read-only layers") } + return d.create(id, parent, 0) +} + +func (d *Driver) create(id, parent string, size uint64) error { dir := d.dir(id) rootIDs := d.idMappings.RootPair() if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0700, rootIDs); err != nil { @@ -84,6 +115,13 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { if err := idtools.MkdirAndChown(dir, 0755, rootIDs); err != nil { return err } + + if size != 0 { + if err := d.setupQuota(dir, size); err != nil { + return err + } + } + labelOpts := []string{"level:s0"} if _, mountLabel, err := label.InitLabels(labelOpts); err == nil { label.SetFileLabel(dir, mountLabel) diff --git a/components/engine/daemon/graphdriver/vfs/quota_linux.go b/components/engine/daemon/graphdriver/vfs/quota_linux.go new file mode 100644 index 0000000000..032c15b9ef --- /dev/null +++ b/components/engine/daemon/graphdriver/vfs/quota_linux.go @@ -0,0 +1,27 @@ +// +build linux + +package vfs + +import "github.com/docker/docker/daemon/graphdriver/quota" + +type driverQuota struct { + quotaCtl *quota.Control +} + +func setupDriverQuota(driver *Driver) error { + if quotaCtl, err := quota.NewControl(driver.home); err == nil { + driver.quotaCtl = quotaCtl + } else if err != quota.ErrQuotaNotSupported { + return err + } + + return nil +} + +func (d *Driver) setupQuota(dir string, size uint64) error { + return d.quotaCtl.SetQuota(dir, quota.Quota{Size: size}) +} + +func (d *Driver) quotaSupported() bool { + return d.quotaCtl != nil +} diff --git a/components/engine/daemon/graphdriver/vfs/quota_unsupported.go b/components/engine/daemon/graphdriver/vfs/quota_unsupported.go new file mode 100644 index 0000000000..9cca53d372 --- /dev/null +++ b/components/engine/daemon/graphdriver/vfs/quota_unsupported.go @@ -0,0 +1,20 @@ +// +build !linux + +package vfs + +import "github.com/docker/docker/daemon/graphdriver/quota" + +type driverQuota struct { +} + +func setupDriverQuota(driver *Driver) error { + return nil +} + +func (d *Driver) setupQuota(dir string, size uint64) error { + return quota.ErrQuotaNotSupported +} + +func (d *Driver) quotaSupported() bool { + return false +} diff --git a/components/engine/daemon/graphdriver/vfs/vfs_test.go b/components/engine/daemon/graphdriver/vfs/vfs_test.go index 9ecf21dbaa..16dc1357f1 100644 --- a/components/engine/daemon/graphdriver/vfs/vfs_test.go +++ b/components/engine/daemon/graphdriver/vfs/vfs_test.go @@ -32,6 +32,10 @@ func TestVfsCreateSnap(t *testing.T) { graphtest.DriverTestCreateSnap(t, "vfs") } +func TestVfsSetQuota(t *testing.T) { + graphtest.DriverTestSetQuota(t, "vfs", false) +} + func TestVfsTeardown(t *testing.T) { graphtest.PutDriver(t) } diff --git a/components/engine/daemon/graphdriver/zfs/zfs_test.go b/components/engine/daemon/graphdriver/zfs/zfs_test.go index 3e22928438..2eb85c4d83 100644 --- a/components/engine/daemon/graphdriver/zfs/zfs_test.go +++ b/components/engine/daemon/graphdriver/zfs/zfs_test.go @@ -27,7 +27,7 @@ func TestZfsCreateSnap(t *testing.T) { } func TestZfsSetQuota(t *testing.T) { - graphtest.DriverTestSetQuota(t, "zfs") + graphtest.DriverTestSetQuota(t, "zfs", true) } func TestZfsTeardown(t *testing.T) { From 7cfb40e0bfc9a0f8a95bda48c3c343cdcf96142f Mon Sep 17 00:00:00 2001 From: John Stephens Date: Mon, 6 Nov 2017 18:21:10 -0800 Subject: [PATCH 45/94] Set OS on scratch image and prevent panic if empty Signed-off-by: John Stephens Upstream-commit: a97817b673cbd3bfaf6e752282c4992ac43ff594 Component: engine --- .../engine/builder/dockerfile/imagecontext.go | 10 ++++++++- components/engine/daemon/create.go | 9 +++++++- .../integration-cli/docker_api_build_test.go | 22 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/components/engine/builder/dockerfile/imagecontext.go b/components/engine/builder/dockerfile/imagecontext.go index 79206dad4d..a22b60b2e5 100644 --- a/components/engine/builder/dockerfile/imagecontext.go +++ b/components/engine/builder/dockerfile/imagecontext.go @@ -1,6 +1,8 @@ package dockerfile import ( + "runtime" + "github.com/docker/docker/api/types/backend" "github.com/docker/docker/builder" "github.com/docker/docker/builder/remotecontext" @@ -73,7 +75,13 @@ func (m *imageSources) Unmount() (retErr error) { func (m *imageSources) Add(im *imageMount) { switch im.image { case nil: - im.image = &dockerimage.Image{} + // set the OS for scratch images + os := runtime.GOOS + // Windows does not support scratch except for LCOW + if runtime.GOOS == "windows" { + os = "linux" + } + im.image = &dockerimage.Image{V1Image: dockerimage.V1Image{OS: os}} default: m.byImageID[im.image.ImageID()] = im } diff --git a/components/engine/daemon/create.go b/components/engine/daemon/create.go index b2014470c4..e4d17cc2df 100644 --- a/components/engine/daemon/create.go +++ b/components/engine/daemon/create.go @@ -95,7 +95,14 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) ( if err != nil { return nil, err } - os = img.OS + if img.OS != "" { + os = img.OS + } else { + // default to the host OS except on Windows with LCOW + if runtime.GOOS == "windows" && system.LCOWSupported() { + os = "linux" + } + } imgID = img.ID() if runtime.GOOS == "windows" && img.OS == "linux" && !system.LCOWSupported() { diff --git a/components/engine/integration-cli/docker_api_build_test.go b/components/engine/integration-cli/docker_api_build_test.go index 8c494f1427..3f43c95432 100644 --- a/components/engine/integration-cli/docker_api_build_test.go +++ b/components/engine/integration-cli/docker_api_build_test.go @@ -619,6 +619,28 @@ func testBuildWithSession(c *check.C, dir, dockerfile string) (outStr string) { return } +func (s *DockerSuite) TestBuildScratchCopy(c *check.C) { + testRequires(c, DaemonIsLinux) + dockerfile := `FROM scratch +ADD Dockerfile / +ENV foo bar` + ctx := fakecontext.New(c, "", + fakecontext.WithDockerfile(dockerfile), + ) + defer ctx.Close() + + res, body, err := request.Post( + "/build", + request.RawContent(ctx.AsTarReader(c)), + request.ContentType("application/x-tar")) + c.Assert(err, checker.IsNil) + c.Assert(res.StatusCode, checker.Equals, http.StatusOK) + + out, err := request.ReadBody(body) + require.NoError(c, err) + assert.Contains(c, string(out), "Successfully built") +} + type buildLine struct { Stream string Aux struct { From e49f1e90758fc7fec24da923a58d4804e285f268 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 7 Nov 2017 16:16:09 +0100 Subject: [PATCH 46/94] Bump opencontainers/image-spec to v1.0.0 Signed-off-by: Sebastiaan van Stijn Upstream-commit: c5ccc7f73fecfa881a8ff9ddc5032892223204ee Component: engine --- components/engine/vendor.conf | 2 +- .../github.com/opencontainers/image-spec/specs-go/v1/config.go | 2 +- .../github.com/opencontainers/image-spec/specs-go/version.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index bec25b7e59..85e4ccfaff 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -67,7 +67,7 @@ google.golang.org/grpc v1.3.0 # When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d github.com/opencontainers/runtime-spec v1.0.0 -github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13 +github.com/opencontainers/image-spec v1.0.0 github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 # libcontainer deps (see src/github.com/opencontainers/runc/Godeps/Godeps.json) diff --git a/components/engine/vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go b/components/engine/vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go index 8475ff7419..fe799bd698 100644 --- a/components/engine/vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go +++ b/components/engine/vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go @@ -37,7 +37,7 @@ type ImageConfig struct { // Cmd defines the default arguments to the entrypoint of the container. Cmd []string `json:"Cmd,omitempty"` - // Volumes is a set of directories which should be created as data volumes in a container running this image. + // Volumes is a set of directories describing where the process is likely write data specific to a container instance. Volumes map[string]struct{} `json:"Volumes,omitempty"` // WorkingDir sets the current working directory of the entrypoint process in the container. diff --git a/components/engine/vendor/github.com/opencontainers/image-spec/specs-go/version.go b/components/engine/vendor/github.com/opencontainers/image-spec/specs-go/version.go index f4cda6ed8d..e3eee29b41 100644 --- a/components/engine/vendor/github.com/opencontainers/image-spec/specs-go/version.go +++ b/components/engine/vendor/github.com/opencontainers/image-spec/specs-go/version.go @@ -25,7 +25,7 @@ const ( VersionPatch = 0 // VersionDev indicates development branch. Releases will be empty string. - VersionDev = "-rc6-dev" + VersionDev = "" ) // Version is the specification version that the package types support. From 19146bf34eed6ad6eff078d7ee0b85f9e531ce68 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Wed, 8 Nov 2017 02:50:52 +1100 Subject: [PATCH 47/94] vendor: update to github.com/vbatts/tar-split@v0.10.2 Update to the latest version of tar-split, which includes a change to fix a memory exhaustion issue where a malformed image could cause the Docker daemon to crash. * tar: asm: store padding in chunks to avoid memory exhaustion Fixes: CVE-2017-14992 Signed-off-by: Aleksa Sarai Upstream-commit: e0ff7cccc3cac73da41ec9ef007b0e4e97c55d01 Component: engine --- components/engine/vendor.conf | 2 +- .../github.com/vbatts/tar-split/README.md | 3 +- .../vbatts/tar-split/tar/asm/disassemble.go | 43 ++++++++++++------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index bec25b7e59..74a81884eb 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -55,7 +55,7 @@ github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7 # get graph and distribution packages github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c -github.com/vbatts/tar-split v0.10.1 +github.com/vbatts/tar-split v0.10.2 github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb # get go-zfs packages diff --git a/components/engine/vendor/github.com/vbatts/tar-split/README.md b/components/engine/vendor/github.com/vbatts/tar-split/README.md index 4c544d823f..03e3ec4308 100644 --- a/components/engine/vendor/github.com/vbatts/tar-split/README.md +++ b/components/engine/vendor/github.com/vbatts/tar-split/README.md @@ -1,6 +1,7 @@ # tar-split [![Build Status](https://travis-ci.org/vbatts/tar-split.svg?branch=master)](https://travis-ci.org/vbatts/tar-split) +[![Go Report Card](https://goreportcard.com/badge/github.com/vbatts/tar-split)](https://goreportcard.com/report/github.com/vbatts/tar-split) Pristinely disassembling a tar archive, and stashing needed raw bytes and offsets to reassemble a validating original archive. @@ -50,7 +51,7 @@ For example stored sparse files that have "holes" in them, will be read as a contiguous file, though the archive contents may be recorded in sparse format. Therefore when adding the file payload to a reassembled tar, to achieve identical output, the file payload would need be precisely re-sparsified. This -is not something I seek to fix imediately, but would rather have an alert that +is not something I seek to fix immediately, but would rather have an alert that precise reassembly is not possible. (see more http://www.gnu.org/software/tar/manual/html_node/Sparse-Formats.html) diff --git a/components/engine/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go b/components/engine/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go index 54ef23aed3..009b3f5d81 100644 --- a/components/engine/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go +++ b/components/engine/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go @@ -2,7 +2,6 @@ package asm import ( "io" - "io/ioutil" "github.com/vbatts/tar-split/archive/tar" "github.com/vbatts/tar-split/tar/storage" @@ -119,20 +118,34 @@ func NewInputTarStream(r io.Reader, p storage.Packer, fp storage.FilePutter) (io } } - // it is allowable, and not uncommon that there is further padding on the - // end of an archive, apart from the expected 1024 null bytes. - remainder, err := ioutil.ReadAll(outputRdr) - if err != nil && err != io.EOF { - pW.CloseWithError(err) - return - } - _, err = p.AddEntry(storage.Entry{ - Type: storage.SegmentType, - Payload: remainder, - }) - if err != nil { - pW.CloseWithError(err) - return + // It is allowable, and not uncommon that there is further padding on + // the end of an archive, apart from the expected 1024 null bytes. We + // do this in chunks rather than in one go to avoid cases where a + // maliciously crafted tar file tries to trick us into reading many GBs + // into memory. + const paddingChunkSize = 1024 * 1024 + var paddingChunk [paddingChunkSize]byte + for { + var isEOF bool + n, err := outputRdr.Read(paddingChunk[:]) + if err != nil { + if err != io.EOF { + pW.CloseWithError(err) + return + } + isEOF = true + } + _, err = p.AddEntry(storage.Entry{ + Type: storage.SegmentType, + Payload: paddingChunk[:n], + }) + if err != nil { + pW.CloseWithError(err) + return + } + if isEOF { + break + } } pW.Close() }() From c18684ecbe4d868f1048bb5de91b1a35bf217a99 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Wed, 8 Nov 2017 03:29:49 +1100 Subject: [PATCH 48/94] internal: testutil: add DevZero helper This helper acts like /dev/zero (outputs \x00 indefinitely) in an OS-independent fashion. This ensures we don't need to special-case around Windows in tests that want to open /dev/zero. Signed-off-by: Aleksa Sarai Upstream-commit: 2f8d3e1c33f77187c68893803018756d43daff15 Component: engine --- components/engine/internal/testutil/helpers.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/components/engine/internal/testutil/helpers.go b/components/engine/internal/testutil/helpers.go index a76056924e..287b3cb48a 100644 --- a/components/engine/internal/testutil/helpers.go +++ b/components/engine/internal/testutil/helpers.go @@ -1,6 +1,8 @@ package testutil import ( + "io" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -11,3 +13,15 @@ func ErrorContains(t require.TestingT, err error, expectedError string, msgAndAr require.Error(t, err, msgAndArgs...) assert.Contains(t, err.Error(), expectedError, msgAndArgs...) } + +// DevZero acts like /dev/zero but in an OS-independent fashion. +var DevZero io.Reader = devZero{} + +type devZero struct{} + +func (d devZero) Read(p []byte) (n int, err error) { + for i := 0; i < len(p); i++ { + p[i] = '\x00' + } + return len(p), nil +} From 22a9ee4f497087becc6a2711dcd2138ad4a695e5 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Wed, 8 Nov 2017 03:30:47 +1100 Subject: [PATCH 49/94] image: add import test for CVE-2017-14992 To ensure that we don't revert CVE-2017-14992, add a test that is quite similar to that upstream tar-split test (create an empty archive with lots of junk and make sure the daemon doesn't crash). Signed-off-by: Aleksa Sarai Upstream-commit: 0a13f827a10d3bf61744d9b3f7165c5885a39c5d Component: engine --- .../engine/integration/image/import_test.go | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 components/engine/integration/image/import_test.go diff --git a/components/engine/integration/image/import_test.go b/components/engine/integration/image/import_test.go new file mode 100644 index 0000000000..955891f288 --- /dev/null +++ b/components/engine/integration/image/import_test.go @@ -0,0 +1,36 @@ +package image + +import ( + "archive/tar" + "bytes" + "context" + "io" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/integration/util/request" + "github.com/docker/docker/internal/testutil" +) + +// Ensure we don't regress on CVE-2017-14992. +func TestImportExtremelyLargeImageWorks(t *testing.T) { + client := request.NewAPIClient(t) + + // Construct an empty tar archive with about 8GB of junk padding at the + // end. This should not cause any crashes (the padding should be mostly + // ignored). + var tarBuffer bytes.Buffer + tw := tar.NewWriter(&tarBuffer) + if err := tw.Close(); err != nil { + t.Fatal(err) + } + imageRdr := io.MultiReader(&tarBuffer, io.LimitReader(testutil.DevZero, 8*1024*1024*1024)) + + _, err := client.ImageImport(context.Background(), + types.ImageImportSource{Source: imageRdr, SourceName: "-"}, + "test1234:v42", + types.ImageImportOptions{}) + if err != nil { + t.Fatal(err) + } +} From 95cc30e089cff3194127625925d6211cf4aead73 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Wed, 8 Nov 2017 10:02:58 +1100 Subject: [PATCH 50/94] devmapper: add a test for mount leak workaround In order to avoid reverting our fix for mount leakage in devicemapper, add a test which checks that devicemapper's Get() and Put() cycle can survive having a command running in an rprivate mount propagation setup in-between. While this is quite rudimentary, it should be sufficient. We have to skip this test for pre-3.18 kernels. Signed-off-by: Aleksa Sarai Upstream-commit: 1af8ea681fba1935c60c11edbbe19b894c9b286f Component: engine --- .../graphdriver/devmapper/devmapper_test.go | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/components/engine/daemon/graphdriver/devmapper/devmapper_test.go b/components/engine/daemon/graphdriver/devmapper/devmapper_test.go index 7501397fdc..24e535869e 100644 --- a/components/engine/daemon/graphdriver/devmapper/devmapper_test.go +++ b/components/engine/daemon/graphdriver/devmapper/devmapper_test.go @@ -5,12 +5,15 @@ package devmapper import ( "fmt" "os" + "os/exec" "syscall" "testing" "time" "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/daemon/graphdriver/graphtest" + "github.com/docker/docker/pkg/parsers/kernel" + "golang.org/x/sys/unix" ) func init() { @@ -150,3 +153,53 @@ func TestDevmapperLockReleasedDeviceDeletion(t *testing.T) { case <-doneChan: } } + +// Ensure that mounts aren't leakedriver. It's non-trivial for us to test the full +// reproducer of #34573 in a unit test, but we can at least make sure that a +// simple command run in a new namespace doesn't break things horribly. +func TestDevmapperMountLeaks(t *testing.T) { + if !kernel.CheckKernelVersion(3, 18, 0) { + t.Skipf("kernel version <3.18.0 and so is missing torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe.") + } + + driver := graphtest.GetDriver(t, "devicemapper", "dm.use_deferred_removal=false", "dm.use_deferred_deletion=false").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver) + defer graphtest.PutDriver(t) + + // We need to create a new (dummy) device. + if err := driver.Create("some-layer", "", nil); err != nil { + t.Fatalf("setting up some-layer: %v", err) + } + + // Mount the device. + _, err := driver.Get("some-layer", "") + if err != nil { + t.Fatalf("mounting some-layer: %v", err) + } + + // Create a new subprocess which will inherit our mountpoint, then + // intentionally leak it and stick around. We can't do this entirely within + // Go because forking and namespaces in Go are really not handled well at + // all. + cmd := exec.Cmd{ + Path: "/bin/sh", + Args: []string{ + "/bin/sh", "-c", + "mount --make-rprivate / && sleep 1000s", + }, + SysProcAttr: &syscall.SysProcAttr{ + Unshareflags: syscall.CLONE_NEWNS, + }, + } + if err := cmd.Start(); err != nil { + t.Fatalf("starting sub-command: %v", err) + } + defer func() { + unix.Kill(cmd.Process.Pid, unix.SIGKILL) + cmd.Wait() + }() + + // Now try to "drop" the device. + if err := driver.Put("some-layer"); err != nil { + t.Fatalf("unmounting some-layer: %v", err) + } +} From e52bfcca399663fc1db3506defb40940f6f1098c Mon Sep 17 00:00:00 2001 From: "Jeeva S. Chelladhurai" Date: Tue, 7 Nov 2017 23:29:13 +0530 Subject: [PATCH 51/94] fixed special character `scope`= local or swarm had special character, which was breaking the Swagger UI Signed-off-by: Jeeva S. Chelladhurai Upstream-commit: a21654c34b741656582b09e09127821311909dcd Component: engine --- components/engine/api/swagger.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/api/swagger.yaml b/components/engine/api/swagger.yaml index 18b26bb1ac..3b532f7c19 100644 --- a/components/engine/api/swagger.yaml +++ b/components/engine/api/swagger.yaml @@ -6995,7 +6995,7 @@ paths: - `network=` network name or ID - `node=` node ID - `plugin`= plugin name or ID - - `scope`= local or swarm + - `scope`= local or swarm - `secret=` secret name or ID - `service=` service name or ID - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` From e0e98f749d5c382c1dac0329f300d2cc9efa6b53 Mon Sep 17 00:00:00 2001 From: John Howard Date: Wed, 8 Nov 2017 12:10:42 -0800 Subject: [PATCH 52/94] Don't special case /sys/firmware in masked paths Signed-off-by: John Howard Upstream-commit: b023a46a074c14d34e09d774c9e847343541a220 Component: engine --- components/engine/oci/defaults.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/components/engine/oci/defaults.go b/components/engine/oci/defaults.go index 41880710bd..28270de9fe 100644 --- a/components/engine/oci/defaults.go +++ b/components/engine/oci/defaults.go @@ -120,6 +120,7 @@ func DefaultLinuxSpec() specs.Spec { "/proc/timer_stats", "/proc/sched_debug", "/proc/scsi", + "/sys/firmware", }, ReadonlyPaths: []string{ "/proc/asound", @@ -205,10 +206,5 @@ func DefaultLinuxSpec() specs.Spec { s.Windows = &specs.Windows{} } - // For LCOW support, don't mask /sys/firmware - if runtime.GOOS != "windows" { - s.Linux.MaskedPaths = append(s.Linux.MaskedPaths, "/sys/firmware") - } - return s } From ca892e4f1a95f16c88bc6a7246970405fc04a3d7 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Tue, 7 Nov 2017 18:27:49 -0500 Subject: [PATCH 53/94] Fix dockerfile parser failing silently on long tokens Signed-off-by: Daniel Nephin Upstream-commit: 59ad3a36e2684bd36a4b02179949bd17f1406918 Component: engine --- .../engine/builder/dockerfile/parser/parser.go | 11 ++++++++++- .../engine/builder/dockerfile/parser/parser_test.go | 13 +++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/components/engine/builder/dockerfile/parser/parser.go b/components/engine/builder/dockerfile/parser/parser.go index 822c42b41a..a4bce1cca6 100644 --- a/components/engine/builder/dockerfile/parser/parser.go +++ b/components/engine/builder/dockerfile/parser/parser.go @@ -321,7 +321,7 @@ func Parse(rwc io.Reader) (*Result, error) { Warnings: warnings, EscapeToken: d.escapeToken, OS: d.platformToken, - }, nil + }, handleScannerError(scanner.Err()) } func trimComments(src []byte) []byte { @@ -358,3 +358,12 @@ func processLine(d *Directive, token []byte, stripLeftWhitespace bool) ([]byte, } return trimComments(token), d.possibleParserDirective(string(token)) } + +func handleScannerError(err error) error { + switch err { + case bufio.ErrTooLong: + return errors.Errorf("dockerfile line greater than max allowed size of %d", bufio.MaxScanTokenSize-1) + default: + return err + } +} diff --git a/components/engine/builder/dockerfile/parser/parser_test.go b/components/engine/builder/dockerfile/parser/parser_test.go index 5f354bb139..7bfbee92e4 100644 --- a/components/engine/builder/dockerfile/parser/parser_test.go +++ b/components/engine/builder/dockerfile/parser/parser_test.go @@ -1,12 +1,14 @@ package parser import ( + "bufio" "bytes" "fmt" "io/ioutil" "os" "path/filepath" "runtime" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -159,3 +161,14 @@ RUN indented \ assert.Contains(t, warnings[1], "RUN another thing") assert.Contains(t, warnings[2], "will become errors in a future release") } + +func TestParseReturnsScannerErrors(t *testing.T) { + label := strings.Repeat("a", bufio.MaxScanTokenSize) + + dockerfile := strings.NewReader(fmt.Sprintf(` + FROM image + LABEL test=%s +`, label)) + _, err := Parse(dockerfile) + assert.EqualError(t, err, "dockerfile line greater than max allowed size of 65535") +} From 84fe2b7d9a236ee8b9cd9fce4f46c41ad485be13 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Wed, 8 Nov 2017 13:06:57 -0500 Subject: [PATCH 54/94] Fix remote build target as Dockerfile The test was passing previously because the preamble was already buffered. After the change to return Scanner.Err() the final read error on the buffer was no longer being ignored. Signed-off-by: Daniel Nephin Upstream-commit: a74cc833450dfc48cc95b2b109cbcb24feff4929 Component: engine --- .../engine/builder/remotecontext/archive.go | 1 - .../engine/builder/remotecontext/detect.go | 33 +++++----- .../engine/builder/remotecontext/remote.go | 65 ++++++------------- .../builder/remotecontext/remote_test.go | 49 ++++---------- 4 files changed, 46 insertions(+), 102 deletions(-) diff --git a/components/engine/builder/remotecontext/archive.go b/components/engine/builder/remotecontext/archive.go index b62d9dd0b7..c670235b61 100644 --- a/components/engine/builder/remotecontext/archive.go +++ b/components/engine/builder/remotecontext/archive.go @@ -79,7 +79,6 @@ func FromArchive(tarStream io.Reader) (builder.Source, error) { } tsc.sums = sum.GetSums() - return tsc, nil } diff --git a/components/engine/builder/remotecontext/detect.go b/components/engine/builder/remotecontext/detect.go index 38aff67985..fe5cd967fb 100644 --- a/components/engine/builder/remotecontext/detect.go +++ b/components/engine/builder/remotecontext/detect.go @@ -97,26 +97,23 @@ func newGitRemote(gitURL string, dockerfilePath string) (builder.Source, *parser } func newURLRemote(url string, dockerfilePath string, progressReader func(in io.ReadCloser) io.ReadCloser) (builder.Source, *parser.Result, error) { - var dockerfile io.ReadCloser - dockerfileFoundErr := errors.New("found-dockerfile") - c, err := MakeRemoteContext(url, map[string]func(io.ReadCloser) (io.ReadCloser, error){ - mimeTypes.TextPlain: func(rc io.ReadCloser) (io.ReadCloser, error) { - dockerfile = rc - return nil, dockerfileFoundErr - }, - // fallback handler (tar context) - "": func(rc io.ReadCloser) (io.ReadCloser, error) { - return progressReader(rc), nil - }, - }) - switch { - case err == dockerfileFoundErr: - res, err := parser.Parse(dockerfile) - return nil, res, err - case err != nil: + contentType, content, err := downloadRemote(url) + if err != nil { return nil, nil, err } - return withDockerfileFromContext(c.(modifiableContext), dockerfilePath) + defer content.Close() + + switch contentType { + case mimeTypes.TextPlain: + res, err := parser.Parse(progressReader(content)) + return nil, res, err + default: + source, err := FromArchive(progressReader(content)) + if err != nil { + return nil, nil, err + } + return withDockerfileFromContext(source.(modifiableContext), dockerfilePath) + } } func removeDockerfile(c modifiableContext, filesToRemove ...string) error { diff --git a/components/engine/builder/remotecontext/remote.go b/components/engine/builder/remotecontext/remote.go index 6c4073bd4c..ee0282f706 100644 --- a/components/engine/builder/remotecontext/remote.go +++ b/components/engine/builder/remotecontext/remote.go @@ -10,7 +10,7 @@ import ( "net/url" "regexp" - "github.com/docker/docker/builder" + "github.com/docker/docker/pkg/ioutils" "github.com/pkg/errors" ) @@ -22,50 +22,23 @@ const acceptableRemoteMIME = `(?:application/(?:(?:x\-)?tar|octet\-stream|((?:x\ var mimeRe = regexp.MustCompile(acceptableRemoteMIME) -// MakeRemoteContext downloads a context from remoteURL and returns it. -// -// If contentTypeHandlers is non-nil, then the Content-Type header is read along with a maximum of -// maxPreambleLength bytes from the body to help detecting the MIME type. -// Look at acceptableRemoteMIME for more details. -// -// If a match is found, then the body is sent to the contentType handler and a (potentially compressed) tar stream is expected -// to be returned. If no match is found, it is assumed the body is a tar stream (compressed or not). -// In either case, an (assumed) tar stream is passed to FromArchive whose result is returned. -func MakeRemoteContext(remoteURL string, contentTypeHandlers map[string]func(io.ReadCloser) (io.ReadCloser, error)) (builder.Source, error) { - f, err := GetWithStatusError(remoteURL) +// downloadRemote context from a url and returns it, along with the parsed content type +func downloadRemote(remoteURL string) (string, io.ReadCloser, error) { + response, err := GetWithStatusError(remoteURL) if err != nil { - return nil, fmt.Errorf("error downloading remote context %s: %v", remoteURL, err) - } - defer f.Body.Close() - - var contextReader io.ReadCloser - if contentTypeHandlers != nil { - contentType := f.Header.Get("Content-Type") - clen := f.ContentLength - - contentType, contextReader, err = inspectResponse(contentType, f.Body, clen) - if err != nil { - return nil, fmt.Errorf("error detecting content type for remote %s: %v", remoteURL, err) - } - defer contextReader.Close() - - // This loop tries to find a content-type handler for the detected content-type. - // If it could not find one from the caller-supplied map, it tries the empty content-type `""` - // which is interpreted as a fallback handler (usually used for raw tar contexts). - for _, ct := range []string{contentType, ""} { - if fn, ok := contentTypeHandlers[ct]; ok { - defer contextReader.Close() - if contextReader, err = fn(contextReader); err != nil { - return nil, err - } - break - } - } + return "", nil, fmt.Errorf("error downloading remote context %s: %v", remoteURL, err) } - // Pass through - this is a pre-packaged context, presumably - // with a Dockerfile with the right name inside it. - return FromArchive(contextReader) + contentType, contextReader, err := inspectResponse( + response.Header.Get("Content-Type"), + response.Body, + response.ContentLength) + if err != nil { + response.Body.Close() + return "", nil, fmt.Errorf("error detecting content type for remote %s: %v", remoteURL, err) + } + + return contentType, ioutils.NewReadCloserWrapper(contextReader, response.Body.Close), nil } // GetWithStatusError does an http.Get() and returns an error if the @@ -110,7 +83,7 @@ func GetWithStatusError(address string) (resp *http.Response, err error) { // - an io.Reader for the response body // - an error value which will be non-nil either when something goes wrong while // reading bytes from r or when the detected content-type is not acceptable. -func inspectResponse(ct string, r io.Reader, clen int64) (string, io.ReadCloser, error) { +func inspectResponse(ct string, r io.Reader, clen int64) (string, io.Reader, error) { plen := clen if plen <= 0 || plen > maxPreambleLength { plen = maxPreambleLength @@ -119,14 +92,14 @@ func inspectResponse(ct string, r io.Reader, clen int64) (string, io.ReadCloser, preamble := make([]byte, plen) rlen, err := r.Read(preamble) if rlen == 0 { - return ct, ioutil.NopCloser(r), errors.New("empty response") + return ct, r, errors.New("empty response") } if err != nil && err != io.EOF { - return ct, ioutil.NopCloser(r), err + return ct, r, err } preambleR := bytes.NewReader(preamble[:rlen]) - bodyReader := ioutil.NopCloser(io.MultiReader(preambleR, r)) + bodyReader := io.MultiReader(preambleR, r) // Some web servers will use application/octet-stream as the default // content type for files without an extension (e.g. 'Dockerfile') // so if we receive this value we better check for text content diff --git a/components/engine/builder/remotecontext/remote_test.go b/components/engine/builder/remotecontext/remote_test.go index c38433b340..35b105f550 100644 --- a/components/engine/builder/remotecontext/remote_test.go +++ b/components/engine/builder/remotecontext/remote_test.go @@ -11,7 +11,7 @@ import ( "github.com/docker/docker/builder" "github.com/docker/docker/internal/testutil" - "github.com/docker/docker/pkg/archive" + "github.com/gotestyourself/gotestyourself/fs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -174,11 +174,10 @@ func TestUnknownContentLength(t *testing.T) { } } -func TestMakeRemoteContext(t *testing.T) { - contextDir, cleanup := createTestTempDir(t, "", "builder-tarsum-test") - defer cleanup() - - createTestTempFile(t, contextDir, builder.DefaultDockerfileName, dockerfileContents, 0777) +func TestDownloadRemote(t *testing.T) { + contextDir := fs.NewDir(t, "test-builder-download-remote", + fs.WithFile(builder.DefaultDockerfileName, dockerfileContents)) + defer contextDir.Remove() mux := http.NewServeMux() server := httptest.NewServer(mux) @@ -187,39 +186,15 @@ func TestMakeRemoteContext(t *testing.T) { serverURL.Path = "/" + builder.DefaultDockerfileName remoteURL := serverURL.String() - mux.Handle("/", http.FileServer(http.Dir(contextDir))) + mux.Handle("/", http.FileServer(http.Dir(contextDir.Path()))) - remoteContext, err := MakeRemoteContext(remoteURL, map[string]func(io.ReadCloser) (io.ReadCloser, error){ - mimeTypes.TextPlain: func(rc io.ReadCloser) (io.ReadCloser, error) { - dockerfile, err := ioutil.ReadAll(rc) - if err != nil { - return nil, err - } + contentType, content, err := downloadRemote(remoteURL) + require.NoError(t, err) - r, err := archive.Generate(builder.DefaultDockerfileName, string(dockerfile)) - if err != nil { - return nil, err - } - return ioutil.NopCloser(r), nil - }, - }) - - if err != nil { - t.Fatalf("Error when executing DetectContextFromRemoteURL: %s", err) - } - - if remoteContext == nil { - t.Fatal("Remote context should not be nil") - } - - h, err := remoteContext.Hash(builder.DefaultDockerfileName) - if err != nil { - t.Fatalf("failed to compute hash %s", err) - } - - if expected, actual := "7b6b6b66bee9e2102fbdc2228be6c980a2a23adf371962a37286a49f7de0f7cc", h; expected != actual { - t.Fatalf("There should be file named %s %s in fileInfoSums", expected, actual) - } + assert.Equal(t, mimeTypes.TextPlain, contentType) + raw, err := ioutil.ReadAll(content) + require.NoError(t, err) + assert.Equal(t, dockerfileContents, string(raw)) } func TestGetWithStatusError(t *testing.T) { From 7918ee610fc9f4a6836fb42bdd1bd7a4740befb2 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Tue, 19 Sep 2017 14:34:41 -0400 Subject: [PATCH 55/94] Create labels when volume exists only remotely Before this, if a volume exists in a driver but not in the local cache, the store would just return a bare volume. This means that if a user supplied options or labels, they will not get stored. Instead only return early if we have the volume stored locally. Note this could still have an issue with labels/opts passed in by the user differing from what is stored, however this isn't really a new problem. This fixes a problem where if there is a shared storage backend between two docker nodes, a create on one node will have labels stored and a create on the other node will not. Signed-off-by: Brian Goff Upstream-commit: 4d8598ad0506b29c12632c1b8ed92eb58fc2f0e2 Component: engine --- components/engine/volume/store/store.go | 26 ++++++++------- components/engine/volume/store/store_test.go | 34 ++++++++++++++++++++ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/components/engine/volume/store/store.go b/components/engine/volume/store/store.go index 5402c6bd9f..fd1ca616ca 100644 --- a/components/engine/volume/store/store.go +++ b/components/engine/volume/store/store.go @@ -385,35 +385,37 @@ func (s *VolumeStore) create(name, driverName string, opts, labels map[string]st } if v != nil { - return v, nil + // there is an existing volume, if we already have this stored locally, return it. + // TODO: there could be some inconsistent details such as labels here + if vv, _ := s.getNamed(v.Name()); vv != nil { + return vv, nil + } } // Since there isn't a specified driver name, let's see if any of the existing drivers have this volume name if driverName == "" { - v, _ := s.getVolume(name) + v, _ = s.getVolume(name) if v != nil { return v, nil } } vd, err := volumedrivers.CreateDriver(driverName) - if err != nil { return nil, &OpErr{Op: "create", Name: name, Err: err} } logrus.Debugf("Registering new volume reference: driver %q, name %q", vd.Name(), name) - - if v, _ := vd.Get(name); v != nil { - return v, nil - } - v, err = vd.Create(name, opts) - if err != nil { - if _, err := volumedrivers.ReleaseDriver(driverName); err != nil { - logrus.WithError(err).WithField("driver", driverName).Error("Error releasing reference to volume driver") + if v, _ = vd.Get(name); v == nil { + v, err = vd.Create(name, opts) + if err != nil { + if _, err := volumedrivers.ReleaseDriver(driverName); err != nil { + logrus.WithError(err).WithField("driver", driverName).Error("Error releasing reference to volume driver") + } + return nil, err } - return nil, err } + s.globalLock.Lock() s.labels[name] = labels s.options[name] = opts diff --git a/components/engine/volume/store/store_test.go b/components/engine/volume/store/store_test.go index f5f00255a1..eb78c85cbe 100644 --- a/components/engine/volume/store/store_test.go +++ b/components/engine/volume/store/store_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/docker/docker/volume" "github.com/docker/docker/volume/drivers" volumetestutils "github.com/docker/docker/volume/testutils" ) @@ -232,3 +233,36 @@ func TestDerefMultipleOfSameRef(t *testing.T) { t.Fatal(err) } } + +func TestCreateKeepOptsLabelsWhenExistsRemotely(t *testing.T) { + 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) + } + + // Create a volume in the driver directly + if _, err := vd.Create("foo", nil); err != nil { + t.Fatal(err) + } + + v, err := s.Create("foo", "fake", nil, map[string]string{"hello": "world"}) + if err != nil { + t.Fatal(err) + } + + switch dv := v.(type) { + case volume.DetailedVolume: + if dv.Labels()["hello"] != "world" { + t.Fatalf("labels don't match") + } + default: + t.Fatalf("got unexpected type: %T", v) + } +} From 2f7df6c0dd9b3636688372c08113730195bf05a2 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Wed, 8 Nov 2017 16:51:06 -0500 Subject: [PATCH 56/94] Update fsnotify to fix deadlock in removing watch Signed-off-by: Brian Goff Upstream-commit: e8aa22645baf8f8254cc435e772314d4a5dee92c Component: engine --- components/engine/vendor.conf | 2 +- .../github.com/fsnotify/fsnotify/README.md | 37 +++++++++-- .../github.com/fsnotify/fsnotify/fsnotify.go | 4 ++ .../github.com/fsnotify/fsnotify/inotify.go | 66 +++++++++++-------- 4 files changed, 77 insertions(+), 32 deletions(-) diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index dc94fb4cf5..44149a18cd 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -85,7 +85,7 @@ github.com/philhofer/fwd 98c11a7a6ec829d672b03833c3d69a7fae1ca972 github.com/tinylib/msgp 75ee40d2601edf122ef667e2a07d600d4c44490c # fsnotify -github.com/fsnotify/fsnotify v1.4.2 +github.com/fsnotify/fsnotify 4da3e2cfbabc9f751898f250b49f2439785783a1 # awslogs deps github.com/aws/aws-sdk-go v1.4.22 diff --git a/components/engine/vendor/github.com/fsnotify/fsnotify/README.md b/components/engine/vendor/github.com/fsnotify/fsnotify/README.md index 3c891e349b..3993207413 100644 --- a/components/engine/vendor/github.com/fsnotify/fsnotify/README.md +++ b/components/engine/vendor/github.com/fsnotify/fsnotify/README.md @@ -8,14 +8,14 @@ fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather go get -u golang.org/x/sys/... ``` -Cross platform: Windows, Linux, BSD and OS X. +Cross platform: Windows, Linux, BSD and macOS. |Adapter |OS |Status | |----------|----------|----------| |inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| -|kqueue |BSD, OS X, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| +|kqueue |BSD, macOS, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| |ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| -|FSEvents |OS X |[Planned](https://github.com/fsnotify/fsnotify/issues/11)| +|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)| |FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)| |fanotify |Linux 2.6.37+ | | |USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)| @@ -23,7 +23,7 @@ Cross platform: Windows, Linux, BSD and OS X. \* Android and iOS are untested. -Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) for usage. Consult the [Wiki](https://github.com/fsnotify/fsnotify/wiki) for the FAQ and further information. +Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information. ## API stability @@ -41,6 +41,35 @@ Please refer to [CONTRIBUTING][] before opening an issue or pull request. See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go). +## FAQ + +**When a file is moved to another directory is it still being watched?** + +No (it shouldn't be, unless you are watching where it was moved to). + +**When I watch a directory, are all subdirectories watched as well?** + +No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]). + +**Do I have to watch the Error and Event channels in a separate goroutine?** + +As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7]) + +**Why am I receiving multiple events for the same file on OS X?** + +Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]). + +**How many files can be watched at once?** + +There are OS-specific limits as to how many watches can be created: +* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error. +* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. + +[#62]: https://github.com/howeyc/fsnotify/issues/62 +[#18]: https://github.com/fsnotify/fsnotify/issues/18 +[#11]: https://github.com/fsnotify/fsnotify/issues/11 +[#7]: https://github.com/howeyc/fsnotify/issues/7 + [contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md ## Related Projects diff --git a/components/engine/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/components/engine/vendor/github.com/fsnotify/fsnotify/fsnotify.go index e7f55fee7a..190bf0de57 100644 --- a/components/engine/vendor/github.com/fsnotify/fsnotify/fsnotify.go +++ b/components/engine/vendor/github.com/fsnotify/fsnotify/fsnotify.go @@ -9,6 +9,7 @@ package fsnotify import ( "bytes" + "errors" "fmt" ) @@ -60,3 +61,6 @@ func (op Op) String() string { func (e Event) String() string { return fmt.Sprintf("%q: %s", e.Name, e.Op.String()) } + +// Common errors that can be reported by a watcher +var ErrEventOverflow = errors.New("fsnotify queue overflow") diff --git a/components/engine/vendor/github.com/fsnotify/fsnotify/inotify.go b/components/engine/vendor/github.com/fsnotify/fsnotify/inotify.go index f3b74c51f0..d9fd1b88a0 100644 --- a/components/engine/vendor/github.com/fsnotify/fsnotify/inotify.go +++ b/components/engine/vendor/github.com/fsnotify/fsnotify/inotify.go @@ -24,7 +24,6 @@ type Watcher struct { Events chan Event Errors chan error mu sync.Mutex // Map access - cv *sync.Cond // sync removing on rm_watch with IN_IGNORE fd int poller *fdPoller watches map[string]*watch // Map of inotify watches (key: path) @@ -56,7 +55,6 @@ func NewWatcher() (*Watcher, error) { done: make(chan struct{}), doneResp: make(chan struct{}), } - w.cv = sync.NewCond(&w.mu) go w.readEvents() return w, nil @@ -103,21 +101,23 @@ func (w *Watcher) Add(name string) error { var flags uint32 = agnosticEvents w.mu.Lock() - watchEntry, found := w.watches[name] - w.mu.Unlock() - if found { - watchEntry.flags |= flags - flags |= unix.IN_MASK_ADD + defer w.mu.Unlock() + watchEntry := w.watches[name] + if watchEntry != nil { + flags |= watchEntry.flags | unix.IN_MASK_ADD } wd, errno := unix.InotifyAddWatch(w.fd, name, flags) if wd == -1 { return errno } - w.mu.Lock() - w.watches[name] = &watch{wd: uint32(wd), flags: flags} - w.paths[wd] = name - w.mu.Unlock() + if watchEntry == nil { + w.watches[name] = &watch{wd: uint32(wd), flags: flags} + w.paths[wd] = name + } else { + watchEntry.wd = uint32(wd) + watchEntry.flags = flags + } return nil } @@ -135,6 +135,13 @@ func (w *Watcher) Remove(name string) error { if !ok { return fmt.Errorf("can't remove non-existent inotify watch for: %s", name) } + + // We successfully removed the watch if InotifyRmWatch doesn't return an + // error, we need to clean up our internal state to ensure it matches + // inotify's kernel state. + delete(w.paths, int(watch.wd)) + delete(w.watches, name) + // inotify_rm_watch will return EINVAL if the file has been deleted; // the inotify will already have been removed. // watches and pathes are deleted in ignoreLinux() implicitly and asynchronously @@ -152,13 +159,6 @@ func (w *Watcher) Remove(name string) error { return errno } - // wait until ignoreLinux() deleting maps - exists := true - for exists { - w.cv.Wait() - _, exists = w.watches[name] - } - return nil } @@ -245,13 +245,31 @@ func (w *Watcher) readEvents() { mask := uint32(raw.Mask) nameLen := uint32(raw.Len) + + if mask&unix.IN_Q_OVERFLOW != 0 { + select { + case w.Errors <- ErrEventOverflow: + case <-w.done: + return + } + } + // If the event happened to the watched directory or the watched file, the kernel // doesn't append the filename to the event, but we would like to always fill the // the "Name" field with a valid filename. We retrieve the path of the watch from // the "paths" map. w.mu.Lock() - name := w.paths[int(raw.Wd)] + name, ok := w.paths[int(raw.Wd)] + // IN_DELETE_SELF occurs when the file/directory being watched is removed. + // This is a sign to clean up the maps, otherwise we are no longer in sync + // with the inotify kernel state which has already deleted the watch + // automatically. + if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF { + delete(w.paths, int(raw.Wd)) + delete(w.watches, name) + } w.mu.Unlock() + if nameLen > 0 { // Point "bytes" at the first byte of the filename bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent])) @@ -262,7 +280,7 @@ func (w *Watcher) readEvents() { event := newEvent(name, mask) // Send the events that are not ignored on the events channel - if !event.ignoreLinux(w, raw.Wd, mask) { + if !event.ignoreLinux(mask) { select { case w.Events <- event: case <-w.done: @@ -279,15 +297,9 @@ func (w *Watcher) readEvents() { // Certain types of events can be "ignored" and not sent over the Events // channel. Such as events marked ignore by the kernel, or MODIFY events // against files that do not exist. -func (e *Event) ignoreLinux(w *Watcher, wd int32, mask uint32) bool { +func (e *Event) ignoreLinux(mask uint32) bool { // Ignore anything the inotify API says to ignore if mask&unix.IN_IGNORED == unix.IN_IGNORED { - w.mu.Lock() - defer w.mu.Unlock() - name := w.paths[int(wd)] - delete(w.paths, int(wd)) - delete(w.watches, name) - w.cv.Broadcast() return true } From 366ab9966ca70f381df2809686c81af07e8a6de5 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Thu, 9 Nov 2017 15:45:11 -0500 Subject: [PATCH 57/94] COPY should be the last op in the Dockerfile Signed-off-by: Brian Goff Upstream-commit: 503b03a3f090550052af960ece2e71d54c1fdf0e Component: engine --- components/engine/Dockerfile | 5 +++-- components/engine/Dockerfile.aarch64 | 6 +++--- components/engine/Dockerfile.armhf | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/components/engine/Dockerfile b/components/engine/Dockerfile index 25da31313e..146f3d901b 100644 --- a/components/engine/Dockerfile +++ b/components/engine/Dockerfile @@ -189,8 +189,9 @@ RUN ln -s /usr/local/completion/bash/docker /etc/bash_completion.d/docker # Wrap all commands in the "docker-in-docker" script to allow nested containers ENTRYPOINT ["hack/dind"] +# Options for hack/validate/gometalinter +ENV GOMETALINTER_OPTS="--deadline 2m" + # Upload docker source COPY . /go/src/github.com/docker/docker -# Options for hack/validate/gometalinter -ENV GOMETALINTER_OPTS="--deadline 2m" diff --git a/components/engine/Dockerfile.aarch64 b/components/engine/Dockerfile.aarch64 index 97dff4654f..e6a12fe370 100644 --- a/components/engine/Dockerfile.aarch64 +++ b/components/engine/Dockerfile.aarch64 @@ -158,8 +158,8 @@ ENV PATH=/usr/local/cli:$PATH # Wrap all commands in the "docker-in-docker" script to allow nested containers ENTRYPOINT ["hack/dind"] -# Upload docker source -COPY . /go/src/github.com/docker/docker - # Options for hack/validate/gometalinter ENV GOMETALINTER_OPTS="--deadline 4m -j2" + +# Upload docker source +COPY . /go/src/github.com/docker/docker diff --git a/components/engine/Dockerfile.armhf b/components/engine/Dockerfile.armhf index 30ddf8c040..d67bdd0658 100644 --- a/components/engine/Dockerfile.armhf +++ b/components/engine/Dockerfile.armhf @@ -146,8 +146,8 @@ ENV PATH=/usr/local/cli:$PATH ENTRYPOINT ["hack/dind"] -# Upload docker source -COPY . /go/src/github.com/docker/docker - # Options for hack/validate/gometalinter ENV GOMETALINTER_OPTS="--deadline 10m -j2" + +# Upload docker source +COPY . /go/src/github.com/docker/docker From 98da41425761bf3cf87074229b9495b9162ef41d Mon Sep 17 00:00:00 2001 From: Chao Wang Date: Fri, 10 Nov 2017 13:18:48 +0800 Subject: [PATCH 58/94] Copy Inslice() to those parts that use it Signed-off-by: Chao Wang Upstream-commit: 5c154cfac89305f7ca9446854e56700e8a660f93 Component: engine --- components/engine/daemon/caps/utils_unix.go | 24 +++++++++++++------ components/engine/daemon/oci_linux.go | 14 +++++++++-- .../docker_api_inspect_test.go | 6 ++--- .../docker_cli_by_digest_test.go | 4 ++-- .../engine/pkg/stringutils/stringutils.go | 11 --------- .../pkg/stringutils/stringutils_test.go | 17 ------------- components/engine/profiles/seccomp/seccomp.go | 20 ++++++++++++---- 7 files changed, 49 insertions(+), 47 deletions(-) diff --git a/components/engine/daemon/caps/utils_unix.go b/components/engine/daemon/caps/utils_unix.go index c99485f51d..28a8df6531 100644 --- a/components/engine/daemon/caps/utils_unix.go +++ b/components/engine/daemon/caps/utils_unix.go @@ -6,7 +6,6 @@ import ( "fmt" "strings" - "github.com/docker/docker/pkg/stringutils" "github.com/syndtr/gocapability/capability" ) @@ -69,6 +68,17 @@ func GetAllCapabilities() []string { return output } +// inSlice tests whether a string is contained in a slice of strings or not. +// Comparison is case insensitive +func inSlice(slice []string, s string) bool { + for _, ss := range slice { + if strings.ToLower(s) == strings.ToLower(ss) { + return true + } + } + return false +} + // TweakCapabilities can tweak capabilities by adding or dropping capabilities // based on the basics capabilities. func TweakCapabilities(basics, adds, drops []string) ([]string, error) { @@ -86,17 +96,17 @@ func TweakCapabilities(basics, adds, drops []string) ([]string, error) { continue } - if !stringutils.InSlice(allCaps, "CAP_"+cap) { + if !inSlice(allCaps, "CAP_"+cap) { return nil, fmt.Errorf("Unknown capability drop: %q", cap) } } // handle --cap-add=all - if stringutils.InSlice(adds, "all") { + if inSlice(adds, "all") { basics = allCaps } - if !stringutils.InSlice(drops, "all") { + if !inSlice(drops, "all") { for _, cap := range basics { // skip `all` already handled above if strings.ToLower(cap) == "all" { @@ -104,7 +114,7 @@ func TweakCapabilities(basics, adds, drops []string) ([]string, error) { } // if we don't drop `all`, add back all the non-dropped caps - if !stringutils.InSlice(drops, cap[4:]) { + if !inSlice(drops, cap[4:]) { newCaps = append(newCaps, strings.ToUpper(cap)) } } @@ -118,12 +128,12 @@ func TweakCapabilities(basics, adds, drops []string) ([]string, error) { cap = "CAP_" + cap - if !stringutils.InSlice(allCaps, cap) { + if !inSlice(allCaps, cap) { return nil, fmt.Errorf("Unknown capability to add: %q", cap) } // add cap if not already in the list - if !stringutils.InSlice(newCaps, cap) { + if !inSlice(newCaps, cap) { newCaps = append(newCaps, strings.ToUpper(cap)) } } diff --git a/components/engine/daemon/oci_linux.go b/components/engine/daemon/oci_linux.go index 2c5d94d99b..8d950db85c 100644 --- a/components/engine/daemon/oci_linux.go +++ b/components/engine/daemon/oci_linux.go @@ -18,7 +18,6 @@ import ( "github.com/docker/docker/oci" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/mount" - "github.com/docker/docker/pkg/stringutils" "github.com/docker/docker/volume" "github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runc/libcontainer/cgroups" @@ -522,6 +521,17 @@ var ( } ) +// inSlice tests whether a string is contained in a slice of strings or not. +// Comparison is case sensitive +func inSlice(slice []string, s string) bool { + for _, ss := range slice { + if s == ss { + return true + } + } + return false +} + func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []container.Mount) error { userMounts := make(map[string]struct{}) for _, m := range mounts { @@ -632,7 +642,7 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c continue } if _, ok := userMounts[m.Destination]; !ok { - if !stringutils.InSlice(m.Options, "ro") { + if !inSlice(m.Options, "ro") { s.Mounts[i].Options = append(s.Mounts[i].Options, "ro") } } diff --git a/components/engine/integration-cli/docker_api_inspect_test.go b/components/engine/integration-cli/docker_api_inspect_test.go index 6386354945..38f3f4dd83 100644 --- a/components/engine/integration-cli/docker_api_inspect_test.go +++ b/components/engine/integration-cli/docker_api_inspect_test.go @@ -10,8 +10,8 @@ import ( "github.com/docker/docker/api/types/versions/v1p20" "github.com/docker/docker/client" "github.com/docker/docker/integration-cli/checker" - "github.com/docker/docker/pkg/stringutils" "github.com/go-check/check" + "github.com/stretchr/testify/assert" ) func (s *DockerSuite) TestInspectAPIContainerResponse(c *check.C) { @@ -115,8 +115,8 @@ func (s *DockerSuite) TestInspectAPIImageResponse(c *check.C) { c.Assert(err, checker.IsNil) c.Assert(imageJSON.RepoTags, checker.HasLen, 2) - c.Assert(stringutils.InSlice(imageJSON.RepoTags, "busybox:latest"), checker.Equals, true) - c.Assert(stringutils.InSlice(imageJSON.RepoTags, "busybox:mytag"), checker.Equals, true) + assert.Contains(c, imageJSON.RepoTags, "busybox:latest") + assert.Contains(c, imageJSON.RepoTags, "busybox:mytag") } // #17131, #17139, #17173 diff --git a/components/engine/integration-cli/docker_cli_by_digest_test.go b/components/engine/integration-cli/docker_cli_by_digest_test.go index 0c682719a4..3a974cfad9 100644 --- a/components/engine/integration-cli/docker_cli_by_digest_test.go +++ b/components/engine/integration-cli/docker_cli_by_digest_test.go @@ -14,9 +14,9 @@ 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/pkg/stringutils" "github.com/go-check/check" "github.com/opencontainers/go-digest" + "github.com/stretchr/testify/assert" ) var ( @@ -403,7 +403,7 @@ func (s *DockerRegistrySuite) TestInspectImageWithDigests(c *check.C) { c.Assert(err, checker.IsNil) c.Assert(imageJSON, checker.HasLen, 1) c.Assert(imageJSON[0].RepoDigests, checker.HasLen, 1) - c.Assert(stringutils.InSlice(imageJSON[0].RepoDigests, imageReference), checker.Equals, true) + assert.Contains(c, imageJSON[0].RepoDigests, imageReference) } func (s *DockerRegistrySuite) TestPsListContainersFilterAncestorImageByDigest(c *check.C) { diff --git a/components/engine/pkg/stringutils/stringutils.go b/components/engine/pkg/stringutils/stringutils.go index b294de2c23..794a225190 100644 --- a/components/engine/pkg/stringutils/stringutils.go +++ b/components/engine/pkg/stringutils/stringutils.go @@ -41,17 +41,6 @@ func Truncate(s string, maxlen int) string { return string(r[:maxlen]) } -// InSlice tests whether a string is contained in a slice of strings or not. -// Comparison is case insensitive -func InSlice(slice []string, s string) bool { - for _, ss := range slice { - if strings.ToLower(s) == strings.ToLower(ss) { - return true - } - } - return false -} - func quote(word string, buf *bytes.Buffer) { // Bail out early for "simple" strings if word != "" && !strings.ContainsAny(word, "\\'\"`${[|&;<>()~*?! \t\n") { diff --git a/components/engine/pkg/stringutils/stringutils_test.go b/components/engine/pkg/stringutils/stringutils_test.go index 15b3cf8e86..6a8cab7224 100644 --- a/components/engine/pkg/stringutils/stringutils_test.go +++ b/components/engine/pkg/stringutils/stringutils_test.go @@ -77,23 +77,6 @@ func TestTruncate(t *testing.T) { } } -func TestInSlice(t *testing.T) { - slice := []string{"t🐳st", "in", "slice"} - - test := InSlice(slice, "t🐳st") - if !test { - t.Fatalf("Expected string t🐳st to be in slice") - } - test = InSlice(slice, "SLICE") - if !test { - t.Fatalf("Expected string SLICE to be in slice") - } - test = InSlice(slice, "notinslice") - if test { - t.Fatalf("Expected string notinslice not to be in slice") - } -} - func TestShellQuoteArgumentsEmpty(t *testing.T) { actual := ShellQuoteArguments([]string{}) expected := "" diff --git a/components/engine/profiles/seccomp/seccomp.go b/components/engine/profiles/seccomp/seccomp.go index 90a3859484..07d522aad6 100644 --- a/components/engine/profiles/seccomp/seccomp.go +++ b/components/engine/profiles/seccomp/seccomp.go @@ -8,7 +8,6 @@ import ( "fmt" "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/stringutils" "github.com/opencontainers/runtime-spec/specs-go" libseccomp "github.com/seccomp/libseccomp-golang" ) @@ -39,6 +38,17 @@ var nativeToSeccomp = map[string]types.Arch{ "s390x": types.ArchS390X, } +// inSlice tests whether a string is contained in a slice of strings or not. +// Comparison is case sensitive +func inSlice(slice []string, s string) bool { + for _, ss := range slice { + if s == ss { + return true + } + } + return false +} + func setupSeccomp(config *types.Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error) { if config == nil { return nil, nil @@ -89,25 +99,25 @@ Loop: // Loop through all syscall blocks and convert them to libcontainer format after filtering them for _, call := range config.Syscalls { if len(call.Excludes.Arches) > 0 { - if stringutils.InSlice(call.Excludes.Arches, arch) { + if inSlice(call.Excludes.Arches, arch) { continue Loop } } if len(call.Excludes.Caps) > 0 { for _, c := range call.Excludes.Caps { - if stringutils.InSlice(rs.Process.Capabilities.Effective, c) { + if inSlice(rs.Process.Capabilities.Effective, c) { continue Loop } } } if len(call.Includes.Arches) > 0 { - if !stringutils.InSlice(call.Includes.Arches, arch) { + if !inSlice(call.Includes.Arches, arch) { continue Loop } } if len(call.Includes.Caps) > 0 { for _, c := range call.Includes.Caps { - if !stringutils.InSlice(rs.Process.Capabilities.Effective, c) { + if !inSlice(rs.Process.Capabilities.Effective, c) { continue Loop } } From 9379e66070fe54c52234dd23f7ef3132cb3ccc5e Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Fri, 10 Nov 2017 15:44:10 -0500 Subject: [PATCH 59/94] Update containerd to v1 beta3 Signed-off-by: Brian Goff Upstream-commit: d3f934e304d78296e606d6e3a2ca523d249e954c Component: engine --- .../engine/hack/dockerfile/binaries-commits | 2 +- .../libcontainerd/remote_daemon_options.go | 4 +- components/engine/vendor.conf | 2 +- .../containerd/containerd/LICENSE.docs | 92 +- .../containerd/containerd/README.md | 13 +- .../containerd/api/services/leases/v1/doc.go | 1 + .../api/services/leases/v1/leases.pb.go | 1573 +++++++++++++++++ .../api/services/leases/v1/leases.proto | 58 + .../containerd/containerd/client.go | 40 +- .../containerd/containerd/container_opts.go | 12 +- .../containerd/content/local/store.go | 111 +- .../containerd/content/local/store_unix.go | 9 + .../containerd/content/local/store_windows.go | 4 + .../containerd/content/local/writer.go | 13 + .../containerd/{ => dialer}/dialer.go | 2 +- .../containerd/{ => dialer}/dialer_unix.go | 14 +- .../containerd/{ => dialer}/dialer_windows.go | 3 +- .../events/{ => exchange}/exchange.go | 19 +- .../github.com/containerd/containerd/gc/gc.go | 6 +- .../github.com/containerd/containerd/image.go | 46 +- .../containerd/containerd/io_unix.go | 2 +- .../github.com/containerd/containerd/lease.go | 91 + .../containerd/containerd/leases/context.go | 24 + .../containerd/containerd/leases/grpc.go | 41 + .../containerd/containerd/linux/bundle.go | 34 +- .../containerd/containerd/linux/runtime.go | 4 +- .../linux/shim/{ => client}/client.go | 60 +- .../linux/shim/{ => client}/client_linux.go | 2 +- .../linux/shim/{ => client}/client_unix.go | 2 +- .../containerd/containerd/linux/shim/init.go | 6 +- .../containerd/linux/shim/service.go | 10 + .../containerd/containerd/linux/task.go | 2 +- .../containerd/containerd/metadata/buckets.go | 2 + .../containerd/containerd/metadata/content.go | 38 +- .../containerd/containerd/metadata/db.go | 104 +- .../containerd/containerd/metadata/gc.go | 72 +- .../containerd/containerd/metadata/leases.go | 201 +++ .../containerd/metadata/snapshot.go | 4 + .../containerd/mount/mount_solaris.go | 83 - .../containerd/containerd/mount/mount_unix.go | 4 + .../containerd/mount/mount_windows.go | 4 + .../containerd/mount/mountinfo_solaris.go | 50 - .../containerd/containerd/plugin/context.go | 26 +- .../containerd/containerd/plugin/plugin.go | 11 +- .../remotes/docker/schema1/converter.go | 34 +- .../containerd/containerd/remotes/handlers.go | 18 +- .../containerd/containerd/runtime/task.go | 14 + .../containerd/runtime/task_list.go | 11 +- .../containerd/containerd/server/config.go | 12 +- .../containerd/containerd/server/server.go | 9 +- .../containerd/server/server_linux.go | 13 - .../containerd/services/content/reader.go | 2 +- .../containerd/services/content/service.go | 33 +- .../containerd/services/content/store.go | 1 + .../containerd/services/diff/client.go | 2 +- .../containerd/services/images/client.go | 1 + .../containerd/services/images/service.go | 23 +- .../containerd/services/namespaces/client.go | 97 + .../containerd/services/namespaces/service.go | 212 +++ .../containerd/snapshot/snapshotter.go | 7 + .../containerd/containerd/spec_opts_unix.go | 23 +- .../containerd/spec_opts_windows.go | 4 + .../containerd/containerd/spec_unix.go | 1 + .../containerd/containerd/sys/oom_windows.go | 3 + .../containerd/sys/prctl_solaris.go | 19 - .../containerd/containerd/sys/stat_bsd.go | 3 + .../containerd/containerd/sys/stat_unix.go | 3 + .../github.com/containerd/containerd/task.go | 32 +- .../containerd/containerd/vendor.conf | 4 +- .../containerd/windows/hcsshimtypes/doc.go | 2 +- 70 files changed, 2925 insertions(+), 559 deletions(-) create mode 100644 components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/doc.go create mode 100644 components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.pb.go create mode 100644 components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto rename components/engine/vendor/github.com/containerd/containerd/{ => dialer}/dialer.go (97%) rename components/engine/vendor/github.com/containerd/containerd/{ => dialer}/dialer_unix.go (97%) rename components/engine/vendor/github.com/containerd/containerd/{ => dialer}/dialer_windows.go (88%) rename components/engine/vendor/github.com/containerd/containerd/events/{ => exchange}/exchange.go (92%) create mode 100644 components/engine/vendor/github.com/containerd/containerd/lease.go create mode 100644 components/engine/vendor/github.com/containerd/containerd/leases/context.go create mode 100644 components/engine/vendor/github.com/containerd/containerd/leases/grpc.go rename components/engine/vendor/github.com/containerd/containerd/linux/shim/{ => client}/client.go (81%) rename components/engine/vendor/github.com/containerd/containerd/linux/shim/{ => client}/client_linux.go (97%) rename components/engine/vendor/github.com/containerd/containerd/linux/shim/{ => client}/client_unix.go (94%) create mode 100644 components/engine/vendor/github.com/containerd/containerd/metadata/leases.go delete mode 100644 components/engine/vendor/github.com/containerd/containerd/mount/mount_solaris.go delete mode 100644 components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_solaris.go create mode 100644 components/engine/vendor/github.com/containerd/containerd/services/namespaces/client.go create mode 100644 components/engine/vendor/github.com/containerd/containerd/services/namespaces/service.go delete mode 100644 components/engine/vendor/github.com/containerd/containerd/sys/prctl_solaris.go diff --git a/components/engine/hack/dockerfile/binaries-commits b/components/engine/hack/dockerfile/binaries-commits index 2643f5756e..fdac17110e 100644 --- a/components/engine/hack/dockerfile/binaries-commits +++ b/components/engine/hack/dockerfile/binaries-commits @@ -4,7 +4,7 @@ TOMLV_COMMIT=9baf8a8a9f2ed20a8e54160840c492f937eeaf9a # When updating RUNC_COMMIT, also update runc in vendor.conf accordingly RUNC_COMMIT=0351df1c5a66838d0c392b4ac4cf9450de844e2d -CONTAINERD_COMMIT=992280e8e265f491f7a624ab82f3e238be086e49 +CONTAINERD_COMMIT=v1.0.0-beta.3 TINI_COMMIT=949e6facb77383876aeff8a6944dde66b3089574 LIBNETWORK_COMMIT=7b2b1feb1de4817d522cc372af149ff48d25028e VNDR_COMMIT=a6e196d8b4b0cbbdc29aebdb20c59ac6926bb384 diff --git a/components/engine/libcontainerd/remote_daemon_options.go b/components/engine/libcontainerd/remote_daemon_options.go index b167f64c8b..bc082777a7 100644 --- a/components/engine/libcontainerd/remote_daemon_options.go +++ b/components/engine/libcontainerd/remote_daemon_options.go @@ -31,8 +31,8 @@ type rpcUser struct { func (u rpcUser) Apply(r Remote) error { if remote, ok := r.(*remote); ok { - remote.GRPC.Uid = u.uid - remote.GRPC.Gid = u.gid + remote.GRPC.UID = u.uid + remote.GRPC.GID = u.gid return nil } return fmt.Errorf("WithRemoteAddr option not supported for this remote") diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index 44149a18cd..ed722129ad 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -103,7 +103,7 @@ github.com/googleapis/gax-go da06d194a00e19ce00d9011a13931c3f6f6887c7 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 # containerd -github.com/containerd/containerd 992280e8e265f491f7a624ab82f3e238be086e49 +github.com/containerd/containerd v1.0.0-beta.3 github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6 github.com/containerd/continuity 35d55c5e8dd23b32037d56cf97174aff3efdfa83 github.com/containerd/cgroups f7dd103d3e4e696aa67152f6b4ddd1779a3455a9 diff --git a/components/engine/vendor/github.com/containerd/containerd/LICENSE.docs b/components/engine/vendor/github.com/containerd/containerd/LICENSE.docs index e26cd4fc8e..2f244ac814 100644 --- a/components/engine/vendor/github.com/containerd/containerd/LICENSE.docs +++ b/components/engine/vendor/github.com/containerd/containerd/LICENSE.docs @@ -1,4 +1,4 @@ -Attribution-ShareAlike 4.0 International +Attribution 4.0 International ======================================================================= @@ -54,18 +54,16 @@ exhaustive, and do not form part of our licenses. ======================================================================= -Creative Commons Attribution-ShareAlike 4.0 International Public -License +Creative Commons Attribution 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons -Attribution-ShareAlike 4.0 International Public License ("Public -License"). To the extent this Public License may be interpreted as a -contract, You are granted the Licensed Rights in consideration of Your -acceptance of these terms and conditions, and the Licensor grants You -such rights in consideration of benefits the Licensor receives from -making the Licensed Material available under these terms and -conditions. +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. Section 1 -- Definitions. @@ -84,11 +82,7 @@ Section 1 -- Definitions. and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. - c. BY-SA Compatible License means a license listed at - creativecommons.org/compatiblelicenses, approved by Creative - Commons as essentially the equivalent of this Public License. - - d. Copyright and Similar Rights means copyright and/or similar rights + c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or @@ -96,33 +90,29 @@ Section 1 -- Definitions. specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. - e. Effective Technological Measures means those measures that, in the + d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. - f. Exceptions and Limitations means fair use, fair dealing, and/or + e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. - g. License Elements means the license attributes listed in the name - of a Creative Commons Public License. The License Elements of this - Public License are Attribution and ShareAlike. - - h. Licensed Material means the artistic or literary work, database, + f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. - i. Licensed Rights means the rights granted to You subject to the + g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. - j. Licensor means the individual(s) or entity(ies) granting rights + h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. - k. Share means to provide material to the public by any means or + i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material @@ -130,13 +120,13 @@ Section 1 -- Definitions. public may access the material from a place and at a time individually chosen by them. - l. Sui Generis Database Rights means rights other than copyright + j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. - m. You means the individual or entity exercising the Licensed Rights + k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. @@ -182,13 +172,7 @@ Section 2 -- Scope. Licensed Rights under the terms and conditions of this Public License. - b. Additional offer from the Licensor -- Adapted Material. - Every recipient of Adapted Material from You - automatically receives an offer from the Licensor to - exercise the Licensed Rights in the Adapted Material - under the conditions of the Adapter's License You apply. - - c. No downstream restrictions. You may not offer or impose + b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the @@ -270,24 +254,9 @@ following conditions. information required by Section 3(a)(1)(A) to the extent reasonably practicable. - b. ShareAlike. - - In addition to the conditions in Section 3(a), if You Share - Adapted Material You produce, the following conditions also apply. - - 1. The Adapter's License You apply must be a Creative Commons - license with the same License Elements, this version or - later, or a BY-SA Compatible License. - - 2. You must include the text of, or the URI or hyperlink to, the - Adapter's License You apply. You may satisfy this condition - in any reasonable manner based on the medium, means, and - context in which You Share Adapted Material. - - 3. You may not offer or impose any additional or different terms - or conditions on, or apply any Effective Technological - Measures to, Adapted Material that restrict exercise of the - rights granted under the Adapter's License You apply. + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. Section 4 -- Sui Generis Database Rights. @@ -302,9 +271,8 @@ apply to Your use of the Licensed Material: b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material, + Rights (but not its individual contents) is Adapted Material; and - including for purposes of Section 3(b); and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. @@ -407,11 +375,13 @@ Section 8 -- Interpretation. ======================================================================= -Creative Commons is not a party to its public licenses. -Notwithstanding, Creative Commons may elect to apply one of its public -licenses to material it publishes and in those instances will be -considered the "Licensor." Except for the limited purpose of indicating -that material is shared under a Creative Commons public license or as +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo @@ -419,7 +389,7 @@ of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the public -licenses. +the avoidance of doubt, this paragraph does not form part of the +public licenses. Creative Commons may be contacted at creativecommons.org. diff --git a/components/engine/vendor/github.com/containerd/containerd/README.md b/components/engine/vendor/github.com/containerd/containerd/README.md index 28635dbf07..3832446b2f 100644 --- a/components/engine/vendor/github.com/containerd/containerd/README.md +++ b/components/engine/vendor/github.com/containerd/containerd/README.md @@ -198,11 +198,10 @@ For sync communication we have a community slack with a #containerd channel that __If you are reporting a security issue, please reach out discreetly at security@containerd.io__. -## Copyright and license +## Licenses -Copyright ©2016-2017 Docker, Inc. All rights reserved, except as follows. Code -is released under the Apache 2.0 license. The README.md file, and files in the -"docs" folder are licensed under the Creative Commons Attribution 4.0 -International License under the terms and conditions set forth in the file -"LICENSE.docs". You may obtain a duplicate copy of the same license, titled -CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/. +The containerd codebase is released under the [Apache 2.0 license](LICENSE.code). +The README.md file, and files in the "docs" folder are licensed under the +Creative Commons Attribution 4.0 International License under the terms and +conditions set forth in the file "[LICENSE.docs](LICENSE.docs)". You may obtain a duplicate +copy of the same license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/. 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 new file mode 100644 index 0000000000..3685b64558 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/doc.go @@ -0,0 +1 @@ +package leases diff --git a/components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.pb.go b/components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.pb.go new file mode 100644 index 0000000000..db2a5f8b6f --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.pb.go @@ -0,0 +1,1573 @@ +// Code generated by protoc-gen-gogo. +// source: github.com/containerd/containerd/api/services/leases/v1/leases.proto +// DO NOT EDIT! + +/* + Package leases is a generated protocol buffer package. + + It is generated from these files: + github.com/containerd/containerd/api/services/leases/v1/leases.proto + + It has these top-level messages: + Lease + CreateRequest + CreateResponse + DeleteRequest + ListRequest + ListResponse +*/ +package leases + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" +import google_protobuf1 "github.com/golang/protobuf/ptypes/empty" +import _ "github.com/gogo/protobuf/types" + +import time "time" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +import github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + +import strings "strings" +import reflect "reflect" +import github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +// Lease is an object which retains resources while it exists. +type Lease struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + CreatedAt time.Time `protobuf:"bytes,2,opt,name=created_at,json=createdAt,stdtime" json:"created_at"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *Lease) Reset() { *m = Lease{} } +func (*Lease) ProtoMessage() {} +func (*Lease) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{0} } + +type CreateRequest struct { + // ID is used to identity the lease, when the id is not set the service + // generates a random identifier for the lease. + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *CreateRequest) Reset() { *m = CreateRequest{} } +func (*CreateRequest) ProtoMessage() {} +func (*CreateRequest) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{1} } + +type CreateResponse struct { + Lease *Lease `protobuf:"bytes,1,opt,name=lease" json:"lease,omitempty"` +} + +func (m *CreateResponse) Reset() { *m = CreateResponse{} } +func (*CreateResponse) ProtoMessage() {} +func (*CreateResponse) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{2} } + +type DeleteRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (m *DeleteRequest) Reset() { *m = DeleteRequest{} } +func (*DeleteRequest) ProtoMessage() {} +func (*DeleteRequest) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{3} } + +type ListRequest struct { + Filters []string `protobuf:"bytes,1,rep,name=filters" json:"filters,omitempty"` +} + +func (m *ListRequest) Reset() { *m = ListRequest{} } +func (*ListRequest) ProtoMessage() {} +func (*ListRequest) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{4} } + +type ListResponse struct { + Leases []*Lease `protobuf:"bytes,1,rep,name=leases" json:"leases,omitempty"` +} + +func (m *ListResponse) Reset() { *m = ListResponse{} } +func (*ListResponse) ProtoMessage() {} +func (*ListResponse) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{5} } + +func init() { + proto.RegisterType((*Lease)(nil), "containerd.services.leases.v1.Lease") + proto.RegisterType((*CreateRequest)(nil), "containerd.services.leases.v1.CreateRequest") + proto.RegisterType((*CreateResponse)(nil), "containerd.services.leases.v1.CreateResponse") + proto.RegisterType((*DeleteRequest)(nil), "containerd.services.leases.v1.DeleteRequest") + proto.RegisterType((*ListRequest)(nil), "containerd.services.leases.v1.ListRequest") + proto.RegisterType((*ListResponse)(nil), "containerd.services.leases.v1.ListResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for Leases service + +type LeasesClient interface { + // Create creates a new lease for managing changes to metadata. A lease + // can be used to protect objects from being removed. + Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) + // Delete deletes the lease and makes any unreferenced objects created + // during the lease eligible for garbage collection if not referenced + // or retained by other resources during the lease. + Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + // ListTransactions lists all active leases, returning the full list of + // leases and optionally including the referenced resources. + List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) +} + +type leasesClient struct { + cc *grpc.ClientConn +} + +func NewLeasesClient(cc *grpc.ClientConn) LeasesClient { + return &leasesClient{cc} +} + +func (c *leasesClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) { + out := new(CreateResponse) + err := grpc.Invoke(ctx, "/containerd.services.leases.v1.Leases/Create", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *leasesClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/containerd.services.leases.v1.Leases/Delete", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *leasesClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) { + out := new(ListResponse) + err := grpc.Invoke(ctx, "/containerd.services.leases.v1.Leases/List", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Leases service + +type LeasesServer interface { + // Create creates a new lease for managing changes to metadata. A lease + // can be used to protect objects from being removed. + Create(context.Context, *CreateRequest) (*CreateResponse, error) + // Delete deletes the lease and makes any unreferenced objects created + // during the lease eligible for garbage collection if not referenced + // or retained by other resources during the lease. + Delete(context.Context, *DeleteRequest) (*google_protobuf1.Empty, error) + // ListTransactions lists all active leases, returning the full list of + // leases and optionally including the referenced resources. + List(context.Context, *ListRequest) (*ListResponse, error) +} + +func RegisterLeasesServer(s *grpc.Server, srv LeasesServer) { + s.RegisterService(&_Leases_serviceDesc, srv) +} + +func _Leases_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LeasesServer).Create(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/containerd.services.leases.v1.Leases/Create", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LeasesServer).Create(ctx, req.(*CreateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Leases_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LeasesServer).Delete(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/containerd.services.leases.v1.Leases/Delete", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LeasesServer).Delete(ctx, req.(*DeleteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Leases_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LeasesServer).List(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/containerd.services.leases.v1.Leases/List", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LeasesServer).List(ctx, req.(*ListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Leases_serviceDesc = grpc.ServiceDesc{ + ServiceName: "containerd.services.leases.v1.Leases", + HandlerType: (*LeasesServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Create", + Handler: _Leases_Create_Handler, + }, + { + MethodName: "Delete", + Handler: _Leases_Delete_Handler, + }, + { + MethodName: "List", + Handler: _Leases_List_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "github.com/containerd/containerd/api/services/leases/v1/leases.proto", +} + +func (m *Lease) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Lease) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + dAtA[i] = 0x12 + i++ + i = encodeVarintLeases(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt))) + n1, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + if len(m.Labels) > 0 { + for k, _ := range m.Labels { + dAtA[i] = 0x1a + i++ + v := m.Labels[k] + mapSize := 1 + len(k) + sovLeases(uint64(len(k))) + 1 + len(v) + sovLeases(uint64(len(v))) + i = encodeVarintLeases(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintLeases(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintLeases(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + return i, nil +} + +func (m *CreateRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CreateRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.Labels) > 0 { + for k, _ := range m.Labels { + dAtA[i] = 0x1a + i++ + v := m.Labels[k] + mapSize := 1 + len(k) + sovLeases(uint64(len(k))) + 1 + len(v) + sovLeases(uint64(len(v))) + i = encodeVarintLeases(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintLeases(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintLeases(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + return i, nil +} + +func (m *CreateResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CreateResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Lease != nil { + dAtA[i] = 0xa + i++ + i = encodeVarintLeases(dAtA, i, uint64(m.Lease.Size())) + n2, err := m.Lease.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + } + return i, nil +} + +func (m *DeleteRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DeleteRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + return i, nil +} + +func (m *ListRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Filters) > 0 { + for _, s := range m.Filters { + dAtA[i] = 0xa + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + return i, nil +} + +func (m *ListResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Leases) > 0 { + for _, msg := range m.Leases { + dAtA[i] = 0xa + i++ + i = encodeVarintLeases(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func encodeFixed64Leases(dAtA []byte, offset int, v uint64) int { + dAtA[offset] = uint8(v) + dAtA[offset+1] = uint8(v >> 8) + dAtA[offset+2] = uint8(v >> 16) + dAtA[offset+3] = uint8(v >> 24) + dAtA[offset+4] = uint8(v >> 32) + dAtA[offset+5] = uint8(v >> 40) + dAtA[offset+6] = uint8(v >> 48) + dAtA[offset+7] = uint8(v >> 56) + return offset + 8 +} +func encodeFixed32Leases(dAtA []byte, offset int, v uint32) int { + dAtA[offset] = uint8(v) + dAtA[offset+1] = uint8(v >> 8) + dAtA[offset+2] = uint8(v >> 16) + dAtA[offset+3] = uint8(v >> 24) + return offset + 4 +} +func encodeVarintLeases(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Lease) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovLeases(uint64(l)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt) + n += 1 + l + sovLeases(uint64(l)) + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovLeases(uint64(len(k))) + 1 + len(v) + sovLeases(uint64(len(v))) + n += mapEntrySize + 1 + sovLeases(uint64(mapEntrySize)) + } + } + return n +} + +func (m *CreateRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovLeases(uint64(l)) + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovLeases(uint64(len(k))) + 1 + len(v) + sovLeases(uint64(len(v))) + n += mapEntrySize + 1 + sovLeases(uint64(mapEntrySize)) + } + } + return n +} + +func (m *CreateResponse) Size() (n int) { + var l int + _ = l + if m.Lease != nil { + l = m.Lease.Size() + n += 1 + l + sovLeases(uint64(l)) + } + return n +} + +func (m *DeleteRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovLeases(uint64(l)) + } + return n +} + +func (m *ListRequest) Size() (n int) { + var l int + _ = l + if len(m.Filters) > 0 { + for _, s := range m.Filters { + l = len(s) + n += 1 + l + sovLeases(uint64(l)) + } + } + return n +} + +func (m *ListResponse) Size() (n int) { + var l int + _ = l + if len(m.Leases) > 0 { + for _, e := range m.Leases { + l = e.Size() + n += 1 + l + sovLeases(uint64(l)) + } + } + return n +} + +func sovLeases(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozLeases(x uint64) (n int) { + return sovLeases(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Lease) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&Lease{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `CreatedAt:` + strings.Replace(strings.Replace(this.CreatedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, + `Labels:` + mapStringForLabels + `,`, + `}`, + }, "") + return s +} +func (this *CreateRequest) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&CreateRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Labels:` + mapStringForLabels + `,`, + `}`, + }, "") + return s +} +func (this *CreateResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateResponse{`, + `Lease:` + strings.Replace(fmt.Sprintf("%v", this.Lease), "Lease", "Lease", 1) + `,`, + `}`, + }, "") + return s +} +func (this *DeleteRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&DeleteRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `}`, + }, "") + return s +} +func (this *ListRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListRequest{`, + `Filters:` + fmt.Sprintf("%v", this.Filters) + `,`, + `}`, + }, "") + return s +} +func (this *ListResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListResponse{`, + `Leases:` + strings.Replace(fmt.Sprintf("%v", this.Leases), "Lease", "Lease", 1) + `,`, + `}`, + }, "") + return s +} +func valueToStringLeases(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Lease) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Lease: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Lease: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreatedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + var keykey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + keykey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthLeases + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey := string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + if m.Labels == nil { + m.Labels = make(map[string]string) + } + if iNdEx < postIndex { + var valuekey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + valuekey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthLeases + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue := string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + m.Labels[mapkey] = mapvalue + } else { + var mapvalue string + m.Labels[mapkey] = mapvalue + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CreateRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CreateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CreateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + var keykey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + keykey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthLeases + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey := string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + if m.Labels == nil { + m.Labels = make(map[string]string) + } + if iNdEx < postIndex { + var valuekey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + valuekey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthLeases + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue := string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + m.Labels[mapkey] = mapvalue + } else { + var mapvalue string + m.Labels[mapkey] = mapvalue + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CreateResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CreateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CreateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Lease == nil { + m.Lease = &Lease{} + } + if err := m.Lease.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DeleteRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeleteRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Filters = append(m.Filters, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Leases", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Leases = append(m.Leases, &Lease{}) + if err := m.Leases[len(m.Leases)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipLeases(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLeases + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLeases + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLeases + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthLeases + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLeases + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipLeases(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthLeases = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowLeases = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/leases/v1/leases.proto", fileDescriptorLeases) +} + +var fileDescriptorLeases = []byte{ + // 501 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0xdf, 0x8a, 0xd3, 0x40, + 0x14, 0xc6, 0x3b, 0xa9, 0x8d, 0xf6, 0xd4, 0x15, 0x19, 0x96, 0x25, 0x44, 0x4c, 0x4b, 0x10, 0xb6, + 0xf8, 0x67, 0xe2, 0xd6, 0x9b, 0x75, 0x15, 0xc1, 0x6e, 0x17, 0x14, 0x82, 0x48, 0xf0, 0x42, 0xbc, + 0x59, 0xd2, 0xf6, 0x6c, 0x0c, 0xa6, 0x9d, 0x98, 0x99, 0x16, 0x7a, 0xe7, 0x23, 0xf8, 0x08, 0x3e, + 0x84, 0x0f, 0xd1, 0x4b, 0x2f, 0xbd, 0x5a, 0xdd, 0xdc, 0xf9, 0x16, 0x92, 0x99, 0x84, 0xfd, 0x23, + 0xda, 0x2a, 0xde, 0x9d, 0xc9, 0x7c, 0xdf, 0x39, 0xbf, 0xf3, 0xc1, 0x04, 0x06, 0x51, 0x2c, 0xdf, + 0xce, 0x86, 0x6c, 0xc4, 0x27, 0xde, 0x88, 0x4f, 0x65, 0x18, 0x4f, 0x31, 0x1b, 0x9f, 0x2d, 0xc3, + 0x34, 0xf6, 0x04, 0x66, 0xf3, 0x78, 0x84, 0xc2, 0x4b, 0x30, 0x14, 0x28, 0xbc, 0xf9, 0x4e, 0x59, + 0xb1, 0x34, 0xe3, 0x92, 0xd3, 0x9b, 0xa7, 0x7a, 0x56, 0x69, 0x59, 0xa9, 0x98, 0xef, 0xd8, 0x9b, + 0x11, 0x8f, 0xb8, 0x52, 0x7a, 0x45, 0xa5, 0x4d, 0xf6, 0x8d, 0x88, 0xf3, 0x28, 0x41, 0x4f, 0x9d, + 0x86, 0xb3, 0x23, 0x0f, 0x27, 0xa9, 0x5c, 0x94, 0x97, 0xed, 0x8b, 0x97, 0x32, 0x9e, 0xa0, 0x90, + 0xe1, 0x24, 0xd5, 0x02, 0xf7, 0x07, 0x81, 0x86, 0x5f, 0x4c, 0xa0, 0x5b, 0x60, 0xc4, 0x63, 0x8b, + 0x74, 0x48, 0xb7, 0xd9, 0x37, 0xf3, 0xe3, 0xb6, 0xf1, 0x7c, 0x10, 0x18, 0xf1, 0x98, 0xee, 0x03, + 0x8c, 0x32, 0x0c, 0x25, 0x8e, 0x0f, 0x43, 0x69, 0x19, 0x1d, 0xd2, 0x6d, 0xf5, 0x6c, 0xa6, 0xfb, + 0xb2, 0xaa, 0x2f, 0x7b, 0x55, 0xf5, 0xed, 0x5f, 0x59, 0x1e, 0xb7, 0x6b, 0x1f, 0xbf, 0xb5, 0x49, + 0xd0, 0x2c, 0x7d, 0x4f, 0x25, 0x7d, 0x06, 0x66, 0x12, 0x0e, 0x31, 0x11, 0x56, 0xbd, 0x53, 0xef, + 0xb6, 0x7a, 0xf7, 0xd9, 0x1f, 0x57, 0x65, 0x0a, 0x89, 0xf9, 0xca, 0x72, 0x30, 0x95, 0xd9, 0x22, + 0x28, 0xfd, 0xf6, 0x43, 0x68, 0x9d, 0xf9, 0x4c, 0xaf, 0x43, 0xfd, 0x1d, 0x2e, 0x34, 0x76, 0x50, + 0x94, 0x74, 0x13, 0x1a, 0xf3, 0x30, 0x99, 0xa1, 0x42, 0x6d, 0x06, 0xfa, 0xb0, 0x67, 0xec, 0x12, + 0xf7, 0x33, 0x81, 0x8d, 0x7d, 0x85, 0x14, 0xe0, 0xfb, 0x19, 0x0a, 0xf9, 0xdb, 0x9d, 0x5f, 0x5e, + 0xc0, 0xdd, 0x5d, 0x81, 0x7b, 0xae, 0xeb, 0xff, 0xc6, 0xf6, 0xe1, 0x5a, 0xd5, 0x5f, 0xa4, 0x7c, + 0x2a, 0x90, 0xee, 0x41, 0x43, 0xcd, 0x56, 0xfe, 0x56, 0xef, 0xd6, 0x3a, 0x61, 0x06, 0xda, 0xe2, + 0x6e, 0xc3, 0xc6, 0x00, 0x13, 0x5c, 0x99, 0x81, 0xbb, 0x0d, 0x2d, 0x3f, 0x16, 0xb2, 0x92, 0x59, + 0x70, 0xf9, 0x28, 0x4e, 0x24, 0x66, 0xc2, 0x22, 0x9d, 0x7a, 0xb7, 0x19, 0x54, 0x47, 0xd7, 0x87, + 0xab, 0x5a, 0x58, 0xd2, 0x3d, 0x06, 0x53, 0xcf, 0x56, 0xc2, 0x75, 0xf1, 0x4a, 0x4f, 0xef, 0x93, + 0x01, 0xa6, 0xfa, 0x22, 0x28, 0x82, 0xa9, 0x17, 0xa7, 0x77, 0xff, 0x26, 0x7f, 0xfb, 0xde, 0x9a, + 0xea, 0x92, 0xf7, 0x05, 0x98, 0x3a, 0x91, 0x95, 0x63, 0xce, 0x05, 0x67, 0x6f, 0xfd, 0xf2, 0x08, + 0x0e, 0x8a, 0x97, 0x47, 0x0f, 0xe1, 0x52, 0x91, 0x07, 0xbd, 0xbd, 0x6a, 0xef, 0xd3, 0x74, 0xed, + 0x3b, 0x6b, 0x69, 0x35, 0x70, 0xff, 0xf5, 0xf2, 0xc4, 0xa9, 0x7d, 0x3d, 0x71, 0x6a, 0x1f, 0x72, + 0x87, 0x2c, 0x73, 0x87, 0x7c, 0xc9, 0x1d, 0xf2, 0x3d, 0x77, 0xc8, 0x9b, 0x27, 0xff, 0xf8, 0x1b, + 0x7a, 0xa4, 0xab, 0xa1, 0xa9, 0x56, 0x79, 0xf0, 0x33, 0x00, 0x00, 0xff, 0xff, 0x1d, 0xb9, 0xa6, + 0x63, 0xcf, 0x04, 0x00, 0x00, +} diff --git a/components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto b/components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto new file mode 100644 index 0000000000..29d58d1119 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto @@ -0,0 +1,58 @@ +syntax = "proto3"; + +package containerd.services.leases.v1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/containerd/containerd/api/services/leases/v1;leases"; + +// Leases service manages resources leases within the metadata store. +service Leases { + // Create creates a new lease for managing changes to metadata. A lease + // can be used to protect objects from being removed. + rpc Create(CreateRequest) returns (CreateResponse); + + // Delete deletes the lease and makes any unreferenced objects created + // during the lease eligible for garbage collection if not referenced + // or retained by other resources during the lease. + rpc Delete(DeleteRequest) returns (google.protobuf.Empty); + + // ListTransactions lists all active leases, returning the full list of + // leases and optionally including the referenced resources. + rpc List(ListRequest) returns (ListResponse); +} + +// Lease is an object which retains resources while it exists. +message Lease { + string id = 1; + + google.protobuf.Timestamp created_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + + map labels = 3; +} + +message CreateRequest { + // ID is used to identity the lease, when the id is not set the service + // generates a random identifier for the lease. + string id = 1; + + map labels = 3; +} + +message CreateResponse { + Lease lease = 1; +} + +message DeleteRequest { + string id = 1; +} + +message ListRequest { + repeated string filters = 1; +} + +message ListResponse { + repeated Lease leases = 1; +} diff --git a/components/engine/vendor/github.com/containerd/containerd/client.go b/components/engine/vendor/github.com/containerd/containerd/client.go index 6f0eb28a2c..663f2fab86 100644 --- a/components/engine/vendor/github.com/containerd/containerd/client.go +++ b/components/engine/vendor/github.com/containerd/containerd/client.go @@ -22,9 +22,11 @@ import ( versionservice "github.com/containerd/containerd/api/services/version/v1" "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" "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/reference" @@ -34,6 +36,7 @@ import ( contentservice "github.com/containerd/containerd/services/content" diffservice "github.com/containerd/containerd/services/diff" imagesservice "github.com/containerd/containerd/services/images" + namespacesservice "github.com/containerd/containerd/services/namespaces" snapshotservice "github.com/containerd/containerd/services/snapshot" "github.com/containerd/containerd/snapshot" "github.com/containerd/typeurl" @@ -70,7 +73,7 @@ func New(address string, opts ...ClientOpt) (*Client, error) { grpc.WithTimeout(60 * time.Second), grpc.FailOnNonTempDialError(true), grpc.WithBackoffMaxDelay(3 * time.Second), - grpc.WithDialer(Dialer), + grpc.WithDialer(dialer.Dialer), } if len(copts.dialOptions) > 0 { gopts = copts.dialOptions @@ -82,7 +85,7 @@ func New(address string, opts ...ClientOpt) (*Client, error) { grpc.WithStreamInterceptor(stream), ) } - conn, err := grpc.Dial(DialAddress(address), gopts...) + conn, err := grpc.Dial(dialer.DialAddress(address), gopts...) if err != nil { return nil, errors.Wrapf(err, "failed to dial %q", address) } @@ -135,6 +138,12 @@ func (c *Client) Containers(ctx context.Context, filters ...string) ([]Container // NewContainer will create a new container in container with the provided id // the id must be unique within the namespace func (c *Client) NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error) { + ctx, done, err := c.withLease(ctx) + if err != nil { + return nil, err + } + defer done() + container := containers.Container{ ID: id, Runtime: containers.RuntimeInfo{ @@ -210,6 +219,12 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image } store := c.ContentStore() + ctx, done, err := c.withLease(ctx) + if err != nil { + return nil, err + } + defer done() + name, desc, err := pullCtx.Resolver.Resolve(ctx, ref) if err != nil { return nil, err @@ -228,7 +243,7 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image handler = images.Handlers(append(pullCtx.BaseHandlers, schema1Converter)...) } else { handler = images.Handlers(append(pullCtx.BaseHandlers, - remotes.FetchHandler(store, fetcher, desc), + remotes.FetchHandler(store, fetcher), images.ChildrenHandler(store, platforms.Default()))..., ) } @@ -265,11 +280,6 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image imgrec = created } - // Remove root tag from manifest now that image refers to it - if _, err := store.Update(ctx, content.Info{Digest: desc.Digest}, "labels.containerd.io/gc.root"); err != nil { - return nil, errors.Wrap(err, "failed to remove manifest root tag") - } - img := &image{ client: c, i: imgrec, @@ -414,9 +424,9 @@ func (c *Client) Close() error { return c.conn.Close() } -// NamespaceService returns the underlying NamespacesClient -func (c *Client) NamespaceService() namespacesapi.NamespacesClient { - return namespacesapi.NewNamespacesClient(c.conn) +// NamespaceService returns the underlying Namespaces Store +func (c *Client) NamespaceService() namespaces.Store { + return namespacesservice.NewStoreFromClient(namespacesapi.NewNamespacesClient(c.conn)) } // ContainerService returns the underlying container Store @@ -449,6 +459,7 @@ func (c *Client) DiffService() diff.Differ { return diffservice.NewDiffServiceFromClient(diffapi.NewDiffClient(c.conn)) } +// IntrospectionService returns the underlying Introspection Client func (c *Client) IntrospectionService() introspectionapi.IntrospectionClient { return introspectionapi.NewIntrospectionClient(c.conn) } @@ -580,6 +591,13 @@ func (c *Client) Import(ctx context.Context, ref string, reader io.Reader, opts if err != nil { return nil, err } + + ctx, done, err := c.withLease(ctx) + if err != nil { + return nil, err + } + defer done() + switch iopts.format { case ociImageFormat: return c.importFromOCITar(ctx, ref, reader, iopts) 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 57ffc0a368..4c534fe12d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/container_opts.go +++ b/components/engine/vendor/github.com/containerd/containerd/container_opts.go @@ -2,12 +2,10 @@ package containerd import ( "context" - "time" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/platforms" - "github.com/containerd/containerd/snapshot" "github.com/containerd/typeurl" "github.com/gogo/protobuf/types" "github.com/opencontainers/image-spec/identity" @@ -93,11 +91,8 @@ func WithNewSnapshot(id string, i Image) NewContainerOpts { return err } setSnapshotterIfEmpty(c) - labels := map[string]string{ - "containerd.io/gc.root": time.Now().String(), - } parent := identity.ChainID(diffIDs).String() - if _, err := client.SnapshotService(c.Snapshotter).Prepare(ctx, id, parent, snapshot.WithLabels(labels)); err != nil { + if _, err := client.SnapshotService(c.Snapshotter).Prepare(ctx, id, parent); err != nil { return err } c.SnapshotKey = id @@ -126,11 +121,8 @@ func WithNewSnapshotView(id string, i Image) NewContainerOpts { return err } setSnapshotterIfEmpty(c) - labels := map[string]string{ - "containerd.io/gc.root": time.Now().String(), - } parent := identity.ChainID(diffIDs).String() - if _, err := client.SnapshotService(c.Snapshotter).View(ctx, id, parent, snapshot.WithLabels(labels)); err != nil { + if _, err := client.SnapshotService(c.Snapshotter).View(ctx, id, parent); err != nil { return err } c.SnapshotKey = id 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 c7854cf9d1..14a98881dd 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 @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "strconv" + "strings" "sync" "time" @@ -27,6 +28,19 @@ var ( } ) +// LabelStore is used to store mutable labels for digests +type LabelStore interface { + // Get returns all the labels for the given digest + Get(digest.Digest) (map[string]string, error) + + // Set sets all the labels for a given digest + Set(digest.Digest, map[string]string) error + + // Update replaces the given labels for a digest, + // a key with an empty value removes a label. + Update(digest.Digest, map[string]string) (map[string]string, error) +} + // Store is digest-keyed store for content. All data written into the store is // stored under a verifiable digest. // @@ -34,16 +48,27 @@ var ( // including resumable ingest. type store struct { root string + ls LabelStore } // NewStore returns a local content store func NewStore(root string) (content.Store, error) { + return NewLabeledStore(root, nil) +} + +// NewLabeledStore returns a new content store using the provided label store +// +// Note: content stores which are used underneath a metadata store may not +// require labels and should use `NewStore`. `NewLabeledStore` is primarily +// useful for tests or standalone implementations. +func NewLabeledStore(root string, ls LabelStore) (content.Store, error) { if err := os.MkdirAll(filepath.Join(root, "ingest"), 0777); err != nil && !os.IsExist(err) { return nil, err } return &store{ root: root, + ls: ls, }, nil } @@ -57,16 +82,23 @@ func (s *store) Info(ctx context.Context, dgst digest.Digest) (content.Info, err return content.Info{}, err } - - return s.info(dgst, fi), nil + var labels map[string]string + if s.ls != nil { + labels, err = s.ls.Get(dgst) + if err != nil { + return content.Info{}, err + } + } + return s.info(dgst, fi, labels), nil } -func (s *store) info(dgst digest.Digest, fi os.FileInfo) content.Info { +func (s *store) info(dgst digest.Digest, fi os.FileInfo, labels map[string]string) content.Info { return content.Info{ Digest: dgst, Size: fi.Size(), CreatedAt: fi.ModTime(), - UpdatedAt: fi.ModTime(), + UpdatedAt: getATime(fi), + Labels: labels, } } @@ -111,8 +143,66 @@ func (s *store) Delete(ctx context.Context, dgst digest.Digest) error { } func (s *store) Update(ctx context.Context, info content.Info, fieldpaths ...string) (content.Info, error) { - // TODO: Support persisting and updating mutable content data - return content.Info{}, errors.Wrapf(errdefs.ErrFailedPrecondition, "update not supported on immutable content store") + if s.ls == nil { + return content.Info{}, errors.Wrapf(errdefs.ErrFailedPrecondition, "update not supported on immutable content store") + } + + p := s.blobPath(info.Digest) + fi, err := os.Stat(p) + if err != nil { + if os.IsNotExist(err) { + err = errors.Wrapf(errdefs.ErrNotFound, "content %v", info.Digest) + } + + return content.Info{}, err + } + + var ( + all bool + labels map[string]string + ) + if len(fieldpaths) > 0 { + for _, path := range fieldpaths { + if strings.HasPrefix(path, "labels.") { + if labels == nil { + labels = map[string]string{} + } + + key := strings.TrimPrefix(path, "labels.") + labels[key] = info.Labels[key] + continue + } + + switch path { + case "labels": + all = true + labels = info.Labels + default: + return content.Info{}, errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on content info %q", path, info.Digest) + } + } + } else { + all = true + labels = info.Labels + } + + if all { + err = s.ls.Set(info.Digest, labels) + } else { + labels, err = s.ls.Update(info.Digest, labels) + } + if err != nil { + return content.Info{}, err + } + + info = s.info(info.Digest, fi, labels) + info.UpdatedAt = time.Now() + + if err := os.Chtimes(p, info.UpdatedAt, info.CreatedAt); err != nil { + log.G(ctx).WithError(err).Warnf("could not change access time for %s", info.Digest) + } + + return info, nil } func (s *store) Walk(ctx context.Context, fn content.WalkFunc, filters ...string) error { @@ -154,7 +244,14 @@ func (s *store) Walk(ctx context.Context, fn content.WalkFunc, filters ...string // store or extra paths not expected previously. } - return fn(s.info(dgst, fi)) + var labels map[string]string + if s.ls != nil { + labels, err = s.ls.Get(dgst) + if err != nil { + return err + } + } + return fn(s.info(dgst, fi, labels)) }) } 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 46eab02c51..0d500b84d0 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 @@ -18,3 +18,12 @@ func getStartTime(fi os.FileInfo) time.Time { return fi.ModTime() } + +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 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 7fb6ad43a5..5f12ea5c42 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 @@ -8,3 +8,7 @@ import ( func getStartTime(fi os.FileInfo) time.Time { return fi.ModTime() } + +func getATime(fi os.FileInfo) time.Time { + return fi.ModTime() +} 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 c4f1a94f31..8f1e92ded6 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 @@ -56,6 +56,13 @@ func (w *writer) Write(p []byte) (n int, err error) { } func (w *writer) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error { + var base content.Info + for _, opt := range opts { + if err := opt(&base); err != nil { + return err + } + } + if w.fp == nil { return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot commit on closed writer") } @@ -123,6 +130,12 @@ func (w *writer) Commit(ctx context.Context, size int64, expected digest.Digest, w.fp = nil unlock(w.ref) + if w.s.ls != nil && base.Labels != nil { + if err := w.s.ls.Set(dgst, base.Labels); err != nil { + return err + } + } + return nil } diff --git a/components/engine/vendor/github.com/containerd/containerd/dialer.go b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer.go similarity index 97% rename from components/engine/vendor/github.com/containerd/containerd/dialer.go rename to components/engine/vendor/github.com/containerd/containerd/dialer/dialer.go index c87cf12d00..65af69f9bc 100644 --- a/components/engine/vendor/github.com/containerd/containerd/dialer.go +++ b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer.go @@ -1,4 +1,4 @@ -package containerd +package dialer import ( "net" diff --git a/components/engine/vendor/github.com/containerd/containerd/dialer_unix.go b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_unix.go similarity index 97% rename from components/engine/vendor/github.com/containerd/containerd/dialer_unix.go rename to components/engine/vendor/github.com/containerd/containerd/dialer/dialer_unix.go index 2e97d17a45..7f8d43b031 100644 --- a/components/engine/vendor/github.com/containerd/containerd/dialer_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_unix.go @@ -1,6 +1,6 @@ // +build !windows -package containerd +package dialer import ( "fmt" @@ -11,6 +11,12 @@ import ( "time" ) +// DialAddress returns the address with unix:// prepended to the +// provided address +func DialAddress(address string) string { + return fmt.Sprintf("unix://%s", address) +} + func isNoent(err error) bool { if err != nil { if nerr, ok := err.(*net.OpError); ok { @@ -28,9 +34,3 @@ func dialer(address string, timeout time.Duration) (net.Conn, error) { address = strings.TrimPrefix(address, "unix://") return net.DialTimeout("unix", address, timeout) } - -// DialAddress returns the address with unix:// prepended to the -// provided address -func DialAddress(address string) string { - return fmt.Sprintf("unix://%s", address) -} diff --git a/components/engine/vendor/github.com/containerd/containerd/dialer_windows.go b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_windows.go similarity index 88% rename from components/engine/vendor/github.com/containerd/containerd/dialer_windows.go rename to components/engine/vendor/github.com/containerd/containerd/dialer/dialer_windows.go index c91a326174..2aac03898a 100644 --- a/components/engine/vendor/github.com/containerd/containerd/dialer_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/dialer/dialer_windows.go @@ -1,4 +1,4 @@ -package containerd +package dialer import ( "net" @@ -24,6 +24,7 @@ func dialer(address string, timeout time.Duration) (net.Conn, error) { return winio.DialPipe(address, &timeout) } +// DialAddress returns the dial address func DialAddress(address string) string { return address } diff --git a/components/engine/vendor/github.com/containerd/containerd/events/exchange.go b/components/engine/vendor/github.com/containerd/containerd/events/exchange/exchange.go similarity index 92% rename from components/engine/vendor/github.com/containerd/containerd/events/exchange.go rename to components/engine/vendor/github.com/containerd/containerd/events/exchange/exchange.go index eeeeea3622..3fefb9c255 100644 --- a/components/engine/vendor/github.com/containerd/containerd/events/exchange.go +++ b/components/engine/vendor/github.com/containerd/containerd/events/exchange/exchange.go @@ -1,12 +1,13 @@ -package events +package exchange import ( "context" "strings" "time" - events "github.com/containerd/containerd/api/services/events/v1" + v1 "github.com/containerd/containerd/api/services/events/v1" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/events" "github.com/containerd/containerd/filters" "github.com/containerd/containerd/identifiers" "github.com/containerd/containerd/log" @@ -34,7 +35,7 @@ func NewExchange() *Exchange { // // This is useful when an event is forwaded on behalf of another namespace or // when the event is propagated on behalf of another publisher. -func (e *Exchange) Forward(ctx context.Context, envelope *events.Envelope) (err error) { +func (e *Exchange) Forward(ctx context.Context, envelope *v1.Envelope) (err error) { if err := validateEnvelope(envelope); err != nil { return err } @@ -59,11 +60,11 @@ func (e *Exchange) Forward(ctx context.Context, envelope *events.Envelope) (err // Publish packages and sends an event. The caller will be considered the // initial publisher of the event. This means the timestamp will be calculated // at this point and this method may read from the calling context. -func (e *Exchange) Publish(ctx context.Context, topic string, event Event) (err error) { +func (e *Exchange) Publish(ctx context.Context, topic string, event events.Event) (err error) { var ( namespace string encoded *types.Any - envelope events.Envelope + envelope v1.Envelope ) namespace, err = namespaces.NamespaceRequired(ctx) @@ -108,9 +109,9 @@ func (e *Exchange) Publish(ctx context.Context, topic string, event Event) (err // Zero or more filters may be provided as strings. Only events that match // *any* of the provided filters will be sent on the channel. The filters use // the standard containerd filters package syntax. -func (e *Exchange) Subscribe(ctx context.Context, fs ...string) (ch <-chan *events.Envelope, errs <-chan error) { +func (e *Exchange) Subscribe(ctx context.Context, fs ...string) (ch <-chan *v1.Envelope, errs <-chan error) { var ( - evch = make(chan *events.Envelope) + evch = make(chan *v1.Envelope) errq = make(chan error, 1) channel = goevents.NewChannel(0) queue = goevents.NewQueue(channel) @@ -150,7 +151,7 @@ func (e *Exchange) Subscribe(ctx context.Context, fs ...string) (ch <-chan *even for { select { case ev := <-channel.C: - env, ok := ev.(*events.Envelope) + env, ok := ev.(*v1.Envelope) if !ok { // TODO(stevvooe): For the most part, we are well protected // from this condition. Both Forward and Publish protect @@ -204,7 +205,7 @@ func validateTopic(topic string) error { return nil } -func validateEnvelope(envelope *events.Envelope) error { +func validateEnvelope(envelope *v1.Envelope) error { if err := namespaces.Validate(envelope.Namespace); err != nil { return errors.Wrapf(err, "event envelope has invalid namespace") } 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 e892be155a..70838a7627 100644 --- a/components/engine/vendor/github.com/containerd/containerd/gc/gc.go +++ b/components/engine/vendor/github.com/containerd/containerd/gc/gc.go @@ -10,7 +10,7 @@ import ( "sync" ) -// Resourcetype represents type of resource at a node +// ResourceType represents type of resource at a node type ResourceType uint8 // Node presents a resource which has a type and key, @@ -145,10 +145,10 @@ func ConcurrentMark(ctx context.Context, root <-chan Node, refs func(context.Con // Sweep removes all nodes returned through the channel which are not in // the reachable set by calling the provided remove function. -func Sweep(reachable map[Node]struct{}, all <-chan Node, remove func(Node) error) error { +func Sweep(reachable map[Node]struct{}, all []Node, remove func(Node) error) error { // All black objects are now reachable, and all white objects are // unreachable. Free those that are white! - for node := range all { + for _, node := range all { if _, ok := reachable[node]; !ok { if err := remove(node); err != nil { return err diff --git a/components/engine/vendor/github.com/containerd/containerd/image.go b/components/engine/vendor/github.com/containerd/containerd/image.go index b41037a16d..4f978e2ea7 100644 --- a/components/engine/vendor/github.com/containerd/containerd/image.go +++ b/components/engine/vendor/github.com/containerd/containerd/image.go @@ -3,9 +3,9 @@ package containerd import ( "context" "fmt" - "time" "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/rootfs" @@ -30,6 +30,8 @@ type Image interface { Size(ctx context.Context) (int64, error) // Config descriptor for the image. Config(ctx context.Context) (ocispec.Descriptor, error) + // IsUnpacked returns whether or not an image is unpacked. + IsUnpacked(context.Context, string) (bool, error) } var _ = (Image)(&image{}) @@ -63,6 +65,26 @@ func (i *image) Config(ctx context.Context) (ocispec.Descriptor, error) { return i.i.Config(ctx, provider, platforms.Default()) } +func (i *image) IsUnpacked(ctx context.Context, snapshotterName string) (bool, error) { + sn := i.client.SnapshotService(snapshotterName) + cs := i.client.ContentStore() + + diffs, err := i.i.RootFS(ctx, cs, platforms.Default()) + if err != nil { + return false, err + } + + chainID := identity.ChainID(diffs) + _, err = sn.Stat(ctx, chainID.String()) + if err == nil { + return true, nil + } else if !errdefs.IsNotFound(err) { + return false, err + } + + return false, nil +} + func (i *image) Unpack(ctx context.Context, snapshotterName string) error { layers, err := i.getLayers(ctx, platforms.Default()) if err != nil { @@ -79,27 +101,14 @@ func (i *image) Unpack(ctx context.Context, snapshotterName string) error { ) for _, layer := range layers { labels := map[string]string{ - "containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339), "containerd.io/uncompressed": layer.Diff.Digest.String(), } - lastUnpacked := unpacked unpacked, err = rootfs.ApplyLayer(ctx, layer, chain, sn, a, snapshot.WithLabels(labels)) if err != nil { return err } - if lastUnpacked { - info := snapshot.Info{ - Name: identity.ChainID(chain).String(), - } - - // Remove previously created gc.root label - if _, err := sn.Update(ctx, info, "labels.containerd.io/gc.root"); err != nil { - return err - } - } - chain = append(chain, layer.Diff.Digest) } @@ -120,15 +129,6 @@ func (i *image) Unpack(ctx context.Context, snapshotterName string) error { if _, err := cs.Update(ctx, cinfo, fmt.Sprintf("labels.containerd.io/gc.ref.snapshot.%s", snapshotterName)); err != nil { return err } - - sinfo := snapshot.Info{ - Name: rootfs, - } - - // Config now referenced snapshot, release root reference - if _, err := sn.Update(ctx, sinfo, "labels.containerd.io/gc.root"); err != nil { - return err - } } return nil diff --git a/components/engine/vendor/github.com/containerd/containerd/io_unix.go b/components/engine/vendor/github.com/containerd/containerd/io_unix.go index 432553fe37..08aba14bac 100644 --- a/components/engine/vendor/github.com/containerd/containerd/io_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/io_unix.go @@ -16,7 +16,7 @@ import ( // NewFifos returns a new set of fifos for the task func NewFifos(id string) (*FIFOSet, error) { - root := filepath.Join(os.TempDir(), "containerd") + root := "/run/containerd/fifo" if err := os.MkdirAll(root, 0700); err != nil { return nil, err } diff --git a/components/engine/vendor/github.com/containerd/containerd/lease.go b/components/engine/vendor/github.com/containerd/containerd/lease.go new file mode 100644 index 0000000000..6ecc58dec3 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/lease.go @@ -0,0 +1,91 @@ +package containerd + +import ( + "context" + "time" + + leasesapi "github.com/containerd/containerd/api/services/leases/v1" + "github.com/containerd/containerd/leases" +) + +// Lease is used to hold a reference to active resources which have not been +// referenced by a root resource. This is useful for preventing garbage +// collection of resources while they are actively being updated. +type Lease struct { + id string + createdAt time.Time + + client *Client +} + +// CreateLease creates a new lease +func (c *Client) CreateLease(ctx context.Context) (Lease, error) { + lapi := leasesapi.NewLeasesClient(c.conn) + resp, err := lapi.Create(ctx, &leasesapi.CreateRequest{}) + if err != nil { + return Lease{}, err + } + + return Lease{ + id: resp.Lease.ID, + client: c, + }, nil +} + +// ListLeases lists active leases +func (c *Client) ListLeases(ctx context.Context) ([]Lease, error) { + lapi := leasesapi.NewLeasesClient(c.conn) + resp, err := lapi.List(ctx, &leasesapi.ListRequest{}) + if err != nil { + return nil, err + } + leases := make([]Lease, len(resp.Leases)) + for i := range resp.Leases { + leases[i] = Lease{ + id: resp.Leases[i].ID, + createdAt: resp.Leases[i].CreatedAt, + client: c, + } + } + + return leases, nil +} + +func (c *Client) withLease(ctx context.Context) (context.Context, func() error, error) { + _, ok := leases.Lease(ctx) + if ok { + return ctx, func() error { + return nil + }, nil + } + + l, err := c.CreateLease(ctx) + if err != nil { + return nil, nil, err + } + + ctx = leases.WithLease(ctx, l.ID()) + return ctx, func() error { + return l.Delete(ctx) + }, nil +} + +// ID returns the lease ID +func (l Lease) ID() string { + return l.id +} + +// CreatedAt returns the time at which the lease was created +func (l Lease) CreatedAt() time.Time { + return l.createdAt +} + +// Delete deletes the lease, removing the reference to all resources created +// during the lease. +func (l Lease) Delete(ctx context.Context) error { + lapi := leasesapi.NewLeasesClient(l.client.conn) + _, err := lapi.Delete(ctx, &leasesapi.DeleteRequest{ + ID: l.id, + }) + return err +} diff --git a/components/engine/vendor/github.com/containerd/containerd/leases/context.go b/components/engine/vendor/github.com/containerd/containerd/leases/context.go new file mode 100644 index 0000000000..cfd7e4a46e --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/leases/context.go @@ -0,0 +1,24 @@ +package leases + +import "context" + +type leaseKey struct{} + +// WithLease sets a given lease on the context +func WithLease(ctx context.Context, lid string) context.Context { + ctx = context.WithValue(ctx, leaseKey{}, lid) + + // also store on the grpc headers so it gets picked up by any clients that + // are using this. + return withGRPCLeaseHeader(ctx, lid) +} + +// Lease returns the lease from the context. +func Lease(ctx context.Context) (string, bool) { + lid, ok := ctx.Value(leaseKey{}).(string) + if !ok { + return fromGRPCHeader(ctx) + } + + return lid, ok +} diff --git a/components/engine/vendor/github.com/containerd/containerd/leases/grpc.go b/components/engine/vendor/github.com/containerd/containerd/leases/grpc.go new file mode 100644 index 0000000000..cea5b25feb --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/leases/grpc.go @@ -0,0 +1,41 @@ +package leases + +import ( + "golang.org/x/net/context" + "google.golang.org/grpc/metadata" +) + +const ( + // GRPCHeader defines the header name for specifying a containerd lease. + GRPCHeader = "containerd-lease" +) + +func withGRPCLeaseHeader(ctx context.Context, lid string) context.Context { + // also store on the grpc headers so it gets picked up by any clients + // that are using this. + txheader := metadata.Pairs(GRPCHeader, lid) + md, ok := metadata.FromOutgoingContext(ctx) // merge with outgoing context. + if !ok { + md = txheader + } else { + // order ensures the latest is first in this list. + md = metadata.Join(txheader, md) + } + + return metadata.NewOutgoingContext(ctx, md) +} + +func fromGRPCHeader(ctx context.Context) (string, bool) { + // try to extract for use in grpc servers. + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return "", false + } + + values := md[GRPCHeader] + if len(values) == 0 { + return "", false + } + + return values[0], true +} 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 3dbfef98d4..72fcab9fe1 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/bundle.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/bundle.go @@ -9,9 +9,10 @@ import ( "os" "path/filepath" - "github.com/containerd/containerd/events" + "github.com/containerd/containerd/events/exchange" "github.com/containerd/containerd/linux/runcopts" - client "github.com/containerd/containerd/linux/shim" + "github.com/containerd/containerd/linux/shim" + "github.com/containerd/containerd/linux/shim/client" "github.com/pkg/errors" ) @@ -70,32 +71,33 @@ type bundle struct { workDir string } -type shimOpt func(*bundle, string, *runcopts.RuncOptions) (client.Config, client.ClientOpt) +// ShimOpt specifies shim options for initialization and connection +type ShimOpt func(*bundle, string, *runcopts.RuncOptions) (shim.Config, client.Opt) -// ShimRemote is a shimOpt for connecting and starting a remote shim -func ShimRemote(shim, daemonAddress, cgroup string, nonewns, debug bool, exitHandler func()) shimOpt { - return func(b *bundle, ns string, ropts *runcopts.RuncOptions) (client.Config, client.ClientOpt) { +// ShimRemote is a ShimOpt for connecting and starting a remote shim +func ShimRemote(shimBinary, daemonAddress, cgroup string, nonewns, debug bool, exitHandler func()) ShimOpt { + return func(b *bundle, ns string, ropts *runcopts.RuncOptions) (shim.Config, client.Opt) { return b.shimConfig(ns, ropts), - client.WithStart(shim, b.shimAddress(ns), daemonAddress, cgroup, nonewns, debug, exitHandler) + client.WithStart(shimBinary, b.shimAddress(ns), daemonAddress, cgroup, nonewns, debug, exitHandler) } } -// ShimLocal is a shimOpt for using an in process shim implementation -func ShimLocal(exchange *events.Exchange) shimOpt { - return func(b *bundle, ns string, ropts *runcopts.RuncOptions) (client.Config, client.ClientOpt) { +// ShimLocal is a ShimOpt for using an in process shim implementation +func ShimLocal(exchange *exchange.Exchange) ShimOpt { + return func(b *bundle, ns string, ropts *runcopts.RuncOptions) (shim.Config, client.Opt) { return b.shimConfig(ns, ropts), client.WithLocal(exchange) } } -// ShimConnect is a shimOpt for connecting to an existing remote shim -func ShimConnect() shimOpt { - return func(b *bundle, ns string, ropts *runcopts.RuncOptions) (client.Config, client.ClientOpt) { +// ShimConnect is a ShimOpt for connecting to an existing remote shim +func ShimConnect() ShimOpt { + return func(b *bundle, ns string, ropts *runcopts.RuncOptions) (shim.Config, client.Opt) { return b.shimConfig(ns, ropts), client.WithConnect(b.shimAddress(ns)) } } // NewShimClient connects to the shim managing the bundle and tasks creating it if needed -func (b *bundle) NewShimClient(ctx context.Context, namespace string, getClientOpts shimOpt, runcOpts *runcopts.RuncOptions) (*client.Client, error) { +func (b *bundle) NewShimClient(ctx context.Context, namespace string, getClientOpts ShimOpt, runcOpts *runcopts.RuncOptions) (*client.Client, error) { cfg, opt := getClientOpts(b, namespace, runcOpts) return client.New(ctx, cfg, opt) } @@ -118,7 +120,7 @@ func (b *bundle) shimAddress(namespace string) string { return filepath.Join(string(filepath.Separator), "containerd-shim", namespace, b.id, "shim.sock") } -func (b *bundle) shimConfig(namespace string, runcOptions *runcopts.RuncOptions) client.Config { +func (b *bundle) shimConfig(namespace string, runcOptions *runcopts.RuncOptions) shim.Config { var ( criuPath string runtimeRoot string @@ -129,7 +131,7 @@ func (b *bundle) shimConfig(namespace string, runcOptions *runcopts.RuncOptions) systemdCgroup = runcOptions.SystemdCgroup runtimeRoot = runcOptions.RuntimeRoot } - return client.Config{ + return shim.Config{ Path: b.path, WorkDir: b.workDir, Namespace: namespace, 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 26d001f8a6..44219e40d0 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/runtime.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/runtime.go @@ -15,7 +15,7 @@ import ( "github.com/containerd/containerd/api/types" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/events" + "github.com/containerd/containerd/events/exchange" "github.com/containerd/containerd/identifiers" "github.com/containerd/containerd/linux/runcopts" client "github.com/containerd/containerd/linux/shim" @@ -143,7 +143,7 @@ type Runtime struct { monitor runtime.TaskMonitor tasks *runtime.TaskList db *metadata.DB - events *events.Exchange + events *exchange.Exchange config *Config } diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client.go b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client.go similarity index 81% rename from components/engine/vendor/github.com/containerd/containerd/linux/shim/client.go rename to components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client.go index eae946bed7..1cfe766c21 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client.go @@ -1,6 +1,6 @@ // +build !windows -package shim +package client import ( "context" @@ -20,19 +20,23 @@ import ( "github.com/sirupsen/logrus" "github.com/containerd/containerd/events" - shim "github.com/containerd/containerd/linux/shim/v1" + "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" + google_protobuf "github.com/golang/protobuf/ptypes/empty" "google.golang.org/grpc" ) -// ClientOpt is an option for a shim client configuration -type ClientOpt func(context.Context, Config) (shim.ShimClient, io.Closer, error) +var empty = &google_protobuf.Empty{} + +// Opt is an option for a shim client configuration +type Opt func(context.Context, shim.Config) (shimapi.ShimClient, io.Closer, error) // WithStart executes a new shim process -func WithStart(binary, address, daemonAddress, cgroup string, nonewns, debug bool, exitHandler func()) ClientOpt { - return func(ctx context.Context, config Config) (_ shim.ShimClient, _ io.Closer, err error) { +func WithStart(binary, address, daemonAddress, cgroup string, nonewns, debug bool, exitHandler func()) Opt { + return func(ctx context.Context, config shim.Config) (_ shimapi.ShimClient, _ io.Closer, err error) { socket, err := newSocket(address) if err != nil { return nil, nil, err @@ -84,24 +88,24 @@ func WithStart(binary, address, daemonAddress, cgroup string, nonewns, debug boo } } -func newCommand(binary, daemonAddress string, nonewns, debug bool, config Config, socket *os.File) *exec.Cmd { +func newCommand(binary, daemonAddress string, nonewns, debug bool, config shim.Config, socket *os.File) *exec.Cmd { args := []string{ - "--namespace", config.Namespace, - "--workdir", config.WorkDir, - "--address", daemonAddress, + "-namespace", config.Namespace, + "-workdir", config.WorkDir, + "-address", daemonAddress, } if config.Criu != "" { - args = append(args, "--criu-path", config.Criu) + args = append(args, "-criu-path", config.Criu) } if config.RuntimeRoot != "" { - args = append(args, "--runtime-root", config.RuntimeRoot) + args = append(args, "-runtime-root", config.RuntimeRoot) } if config.SystemdCgroup { - args = append(args, "--systemd-cgroup") + args = append(args, "-systemd-cgroup") } if debug { - args = append(args, "--debug") + args = append(args, "-debug") } cmd := exec.Command(binary, args...) @@ -160,39 +164,29 @@ func dialAddress(address string) string { } // WithConnect connects to an existing shim -func WithConnect(address string) ClientOpt { - return func(ctx context.Context, config Config) (shim.ShimClient, io.Closer, error) { +func WithConnect(address string) Opt { + return func(ctx context.Context, config shim.Config) (shimapi.ShimClient, io.Closer, error) { conn, err := connect(address, annonDialer) if err != nil { return nil, nil, err } - return shim.NewShimClient(conn), conn, nil + return shimapi.NewShimClient(conn), conn, nil } } // WithLocal uses an in process shim -func WithLocal(publisher events.Publisher) func(context.Context, Config) (shim.ShimClient, io.Closer, error) { - return func(ctx context.Context, config Config) (shim.ShimClient, io.Closer, error) { - service, err := NewService(config, publisher) +func WithLocal(publisher events.Publisher) func(context.Context, shim.Config) (shimapi.ShimClient, io.Closer, error) { + return func(ctx context.Context, config shim.Config) (shimapi.ShimClient, io.Closer, error) { + service, err := shim.NewService(config, publisher) if err != nil { return nil, nil, err } - return NewLocal(service), nil, nil + return shim.NewLocal(service), nil, nil } } -// Config contains shim specific configuration -type Config struct { - Path string - Namespace string - WorkDir string - Criu string - RuntimeRoot string - SystemdCgroup bool -} - // New returns a new shim client -func New(ctx context.Context, config Config, opt ClientOpt) (*Client, error) { +func New(ctx context.Context, config shim.Config, opt Opt) (*Client, error) { s, c, err := opt(ctx, config) if err != nil { return nil, err @@ -206,7 +200,7 @@ func New(ctx context.Context, config Config, opt ClientOpt) (*Client, error) { // Client is a shim client containing the connection to a shim type Client struct { - shim.ShimClient + shimapi.ShimClient c io.Closer exitCh chan struct{} diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client_linux.go b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_linux.go similarity index 97% rename from components/engine/vendor/github.com/containerd/containerd/linux/shim/client_linux.go rename to components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_linux.go index 515e88c476..03ebba00cf 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client_linux.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_linux.go @@ -1,6 +1,6 @@ // +build linux -package shim +package client import ( "os/exec" diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client_unix.go b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_unix.go similarity index 94% rename from components/engine/vendor/github.com/containerd/containerd/linux/shim/client_unix.go rename to components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_unix.go index d478f3dd71..b34cf4d368 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/shim/client_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/shim/client/client_unix.go @@ -1,6 +1,6 @@ // +build !linux,!windows -package shim +package client import ( "os/exec" diff --git a/components/engine/vendor/github.com/containerd/containerd/linux/shim/init.go b/components/engine/vendor/github.com/containerd/containerd/linux/shim/init.go index 88c39a6f7a..01c305bb67 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/shim/init.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/shim/init.go @@ -98,12 +98,16 @@ func (s *Service) newInitProcess(context context.Context, r *shimapi.CreateTaskR return nil, errors.Wrapf(err, "failed to mount rootfs component %v", m) } } + root := s.config.RuntimeRoot + if root == "" { + root = RuncRoot + } runtime := &runc.Runc{ Command: r.Runtime, Log: filepath.Join(s.config.Path, "log.json"), LogFormat: runc.JSON, PdeathSignal: syscall.SIGKILL, - Root: filepath.Join(s.config.RuntimeRoot, s.config.Namespace), + Root: filepath.Join(root, s.config.Namespace), Criu: s.config.Criu, SystemdCgroup: s.config.SystemdCgroup, } 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 9c9f506022..7b5c5e1164 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 @@ -32,6 +32,16 @@ var empty = &google_protobuf.Empty{} // RuncRoot is the path to the root runc state directory const RuncRoot = "/run/containerd/runc" +// Config contains shim specific configuration +type Config struct { + Path string + Namespace string + WorkDir string + Criu string + RuntimeRoot string + SystemdCgroup bool +} + // NewService returns a new shim service that can be used via GRPC func NewService(config Config, publisher events.Publisher) (*Service, error) { if config.Namespace == "" { 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 a851ee77ef..268e91a94a 100644 --- a/components/engine/vendor/github.com/containerd/containerd/linux/task.go +++ b/components/engine/vendor/github.com/containerd/containerd/linux/task.go @@ -11,7 +11,7 @@ import ( "github.com/containerd/cgroups" "github.com/containerd/containerd/api/types/task" "github.com/containerd/containerd/errdefs" - client "github.com/containerd/containerd/linux/shim" + "github.com/containerd/containerd/linux/shim/client" shim "github.com/containerd/containerd/linux/shim/v1" "github.com/containerd/containerd/runtime" "github.com/gogo/protobuf/types" 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 43849e0804..b6a66ba48c 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/buckets.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/buckets.go @@ -38,6 +38,7 @@ var ( bucketKeyObjectContent = []byte("content") // stores content references bucketKeyObjectBlob = []byte("blob") // stores content links bucketKeyObjectIngest = []byte("ingest") // stores ingest links + bucketKeyObjectLeases = []byte("leases") // stores leases bucketKeyDigest = []byte("digest") bucketKeyMediaType = []byte("mediatype") @@ -53,6 +54,7 @@ var ( bucketKeySnapshotter = []byte("snapshotter") bucketKeyTarget = []byte("target") bucketKeyExtensions = []byte("extensions") + bucketKeyCreatedAt = []byte("createdat") ) func getBucket(tx *bolt.Tx, keys ...[]byte) *bolt.Bucket { 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 05064fdec5..0797345e21 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/content.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/content.go @@ -391,27 +391,31 @@ func (nw *namespacedWriter) Commit(ctx context.Context, size int64, expected dig return err } } - return nw.commit(ctx, tx, size, expected, opts...) + dgst, err := nw.commit(ctx, tx, size, expected, opts...) + if err != nil { + return err + } + return addContentLease(ctx, tx, dgst) }) } -func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64, expected digest.Digest, opts ...content.Opt) error { +func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64, expected digest.Digest, opts ...content.Opt) (digest.Digest, error) { var base content.Info for _, opt := range opts { if err := opt(&base); err != nil { - return err + return "", err } } if err := validateInfo(&base); err != nil { - return err + return "", err } status, err := nw.Writer.Status() if err != nil { - return err + return "", err } if size != 0 && size != status.Offset { - return errors.Errorf("%q failed size validation: %v != %v", nw.ref, status.Offset, size) + return "", errors.Errorf("%q failed size validation: %v != %v", nw.ref, status.Offset, size) } size = status.Offset @@ -419,32 +423,32 @@ func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64, if err := nw.Writer.Commit(ctx, size, expected); err != nil { if !errdefs.IsAlreadyExists(err) { - return err + return "", err } if getBlobBucket(tx, nw.namespace, actual) != nil { - return errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", actual) + return "", errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", actual) } } bkt, err := createBlobBucket(tx, nw.namespace, actual) if err != nil { - return err + return "", err } commitTime := time.Now().UTC() sizeEncoded, err := encodeInt(size) if err != nil { - return err + return "", err } if err := boltutil.WriteTimestamps(bkt, commitTime, commitTime); err != nil { - return err + return "", err } if err := boltutil.WriteLabels(bkt, base.Labels); err != nil { - return err + return "", err } - return bkt.Put(bucketKeySize, sizeEncoded) + return actual, bkt.Put(bucketKeySize, sizeEncoded) } func (nw *namespacedWriter) Status() (content.Status, error) { @@ -566,7 +570,7 @@ func (cs *contentStore) garbageCollect(ctx context.Context) error { return err } - if err := cs.Store.Walk(ctx, func(info content.Info) error { + return cs.Store.Walk(ctx, func(info content.Info) error { if _, ok := seen[info.Digest.String()]; !ok { if err := cs.Store.Delete(ctx, info.Digest); err != nil { return err @@ -574,9 +578,5 @@ func (cs *contentStore) garbageCollect(ctx context.Context) error { log.G(ctx).WithField("digest", info.Digest).Debug("removed content") } return nil - }); err != nil { - return err - } - - return nil + }) } 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 510d14a2a0..7c366ebcc4 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/db.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/db.go @@ -190,6 +190,7 @@ func (m *DB) Update(fn func(*bolt.Tx) error) error { return m.db.Update(fn) } +// GarbageCollect starts garbage collection func (m *DB) GarbageCollect(ctx context.Context) error { lt1 := time.Now() m.wlock.Lock() @@ -198,39 +199,8 @@ func (m *DB) GarbageCollect(ctx context.Context) error { log.G(ctx).WithField("d", time.Now().Sub(lt1)).Debug("metadata garbage collected") }() - var marked map[gc.Node]struct{} - - if err := m.db.View(func(tx *bolt.Tx) error { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - roots := make(chan gc.Node) - errChan := make(chan error) - go func() { - defer close(errChan) - defer close(roots) - - // Call roots - if err := scanRoots(ctx, tx, roots); err != nil { - cancel() - errChan <- err - } - }() - - refs := func(ctx context.Context, n gc.Node, fn func(gc.Node)) error { - return references(ctx, tx, n, fn) - } - - reachable, err := gc.ConcurrentMark(ctx, roots, refs) - if rerr := <-errChan; rerr != nil { - return rerr - } - if err != nil { - return err - } - marked = reachable - return nil - }); err != nil { + marked, err := m.getMarked(ctx) + if err != nil { return err } @@ -241,15 +211,11 @@ func (m *DB) GarbageCollect(ctx context.Context) error { ctx, cancel := context.WithCancel(ctx) defer cancel() - nodeC := make(chan gc.Node) - var scanErr error + rm := func(ctx context.Context, n gc.Node) error { + if _, ok := marked[n]; ok { + return nil + } - go func() { - defer close(nodeC) - scanErr = scanAll(ctx, tx, nodeC) - }() - - rm := func(n gc.Node) error { if n.Type == ResourceSnapshot { if idx := strings.IndexRune(n.Key, '/'); idx > 0 { m.dirtySS[n.Key[:idx]] = struct{}{} @@ -260,12 +226,8 @@ func (m *DB) GarbageCollect(ctx context.Context) error { return remove(ctx, tx, n) } - if err := gc.Sweep(marked, nodeC, rm); err != nil { - return errors.Wrap(err, "failed to sweep") - } - - if scanErr != nil { - return errors.Wrap(scanErr, "failed to scan all") + if err := scanAll(ctx, tx, rm); err != nil { + return errors.Wrap(err, "failed to scan and remove") } return nil @@ -292,6 +254,54 @@ func (m *DB) GarbageCollect(ctx context.Context) error { return nil } +func (m *DB) getMarked(ctx context.Context) (map[gc.Node]struct{}, error) { + var marked map[gc.Node]struct{} + if err := m.db.View(func(tx *bolt.Tx) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + var ( + nodes []gc.Node + wg sync.WaitGroup + roots = make(chan gc.Node) + ) + wg.Add(1) + go func() { + defer wg.Done() + for n := range roots { + nodes = append(nodes, n) + } + }() + // Call roots + if err := scanRoots(ctx, tx, roots); err != nil { + cancel() + return err + } + close(roots) + wg.Wait() + + refs := func(n gc.Node) ([]gc.Node, error) { + var sn []gc.Node + if err := references(ctx, tx, n, func(nn gc.Node) { + sn = append(sn, nn) + }); err != nil { + return nil, err + } + return sn, nil + } + + reachable, err := gc.Tricolor(nodes, refs) + if err != nil { + return err + } + marked = reachable + return nil + }); err != nil { + return nil, err + } + return marked, nil +} + func (m *DB) cleanupSnapshotter(name string) { ctx := context.Background() sn, ok := m.ss[name] 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 8434d694ba..7fe6f7da21 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/gc.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/gc.go @@ -12,10 +12,15 @@ import ( ) const ( + // ResourceUnknown specifies an unknown resource ResourceUnknown gc.ResourceType = iota + // ResourceContent specifies a content resource ResourceContent + // ResourceSnapshot specifies a snapshot resource ResourceSnapshot + // ResourceContainer specifies a container resource ResourceContainer + // ResourceTask specifies a task resource ResourceTask ) @@ -41,6 +46,55 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { nbkt := v1bkt.Bucket(k) ns := string(k) + lbkt := nbkt.Bucket(bucketKeyObjectLeases) + if lbkt != nil { + if err := lbkt.ForEach(func(k, v []byte) error { + if v != nil { + return nil + } + libkt := lbkt.Bucket(k) + + cbkt := libkt.Bucket(bucketKeyObjectContent) + if cbkt != nil { + if err := cbkt.ForEach(func(k, v []byte) error { + select { + case nc <- gcnode(ResourceContent, ns, string(k)): + case <-ctx.Done(): + return ctx.Err() + } + return nil + }); err != nil { + return err + } + } + + sbkt := libkt.Bucket(bucketKeyObjectSnapshots) + if sbkt != nil { + if err := sbkt.ForEach(func(sk, sv []byte) error { + if sv != nil { + return nil + } + snbkt := sbkt.Bucket(sk) + + return snbkt.ForEach(func(k, v []byte) error { + select { + case nc <- gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)): + case <-ctx.Done(): + return ctx.Err() + } + return nil + }) + }); err != nil { + return err + } + } + + return nil + }); err != nil { + return err + } + } + ibkt := nbkt.Bucket(bucketKeyObjectImages) if ibkt != nil { if err := ibkt.ForEach(func(k, v []byte) error { @@ -174,7 +228,7 @@ func references(ctx context.Context, tx *bolt.Tx, node gc.Node, fn func(gc.Node) return nil } -func scanAll(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { +func scanAll(ctx context.Context, tx *bolt.Tx, fn func(ctx context.Context, n gc.Node) error) error { v1bkt := tx.Bucket(bucketKeyVersion) if v1bkt == nil { return nil @@ -201,12 +255,8 @@ func scanAll(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { if v != nil { return nil } - select { - case nc <- gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)): - case <-ctx.Done(): - return ctx.Err() - } - return nil + node := gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)) + return fn(ctx, node) }) }); err != nil { return err @@ -222,12 +272,8 @@ func scanAll(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { if v != nil { return nil } - select { - case nc <- gcnode(ResourceContent, ns, string(k)): - case <-ctx.Done(): - return ctx.Err() - } - return nil + node := gcnode(ResourceContent, ns, string(k)) + return fn(ctx, node) }); 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 new file mode 100644 index 0000000000..006123d45f --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/leases.go @@ -0,0 +1,201 @@ +package metadata + +import ( + "context" + "time" + + "github.com/boltdb/bolt" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/leases" + "github.com/containerd/containerd/metadata/boltutil" + "github.com/containerd/containerd/namespaces" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +// Lease retains resources to prevent garbage collection before +// the resources can be fully referenced. +type Lease struct { + ID string + CreatedAt time.Time + Labels map[string]string + + Content []string + Snapshots map[string][]string +} + +// LeaseManager manages the create/delete lifecyle of leases +// and also returns existing leases +type LeaseManager struct { + tx *bolt.Tx +} + +// NewLeaseManager creates a new lease manager for managing leases using +// the provided database transaction. +func NewLeaseManager(tx *bolt.Tx) *LeaseManager { + return &LeaseManager{ + tx: tx, + } +} + +// Create creates a new lease using the provided lease +func (lm *LeaseManager) Create(ctx context.Context, lid string, labels map[string]string) (Lease, error) { + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return Lease{}, err + } + + topbkt, err := createBucketIfNotExists(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) + if err != nil { + return Lease{}, err + } + + txbkt, err := topbkt.CreateBucket([]byte(lid)) + if err != nil { + if err == bolt.ErrBucketExists { + err = errdefs.ErrAlreadyExists + } + return Lease{}, err + } + + t := time.Now().UTC() + createdAt, err := t.MarshalBinary() + if err != nil { + return Lease{}, err + } + if err := txbkt.Put(bucketKeyCreatedAt, createdAt); err != nil { + return Lease{}, err + } + + if labels != nil { + if err := boltutil.WriteLabels(txbkt, labels); err != nil { + return Lease{}, err + } + } + + return Lease{ + ID: lid, + CreatedAt: t, + Labels: labels, + }, nil +} + +// Delete delets the lease with the provided lease ID +func (lm *LeaseManager) Delete(ctx context.Context, lid string) error { + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return err + } + + topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) + if topbkt == nil { + return nil + } + if err := topbkt.DeleteBucket([]byte(lid)); err != nil && err != bolt.ErrBucketNotFound { + return err + } + return nil +} + +// List lists all active leases +func (lm *LeaseManager) List(ctx context.Context, includeResources bool, filter ...string) ([]Lease, error) { + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return nil, err + } + + var leases []Lease + + topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) + if topbkt == nil { + return leases, nil + } + + if err := topbkt.ForEach(func(k, v []byte) error { + if v != nil { + return nil + } + txbkt := topbkt.Bucket(k) + + l := Lease{ + ID: string(k), + } + + if v := txbkt.Get(bucketKeyCreatedAt); v != nil { + t := &l.CreatedAt + if err := t.UnmarshalBinary(v); err != nil { + return err + } + } + + labels, err := boltutil.ReadLabels(txbkt) + if err != nil { + return err + } + l.Labels = labels + + // TODO: Read Snapshots + // TODO: Read Content + + leases = append(leases, l) + + return nil + }); err != nil { + return nil, err + } + + return leases, nil +} + +func addSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error { + lid, ok := leases.Lease(ctx) + if !ok { + return nil + } + + namespace, ok := namespaces.Namespace(ctx) + if !ok { + panic("namespace must already be required") + } + + bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid)) + if bkt == nil { + return errors.Wrap(errdefs.ErrNotFound, "lease does not exist") + } + + bkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectSnapshots) + if err != nil { + return err + } + + bkt, err = bkt.CreateBucketIfNotExists([]byte(snapshotter)) + if err != nil { + return err + } + + return bkt.Put([]byte(key), nil) +} + +func addContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error { + lid, ok := leases.Lease(ctx) + if !ok { + return nil + } + + namespace, ok := namespaces.Namespace(ctx) + if !ok { + panic("namespace must already be required") + } + + bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid)) + if bkt == nil { + return errors.Wrap(errdefs.ErrNotFound, "lease does not exist") + } + + bkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectContent) + if err != nil { + return err + } + + return bkt.Put([]byte(dgst.String()), 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 ad38e5915b..22ce3c8c03 100644 --- a/components/engine/vendor/github.com/containerd/containerd/metadata/snapshot.go +++ b/components/engine/vendor/github.com/containerd/containerd/metadata/snapshot.go @@ -326,6 +326,10 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re return err } + if err := addSnapshotLease(ctx, tx, s.name, key); err != nil { + return err + } + // TODO: Consider doing this outside of transaction to lessen // metadata lock time if readonly { diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/mount_solaris.go b/components/engine/vendor/github.com/containerd/containerd/mount/mount_solaris.go deleted file mode 100644 index 3b6c35d748..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/mount/mount_solaris.go +++ /dev/null @@ -1,83 +0,0 @@ -package mount - -// On Solaris we can't invoke the mount system call directly. First, -// the mount system call takes more than 6 arguments, and go doesn't -// support invoking system calls that take more than 6 arguments. Past -// that, the mount system call is a private interfaces. For example, -// the arguments and data structures passed to the kernel to create an -// nfs mount are private and can change at any time. The only public -// and stable interface for creating mounts on Solaris is the mount.8 -// command, so we'll invoke that here. - -import ( - "bytes" - "errors" - "fmt" - "os/exec" - "strings" - - "golang.org/x/sys/unix" -) - -const ( - mountCmd = "/usr/sbin/mount" -) - -func doMount(arg ...string) error { - cmd := exec.Command(mountCmd, arg...) - - /* Setup Stdin, Stdout, and Stderr */ - stderr := new(bytes.Buffer) - cmd.Stdin = nil - cmd.Stdout = nil - cmd.Stderr = stderr - - /* - * Run the command. If the command fails create a new error - * object to return that includes stderr output. - */ - err := cmd.Start() - if err != nil { - return err - } - err = cmd.Wait() - if err != nil { - return errors.New(fmt.Sprintf("%v: %s", err, stderr.String())) - } - return nil -} - -func (m *Mount) Mount(target string) error { - var err error - - if len(m.Options) == 0 { - err = doMount("-F", m.Type, m.Source, target) - } else { - err = doMount("-F", m.Type, "-o", strings.Join(m.Options, ","), - m.Source, target) - } - return err -} - -func Unmount(mount string, flags int) error { - return unix.Unmount(mount, flags) -} - -// UnmountAll repeatedly unmounts the given mount point until there -// are no mounts remaining (EINVAL is returned by mount), which is -// useful for undoing a stack of mounts on the same mount point. -func UnmountAll(mount string, flags int) error { - for { - if err := Unmount(mount, flags); err != nil { - // EINVAL is returned if the target is not a - // mount point, indicating that we are - // done. It can also indicate a few other - // things (such as invalid flags) which we - // unfortunately end up squelching here too. - if err == unix.EINVAL { - return nil - } - return err - } - } -} 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 23467a8cc7..edb0e8dd19 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 @@ -5,17 +5,21 @@ package mount import "github.com/pkg/errors" var ( + // ErrNotImplementOnUnix is returned for methods that are not implemented ErrNotImplementOnUnix = errors.New("not implemented under unix") ) +// Mount is not implemented on this platform func (m *Mount) Mount(target string) error { return ErrNotImplementOnUnix } +// Unmount is not implemented on this platform func Unmount(mount string, flags int) error { return ErrNotImplementOnUnix } +// UnmountAll is not implemented on this platform func UnmountAll(mount string, flags int) error { return ErrNotImplementOnUnix } 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 8eeca68174..8ad7eab129 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 @@ -3,17 +3,21 @@ package mount import "github.com/pkg/errors" var ( + // ErrNotImplementOnWindows is returned when an action is not implemented for windows ErrNotImplementOnWindows = errors.New("not implemented under windows") ) +// Mount to the provided target func (m *Mount) Mount(target string) error { return ErrNotImplementOnWindows } +// Unmount the mount at the provided path func Unmount(mount string, flags int) error { return ErrNotImplementOnWindows } +// UnmountAll mounts at the provided path func UnmountAll(mount string, flags int) error { return ErrNotImplementOnWindows } diff --git a/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_solaris.go b/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_solaris.go deleted file mode 100644 index aaafad36a8..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/mount/mountinfo_solaris.go +++ /dev/null @@ -1,50 +0,0 @@ -// +build solaris,cgo - -package mount - -/* -#include -#include -#include -*/ -import "C" - -import ( - "fmt" - "unsafe" -) - -// Self retrieves a list of mounts for the current running process. -func Self() ([]Info, error) { - path := C.CString(C.MNTTAB) - defer C.free(unsafe.Pointer(path)) - mode := C.CString("r") - defer C.free(unsafe.Pointer(mode)) - - mnttab := C.fopen(path, mode) - if mnttab == nil { - return nil, fmt.Errorf("Failed to open %s", C.MNTTAB) - } - - var out []Info - var mp C.struct_mnttab - - ret := C.getmntent(mnttab, &mp) - for ret == 0 { - var mountinfo Info - mountinfo.Mountpoint = C.GoString(mp.mnt_mountp) - mountinfo.Source = C.GoString(mp.mnt_special) - mountinfo.FSType = C.GoString(mp.mnt_fstype) - mountinfo.Options = C.GoString(mp.mnt_mntopts) - out = append(out, mountinfo) - ret = C.getmntent(mnttab, &mp) - } - - C.fclose(mnttab) - return out, nil -} - -// PID collects the mounts for a specific process ID. -func PID(pid int) ([]Info, error) { - return nil, fmt.Errorf("mountinfo.PID is not implemented on solaris") -} 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 7fff5c6c63..87e53b84f2 100644 --- a/components/engine/vendor/github.com/containerd/containerd/plugin/context.go +++ b/components/engine/vendor/github.com/containerd/containerd/plugin/context.go @@ -5,7 +5,7 @@ import ( "path/filepath" "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/events" + "github.com/containerd/containerd/events/exchange" "github.com/containerd/containerd/log" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -18,15 +18,15 @@ type InitContext struct { State string Config interface{} Address string - Events *events.Exchange + Events *exchange.Exchange Meta *Meta // plugins can fill in metadata at init. - plugins *PluginSet + plugins *Set } // NewContext returns a new plugin InitContext -func NewContext(ctx context.Context, r *Registration, plugins *PluginSet, root, state string) *InitContext { +func NewContext(ctx context.Context, r *Registration, plugins *Set, root, state string) *InitContext { return &InitContext{ Context: log.WithModule(ctx, r.URI()), Root: filepath.Join(root, r.URI()), @@ -61,32 +61,37 @@ type Plugin struct { err error // will be set if there was an error initializing the plugin } +// Err returns the errors during initialization. +// returns nil if not error was encountered func (p *Plugin) Err() error { return p.err } +// Instance returns the instance and any initialization error of the plugin func (p *Plugin) Instance() (interface{}, error) { return p.instance, p.err } -// PluginSet defines a plugin collection, used with InitContext. +// Set defines a plugin collection, used with InitContext. // // This maintains ordering and unique indexing over the set. // // After iteratively instantiating plugins, this set should represent, the // ordered, initialization set of plugins for a containerd instance. -type PluginSet struct { +type Set struct { ordered []*Plugin // order of initialization byTypeAndID map[Type]map[string]*Plugin } -func NewPluginSet() *PluginSet { - return &PluginSet{ +// NewPluginSet returns an initialized plugin set +func NewPluginSet() *Set { + return &Set{ byTypeAndID: make(map[Type]map[string]*Plugin), } } -func (ps *PluginSet) Add(p *Plugin) error { +// Add a plugin to the set +func (ps *Set) Add(p *Plugin) error { if byID, typeok := ps.byTypeAndID[p.Registration.Type]; !typeok { ps.byTypeAndID[p.Registration.Type] = map[string]*Plugin{ p.Registration.ID: p, @@ -102,13 +107,14 @@ func (ps *PluginSet) Add(p *Plugin) error { } // Get returns the first plugin by its type -func (ps *PluginSet) Get(t Type) (interface{}, error) { +func (ps *Set) Get(t Type) (interface{}, error) { for _, v := range ps.byTypeAndID[t] { return v.Instance() } return nil, errors.Wrapf(errdefs.ErrNotFound, "no plugins registered for %s", t) } +// GetAll plugins in the set func (i *InitContext) GetAll() []*Plugin { return i.plugins.ordered } 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 d7b1c0a61a..9bda46cbfa 100644 --- a/components/engine/vendor/github.com/containerd/containerd/plugin/plugin.go +++ b/components/engine/vendor/github.com/containerd/containerd/plugin/plugin.go @@ -58,9 +58,13 @@ const ( // Registration contains information for registering a plugin type Registration struct { - Type Type - ID string - Config interface{} + // Type of the plugin + Type Type + // ID of the plugin + ID string + // Config specific to the plugin + Config interface{} + // Requires is a list of plugins that the registered plugin requires to be available Requires []Type // InitFn is called when initializing a plugin. The registration and @@ -69,6 +73,7 @@ type Registration struct { InitFn func(*InitContext) (interface{}, error) } +// Init the registered plugin func (r *Registration) Init(ic *InitContext) *Plugin { p, err := r.InitFn(ic) return &Plugin{ 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 99940c88ab..52f83d43fa 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 @@ -9,6 +9,7 @@ import ( "fmt" "io" "io/ioutil" + "math/rand" "strings" "sync" "time" @@ -159,7 +160,6 @@ func (c *Converter) Convert(ctx context.Context) (ocispec.Descriptor, error) { } labels := map[string]string{} - labels["containerd.io/gc.root"] = time.Now().UTC().Format(time.RFC3339) labels["containerd.io/gc.ref.content.0"] = manifest.Config.Digest.String() for i, ch := range manifest.Layers { labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i+1)] = ch.Digest.String() @@ -175,12 +175,6 @@ func (c *Converter) Convert(ctx context.Context) (ocispec.Descriptor, error) { return ocispec.Descriptor{}, errors.Wrap(err, "failed to write config") } - for _, ch := range manifest.Layers { - if _, err := c.contentStore.Update(ctx, content.Info{Digest: ch.Digest}, "labels.containerd.io/gc.root"); err != nil { - return ocispec.Descriptor{}, errors.Wrap(err, "failed to remove blob root tag") - } - } - return desc, nil } @@ -215,13 +209,26 @@ func (c *Converter) fetchManifest(ctx context.Context, desc ocispec.Descriptor) func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) error { log.G(ctx).Debug("fetch blob") - ref := remotes.MakeRefKey(ctx, desc) - - calc := newBlobStateCalculator() + var ( + ref = remotes.MakeRefKey(ctx, desc) + calc = newBlobStateCalculator() + retry = 16 + ) +tryit: cw, err := c.contentStore.Writer(ctx, ref, desc.Size, desc.Digest) if err != nil { - if !errdefs.IsAlreadyExists(err) { + if errdefs.IsUnavailable(err) { + select { + case <-time.After(time.Millisecond * time.Duration(rand.Intn(retry))): + if retry < 2048 { + retry = retry << 1 + } + goto tryit + case <-ctx.Done(): + return err + } + } else if !errdefs.IsAlreadyExists(err) { return err } @@ -270,10 +277,7 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro eg.Go(func() error { defer pw.Close() - opt := content.WithLabels(map[string]string{ - "containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339), - }) - return content.Copy(ctx, cw, io.TeeReader(rc, pw), desc.Size, desc.Digest, opt) + return content.Copy(ctx, cw, io.TeeReader(rc, pw), desc.Size, desc.Digest) }) if err := eg.Wait(); err != nil { 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 e6d2132996..e583391d86 100644 --- a/components/engine/vendor/github.com/containerd/containerd/remotes/handlers.go +++ b/components/engine/vendor/github.com/containerd/containerd/remotes/handlers.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "math/rand" "time" "github.com/containerd/containerd/content" @@ -44,7 +45,7 @@ func MakeRefKey(ctx context.Context, desc ocispec.Descriptor) string { // FetchHandler returns a handler that will fetch all content into the ingester // discovered in a call to Dispatch. Use with ChildrenHandler to do a full // recursive fetch. -func FetchHandler(ingester content.Ingester, fetcher Fetcher, root ocispec.Descriptor) images.HandlerFunc { +func FetchHandler(ingester content.Ingester, fetcher Fetcher) images.HandlerFunc { return func(ctx context.Context, desc ocispec.Descriptor) (subdescs []ocispec.Descriptor, err error) { ctx = log.WithLogger(ctx, log.G(ctx).WithFields(logrus.Fields{ "digest": desc.Digest, @@ -56,13 +57,13 @@ func FetchHandler(ingester content.Ingester, fetcher Fetcher, root ocispec.Descr case images.MediaTypeDockerSchema1Manifest: return nil, fmt.Errorf("%v not supported", desc.MediaType) default: - err := fetch(ctx, ingester, fetcher, desc, desc.Digest == root.Digest) + err := fetch(ctx, ingester, fetcher, desc) return nil, err } } } -func fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc ocispec.Descriptor, root bool) error { +func fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc ocispec.Descriptor) error { log.G(ctx).Debug("fetch") var ( @@ -84,7 +85,7 @@ func fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc // of writer and abort if not updated recently. select { - case <-time.After(time.Millisecond * time.Duration(retry)): + case <-time.After(time.Millisecond * time.Duration(rand.Intn(retry))): if retry < 2048 { retry = retry << 1 } @@ -104,13 +105,13 @@ func fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc } defer rc.Close() - r, opts := commitOpts(desc, rc, root) + 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, root bool) (io.Reader, []content.Opt) { +func commitOpts(desc ocispec.Descriptor, r io.Reader) (io.Reader, []content.Opt) { var childrenF func(r io.Reader) ([]ocispec.Descriptor, error) switch desc.MediaType { @@ -162,13 +163,10 @@ func commitOpts(desc ocispec.Descriptor, r io.Reader, root bool) (io.Reader, []c return errors.Wrap(err, "unable to get commit labels") } - if len(children) > 0 || root { + if len(children) > 0 { if info.Labels == nil { info.Labels = map[string]string{} } - if root { - info.Labels["containerd.io/gc.root"] = time.Now().UTC().Format(time.RFC3339) - } for i, ch := range children { info.Labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = ch.Digest.String() } 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 019d4c8cfc..4c02455dcf 100644 --- a/components/engine/vendor/github.com/containerd/containerd/runtime/task.go +++ b/components/engine/vendor/github.com/containerd/containerd/runtime/task.go @@ -7,6 +7,7 @@ import ( "github.com/gogo/protobuf/types" ) +// TaskInfo provides task specific information type TaskInfo struct { ID string Runtime string @@ -14,6 +15,7 @@ type TaskInfo struct { Namespace string } +// Process is a runtime object for an executing process inside a container type Process interface { ID() string // State returns the process state @@ -30,6 +32,7 @@ type Process interface { Wait(context.Context) (*Exit, error) } +// Task is the runtime object for an executing container type Task interface { Process @@ -55,27 +58,37 @@ type Task interface { Metrics(context.Context) (interface{}, error) } +// ExecOpts provides additional options for additional processes running in a task type ExecOpts struct { Spec *types.Any IO IO } +// ConsoleSize of a pty or windows terminal type ConsoleSize struct { Width uint32 Height uint32 } +// Status is the runtime status of a task and/or process type Status int const ( + // CreatedStatus when a process has been created CreatedStatus Status = iota + 1 + // RunningStatus when a process is running RunningStatus + // StoppedStatus when a process has stopped StoppedStatus + // DeletedStatus when a process has been deleted DeletedStatus + // PausedStatus when a process is paused PausedStatus + // PausingStatus when a process is currently pausing PausingStatus ) +// State information for a process type State struct { // Status is the current status of the container Status Status @@ -93,6 +106,7 @@ type State struct { Terminal bool } +// ProcessInfo holds platform specific process information type ProcessInfo struct { // Pid is the process ID Pid uint32 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 12062cef59..7c522655fc 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 @@ -9,21 +9,26 @@ import ( ) var ( - ErrTaskNotExists = errors.New("task does not exist") + // ErrTaskNotExists is returned when a task does not exist + ErrTaskNotExists = errors.New("task does not exist") + // ErrTaskAlreadyExists is returned when a task already exists ErrTaskAlreadyExists = errors.New("task already exists") ) +// NewTaskList returns a new TaskList func NewTaskList() *TaskList { return &TaskList{ tasks: make(map[string]map[string]Task), } } +// TaskList holds and provides locking around tasks type TaskList struct { mu sync.Mutex tasks map[string]map[string]Task } +// Get a task func (l *TaskList) Get(ctx context.Context, id string) (Task, error) { l.mu.Lock() defer l.mu.Unlock() @@ -42,6 +47,7 @@ func (l *TaskList) Get(ctx context.Context, id string) (Task, error) { return t, nil } +// GetAll tasks under a namespace func (l *TaskList) GetAll(ctx context.Context) ([]Task, error) { namespace, err := namespaces.NamespaceRequired(ctx) if err != nil { @@ -58,6 +64,7 @@ func (l *TaskList) GetAll(ctx context.Context) ([]Task, error) { return o, nil } +// Add a task func (l *TaskList) Add(ctx context.Context, t Task) error { namespace, err := namespaces.NamespaceRequired(ctx) if err != nil { @@ -66,6 +73,7 @@ func (l *TaskList) Add(ctx context.Context, t Task) error { return l.AddWithNamespace(namespace, t) } +// AddWithNamespace adds a task with the provided namespace func (l *TaskList) AddWithNamespace(namespace string, t Task) error { l.mu.Lock() defer l.mu.Unlock() @@ -81,6 +89,7 @@ func (l *TaskList) AddWithNamespace(namespace string, t Task) error { return nil } +// Delete a task func (l *TaskList) Delete(ctx context.Context, t Task) { l.mu.Lock() defer l.mu.Unlock() 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 764f6bdf26..26af539acc 100644 --- a/components/engine/vendor/github.com/containerd/containerd/server/config.go +++ b/components/engine/vendor/github.com/containerd/containerd/server/config.go @@ -33,23 +33,27 @@ type Config struct { md toml.MetaData } +// GRPCConfig provides GRPC configuration for the socket type GRPCConfig struct { Address string `toml:"address"` - Uid int `toml:"uid"` - Gid int `toml:"gid"` + UID int `toml:"uid"` + GID int `toml:"gid"` } +// Debug provides debug configuration type Debug struct { Address string `toml:"address"` - Uid int `toml:"uid"` - Gid int `toml:"gid"` + UID int `toml:"uid"` + GID int `toml:"gid"` Level string `toml:"level"` } +// MetricsConfig provides metrics configuration type MetricsConfig struct { Address string `toml:"address"` } +// CgroupConfig provides cgroup configuration type CgroupConfig struct { Path string `toml:"path"` } 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 d1a58e9159..f9ca044835 100644 --- a/components/engine/vendor/github.com/containerd/containerd/server/server.go +++ b/components/engine/vendor/github.com/containerd/containerd/server/server.go @@ -16,13 +16,14 @@ import ( 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" snapshotapi "github.com/containerd/containerd/api/services/snapshot/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" + "github.com/containerd/containerd/events/exchange" "github.com/containerd/containerd/log" "github.com/containerd/containerd/metadata" "github.com/containerd/containerd/plugin" @@ -65,7 +66,7 @@ func New(ctx context.Context, config *Config) (*Server, error) { services []plugin.Service s = &Server{ rpc: rpc, - events: events.NewExchange(), + events: exchange.NewExchange(), } initialized = plugin.NewPluginSet() ) @@ -122,7 +123,7 @@ func New(ctx context.Context, config *Config) (*Server, error) { // Server is the containerd main daemon type Server struct { rpc *grpc.Server - events *events.Exchange + events *exchange.Exchange } // ServeGRPC provides the containerd grpc APIs on the provided listener @@ -255,6 +256,8 @@ func interceptor( 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) } 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 b2f5e8b4fe..03244e90dd 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 @@ -10,19 +10,6 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" ) -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" -) - // apply sets config settings on the server process func apply(ctx context.Context, config *Config) error { if config.Subreaper { diff --git a/components/engine/vendor/github.com/containerd/containerd/services/content/reader.go b/components/engine/vendor/github.com/containerd/containerd/services/content/reader.go index a8cc55430e..024251c6d6 100644 --- a/components/engine/vendor/github.com/containerd/containerd/services/content/reader.go +++ b/components/engine/vendor/github.com/containerd/containerd/services/content/reader.go @@ -44,6 +44,6 @@ func (ra *remoteReaderAt) ReadAt(p []byte, off int64) (n int, err error) { return n, nil } -func (rr *remoteReaderAt) Close() error { +func (ra *remoteReaderAt) Close() error { return nil } diff --git a/components/engine/vendor/github.com/containerd/containerd/services/content/service.go b/components/engine/vendor/github.com/containerd/containerd/services/content/service.go index 8289e1e64b..3784579d56 100644 --- a/components/engine/vendor/github.com/containerd/containerd/services/content/service.go +++ b/components/engine/vendor/github.com/containerd/containerd/services/content/service.go @@ -21,7 +21,7 @@ import ( "google.golang.org/grpc/codes" ) -type Service struct { +type service struct { store content.Store publisher events.Publisher } @@ -32,7 +32,7 @@ var bufPool = sync.Pool{ }, } -var _ api.ContentServer = &Service{} +var _ api.ContentServer = &service{} func init() { plugin.Register(&plugin.Registration{ @@ -53,19 +53,20 @@ func init() { }) } -func NewService(cs content.Store, publisher events.Publisher) (*Service, error) { - return &Service{ +// NewService returns the content GRPC server +func NewService(cs content.Store, publisher events.Publisher) (api.ContentServer, error) { + return &service{ store: cs, publisher: publisher, }, nil } -func (s *Service) Register(server *grpc.Server) error { +func (s *service) Register(server *grpc.Server) error { api.RegisterContentServer(server, s) return nil } -func (s *Service) Info(ctx context.Context, req *api.InfoRequest) (*api.InfoResponse, error) { +func (s *service) Info(ctx context.Context, req *api.InfoRequest) (*api.InfoResponse, error) { if err := req.Digest.Validate(); err != nil { return nil, grpc.Errorf(codes.InvalidArgument, "%q failed validation", req.Digest) } @@ -80,7 +81,7 @@ func (s *Service) Info(ctx context.Context, req *api.InfoRequest) (*api.InfoResp }, nil } -func (s *Service) Update(ctx context.Context, req *api.UpdateRequest) (*api.UpdateResponse, error) { +func (s *service) Update(ctx context.Context, req *api.UpdateRequest) (*api.UpdateResponse, error) { if err := req.Info.Digest.Validate(); err != nil { return nil, grpc.Errorf(codes.InvalidArgument, "%q failed validation", req.Info.Digest) } @@ -95,7 +96,7 @@ func (s *Service) Update(ctx context.Context, req *api.UpdateRequest) (*api.Upda }, nil } -func (s *Service) List(req *api.ListContentRequest, session api.Content_ListServer) error { +func (s *service) List(req *api.ListContentRequest, session api.Content_ListServer) error { var ( buffer []api.Info sendBlock = func(block []api.Info) error { @@ -137,7 +138,7 @@ func (s *Service) List(req *api.ListContentRequest, session api.Content_ListServ return nil } -func (s *Service) Delete(ctx context.Context, req *api.DeleteContentRequest) (*empty.Empty, error) { +func (s *service) Delete(ctx context.Context, req *api.DeleteContentRequest) (*empty.Empty, error) { if err := req.Digest.Validate(); err != nil { return nil, grpc.Errorf(codes.InvalidArgument, err.Error()) } @@ -155,7 +156,7 @@ func (s *Service) Delete(ctx context.Context, req *api.DeleteContentRequest) (*e return &empty.Empty{}, nil } -func (s *Service) Read(req *api.ReadContentRequest, session api.Content_ReadServer) error { +func (s *service) Read(req *api.ReadContentRequest, session api.Content_ReadServer) error { if err := req.Digest.Validate(); err != nil { return grpc.Errorf(codes.InvalidArgument, "%v: %v", req.Digest, err) } @@ -223,7 +224,7 @@ func (rw *readResponseWriter) Write(p []byte) (n int, err error) { return len(p), nil } -func (s *Service) Status(ctx context.Context, req *api.StatusRequest) (*api.StatusResponse, error) { +func (s *service) Status(ctx context.Context, req *api.StatusRequest) (*api.StatusResponse, error) { status, err := s.store.Status(ctx, req.Ref) if err != nil { return nil, errdefs.ToGRPCf(err, "could not get status for ref %q", req.Ref) @@ -242,7 +243,7 @@ func (s *Service) Status(ctx context.Context, req *api.StatusRequest) (*api.Stat return &resp, nil } -func (s *Service) ListStatuses(ctx context.Context, req *api.ListStatusesRequest) (*api.ListStatusesResponse, error) { +func (s *service) ListStatuses(ctx context.Context, req *api.ListStatusesRequest) (*api.ListStatusesResponse, error) { statuses, err := s.store.ListStatuses(ctx, req.Filters...) if err != nil { return nil, errdefs.ToGRPC(err) @@ -263,7 +264,7 @@ func (s *Service) ListStatuses(ctx context.Context, req *api.ListStatusesRequest return &resp, nil } -func (s *Service) Write(session api.Content_WriteServer) (err error) { +func (s *service) Write(session api.Content_WriteServer) (err error) { var ( ctx = session.Context() msg api.WriteContentResponse @@ -283,7 +284,7 @@ func (s *Service) Write(session api.Content_WriteServer) (err error) { // identically across all GRPC methods. // // This is pretty noisy, so we can remove it but leave it for now. - log.G(ctx).WithError(err).Error("(*Service).Write failed") + log.G(ctx).WithError(err).Error("(*service).Write failed") } return @@ -319,7 +320,7 @@ func (s *Service) Write(session api.Content_WriteServer) (err error) { ctx = log.WithLogger(ctx, log.G(ctx).WithFields(fields)) - log.G(ctx).Debug("(*Service).Write started") + log.G(ctx).Debug("(*service).Write started") // this action locks the writer for the session. wr, err := s.store.Writer(ctx, ref, total, expected) if err != nil { @@ -444,7 +445,7 @@ func (s *Service) Write(session api.Content_WriteServer) (err error) { } } -func (s *Service) Abort(ctx context.Context, req *api.AbortRequest) (*empty.Empty, error) { +func (s *service) Abort(ctx context.Context, req *api.AbortRequest) (*empty.Empty, error) { if err := s.store.Abort(ctx, req.Ref); err != nil { return nil, errdefs.ToGRPC(err) } diff --git a/components/engine/vendor/github.com/containerd/containerd/services/content/store.go b/components/engine/vendor/github.com/containerd/containerd/services/content/store.go index 11fcc03651..b5aaa8577c 100644 --- a/components/engine/vendor/github.com/containerd/containerd/services/content/store.go +++ b/components/engine/vendor/github.com/containerd/containerd/services/content/store.go @@ -15,6 +15,7 @@ type remoteStore struct { client contentapi.ContentClient } +// NewStoreFromClient returns a new content store func NewStoreFromClient(client contentapi.ContentClient) content.Store { return &remoteStore{ client: client, diff --git a/components/engine/vendor/github.com/containerd/containerd/services/diff/client.go b/components/engine/vendor/github.com/containerd/containerd/services/diff/client.go index 5267f97077..d34848be20 100644 --- a/components/engine/vendor/github.com/containerd/containerd/services/diff/client.go +++ b/components/engine/vendor/github.com/containerd/containerd/services/diff/client.go @@ -9,7 +9,7 @@ import ( "golang.org/x/net/context" ) -// NewApplierFromClient returns a new Applier which communicates +// NewDiffServiceFromClient returns a new diff service which communicates // over a GRPC connection. func NewDiffServiceFromClient(client diffapi.DiffClient) diff.Differ { return &remote{ diff --git a/components/engine/vendor/github.com/containerd/containerd/services/images/client.go b/components/engine/vendor/github.com/containerd/containerd/services/images/client.go index eebe776fac..f746ddce8b 100644 --- a/components/engine/vendor/github.com/containerd/containerd/services/images/client.go +++ b/components/engine/vendor/github.com/containerd/containerd/services/images/client.go @@ -13,6 +13,7 @@ type remoteStore struct { client imagesapi.ImagesClient } +// NewStoreFromClient returns a new image store client func NewStoreFromClient(client imagesapi.ImagesClient) images.Store { return &remoteStore{ client: client, diff --git a/components/engine/vendor/github.com/containerd/containerd/services/images/service.go b/components/engine/vendor/github.com/containerd/containerd/services/images/service.go index fa8a00aaea..3843df554c 100644 --- a/components/engine/vendor/github.com/containerd/containerd/services/images/service.go +++ b/components/engine/vendor/github.com/containerd/containerd/services/images/service.go @@ -34,24 +34,25 @@ func init() { }) } -type Service struct { +type service struct { db *metadata.DB publisher events.Publisher } +// NewService returns the GRPC image server func NewService(db *metadata.DB, publisher events.Publisher) imagesapi.ImagesServer { - return &Service{ + return &service{ db: db, publisher: publisher, } } -func (s *Service) Register(server *grpc.Server) error { +func (s *service) Register(server *grpc.Server) error { imagesapi.RegisterImagesServer(server, s) return nil } -func (s *Service) Get(ctx context.Context, req *imagesapi.GetImageRequest) (*imagesapi.GetImageResponse, error) { +func (s *service) Get(ctx context.Context, req *imagesapi.GetImageRequest) (*imagesapi.GetImageResponse, error) { var resp imagesapi.GetImageResponse return &resp, errdefs.ToGRPC(s.withStoreView(ctx, func(ctx context.Context, store images.Store) error { @@ -65,7 +66,7 @@ func (s *Service) Get(ctx context.Context, req *imagesapi.GetImageRequest) (*ima })) } -func (s *Service) List(ctx context.Context, req *imagesapi.ListImagesRequest) (*imagesapi.ListImagesResponse, error) { +func (s *service) List(ctx context.Context, req *imagesapi.ListImagesRequest) (*imagesapi.ListImagesResponse, error) { var resp imagesapi.ListImagesResponse return &resp, errdefs.ToGRPC(s.withStoreView(ctx, func(ctx context.Context, store images.Store) error { @@ -79,7 +80,7 @@ func (s *Service) List(ctx context.Context, req *imagesapi.ListImagesRequest) (* })) } -func (s *Service) Create(ctx context.Context, req *imagesapi.CreateImageRequest) (*imagesapi.CreateImageResponse, error) { +func (s *service) Create(ctx context.Context, req *imagesapi.CreateImageRequest) (*imagesapi.CreateImageResponse, error) { if req.Image.Name == "" { return nil, status.Errorf(codes.InvalidArgument, "Image.Name required") } @@ -111,7 +112,7 @@ func (s *Service) Create(ctx context.Context, req *imagesapi.CreateImageRequest) } -func (s *Service) Update(ctx context.Context, req *imagesapi.UpdateImageRequest) (*imagesapi.UpdateImageResponse, error) { +func (s *service) Update(ctx context.Context, req *imagesapi.UpdateImageRequest) (*imagesapi.UpdateImageResponse, error) { if req.Image.Name == "" { return nil, status.Errorf(codes.InvalidArgument, "Image.Name required") } @@ -149,7 +150,7 @@ func (s *Service) Update(ctx context.Context, req *imagesapi.UpdateImageRequest) return &resp, nil } -func (s *Service) Delete(ctx context.Context, req *imagesapi.DeleteImageRequest) (*empty.Empty, error) { +func (s *service) Delete(ctx context.Context, req *imagesapi.DeleteImageRequest) (*empty.Empty, error) { if err := s.withStoreUpdate(ctx, func(ctx context.Context, store images.Store) error { return errdefs.ToGRPC(store.Delete(ctx, req.Name)) }); err != nil { @@ -169,14 +170,14 @@ func (s *Service) Delete(ctx context.Context, req *imagesapi.DeleteImageRequest) return &empty.Empty{}, nil } -func (s *Service) withStore(ctx context.Context, fn func(ctx context.Context, store images.Store) error) func(tx *bolt.Tx) error { +func (s *service) withStore(ctx context.Context, fn func(ctx context.Context, store images.Store) error) func(tx *bolt.Tx) error { return func(tx *bolt.Tx) error { return fn(ctx, metadata.NewImageStore(tx)) } } -func (s *Service) withStoreView(ctx context.Context, fn func(ctx context.Context, store images.Store) error) error { +func (s *service) withStoreView(ctx context.Context, fn func(ctx context.Context, store images.Store) error) error { return s.db.View(s.withStore(ctx, fn)) } -func (s *Service) withStoreUpdate(ctx context.Context, fn func(ctx context.Context, store images.Store) error) error { +func (s *service) withStoreUpdate(ctx context.Context, fn func(ctx context.Context, store images.Store) error) error { return s.db.Update(s.withStore(ctx, fn)) } diff --git a/components/engine/vendor/github.com/containerd/containerd/services/namespaces/client.go b/components/engine/vendor/github.com/containerd/containerd/services/namespaces/client.go new file mode 100644 index 0000000000..fd59ec619d --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/services/namespaces/client.go @@ -0,0 +1,97 @@ +package namespaces + +import ( + "context" + "strings" + + api "github.com/containerd/containerd/api/services/namespaces/v1" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/namespaces" + "github.com/gogo/protobuf/types" +) + +// NewStoreFromClient returns a new namespace store +func NewStoreFromClient(client api.NamespacesClient) namespaces.Store { + return &remote{client: client} +} + +type remote struct { + client api.NamespacesClient +} + +func (r *remote) Create(ctx context.Context, namespace string, labels map[string]string) error { + var req api.CreateNamespaceRequest + + req.Namespace = api.Namespace{ + Name: namespace, + Labels: labels, + } + + _, err := r.client.Create(ctx, &req) + if err != nil { + return errdefs.FromGRPC(err) + } + + return nil +} + +func (r *remote) Labels(ctx context.Context, namespace string) (map[string]string, error) { + var req api.GetNamespaceRequest + req.Name = namespace + + resp, err := r.client.Get(ctx, &req) + if err != nil { + return nil, errdefs.FromGRPC(err) + } + + return resp.Namespace.Labels, nil +} + +func (r *remote) SetLabel(ctx context.Context, namespace, key, value string) error { + var req api.UpdateNamespaceRequest + + req.Namespace = api.Namespace{ + Name: namespace, + Labels: map[string]string{key: value}, + } + + req.UpdateMask = &types.FieldMask{ + Paths: []string{strings.Join([]string{"labels", key}, ".")}, + } + + _, err := r.client.Update(ctx, &req) + if err != nil { + return errdefs.FromGRPC(err) + } + + return nil +} + +func (r *remote) List(ctx context.Context) ([]string, error) { + var req api.ListNamespacesRequest + + resp, err := r.client.List(ctx, &req) + if err != nil { + return nil, errdefs.FromGRPC(err) + } + + var namespaces []string + + for _, ns := range resp.Namespaces { + namespaces = append(namespaces, ns.Name) + } + + return namespaces, nil +} + +func (r *remote) Delete(ctx context.Context, namespace string) error { + var req api.DeleteNamespaceRequest + + req.Name = namespace + _, err := r.client.Delete(ctx, &req) + if err != nil { + return errdefs.FromGRPC(err) + } + + return nil +} diff --git a/components/engine/vendor/github.com/containerd/containerd/services/namespaces/service.go b/components/engine/vendor/github.com/containerd/containerd/services/namespaces/service.go new file mode 100644 index 0000000000..b795ab52f7 --- /dev/null +++ b/components/engine/vendor/github.com/containerd/containerd/services/namespaces/service.go @@ -0,0 +1,212 @@ +package namespaces + +import ( + "strings" + + "github.com/boltdb/bolt" + eventsapi "github.com/containerd/containerd/api/services/events/v1" + api "github.com/containerd/containerd/api/services/namespaces/v1" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/events" + "github.com/containerd/containerd/metadata" + "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/plugin" + "github.com/golang/protobuf/ptypes/empty" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +func init() { + plugin.Register(&plugin.Registration{ + Type: plugin.GRPCPlugin, + ID: "namespaces", + Requires: []plugin.Type{ + plugin.MetadataPlugin, + }, + InitFn: func(ic *plugin.InitContext) (interface{}, error) { + m, err := ic.Get(plugin.MetadataPlugin) + if err != nil { + return nil, err + } + return NewService(m.(*metadata.DB), ic.Events), nil + }, + }) +} + +type service struct { + db *metadata.DB + publisher events.Publisher +} + +var _ api.NamespacesServer = &service{} + +// NewService returns the GRPC namespaces server +func NewService(db *metadata.DB, publisher events.Publisher) api.NamespacesServer { + return &service{ + db: db, + publisher: publisher, + } +} + +func (s *service) Register(server *grpc.Server) error { + api.RegisterNamespacesServer(server, s) + return nil +} + +func (s *service) Get(ctx context.Context, req *api.GetNamespaceRequest) (*api.GetNamespaceResponse, error) { + var resp api.GetNamespaceResponse + + return &resp, s.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error { + labels, err := store.Labels(ctx, req.Name) + if err != nil { + return errdefs.ToGRPC(err) + } + + resp.Namespace = api.Namespace{ + Name: req.Name, + Labels: labels, + } + + return nil + }) +} + +func (s *service) List(ctx context.Context, req *api.ListNamespacesRequest) (*api.ListNamespacesResponse, error) { + var resp api.ListNamespacesResponse + + return &resp, s.withStoreView(ctx, func(ctx context.Context, store namespaces.Store) error { + namespaces, err := store.List(ctx) + if err != nil { + return err + } + + for _, namespace := range namespaces { + labels, err := store.Labels(ctx, namespace) + if err != nil { + // In general, this should be unlikely, since we are holding a + // transaction to service this request. + return errdefs.ToGRPC(err) + } + + resp.Namespaces = append(resp.Namespaces, api.Namespace{ + Name: namespace, + Labels: labels, + }) + } + + return nil + }) +} + +func (s *service) Create(ctx context.Context, req *api.CreateNamespaceRequest) (*api.CreateNamespaceResponse, error) { + var resp api.CreateNamespaceResponse + + if err := s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error { + if err := store.Create(ctx, req.Namespace.Name, req.Namespace.Labels); err != nil { + return errdefs.ToGRPC(err) + } + + for k, v := range req.Namespace.Labels { + if err := store.SetLabel(ctx, req.Namespace.Name, k, v); err != nil { + return err + } + } + + resp.Namespace = req.Namespace + return nil + }); err != nil { + return &resp, err + } + + if err := s.publisher.Publish(ctx, "/namespaces/create", &eventsapi.NamespaceCreate{ + Name: req.Namespace.Name, + Labels: req.Namespace.Labels, + }); err != nil { + return &resp, err + } + + return &resp, nil + +} + +func (s *service) Update(ctx context.Context, req *api.UpdateNamespaceRequest) (*api.UpdateNamespaceResponse, error) { + var resp api.UpdateNamespaceResponse + if err := s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error { + if req.UpdateMask != nil && len(req.UpdateMask.Paths) > 0 { + for _, path := range req.UpdateMask.Paths { + switch { + case strings.HasPrefix(path, "labels."): + key := strings.TrimPrefix(path, "labels.") + if err := store.SetLabel(ctx, req.Namespace.Name, key, req.Namespace.Labels[key]); err != nil { + return err + } + default: + return grpc.Errorf(codes.InvalidArgument, "cannot update %q field", path) + } + } + } else { + // clear out the existing labels and then set them to the incoming request. + // get current set of labels + labels, err := store.Labels(ctx, req.Namespace.Name) + if err != nil { + return errdefs.ToGRPC(err) + } + + for k := range labels { + if err := store.SetLabel(ctx, req.Namespace.Name, k, ""); err != nil { + return err + } + } + + for k, v := range req.Namespace.Labels { + if err := store.SetLabel(ctx, req.Namespace.Name, k, v); err != nil { + return err + } + + } + } + + return nil + }); err != nil { + return &resp, err + } + + if err := s.publisher.Publish(ctx, "/namespaces/update", &eventsapi.NamespaceUpdate{ + Name: req.Namespace.Name, + Labels: req.Namespace.Labels, + }); err != nil { + return &resp, err + } + + return &resp, nil +} + +func (s *service) Delete(ctx context.Context, req *api.DeleteNamespaceRequest) (*empty.Empty, error) { + if err := s.withStoreUpdate(ctx, func(ctx context.Context, store namespaces.Store) error { + return errdefs.ToGRPC(store.Delete(ctx, req.Name)) + }); err != nil { + return &empty.Empty{}, err + } + // set the namespace in the context before publishing the event + ctx = namespaces.WithNamespace(ctx, req.Name) + if err := s.publisher.Publish(ctx, "/namespaces/delete", &eventsapi.NamespaceDelete{ + Name: req.Name, + }); err != nil { + return &empty.Empty{}, err + } + + return &empty.Empty{}, nil +} + +func (s *service) withStore(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) func(tx *bolt.Tx) error { + return func(tx *bolt.Tx) error { return fn(ctx, metadata.NewNamespaceStore(tx)) } +} + +func (s *service) withStoreView(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) error { + return s.db.View(s.withStore(ctx, fn)) +} + +func (s *service) withStoreUpdate(ctx context.Context, fn func(ctx context.Context, store namespaces.Store) error) error { + return s.db.Update(s.withStore(ctx, fn)) +} diff --git a/components/engine/vendor/github.com/containerd/containerd/snapshot/snapshotter.go b/components/engine/vendor/github.com/containerd/containerd/snapshot/snapshotter.go index 6beafd48af..2b3fe62755 100644 --- a/components/engine/vendor/github.com/containerd/containerd/snapshot/snapshotter.go +++ b/components/engine/vendor/github.com/containerd/containerd/snapshot/snapshotter.go @@ -20,6 +20,9 @@ const ( KindCommitted ) +// ParseKind parses the provided string into a Kind +// +// If the string cannot be parsed KindUnknown is returned func ParseKind(s string) Kind { s = strings.ToLower(s) switch s { @@ -34,6 +37,7 @@ func ParseKind(s string) Kind { return KindUnknown } +// String returns the string representation of the Kind func (k Kind) String() string { switch k { case KindView: @@ -47,10 +51,12 @@ func (k Kind) String() string { return "Unknown" } +// MarshalJSON the Kind to JSON func (k Kind) MarshalJSON() ([]byte, error) { return json.Marshal(k.String()) } +// UnmarshalJSON the Kind from JSON func (k *Kind) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { @@ -81,6 +87,7 @@ type Usage struct { Size int64 // provides usage, in bytes, of snapshot } +// Add the provided usage to the current usage func (u *Usage) Add(other Usage) { u.Size += other.Size diff --git a/components/engine/vendor/github.com/containerd/containerd/spec_opts_unix.go b/components/engine/vendor/github.com/containerd/containerd/spec_opts_unix.go index 7009522d22..01d5121d41 100644 --- a/components/engine/vendor/github.com/containerd/containerd/spec_opts_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/spec_opts_unix.go @@ -11,17 +11,16 @@ import ( "path/filepath" "strconv" "strings" - "time" "golang.org/x/sys/unix" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/fs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/platforms" - "github.com/containerd/containerd/snapshot" "github.com/opencontainers/image-spec/identity" "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/runc/libcontainer/user" @@ -260,19 +259,17 @@ func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool snapshotter = client.SnapshotService(c.Snapshotter) parent = identity.ChainID(diffIDs).String() usernsID = fmt.Sprintf("%s-%d-%d", parent, uid, gid) - opt = snapshot.WithLabels(map[string]string{ - "containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339), - }) ) if _, err := snapshotter.Stat(ctx, usernsID); err == nil { - if _, err := snapshotter.Prepare(ctx, id, usernsID, opt); err != nil { + if _, err := snapshotter.Prepare(ctx, id, usernsID); err == nil { + c.SnapshotKey = id + c.Image = i.Name() + return nil + } else if !errdefs.IsNotFound(err) { return err } - c.SnapshotKey = id - c.Image = i.Name() - return nil } - mounts, err := snapshotter.Prepare(ctx, usernsID+"-remap", parent, opt) + mounts, err := snapshotter.Prepare(ctx, usernsID+"-remap", parent) if err != nil { return err } @@ -280,13 +277,13 @@ func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool snapshotter.Remove(ctx, usernsID) return err } - if err := snapshotter.Commit(ctx, usernsID, usernsID+"-remap", opt); err != nil { + if err := snapshotter.Commit(ctx, usernsID, usernsID+"-remap"); err != nil { return err } if readonly { - _, err = snapshotter.View(ctx, id, usernsID, opt) + _, err = snapshotter.View(ctx, id, usernsID) } else { - _, err = snapshotter.Prepare(ctx, id, usernsID, opt) + _, err = snapshotter.Prepare(ctx, id, usernsID) } if err != nil { return err diff --git a/components/engine/vendor/github.com/containerd/containerd/spec_opts_windows.go b/components/engine/vendor/github.com/containerd/containerd/spec_opts_windows.go index 5aa5c30297..1fc5d5e37d 100644 --- a/components/engine/vendor/github.com/containerd/containerd/spec_opts_windows.go +++ b/components/engine/vendor/github.com/containerd/containerd/spec_opts_windows.go @@ -15,6 +15,7 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" ) +// WithImageConfig configures the spec to from the configuration of an Image func WithImageConfig(i Image) SpecOpts { return func(ctx context.Context, client *Client, _ *containers.Container, s *specs.Spec) error { var ( @@ -51,6 +52,8 @@ func WithImageConfig(i Image) SpecOpts { } } +// WithTTY sets the information on the spec as well as the environment variables for +// using a TTY func WithTTY(width, height int) SpecOpts { return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { s.Process.Terminal = true @@ -63,6 +66,7 @@ func WithTTY(width, height int) SpecOpts { } } +// WithResources sets the provided resources on the spec for task updates func WithResources(resources *specs.WindowsResources) UpdateTaskOpts { return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error { r.Resources = resources diff --git a/components/engine/vendor/github.com/containerd/containerd/spec_unix.go b/components/engine/vendor/github.com/containerd/containerd/spec_unix.go index 9a0b537dc5..957f90ef91 100644 --- a/components/engine/vendor/github.com/containerd/containerd/spec_unix.go +++ b/components/engine/vendor/github.com/containerd/containerd/spec_unix.go @@ -151,6 +151,7 @@ func createDefaultSpec(ctx context.Context, id string) (*specs.Spec, error) { "/proc/timer_stats", "/proc/sched_debug", "/sys/firmware", + "/proc/scsi", }, ReadonlyPaths: []string{ "/proc/asound", 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 a72568b279..6e42ddce8e 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,5 +1,8 @@ package sys +// SetOOMScore sets the oom score for the process +// +// Not implemented on Windows func SetOOMScore(pid, score int) error { return nil } diff --git a/components/engine/vendor/github.com/containerd/containerd/sys/prctl_solaris.go b/components/engine/vendor/github.com/containerd/containerd/sys/prctl_solaris.go deleted file mode 100644 index 9443f14dbd..0000000000 --- a/components/engine/vendor/github.com/containerd/containerd/sys/prctl_solaris.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build solaris - -package sys - -import ( - "errors" -) - -//Solaris TODO - -// GetSubreaper returns the subreaper setting for the calling process -func GetSubreaper() (int, error) { - return 0, errors.New("osutils GetSubreaper not implemented on Solaris") -} - -// SetSubreaper sets the value i as the subreaper setting for the calling process -func SetSubreaper(i int) error { - return errors.New("osutils SetSubreaper not implemented on Solaris") -} 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 13db2b32e7..e043ae52bf 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 @@ -6,14 +6,17 @@ import ( "syscall" ) +// StatAtime returns the access time from a stat struct func StatAtime(st *syscall.Stat_t) syscall.Timespec { return st.Atimespec } +// StatCtime returns the created time from a stat struct func StatCtime(st *syscall.Stat_t) syscall.Timespec { return st.Ctimespec } +// StatMtime returns the modified time from a stat struct func StatMtime(st *syscall.Stat_t) syscall.Timespec { return st.Mtimespec } 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 da13ed26e2..1f983a98db 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 @@ -6,14 +6,17 @@ import ( "syscall" ) +// StatAtime returns the Atim func StatAtime(st *syscall.Stat_t) syscall.Timespec { return st.Atim } +// StatCtime returns the Ctim func StatCtime(st *syscall.Stat_t) syscall.Timespec { return st.Ctim } +// StatMtime returns the Mtim func StatMtime(st *syscall.Stat_t) syscall.Timespec { return st.Mtim } diff --git a/components/engine/vendor/github.com/containerd/containerd/task.go b/components/engine/vendor/github.com/containerd/containerd/task.go index 6b9af1d410..7ae1bf6228 100644 --- a/components/engine/vendor/github.com/containerd/containerd/task.go +++ b/components/engine/vendor/github.com/containerd/containerd/task.go @@ -18,7 +18,6 @@ import ( "github.com/containerd/containerd/diff" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" - "github.com/containerd/containerd/log" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/rootfs" @@ -26,7 +25,6 @@ import ( google_protobuf "github.com/gogo/protobuf/types" digest "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/specs-go/v1" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -51,6 +49,7 @@ type Status struct { ExitTime time.Time } +// ProcessInfo provides platform specific process information type ProcessInfo struct { // Pid is the process ID Pid uint32 @@ -358,6 +357,12 @@ func (t *task) Resize(ctx context.Context, w, h uint32) error { } func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Image, error) { + ctx, done, err := t.client.withLease(ctx) + if err != nil { + return nil, err + } + defer done() + request := &tasks.CheckpointTaskRequest{ ContainerID: t.id, } @@ -391,15 +396,6 @@ func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Imag index := v1.Index{ Annotations: make(map[string]string), } - // make sure we clear the gc root labels reguardless of success - var clearRoots []ocispec.Descriptor - defer func() { - for _, r := range append(index.Manifests, clearRoots...) { - if err := clearRootGCLabel(ctx, t.client, r); err != nil { - log.G(ctx).WithError(err).WithField("dgst", r.Digest).Warnf("failed to remove root marker") - } - } - }() if err := t.checkpointTask(ctx, &index, request); err != nil { return nil, err } @@ -418,7 +414,6 @@ func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Imag if err != nil { return nil, err } - clearRoots = append(clearRoots, desc) im := images.Image{ Name: i.Name, Target: desc, @@ -534,9 +529,6 @@ func (t *task) checkpointTask(ctx context.Context, index *v1.Index, request *tas func (t *task) checkpointRWSnapshot(ctx context.Context, index *v1.Index, snapshotterName string, id string) error { opts := []diff.Opt{ diff.WithReference(fmt.Sprintf("checkpoint-rw-%s", id)), - diff.WithLabels(map[string]string{ - "containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339), - }), } rw, err := rootfs.Diff(ctx, id, t.client.SnapshotService(snapshotterName), t.client.DiffService(), opts...) if err != nil { @@ -563,9 +555,7 @@ func (t *task) checkpointImage(ctx context.Context, index *v1.Index, image strin } func (t *task) writeIndex(ctx context.Context, index *v1.Index) (d v1.Descriptor, err error) { - labels := map[string]string{ - "containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339), - } + labels := map[string]string{} for i, m := range index.Manifests { labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = m.Digest.String() } @@ -595,9 +585,3 @@ func writeContent(ctx context.Context, store content.Store, mediaType, ref strin Size: size, }, nil } - -func clearRootGCLabel(ctx context.Context, client *Client, desc ocispec.Descriptor) error { - info := content.Info{Digest: desc.Digest} - _, err := client.ContentStore().Update(ctx, info, "labels.containerd.io/gc.root") - return err -} diff --git a/components/engine/vendor/github.com/containerd/containerd/vendor.conf b/components/engine/vendor/github.com/containerd/containerd/vendor.conf index 1255a4e1a3..671346821c 100644 --- a/components/engine/vendor/github.com/containerd/containerd/vendor.conf +++ b/components/engine/vendor/github.com/containerd/containerd/vendor.conf @@ -16,14 +16,14 @@ github.com/docker/go-units v0.3.1 github.com/gogo/protobuf d2e1ade2d719b78fe5b061b4c18a9f7111b5bdc8 github.com/golang/protobuf 5a0f697c9ed9d68fef0116532c6e05cfeae00e55 github.com/opencontainers/runtime-spec v1.0.0 -github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d +github.com/opencontainers/runc 74a17296470088de3805e138d3d87c62e613dfc4 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 8ba6f23b6e36d03666a14bd9421f5e3efcb59aca +github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 google.golang.org/grpc v1.3.0 github.com/pkg/errors v0.8.0 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 1712b1aedc..4b1b4b3414 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,2 @@ -// hcsshimtypes holds the windows runtime specific types +// Package hcsshimtypes holds the windows runtime specific types package hcsshimtypes From aa1c9fab4fcb3fb8b4233c255030595927febb88 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 12 Nov 2017 03:09:28 +0100 Subject: [PATCH 60/94] Remove deprecated support for duplicate label-keys Support for duplicate labels (but different values) was deprecated in commit e4c9079d091a2eeac8a74a0356e3f348db873b87 (Docker 1.13), and scheduled for removal in 17.12 Signed-off-by: Sebastiaan van Stijn Upstream-commit: 8c6322338c91cdb88b1fef4def393d9a7b670366 Component: engine --- components/engine/cmd/dockerd/daemon.go | 20 ++++---------- components/engine/cmd/dockerd/daemon_test.go | 22 +++++++++++++++ components/engine/daemon/config/config.go | 20 ++++---------- .../engine/daemon/config/config_test.go | 27 +++++++++++++++++++ .../integration-cli/docker_cli_info_test.go | 11 -------- 5 files changed, 59 insertions(+), 41 deletions(-) diff --git a/components/engine/cmd/dockerd/daemon.go b/components/engine/cmd/dockerd/daemon.go index 44e16677e7..02a03141df 100644 --- a/components/engine/cmd/dockerd/daemon.go +++ b/components/engine/cmd/dockerd/daemon.go @@ -480,22 +480,12 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) { logrus.Warnf(`The "-g / --graph" flag is deprecated. Please use "--data-root" instead`) } - // Labels of the docker engine used to allow multiple values associated with the same key. - // This is deprecated in 1.13, and, be removed after 3 release cycles. - // The following will check the conflict of labels, and report a warning for deprecation. - // - // TODO: After 3 release cycles (17.12) an error will be returned, and labels will be - // sanitized to consolidate duplicate key-value pairs (config.Labels = newLabels): - // - // newLabels, err := daemon.GetConflictFreeLabels(config.Labels) - // if err != nil { - // return nil, err - // } - // config.Labels = newLabels - // - if _, err := config.GetConflictFreeLabels(conf.Labels); err != nil { - logrus.Warnf("Engine labels with duplicate keys and conflicting values have been deprecated: %s", err) + // Check if duplicate label-keys with different values are found + newLabels, err := config.GetConflictFreeLabels(conf.Labels) + if err != nil { + return nil, err } + conf.Labels = newLabels // Regardless of whether the user sets it to true or false, if they // specify TLSVerify at all then we need to turn on TLS diff --git a/components/engine/cmd/dockerd/daemon_test.go b/components/engine/cmd/dockerd/daemon_test.go index c559ee82a5..e5e4aa34e2 100644 --- a/components/engine/cmd/dockerd/daemon_test.go +++ b/components/engine/cmd/dockerd/daemon_test.go @@ -61,6 +61,28 @@ func TestLoadDaemonCliConfigWithConflicts(t *testing.T) { testutil.ErrorContains(t, err, "as a flag and in the configuration file: labels") } +func TestLoadDaemonCliWithConflictingLabels(t *testing.T) { + opts := defaultOptions("") + flags := opts.flags + + assert.NoError(t, flags.Set("label", "foo=bar")) + assert.NoError(t, flags.Set("label", "foo=baz")) + + _, err := loadDaemonCliConfig(opts) + assert.EqualError(t, err, "conflict labels for foo=baz and foo=bar") +} + +func TestLoadDaemonCliWithDuplicateLabels(t *testing.T) { + opts := defaultOptions("") + flags := opts.flags + + assert.NoError(t, flags.Set("label", "foo=the-same")) + assert.NoError(t, flags.Set("label", "foo=the-same")) + + _, err := loadDaemonCliConfig(opts) + assert.NoError(t, err) +} + func TestLoadDaemonCliConfigWithTLSVerify(t *testing.T) { tempFile := fs.NewFile(t, "config", fs.WithContent(`{"tlsverify": true}`)) defer tempFile.Remove() diff --git a/components/engine/daemon/config/config.go b/components/engine/daemon/config/config.go index f7a0df58ed..1e22a6fea7 100644 --- a/components/engine/daemon/config/config.go +++ b/components/engine/daemon/config/config.go @@ -258,22 +258,12 @@ func Reload(configFile string, flags *pflag.FlagSet, reload func(*Config)) error return fmt.Errorf("file configuration validation failed (%v)", err) } - // Labels of the docker engine used to allow multiple values associated with the same key. - // This is deprecated in 1.13, and, be removed after 3 release cycles. - // The following will check the conflict of labels, and report a warning for deprecation. - // - // TODO: After 3 release cycles (17.12) an error will be returned, and labels will be - // sanitized to consolidate duplicate key-value pairs (config.Labels = newLabels): - // - // newLabels, err := GetConflictFreeLabels(newConfig.Labels) - // if err != nil { - // return err - // } - // newConfig.Labels = newLabels - // - if _, err := GetConflictFreeLabels(newConfig.Labels); err != nil { - logrus.Warnf("Engine labels with duplicate keys and conflicting values have been deprecated: %s", err) + // Check if duplicate label-keys with different values are found + newLabels, err := GetConflictFreeLabels(newConfig.Labels) + if err != nil { + return err } + newConfig.Labels = newLabels reload(newConfig) return nil diff --git a/components/engine/daemon/config/config_test.go b/components/engine/daemon/config/config_test.go index bdd046bcdc..cb7fb00a72 100644 --- a/components/engine/daemon/config/config_test.go +++ b/components/engine/daemon/config/config_test.go @@ -9,6 +9,7 @@ import ( "github.com/docker/docker/daemon/discovery" "github.com/docker/docker/internal/testutil" "github.com/docker/docker/opts" + "github.com/gotestyourself/gotestyourself/fs" "github.com/spf13/pflag" "github.com/stretchr/testify/assert" ) @@ -459,3 +460,29 @@ func TestReloadBadDefaultConfig(t *testing.T) { assert.Error(t, err) testutil.ErrorContains(t, err, "unable to configure the Docker daemon with file") } + +func TestReloadWithConflictingLabels(t *testing.T) { + tempFile := fs.NewFile(t, "config", fs.WithContent(`{"labels":["foo=bar","foo=baz"]}`)) + defer tempFile.Remove() + configFile := tempFile.Path() + + var lbls []string + flags := pflag.NewFlagSet("test", pflag.ContinueOnError) + flags.String("config-file", configFile, "") + flags.StringSlice("labels", lbls, "") + err := Reload(configFile, flags, func(c *Config) {}) + testutil.ErrorContains(t, err, "conflict labels for foo=baz and foo=bar") +} + +func TestReloadWithDuplicateLabels(t *testing.T) { + tempFile := fs.NewFile(t, "config", fs.WithContent(`{"labels":["foo=the-same","foo=the-same"]}`)) + defer tempFile.Remove() + configFile := tempFile.Path() + + var lbls []string + flags := pflag.NewFlagSet("test", pflag.ContinueOnError) + flags.String("config-file", configFile, "") + flags.StringSlice("labels", lbls, "") + err := Reload(configFile, flags, func(c *Config) {}) + assert.NoError(t, err) +} diff --git a/components/engine/integration-cli/docker_cli_info_test.go b/components/engine/integration-cli/docker_cli_info_test.go index b6f867373b..d7ce238bfd 100644 --- a/components/engine/integration-cli/docker_cli_info_test.go +++ b/components/engine/integration-cli/docker_cli_info_test.go @@ -233,17 +233,6 @@ func (s *DockerDaemonSuite) TestRegistryMirrors(c *check.C) { c.Assert(out, checker.Contains, fmt.Sprintf(" %s", registryMirror2)) } -// Test case for #24392 -func (s *DockerDaemonSuite) TestInfoLabels(c *check.C) { - testRequires(c, SameHostDaemon, DaemonIsLinux) - - s.d.Start(c, "--label", `test.empty=`, "--label", `test.empty=`, "--label", `test.label="1"`, "--label", `test.label="2"`) - - out, err := s.d.Cmd("info") - c.Assert(err, checker.IsNil) - c.Assert(out, checker.Contains, "WARNING: labels with duplicate keys and conflicting values have been deprecated") -} - func existingContainerStates(c *check.C) map[string]int { out, _ := dockerCmd(c, "info", "--format", "{{json .}}") var m map[string]interface{} From 8369aeca8e718143c28b7227ac4d18d412dbde71 Mon Sep 17 00:00:00 2001 From: Chao Wang Date: Mon, 13 Nov 2017 10:01:11 +0800 Subject: [PATCH 61/94] Split and remove pkg/stringutils Signed-off-by: Chao Wang Upstream-commit: 97e406678c8d695ee6eaefce41b83e9e27e239c7 Component: engine --- components/engine/pkg/stringutils/README.md | 1 - .../engine/pkg/stringutils/stringutils.go | 77 --------------- .../pkg/stringutils/stringutils_test.go | 96 ------------------- 3 files changed, 174 deletions(-) delete mode 100644 components/engine/pkg/stringutils/README.md delete mode 100644 components/engine/pkg/stringutils/stringutils.go delete mode 100644 components/engine/pkg/stringutils/stringutils_test.go diff --git a/components/engine/pkg/stringutils/README.md b/components/engine/pkg/stringutils/README.md deleted file mode 100644 index b3e454573c..0000000000 --- a/components/engine/pkg/stringutils/README.md +++ /dev/null @@ -1 +0,0 @@ -This package provides helper functions for dealing with strings diff --git a/components/engine/pkg/stringutils/stringutils.go b/components/engine/pkg/stringutils/stringutils.go deleted file mode 100644 index 794a225190..0000000000 --- a/components/engine/pkg/stringutils/stringutils.go +++ /dev/null @@ -1,77 +0,0 @@ -// Package stringutils provides helper functions for dealing with strings. -package stringutils - -import ( - "bytes" - "math/rand" - "strings" -) - -// GenerateRandomASCIIString generates an ASCII random string with length n. -func GenerateRandomASCIIString(n int) string { - chars := "abcdefghijklmnopqrstuvwxyz" + - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "~!@#$%^&*()-_+={}[]\\|<,>.?/\"';:` " - res := make([]byte, n) - for i := 0; i < n; i++ { - res[i] = chars[rand.Intn(len(chars))] - } - return string(res) -} - -// Ellipsis truncates a string to fit within maxlen, and appends ellipsis (...). -// For maxlen of 3 and lower, no ellipsis is appended. -func Ellipsis(s string, maxlen int) string { - r := []rune(s) - if len(r) <= maxlen { - return s - } - if maxlen <= 3 { - return string(r[:maxlen]) - } - return string(r[:maxlen-3]) + "..." -} - -// Truncate truncates a string to maxlen. -func Truncate(s string, maxlen int) string { - r := []rune(s) - if len(r) <= maxlen { - return s - } - return string(r[:maxlen]) -} - -func quote(word string, buf *bytes.Buffer) { - // Bail out early for "simple" strings - if word != "" && !strings.ContainsAny(word, "\\'\"`${[|&;<>()~*?! \t\n") { - buf.WriteString(word) - return - } - - buf.WriteString("'") - - for i := 0; i < len(word); i++ { - b := word[i] - if b == '\'' { - // Replace literal ' with a close ', a \', and an open ' - buf.WriteString("'\\''") - } else { - buf.WriteByte(b) - } - } - - buf.WriteString("'") -} - -// ShellQuoteArguments takes a list of strings and escapes them so they will be -// handled right when passed as arguments to a program via a shell -func ShellQuoteArguments(args []string) string { - var buf bytes.Buffer - for i, arg := range args { - if i != 0 { - buf.WriteByte(' ') - } - quote(arg, &buf) - } - return buf.String() -} diff --git a/components/engine/pkg/stringutils/stringutils_test.go b/components/engine/pkg/stringutils/stringutils_test.go deleted file mode 100644 index 6a8cab7224..0000000000 --- a/components/engine/pkg/stringutils/stringutils_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package stringutils - -import "testing" - -func testLengthHelper(generator func(int) string, t *testing.T) { - expectedLength := 20 - s := generator(expectedLength) - if len(s) != expectedLength { - t.Fatalf("Length of %s was %d but expected length %d", s, len(s), expectedLength) - } -} - -func testUniquenessHelper(generator func(int) string, t *testing.T) { - repeats := 25 - set := make(map[string]struct{}, repeats) - for i := 0; i < repeats; i = i + 1 { - str := generator(64) - if len(str) != 64 { - t.Fatalf("Id returned is incorrect: %s", str) - } - if _, ok := set[str]; ok { - t.Fatalf("Random number is repeated") - } - set[str] = struct{}{} - } -} - -func isASCII(s string) bool { - for _, c := range s { - if c > 127 { - return false - } - } - return true -} - -func TestGenerateRandomAsciiStringLength(t *testing.T) { - testLengthHelper(GenerateRandomASCIIString, t) -} - -func TestGenerateRandomAsciiStringUniqueness(t *testing.T) { - testUniquenessHelper(GenerateRandomASCIIString, t) -} - -func TestGenerateRandomAsciiStringIsAscii(t *testing.T) { - str := GenerateRandomASCIIString(64) - if !isASCII(str) { - t.Fatalf("%s contained non-ascii characters", str) - } -} - -func TestEllipsis(t *testing.T) { - str := "t🐳ststring" - newstr := Ellipsis(str, 3) - if newstr != "t🐳s" { - t.Fatalf("Expected t🐳s, got %s", newstr) - } - newstr = Ellipsis(str, 8) - if newstr != "t🐳sts..." { - t.Fatalf("Expected tests..., got %s", newstr) - } - newstr = Ellipsis(str, 20) - if newstr != "t🐳ststring" { - t.Fatalf("Expected t🐳ststring, got %s", newstr) - } -} - -func TestTruncate(t *testing.T) { - str := "t🐳ststring" - newstr := Truncate(str, 4) - if newstr != "t🐳st" { - t.Fatalf("Expected t🐳st, got %s", newstr) - } - newstr = Truncate(str, 20) - if newstr != "t🐳ststring" { - t.Fatalf("Expected t🐳ststring, got %s", newstr) - } -} - -func TestShellQuoteArgumentsEmpty(t *testing.T) { - actual := ShellQuoteArguments([]string{}) - expected := "" - if actual != expected { - t.Fatalf("Expected an empty string") - } -} - -func TestShellQuoteArguments(t *testing.T) { - simpleString := "simpleString" - complexString := "This is a 'more' complex $tring with some special char *" - actual := ShellQuoteArguments([]string{simpleString, complexString}) - expected := "simpleString 'This is a '\\''more'\\'' complex $tring with some special char *'" - if actual != expected { - t.Fatalf("Expected \"%v\", got \"%v\"", expected, actual) - } -} From f9f5db27c1cb6ed9f5324cb4d9dbc6dbf6df2a52 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 27 Oct 2017 00:21:41 -0700 Subject: [PATCH 62/94] Fix user mount /dev/shm size Commit 7120976d74195 ("Implement none, private, and shareable ipc modes") introduces a bug: if a user-specified mount for /dev/shm is provided, its size is overriden by value of ShmSize. A reproducer is simple: docker run --rm --mount type=tmpfs,dst=/dev/shm,tmpfs-size=100K \ alpine df /dev/shm This commit is an attempt to fix the bug, as well as optimize things a but and make the code easier to read. https://github.com/moby/moby/issues/35271 Signed-off-by: Kir Kolyshkin Upstream-commit: 31d30a985d99a0eef92116a22159727f5c332784 Component: engine --- components/engine/daemon/oci_linux.go | 38 +++++++++++++++------------ 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/components/engine/daemon/oci_linux.go b/components/engine/daemon/oci_linux.go index 2c5d94d99b..e876e3b6ce 100644 --- a/components/engine/daemon/oci_linux.go +++ b/components/engine/daemon/oci_linux.go @@ -528,23 +528,35 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c userMounts[m.Destination] = struct{}{} } - // Filter out mounts from spec - noIpc := c.HostConfig.IpcMode.IsNone() - // Filter out mounts that are overridden by user supplied mounts + // Copy all mounts from spec to defaultMounts, except for + // - mounts overriden by a user supplied mount; + // - all mounts under /dev if a user supplied /dev is present; + // - /dev/shm, in case IpcMode is none. + // While at it, also + // - set size for /dev/shm from shmsize. var defaultMounts []specs.Mount _, mountDev := userMounts["/dev"] for _, m := range s.Mounts { - // filter out /dev/shm mount if case IpcMode is none - if noIpc && m.Destination == "/dev/shm" { + if _, ok := userMounts[m.Destination]; ok { + // filter out mount overridden by a user supplied mount continue } - // filter out mount overridden by a user supplied mount - if _, ok := userMounts[m.Destination]; !ok { - if mountDev && strings.HasPrefix(m.Destination, "/dev/") { + if mountDev && strings.HasPrefix(m.Destination, "/dev/") { + // filter out everything under /dev if /dev is user-mounted + continue + } + + if m.Destination == "/dev/shm" { + if c.HostConfig.IpcMode.IsNone() { + // filter out /dev/shm for "none" IpcMode continue } - defaultMounts = append(defaultMounts, m) + // set size for /dev/shm mount from spec + sizeOpt := "size=" + strconv.FormatInt(c.HostConfig.ShmSize, 10) + m.Options = append(m.Options, sizeOpt) } + + defaultMounts = append(defaultMounts, m) } s.Mounts = defaultMounts @@ -652,14 +664,6 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c s.Linux.MaskedPaths = nil } - // Set size for /dev/shm mount that comes from spec (IpcMode: private only) - for i, m := range s.Mounts { - if m.Destination == "/dev/shm" { - sizeOpt := "size=" + strconv.FormatInt(c.HostConfig.ShmSize, 10) - s.Mounts[i].Options = append(s.Mounts[i].Options, sizeOpt) - } - } - // TODO: until a kernel/mount solution exists for handling remount in a user namespace, // we must clear the readonly flag for the cgroups mount (@mrunalp concurs) if uidMap := daemon.idMappings.UIDs(); uidMap != nil || c.HostConfig.Privileged { From 628966a8d48d3fd347b190df9b800b33d465e677 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Mon, 13 Nov 2017 08:55:03 -0500 Subject: [PATCH 63/94] Bump runc vendor Updates runc to b2567b37d7b75eb4cf325b77297b140ea686ce8f which removes some cross-repo dependencies. Signed-off-by: Brian Goff Upstream-commit: af248a3fe087805907e4b69ab017ef60d44ce093 Component: engine --- .../engine/hack/dockerfile/binaries-commits | 2 +- components/engine/vendor.conf | 2 +- .../libcontainer/devices/devices_linux.go | 8 +++--- .../runc/libcontainer/devices/number.go | 24 ------------------ ...scall_linux_386.go => syscall_linux_32.go} | 3 ++- .../libcontainer/system/syscall_linux_64.go | 3 ++- .../libcontainer/system/syscall_linux_arm.go | 25 ------------------- .../opencontainers/runc/vendor.conf | 4 +-- 8 files changed, 12 insertions(+), 59 deletions(-) delete mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/devices/number.go rename components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/{syscall_linux_386.go => syscall_linux_32.go} (93%) delete mode 100644 components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_arm.go diff --git a/components/engine/hack/dockerfile/binaries-commits b/components/engine/hack/dockerfile/binaries-commits index fdac17110e..1a20352aa0 100644 --- a/components/engine/hack/dockerfile/binaries-commits +++ b/components/engine/hack/dockerfile/binaries-commits @@ -3,7 +3,7 @@ TOMLV_COMMIT=9baf8a8a9f2ed20a8e54160840c492f937eeaf9a # When updating RUNC_COMMIT, also update runc in vendor.conf accordingly -RUNC_COMMIT=0351df1c5a66838d0c392b4ac4cf9450de844e2d +RUNC_COMMIT=b2567b37d7b75eb4cf325b77297b140ea686ce8f CONTAINERD_COMMIT=v1.0.0-beta.3 TINI_COMMIT=949e6facb77383876aeff8a6944dde66b3089574 LIBNETWORK_COMMIT=7b2b1feb1de4817d522cc372af149ff48d25028e diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index ed722129ad..22a5d90540 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -65,7 +65,7 @@ github.com/pborman/uuid v1.0 google.golang.org/grpc v1.3.0 # When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly -github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d +github.com/opencontainers/runc b2567b37d7b75eb4cf325b77297b140ea686ce8f github.com/opencontainers/runtime-spec v1.0.0 github.com/opencontainers/image-spec v1.0.0 github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/devices/devices_linux.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/devices/devices_linux.go index 326ad3b159..3619258905 100644 --- a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/devices/devices_linux.go +++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/devices/devices_linux.go @@ -30,8 +30,8 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) { } var ( - devNumber = int(stat.Rdev) - major = Major(devNumber) + devNumber = stat.Rdev + major = unix.Major(devNumber) ) if major == 0 { return nil, ErrNotADevice @@ -50,8 +50,8 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) { return &configs.Device{ Type: devType, Path: path, - Major: major, - Minor: Minor(devNumber), + Major: int64(major), + Minor: int64(unix.Minor(devNumber)), Permissions: permissions, FileMode: os.FileMode(mode), Uid: stat.Uid, diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/devices/number.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/devices/number.go deleted file mode 100644 index 885b6e5dd9..0000000000 --- a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/devices/number.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build linux freebsd - -package devices - -/* - -This code provides support for manipulating linux device numbers. It should be replaced by normal syscall functions once http://code.google.com/p/go/issues/detail?id=8106 is solved. - -You can read what they are here: - - - http://www.makelinux.net/ldd3/chp-3-sect-2 - - http://www.linux-tutorial.info/modules.php?name=MContent&pageid=94 - -Note! These are NOT the same as the MAJOR(dev_t device);, MINOR(dev_t device); and MKDEV(int major, int minor); functions as defined in as the representation of device numbers used by go is different than the one used internally to the kernel! - https://github.com/torvalds/linux/blob/master/include/linux/kdev_t.h#L9 - -*/ - -func Major(devNumber int) int64 { - return int64((devNumber >> 8) & 0xfff) -} - -func Minor(devNumber int) int64 { - return int64((devNumber & 0xff) | ((devNumber >> 12) & 0xfff00)) -} diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_386.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_32.go similarity index 93% rename from components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_386.go rename to components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_32.go index 3f7235ed15..c5ca5d8623 100644 --- a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_386.go +++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_32.go @@ -1,4 +1,5 @@ -// +build linux,386 +// +build linux +// +build 386 arm package system diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_64.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_64.go index d7891a2ffa..11c3faafbf 100644 --- a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_64.go +++ b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_64.go @@ -1,4 +1,5 @@ -// +build linux,arm64 linux,amd64 linux,ppc linux,ppc64 linux,ppc64le linux,s390x +// +build linux +// +build arm64 amd64 mips mipsle mips64 mips64le ppc ppc64 ppc64le s390x package system diff --git a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_arm.go b/components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_arm.go deleted file mode 100644 index 31ff3deb13..0000000000 --- a/components/engine/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_arm.go +++ /dev/null @@ -1,25 +0,0 @@ -// +build linux,arm - -package system - -import ( - "golang.org/x/sys/unix" -) - -// Setuid sets the uid of the calling thread to the specified uid. -func Setuid(uid int) (err error) { - _, _, e1 := unix.RawSyscall(unix.SYS_SETUID32, uintptr(uid), 0, 0) - if e1 != 0 { - err = e1 - } - return -} - -// Setgid sets the gid of the calling thread to the specified gid. -func Setgid(gid int) (err error) { - _, _, e1 := unix.RawSyscall(unix.SYS_SETGID32, uintptr(gid), 0, 0) - if e1 != 0 { - err = e1 - } - return -} diff --git a/components/engine/vendor/github.com/opencontainers/runc/vendor.conf b/components/engine/vendor/github.com/opencontainers/runc/vendor.conf index 1266ee485f..0ab4685fd7 100644 --- a/components/engine/vendor/github.com/opencontainers/runc/vendor.conf +++ b/components/engine/vendor/github.com/opencontainers/runc/vendor.conf @@ -5,7 +5,7 @@ github.com/opencontainers/runtime-spec v1.0.0 # Core libcontainer functionality. github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08 github.com/opencontainers/selinux v1.0.0-rc1 -github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 +github.com/seccomp/libseccomp-golang 84e90a91acea0f4e51e62bc1a75de18b1fc0790f github.com/sirupsen/logrus a3f95b5c423586578a4e099b11a46c2479628cac github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 github.com/vishvananda/netlink 1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270 @@ -15,7 +15,7 @@ github.com/coreos/pkg v3 github.com/godbus/dbus v3 github.com/golang/protobuf 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8 # Command-line interface. -github.com/docker/docker 0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d +github.com/cyphar/filepath-securejoin v0.2.1 github.com/docker/go-units v0.2.0 github.com/urfave/cli d53eb991652b1d438abdd34ce4bfa3ef1539108e golang.org/x/sys 7ddbeae9ae08c6a06a59597f0c9edbc5ff2444ce https://github.com/golang/sys From 60d1b81b0da10739700641f84ca414326a25f61c Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Thu, 9 Nov 2017 16:20:51 -0500 Subject: [PATCH 64/94] Add deepCopyRunConfig for copying buidler runConfig Signed-off-by: Daniel Nephin Upstream-commit: 9bcd5d2574fe0c84542d2fa18232c34e2a9c0cac Component: engine --- .../engine/builder/dockerfile/evaluator.go | 3 +- .../engine/builder/dockerfile/internals.go | 51 ++++++++++++++++--- .../builder/dockerfile/internals_test.go | 39 ++++++++++++++ .../engine/integration/build/build_test.go | 40 +++++++++++++++ 4 files changed, 124 insertions(+), 9 deletions(-) diff --git a/components/engine/builder/dockerfile/evaluator.go b/components/engine/builder/dockerfile/evaluator.go index da97a7ff6e..6236a194d3 100644 --- a/components/engine/builder/dockerfile/evaluator.go +++ b/components/engine/builder/dockerfile/evaluator.go @@ -214,7 +214,8 @@ func (s *dispatchState) beginStage(stageName string, image builder.Image) { s.imageID = image.ImageID() if image.RunConfig() != nil { - s.runConfig = copyRunConfig(image.RunConfig()) // copy avoids referencing the same instance when 2 stages have the same base + // copy avoids referencing the same instance when 2 stages have the same base + s.runConfig = copyRunConfig(image.RunConfig()) } else { s.runConfig = &container.Config{} } diff --git a/components/engine/builder/dockerfile/internals.go b/components/engine/builder/dockerfile/internals.go index 0e08ec25f0..c38f48afc0 100644 --- a/components/engine/builder/dockerfile/internals.go +++ b/components/engine/builder/dockerfile/internals.go @@ -25,6 +25,7 @@ import ( "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/symlink" "github.com/docker/docker/pkg/system" + "github.com/docker/go-connections/nat" lcUser "github.com/opencontainers/runc/libcontainer/user" "github.com/pkg/errors" ) @@ -385,14 +386,6 @@ func hashStringSlice(prefix string, slice []string) string { type runConfigModifier func(*container.Config) -func copyRunConfig(runConfig *container.Config, modifiers ...runConfigModifier) *container.Config { - copy := *runConfig - for _, modifier := range modifiers { - modifier(©) - } - return © -} - func withCmd(cmd []string) runConfigModifier { return func(runConfig *container.Config) { runConfig.Cmd = cmd @@ -438,6 +431,48 @@ func withEntrypointOverride(cmd []string, entrypoint []string) runConfigModifier } } +func copyRunConfig(runConfig *container.Config, modifiers ...runConfigModifier) *container.Config { + copy := *runConfig + copy.Cmd = copyStringSlice(runConfig.Cmd) + copy.Env = copyStringSlice(runConfig.Env) + copy.Entrypoint = copyStringSlice(runConfig.Entrypoint) + copy.OnBuild = copyStringSlice(runConfig.OnBuild) + copy.Shell = copyStringSlice(runConfig.Shell) + + if copy.Volumes != nil { + copy.Volumes = make(map[string]struct{}, len(runConfig.Volumes)) + for k, v := range runConfig.Volumes { + copy.Volumes[k] = v + } + } + + if copy.ExposedPorts != nil { + copy.ExposedPorts = make(nat.PortSet, len(runConfig.ExposedPorts)) + for k, v := range runConfig.ExposedPorts { + copy.ExposedPorts[k] = v + } + } + + if copy.Labels != nil { + copy.Labels = make(map[string]string, len(runConfig.Labels)) + for k, v := range runConfig.Labels { + copy.Labels[k] = v + } + } + + for _, modifier := range modifiers { + modifier(©) + } + return © +} + +func copyStringSlice(orig []string) []string { + if orig == nil { + return nil + } + return append([]string{}, orig...) +} + // getShell is a helper function which gets the right shell for prefixing the // shell-form of RUN, ENTRYPOINT and CMD instructions func getShell(c *container.Config, os string) []string { diff --git a/components/engine/builder/dockerfile/internals_test.go b/components/engine/builder/dockerfile/internals_test.go index 380d86108e..83a207c455 100644 --- a/components/engine/builder/dockerfile/internals_test.go +++ b/components/engine/builder/dockerfile/internals_test.go @@ -14,6 +14,7 @@ import ( "github.com/docker/docker/builder/remotecontext" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/idtools" + "github.com/docker/go-connections/nat" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -133,6 +134,44 @@ func TestCopyRunConfig(t *testing.T) { } +func fullMutableRunConfig() *container.Config { + return &container.Config{ + Cmd: []string{"command", "arg1"}, + Env: []string{"env1=foo", "env2=bar"}, + ExposedPorts: nat.PortSet{ + "1000/tcp": {}, + "1001/tcp": {}, + }, + Volumes: map[string]struct{}{ + "one": {}, + "two": {}, + }, + Entrypoint: []string{"entry", "arg1"}, + OnBuild: []string{"first", "next"}, + Labels: map[string]string{ + "label1": "value1", + "label2": "value2", + }, + Shell: []string{"shell", "-c"}, + } +} + +func TestDeepCopyRunConfig(t *testing.T) { + runConfig := fullMutableRunConfig() + copy := copyRunConfig(runConfig) + assert.Equal(t, fullMutableRunConfig(), copy) + + copy.Cmd[1] = "arg2" + copy.Env[1] = "env2=new" + copy.ExposedPorts["10002"] = struct{}{} + copy.Volumes["three"] = struct{}{} + copy.Entrypoint[1] = "arg2" + copy.OnBuild[0] = "start" + copy.Labels["label3"] = "value3" + copy.Shell[0] = "sh" + assert.Equal(t, fullMutableRunConfig(), runConfig) +} + func TestChownFlagParsing(t *testing.T) { testFiles := map[string]string{ "passwd": `root:x:0:0::/bin:/bin/false diff --git a/components/engine/integration/build/build_test.go b/components/engine/integration/build/build_test.go index b04cbbf205..cbaa7dc9bb 100644 --- a/components/engine/integration/build/build_test.go +++ b/components/engine/integration/build/build_test.go @@ -6,13 +6,16 @@ import ( "context" "encoding/json" "io" + "io/ioutil" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/integration-cli/cli/build/fakecontext" "github.com/docker/docker/integration/util/request" "github.com/docker/docker/pkg/jsonmessage" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -129,3 +132,40 @@ func buildContainerIdsFilter(buildOutput io.Reader) (filters.Args, error) { } } } + +func TestBuildMultiStageParentConfig(t *testing.T) { + dockerfile := ` + FROM busybox AS stage0 + ENV WHO=parent + WORKDIR /foo + + FROM stage0 + ENV WHO=sibling1 + WORKDIR sub1 + + FROM stage0 + WORKDIR sub2 + ` + ctx := context.Background() + source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile)) + defer source.Close() + + apiclient := testEnv.APIClient() + resp, err := apiclient.ImageBuild(ctx, + source.AsTarReader(t), + types.ImageBuildOptions{ + Remove: true, + ForceRemove: true, + Tags: []string{"build1"}, + }) + require.NoError(t, err) + _, err = io.Copy(ioutil.Discard, resp.Body) + resp.Body.Close() + require.NoError(t, err) + + image, _, err := apiclient.ImageInspectWithRaw(ctx, "build1") + require.NoError(t, err) + + assert.Equal(t, "/foo/sub2", image.Config.WorkingDir) + assert.Contains(t, image.Config.Env, "WHO=parent") +} From 10c76971348b998b31587317619bedd0f0c8099d Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Mon, 13 Nov 2017 13:31:28 -0800 Subject: [PATCH 65/94] container: protect health monitor channel While this code was likely called from a single thread before, we have now seen panics, indicating that it could be called in parallel. This change adds a mutex to protect opening and closing of the channel. There may be another root cause associated with this panic, such as something that led to the calling of this in parallel, as this code is old and we had seen this condition until recently. This fix is by no means a permanent fix. Typically, bugs like this indicate misplaced channel ownership. In idiomatic uses, the channel should have a particular "owner" that coordinates sending and closure. In this case, the owner of the channel is unclear, so it gets opened lazily. Synchronizing this access is a decent solution, but a refactor may yield better results. Signed-off-by: Stephen J Day Upstream-commit: 5b55747a523671fa6e626848060460a48d058451 Component: engine --- components/engine/container/health.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/components/engine/container/health.go b/components/engine/container/health.go index 5919008d27..75365fec6b 100644 --- a/components/engine/container/health.go +++ b/components/engine/container/health.go @@ -1,6 +1,8 @@ package container import ( + "sync" + "github.com/docker/docker/api/types" "github.com/sirupsen/logrus" ) @@ -9,6 +11,7 @@ import ( type Health struct { types.Health stop chan struct{} // Write struct{} to stop the monitor + mu sync.Mutex } // String returns a human-readable description of the health-check state @@ -26,9 +29,12 @@ func (s *Health) String() string { } } -// OpenMonitorChannel creates and returns a new monitor channel. If there already is one, -// it returns nil. +// OpenMonitorChannel creates and returns a new monitor channel. If there +// already is one, it returns nil. func (s *Health) OpenMonitorChannel() chan struct{} { + s.mu.Lock() + defer s.mu.Unlock() + if s.stop == nil { logrus.Debug("OpenMonitorChannel") s.stop = make(chan struct{}) @@ -39,6 +45,9 @@ func (s *Health) OpenMonitorChannel() chan struct{} { // CloseMonitorChannel closes any existing monitor channel. func (s *Health) CloseMonitorChannel() { + s.mu.Lock() + defer s.mu.Unlock() + if s.stop != nil { logrus.Debug("CloseMonitorChannel: waiting for probe to stop") close(s.stop) From ce5c429be8f160394313afee1ac050d8d735b8f7 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Mon, 13 Nov 2017 14:53:56 -0800 Subject: [PATCH 66/94] libcontainerd: fix leaking container/exec state Signed-off-by: Tonis Tiigi Upstream-commit: 6c4ce7cb6c62fb82ed2db1d4ee3a02bc5148cdee Component: engine --- .../integration-cli/docker_api_exec_test.go | 41 +++++++++++++++++++ .../engine/libcontainerd/client_daemon.go | 24 ++++++++--- .../libcontainerd/client_daemon_linux.go | 11 +++++ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/components/engine/integration-cli/docker_api_exec_test.go b/components/engine/integration-cli/docker_api_exec_test.go index 6e18df8fee..ffc7da4fc5 100644 --- a/components/engine/integration-cli/docker_api_exec_test.go +++ b/components/engine/integration-cli/docker_api_exec_test.go @@ -8,6 +8,8 @@ import ( "fmt" "io/ioutil" "net/http" + "os" + "strings" "time" "github.com/docker/docker/api/types" @@ -198,6 +200,45 @@ func (s *DockerSuite) TestExecAPIStartInvalidCommand(c *check.C) { c.Assert(inspectJSON.ExecIDs, checker.IsNil) } +func (s *DockerSuite) TestExecStateCleanup(c *check.C) { + testRequires(c, DaemonIsLinux, SameHostDaemon) + + // This test checks accidental regressions. Not part of stable API. + + name := "exec_cleanup" + cid, _ := dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh") + cid = strings.TrimSpace(cid) + + stateDir := "/var/run/docker/containerd/" + cid + + checkReadDir := func(c *check.C) (interface{}, check.CommentInterface) { + fi, err := ioutil.ReadDir(stateDir) + c.Assert(err, checker.IsNil) + return len(fi), nil + } + + fi, err := ioutil.ReadDir(stateDir) + c.Assert(err, checker.IsNil) + c.Assert(len(fi), checker.GreaterThan, 1) + + id := createExecCmd(c, name, "ls") + startExec(c, id, http.StatusOK) + waitForExec(c, id) + + waitAndAssert(c, 5*time.Second, checkReadDir, checker.Equals, len(fi)) + + id = createExecCmd(c, name, "invalid") + startExec(c, id, http.StatusBadRequest) + waitForExec(c, id) + + waitAndAssert(c, 5*time.Second, checkReadDir, checker.Equals, len(fi)) + + dockerCmd(c, "stop", name) + _, err = os.Stat(stateDir) + c.Assert(err, checker.NotNil) + c.Assert(os.IsNotExist(err), checker.True) +} + func createExec(c *check.C, name string) string { return createExecCmd(c, name, "true") } diff --git a/components/engine/libcontainerd/client_daemon.go b/components/engine/libcontainerd/client_daemon.go index b0cdcfcfd9..24c0c80197 100644 --- a/components/engine/libcontainerd/client_daemon.go +++ b/components/engine/libcontainerd/client_daemon.go @@ -28,7 +28,7 @@ import ( "github.com/containerd/typeurl" "github.com/docker/docker/pkg/ioutils" "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/opencontainers/runtime-spec/specs-go" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -202,7 +202,8 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin uid, gid := getSpecUser(spec) t, err = ctr.ctr.NewTask(ctx, func(id string) (containerd.IO, error) { - cio, err = c.createIO(ctr.bundleDir, id, InitProcessName, stdinCloseSync, withStdin, spec.Process.Terminal, attachStdio) + fifos := newFIFOSet(ctr.bundleDir, id, InitProcessName, withStdin, spec.Process.Terminal) + cio, err = c.createIO(fifos, id, InitProcessName, stdinCloseSync, attachStdio) return cio, err }, func(_ context.Context, _ *containerd.Client, info *containerd.TaskInfo) error { @@ -260,17 +261,21 @@ func (c *client) Exec(ctx context.Context, containerID, processID string, spec * err error stdinCloseSync = make(chan struct{}) ) + + fifos := newFIFOSet(ctr.bundleDir, containerID, processID, withStdin, spec.Terminal) + defer func() { if err != nil { if cio != nil { cio.Cancel() cio.Close() } + rmFIFOSet(fifos) } }() p, err = ctr.task.Exec(ctx, processID, spec, func(id string) (containerd.IO, error) { - cio, err = c.createIO(ctr.bundleDir, containerID, processID, stdinCloseSync, withStdin, spec.Terminal, attachStdio) + cio, err = c.createIO(fifos, containerID, processID, stdinCloseSync, attachStdio) return cio, err }) if err != nil { @@ -441,7 +446,7 @@ func (c *client) Delete(ctx context.Context, containerID string) error { return err } - if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" { + if os.Getenv("LIBCONTAINERD_NOCLEAN") != "1" { if err := os.RemoveAll(ctr.bundleDir); err != nil { c.logger.WithError(err).WithFields(logrus.Fields{ "container": containerID, @@ -562,8 +567,7 @@ func (c *client) getProcess(containerID, processID string) (containerd.Process, // createIO creates the io to be used by a process // This needs to get a pointer to interface as upon closure the process may not have yet been registered -func (c *client) createIO(bundleDir, containerID, processID string, stdinCloseSync chan struct{}, withStdin, withTerminal bool, attachStdio StdioCallback) (containerd.IO, error) { - fifos := newFIFOSet(bundleDir, containerID, processID, withStdin, withTerminal) +func (c *client) createIO(fifos *containerd.FIFOSet, containerID, processID string, stdinCloseSync chan struct{}, attachStdio StdioCallback) (containerd.IO, error) { io, err := newIOPipe(fifos) if err != nil { return nil, err @@ -639,6 +643,14 @@ func (c *client) processEvent(ctr *container, et EventType, ei EventInfo) { c.Lock() delete(ctr.execs, ei.ProcessID) c.Unlock() + ctr := c.getContainer(ei.ContainerID) + if ctr == nil { + c.logger.WithFields(logrus.Fields{ + "container": ei.ContainerID, + }).Error("failed to find container") + } else { + rmFIFOSet(newFIFOSet(ctr.bundleDir, ei.ContainerID, ei.ProcessID, true, false)) + } } }) } diff --git a/components/engine/libcontainerd/client_daemon_linux.go b/components/engine/libcontainerd/client_daemon_linux.go index 03371954cc..14966f00b1 100644 --- a/components/engine/libcontainerd/client_daemon_linux.go +++ b/components/engine/libcontainerd/client_daemon_linux.go @@ -10,6 +10,7 @@ import ( "github.com/containerd/containerd" "github.com/docker/docker/pkg/idtools" specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/sirupsen/logrus" ) func summaryFromInterface(i interface{}) (*Summary, error) { @@ -94,3 +95,13 @@ func newFIFOSet(bundleDir, containerID, processID string, withStdin, withTermina return fifos } + +func rmFIFOSet(fset *containerd.FIFOSet) { + for _, fn := range []string{fset.Out, fset.In, fset.Err} { + if fn != "" { + if err := os.RemoveAll(fn); err != nil { + logrus.Warnf("libcontainerd: failed to remove fifo %v: %v", fn, err) + } + } + } +} From 31c1f926231cd466450da56ba4251f122b9b6a87 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Sun, 12 Nov 2017 18:27:05 -0800 Subject: [PATCH 67/94] integration: test case for #35271 This test case is checking that the built-in default size for /dev/shm (which is used for `--ipcmode` being `private` or `shareable`) is not overriding the size of user-defined tmpfs mount for /dev/shm. In other words, this is a regression test case for issue #35271, https://github.com/moby/moby/issues/35271 Signed-off-by: Kir Kolyshkin Upstream-commit: 2e0a98b605fa278ee1f348c68fe7e07aed57b834 Component: engine --- components/engine/daemon/daemon_linux_test.go | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/components/engine/daemon/daemon_linux_test.go b/components/engine/daemon/daemon_linux_test.go index c7d5117195..36ef52e25a 100644 --- a/components/engine/daemon/daemon_linux_test.go +++ b/components/engine/daemon/daemon_linux_test.go @@ -5,6 +5,13 @@ package daemon import ( "strings" "testing" + + containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/container" + "github.com/docker/docker/oci" + "github.com/docker/docker/pkg/idtools" + + "github.com/stretchr/testify/assert" ) const mountsFixture = `142 78 0:38 / / rw,relatime - aufs none rw,si=573b861da0b3a05b,dio @@ -102,3 +109,51 @@ func TestNotCleanupMounts(t *testing.T) { t.Fatal("Expected not to clean up /dev/shm") } } + +// TestTmpfsDevShmSizeOverride checks that user-specified /dev/tmpfs mount +// size is not overriden by the default shmsize (that should only be used +// for default /dev/shm (as in "shareable" and "private" ipc modes). +// https://github.com/moby/moby/issues/35271 +func TestTmpfsDevShmSizeOverride(t *testing.T) { + size := "777m" + mnt := "/dev/shm" + + d := Daemon{ + idMappings: &idtools.IDMappings{}, + } + c := &container.Container{ + HostConfig: &containertypes.HostConfig{ + ShmSize: 48 * 1024, // size we should NOT end up with + }, + } + ms := []container.Mount{ + { + Source: "tmpfs", + Destination: mnt, + Data: "size=" + size, + }, + } + + // convert ms to spec + spec := oci.DefaultSpec() + err := setMounts(&d, &spec, c, ms) + assert.NoError(t, err) + + // Check the resulting spec for the correct size + found := false + for _, m := range spec.Mounts { + if m.Destination == mnt { + for _, o := range m.Options { + if !strings.HasPrefix(o, "size=") { + continue + } + t.Logf("%+v\n", m.Options) + assert.Equal(t, "size="+size, o) + found = true + } + } + } + if !found { + t.Fatal("/dev/shm not found in spec, or size option missing") + } +} From d8cda430a70d88ca32d323117d81188f8291abc1 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 14 Nov 2017 14:17:44 +0100 Subject: [PATCH 68/94] Bump docker-py to 1d6b5b203222ba5df7dedfcd1ee061a452f99c8a Signed-off-by: Sebastiaan van Stijn Upstream-commit: d1a0773d3a834a4bfa98deb07ae3f87dd6438da6 Component: engine --- components/engine/Dockerfile | 2 +- components/engine/Dockerfile.aarch64 | 2 +- components/engine/Dockerfile.armhf | 2 +- components/engine/Dockerfile.ppc64le | 2 +- components/engine/Dockerfile.s390x | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/engine/Dockerfile b/components/engine/Dockerfile index 146f3d901b..e027d48554 100644 --- a/components/engine/Dockerfile +++ b/components/engine/Dockerfile @@ -133,7 +133,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT ca7a6132a418c32df6bb11ba9b2a8b9b2727227a +ENV DOCKER_PY_COMMIT 1d6b5b203222ba5df7dedfcd1ee061a452f99c8a # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.aarch64 b/components/engine/Dockerfile.aarch64 index e6a12fe370..876d80935e 100644 --- a/components/engine/Dockerfile.aarch64 +++ b/components/engine/Dockerfile.aarch64 @@ -105,7 +105,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT ca7a6132a418c32df6bb11ba9b2a8b9b2727227a +ENV DOCKER_PY_COMMIT 1d6b5b203222ba5df7dedfcd1ee061a452f99c8a # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.armhf b/components/engine/Dockerfile.armhf index d67bdd0658..33304b55e4 100644 --- a/components/engine/Dockerfile.armhf +++ b/components/engine/Dockerfile.armhf @@ -103,7 +103,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT ca7a6132a418c32df6bb11ba9b2a8b9b2727227a +ENV DOCKER_PY_COMMIT 1d6b5b203222ba5df7dedfcd1ee061a452f99c8a # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.ppc64le b/components/engine/Dockerfile.ppc64le index 709a6e6241..4abd8891f7 100644 --- a/components/engine/Dockerfile.ppc64le +++ b/components/engine/Dockerfile.ppc64le @@ -101,7 +101,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT ca7a6132a418c32df6bb11ba9b2a8b9b2727227a +ENV DOCKER_PY_COMMIT 1d6b5b203222ba5df7dedfcd1ee061a452f99c8a # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.s390x b/components/engine/Dockerfile.s390x index 752d052c62..33dfc4319b 100644 --- a/components/engine/Dockerfile.s390x +++ b/components/engine/Dockerfile.s390x @@ -95,7 +95,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT ca7a6132a418c32df6bb11ba9b2a8b9b2727227a +ENV DOCKER_PY_COMMIT 1d6b5b203222ba5df7dedfcd1ee061a452f99c8a # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ From ae3d074947085319e62db8177d7a587d718104a1 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Tue, 14 Nov 2017 17:00:47 -0500 Subject: [PATCH 69/94] Cancelation errors should not be logged Signed-off-by: Brian Goff Upstream-commit: b86746d60d4c43dcadd3b95fc4b2da7c03323d84 Component: engine --- components/engine/daemon/cluster/noderunner.go | 7 ++++++- components/engine/libcontainerd/client_daemon.go | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/components/engine/daemon/cluster/noderunner.go b/components/engine/daemon/cluster/noderunner.go index aa12737cdd..6aae6c3271 100644 --- a/components/engine/daemon/cluster/noderunner.go +++ b/components/engine/daemon/cluster/noderunner.go @@ -17,6 +17,8 @@ import ( "github.com/sirupsen/logrus" "golang.org/x/net/context" "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // nodeRunner implements a manager for continuously running swarmkit node, restarting them with backoff delays if needed. @@ -217,7 +219,10 @@ func (n *nodeRunner) watchClusterEvents(ctx context.Context, conn *grpc.ClientCo msg, err := watch.Recv() if err != nil { // store watch is broken - logrus.WithError(err).Error("failed to receive changes from store watch API") + errStatus, ok := status.FromError(err) + if !ok || errStatus.Code() != codes.Canceled { + logrus.WithError(err).Error("failed to receive changes from store watch API") + } return } select { diff --git a/components/engine/libcontainerd/client_daemon.go b/components/engine/libcontainerd/client_daemon.go index 24c0c80197..f1b5f011f8 100644 --- a/components/engine/libcontainerd/client_daemon.go +++ b/components/engine/libcontainerd/client_daemon.go @@ -17,6 +17,8 @@ import ( "time" "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/containerd/containerd" eventsapi "github.com/containerd/containerd/api/services/events/v1" @@ -687,7 +689,10 @@ func (c *client) processEventStream(ctx context.Context) { for { ev, err = eventStream.Recv() if err != nil { - c.logger.WithError(err).Error("failed to get event") + errStatus, ok := status.FromError(err) + if !ok || errStatus.Code() != codes.Canceled { + c.logger.WithError(err).Error("failed to get event") + } return } From ce594a83b1e1be8cc362b2d160ef7247f3ebe809 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 15 Nov 2017 00:02:11 -0800 Subject: [PATCH 70/94] daemon/graphdriver/register: separate overlay2 Make it possible to disable overlay and overlay2 separately. With this commit, we now have `exclude_graphdriver_overlay` and `exclude_graphdriver_overlay2` build tags for the engine, which is in line with any other graph driver. Signed-off-by: Kir Kolyshkin Upstream-commit: d014be5426c869d429c1a11cad9e76321dd7a326 Component: engine --- .../daemon/graphdriver/register/register_overlay.go | 1 - .../daemon/graphdriver/register/register_overlay2.go | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 components/engine/daemon/graphdriver/register/register_overlay2.go diff --git a/components/engine/daemon/graphdriver/register/register_overlay.go b/components/engine/daemon/graphdriver/register/register_overlay.go index 9ba849cedc..3a9526420f 100644 --- a/components/engine/daemon/graphdriver/register/register_overlay.go +++ b/components/engine/daemon/graphdriver/register/register_overlay.go @@ -5,5 +5,4 @@ package register import ( // register the overlay graphdriver _ "github.com/docker/docker/daemon/graphdriver/overlay" - _ "github.com/docker/docker/daemon/graphdriver/overlay2" ) diff --git a/components/engine/daemon/graphdriver/register/register_overlay2.go b/components/engine/daemon/graphdriver/register/register_overlay2.go new file mode 100644 index 0000000000..b2da0f4763 --- /dev/null +++ b/components/engine/daemon/graphdriver/register/register_overlay2.go @@ -0,0 +1,8 @@ +// +build !exclude_graphdriver_overlay2,linux + +package register + +import ( + // register the overlay2 graphdriver + _ "github.com/docker/docker/daemon/graphdriver/overlay2" +) From 9ef2726179829ebe41286af13d85a5bb6d2b9472 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Wed, 15 Nov 2017 13:13:22 -0500 Subject: [PATCH 71/94] Replace vol plugin integration test w/ unit test Signed-off-by: Brian Goff Upstream-commit: 00d801dd85b486eb46eff7bd041c33f04e373699 Component: engine --- .../integration/plugin/volume/cmd/cmd_test.go | 1 - .../plugin/volume/cmd/create-error/main.go | 23 ---- .../volume/cmd/create-error/main_test.go | 1 - .../integration/plugin/volume/create_test.go | 51 --------- .../integration/plugin/volume/main_test.go | 69 ------------ components/engine/volume/store/store_test.go | 54 ++++++++++ .../engine/volume/testutils/testutils.go | 102 ++++++++++++++++++ 7 files changed, 156 insertions(+), 145 deletions(-) delete mode 100644 components/engine/integration/plugin/volume/cmd/cmd_test.go delete mode 100644 components/engine/integration/plugin/volume/cmd/create-error/main.go delete mode 100644 components/engine/integration/plugin/volume/cmd/create-error/main_test.go delete mode 100644 components/engine/integration/plugin/volume/create_test.go delete mode 100644 components/engine/integration/plugin/volume/main_test.go diff --git a/components/engine/integration/plugin/volume/cmd/cmd_test.go b/components/engine/integration/plugin/volume/cmd/cmd_test.go deleted file mode 100644 index 1d619dd05e..0000000000 --- a/components/engine/integration/plugin/volume/cmd/cmd_test.go +++ /dev/null @@ -1 +0,0 @@ -package cmd diff --git a/components/engine/integration/plugin/volume/cmd/create-error/main.go b/components/engine/integration/plugin/volume/cmd/create-error/main.go deleted file mode 100644 index f23be51d66..0000000000 --- a/components/engine/integration/plugin/volume/cmd/create-error/main.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "net" - "net/http" -) - -func main() { - l, err := net.Listen("unix", "/run/docker/plugins/plugin.sock") - if err != nil { - panic(err) - } - - mux := http.NewServeMux() - server := http.Server{ - Addr: l.Addr().String(), - Handler: http.NewServeMux(), - } - mux.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) { - http.Error(w, "error during create", http.StatusInternalServerError) - }) - server.Serve(l) -} diff --git a/components/engine/integration/plugin/volume/cmd/create-error/main_test.go b/components/engine/integration/plugin/volume/cmd/create-error/main_test.go deleted file mode 100644 index 06ab7d0f9a..0000000000 --- a/components/engine/integration/plugin/volume/cmd/create-error/main_test.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/components/engine/integration/plugin/volume/create_test.go b/components/engine/integration/plugin/volume/create_test.go deleted file mode 100644 index ce9b4dca43..0000000000 --- a/components/engine/integration/plugin/volume/create_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// +build linux - -package volume - -import ( - "context" - "testing" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/volume" - "github.com/docker/docker/integration-cli/daemon" -) - -// TestCreateDerefOnError ensures that if a volume create fails, that the plugin is dereferenced -// Normally 1 volume == 1 reference to a plugin, which prevents a plugin from being removed. -// If the volume create fails, we should make sure to dereference the plugin. -func TestCreateDerefOnError(t *testing.T) { - t.Parallel() - - d := daemon.New(t, "", dockerdBinary, daemon.Config{}) - d.Start(t) - defer d.Stop(t) - - c, err := d.NewClient() - if err != nil { - t.Fatal(err) - } - - pName := "testderef" - createPlugin(t, c, pName, "create-error", asVolumeDriver) - - if err := c.PluginEnable(context.Background(), pName, types.PluginEnableOptions{Timeout: 30}); err != nil { - t.Fatal(err) - } - - _, err = c.VolumeCreate(context.Background(), volume.VolumesCreateBody{ - Driver: pName, - Name: "fake", - }) - if err == nil { - t.Fatal("volume create should have failed") - } - - if err := c.PluginDisable(context.Background(), pName, types.PluginDisableOptions{}); err != nil { - t.Fatal(err) - } - - if err := c.PluginRemove(context.Background(), pName, types.PluginRemoveOptions{}); err != nil { - t.Fatal(err) - } -} diff --git a/components/engine/integration/plugin/volume/main_test.go b/components/engine/integration/plugin/volume/main_test.go deleted file mode 100644 index 8cfbf37978..0000000000 --- a/components/engine/integration/plugin/volume/main_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package volume - -import ( - "context" - "os" - "os/exec" - "path/filepath" - "testing" - "time" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/integration-cli/fixtures/plugin" - "github.com/docker/docker/pkg/locker" - "github.com/pkg/errors" -) - -const dockerdBinary = "dockerd" - -var pluginBuildLock = locker.New() - -func ensurePlugin(t *testing.T, name string) string { - pluginBuildLock.Lock(name) - defer pluginBuildLock.Unlock(name) - - installPath := filepath.Join(os.Getenv("GOPATH"), "bin", name) - if _, err := os.Stat(installPath); err == nil { - return installPath - } - - goBin, err := exec.LookPath("go") - if err != nil { - t.Fatal(err) - } - - cmd := exec.Command(goBin, "build", "-o", installPath, "./"+filepath.Join("cmd", name)) - cmd.Env = append(cmd.Env, "CGO_ENABLED=0") - if out, err := cmd.CombinedOutput(); err != nil { - t.Fatal(errors.Wrapf(err, "error building basic plugin bin: %s", string(out))) - } - - return installPath -} - -func asVolumeDriver(cfg *plugin.Config) { - cfg.Interface.Types = []types.PluginInterfaceType{ - {Capability: "volumedriver", Prefix: "docker", Version: "1.0"}, - } -} - -func withSockPath(name string) func(*plugin.Config) { - return func(cfg *plugin.Config) { - cfg.Interface.Socket = name - } -} - -func createPlugin(t *testing.T, client plugin.CreateClient, alias, bin string, opts ...plugin.CreateOpt) { - pluginBin := ensurePlugin(t, bin) - - opts = append(opts, withSockPath("plugin.sock")) - opts = append(opts, plugin.WithBinary(pluginBin)) - - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) - err := plugin.Create(ctx, client, alias, opts...) - cancel() - - if err != nil { - t.Fatal(err) - } -} diff --git a/components/engine/volume/store/store_test.go b/components/engine/volume/store/store_test.go index eb78c85cbe..7d5294043d 100644 --- a/components/engine/volume/store/store_test.go +++ b/components/engine/volume/store/store_test.go @@ -2,7 +2,9 @@ package store import ( "errors" + "fmt" "io/ioutil" + "net" "os" "strings" "testing" @@ -266,3 +268,55 @@ func TestCreateKeepOptsLabelsWhenExistsRemotely(t *testing.T) { t.Fatalf("got unexpected type: %T", v) } } + +func TestDefererencePluginOnCreateError(t *testing.T) { + var ( + l net.Listener + err error + ) + + for i := 32768; l == nil && i < 40000; i++ { + l, err = net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", i)) + } + if l == nil { + t.Fatalf("could not create listener: %v", err) + } + defer l.Close() + + d := volumetestutils.NewFakeDriver("TestDefererencePluginOnCreateError") + p, err := volumetestutils.MakeFakePlugin(d, l) + if err != nil { + t.Fatal(err) + } + + pg := volumetestutils.NewFakePluginGetter(p) + volumedrivers.RegisterPluginGetter(pg) + + 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) + } + + // create a good volume so we have a plugin reference + _, err = s.Create("fake1", d.Name(), nil, nil) + if err != nil { + t.Fatal(err) + } + + // Now create another one expecting an error + _, err = s.Create("fake2", d.Name(), map[string]string{"error": "some error"}, nil) + if err == nil || !strings.Contains(err.Error(), "some error") { + t.Fatalf("expected an error on create: %v", err) + } + + // There should be only 1 plugin reference + if refs := volumetestutils.FakeRefs(p); refs != 1 { + t.Fatalf("expected 1 plugin reference, got: %d", refs) + } +} diff --git a/components/engine/volume/testutils/testutils.go b/components/engine/volume/testutils/testutils.go index 359d923822..84ab55ff77 100644 --- a/components/engine/volume/testutils/testutils.go +++ b/components/engine/volume/testutils/testutils.go @@ -1,9 +1,15 @@ package testutils import ( + "encoding/json" + "errors" "fmt" + "net" + "net/http" "time" + "github.com/docker/docker/pkg/plugingetter" + "github.com/docker/docker/pkg/plugins" "github.com/docker/docker/volume" ) @@ -121,3 +127,99 @@ func (d *FakeDriver) Get(name string) (volume.Volume, error) { func (*FakeDriver) Scope() string { return "local" } + +type fakePlugin struct { + client *plugins.Client + name string + refs int +} + +// MakeFakePlugin creates a fake plugin from the passed in driver +// Note: currently only "Create" is implemented because that's all that's needed +// so far. If you need it to test something else, add it here, but probably you +// shouldn't need to use this except for very specific cases with v2 plugin handling. +func MakeFakePlugin(d volume.Driver, l net.Listener) (plugingetter.CompatPlugin, error) { + c, err := plugins.NewClient(l.Addr().Network()+"://"+l.Addr().String(), nil) + if err != nil { + return nil, err + } + mux := http.NewServeMux() + + mux.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) { + createReq := struct { + Name string + Opts map[string]string + }{} + if err := json.NewDecoder(r.Body).Decode(&createReq); err != nil { + fmt.Fprintf(w, `{"Err": "%s"}`, err.Error()) + return + } + _, err := d.Create(createReq.Name, createReq.Opts) + if err != nil { + fmt.Fprintf(w, `{"Err": "%s"}`, err.Error()) + return + } + w.Write([]byte("{}")) + }) + + go http.Serve(l, mux) + return &fakePlugin{client: c, name: d.Name()}, nil +} + +func (p *fakePlugin) Client() *plugins.Client { + return p.client +} + +func (p *fakePlugin) Name() string { + return p.name +} + +func (p *fakePlugin) IsV1() bool { + return false +} + +func (p *fakePlugin) BasePath() string { + return "" +} + +type fakePluginGetter struct { + plugins map[string]plugingetter.CompatPlugin +} + +// NewFakePluginGetter returns a plugin getter for fake plugins +func NewFakePluginGetter(pls ...plugingetter.CompatPlugin) plugingetter.PluginGetter { + idx := make(map[string]plugingetter.CompatPlugin, len(pls)) + for _, p := range pls { + idx[p.Name()] = p + } + return &fakePluginGetter{plugins: idx} +} + +// This ignores the second argument since we only care about volume drivers here, +// there shouldn't be any other kind of plugin in here +func (g *fakePluginGetter) Get(name, _ string, mode int) (plugingetter.CompatPlugin, error) { + p, ok := g.plugins[name] + if !ok { + return nil, errors.New("not found") + } + p.(*fakePlugin).refs += mode + return p, nil +} + +func (g *fakePluginGetter) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) { + panic("GetAllByCap shouldn't be called") +} + +func (g *fakePluginGetter) GetAllManagedPluginsByCap(capability string) []plugingetter.CompatPlugin { + panic("GetAllManagedPluginsByCap should not be called") +} + +func (g *fakePluginGetter) Handle(capability string, callback func(string, *plugins.Client)) { + panic("Handle should not be called") +} + +// FakeRefs checks ref count on a fake plugin. +func FakeRefs(p plugingetter.CompatPlugin) int { + // this should panic if something other than a `*fakePlugin` is passed in + return p.(*fakePlugin).refs +} From a1c54edb954528a68092241e5f02a1d3815c8bea Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Wed, 15 Nov 2017 19:28:36 -0800 Subject: [PATCH 72/94] container: protect the health status with mutex Adds a mutex to protect the status, as well. When running the race detector with the unit test, we can see that the Status field is written without holding this lock. Adding a mutex to read and set status addresses the issue. Signed-off-by: Stephen J Day Upstream-commit: 7db30ab0cdf072956d2ceda833b7de22fe17655c Component: engine --- components/engine/container/health.go | 37 ++++++++++++++++++++----- components/engine/daemon/health.go | 17 ++++++------ components/engine/daemon/health_test.go | 14 +++++----- components/engine/daemon/inspect.go | 2 +- 4 files changed, 47 insertions(+), 23 deletions(-) diff --git a/components/engine/container/health.go b/components/engine/container/health.go index 75365fec6b..6f75012fe1 100644 --- a/components/engine/container/health.go +++ b/components/engine/container/health.go @@ -16,19 +16,42 @@ type Health struct { // String returns a human-readable description of the health-check state func (s *Health) String() string { - // This happens when the monitor has yet to be setup. - if s.Status == "" { - return types.Unhealthy - } + status := s.Status() - switch s.Status { + switch status { case types.Starting: return "health: starting" default: // Healthy and Unhealthy are clear on their own - return s.Status + return s.Health.Status } } +// Status returns the current health status. +// +// Note that this takes a lock and the value may change after being read. +func (s *Health) Status() string { + s.mu.Lock() + defer s.mu.Unlock() + + // This happens when the monitor has yet to be setup. + if s.Health.Status == "" { + return types.Unhealthy + } + + return s.Health.Status +} + +// SetStatus writes the current status to the underlying health structure, +// obeying the locking semantics. +// +// Status may be set directly if another lock is used. +func (s *Health) SetStatus(new string) { + s.mu.Lock() + defer s.mu.Unlock() + + s.Health.Status = new +} + // OpenMonitorChannel creates and returns a new monitor channel. If there // already is one, it returns nil. func (s *Health) OpenMonitorChannel() chan struct{} { @@ -53,7 +76,7 @@ func (s *Health) CloseMonitorChannel() { close(s.stop) s.stop = nil // unhealthy when the monitor has stopped for compatibility reasons - s.Status = types.Unhealthy + s.Health.Status = types.Unhealthy logrus.Debug("CloseMonitorChannel done") } } diff --git a/components/engine/daemon/health.go b/components/engine/daemon/health.go index 7d8c84a397..26ae20f9b1 100644 --- a/components/engine/daemon/health.go +++ b/components/engine/daemon/health.go @@ -129,7 +129,7 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch } h := c.State.Health - oldStatus := h.Status + oldStatus := h.Status() if len(h.Log) >= maxLogEntries { h.Log = append(h.Log[len(h.Log)+1-maxLogEntries:], result) @@ -139,14 +139,14 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch if result.ExitCode == exitStatusHealthy { h.FailingStreak = 0 - h.Status = types.Healthy + h.SetStatus(types.Healthy) } else { // Failure (including invalid exit code) shouldIncrementStreak := true // If the container is starting (i.e. we never had a successful health check) // then we check if we are within the start period of the container in which // case we do not increment the failure streak. - if h.Status == types.Starting { + if h.Status() == types.Starting { startPeriod := timeoutWithDefault(c.Config.Healthcheck.StartPeriod, defaultStartPeriod) timeSinceStart := result.Start.Sub(c.State.StartedAt) @@ -160,7 +160,7 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch h.FailingStreak++ if h.FailingStreak >= retries { - h.Status = types.Unhealthy + h.SetStatus(types.Unhealthy) } } // Else we're starting or healthy. Stay in that state. @@ -173,8 +173,9 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch logrus.Errorf("Error replicating health state for container %s: %v", c.ID, err) } - if oldStatus != h.Status { - d.LogContainerEvent(c, "health_status: "+h.Status) + current := h.Status() + if oldStatus != current { + d.LogContainerEvent(c, "health_status: "+current) } } @@ -293,11 +294,11 @@ func (d *Daemon) initHealthMonitor(c *container.Container) { d.stopHealthchecks(c) if h := c.State.Health; h != nil { - h.Status = types.Starting + h.SetStatus(types.Starting) h.FailingStreak = 0 } else { h := &container.Health{} - h.Status = types.Starting + h.SetStatus(types.Starting) c.State.Health = h } diff --git a/components/engine/daemon/health_test.go b/components/engine/daemon/health_test.go index 4fd89140d3..479ff24c52 100644 --- a/components/engine/daemon/health_test.go +++ b/components/engine/daemon/health_test.go @@ -14,7 +14,7 @@ import ( func reset(c *container.Container) { c.State = &container.State{} c.State.Health = &container.Health{} - c.State.Health.Status = types.Starting + c.State.Health.SetStatus(types.Starting) } func TestNoneHealthcheck(t *testing.T) { @@ -111,8 +111,8 @@ func TestHealthStates(t *testing.T) { handleResult(c.State.StartedAt.Add(20*time.Second), 1) handleResult(c.State.StartedAt.Add(40*time.Second), 1) - if c.State.Health.Status != types.Starting { - t.Errorf("Expecting starting, but got %#v\n", c.State.Health.Status) + if status := c.State.Health.Status(); status != types.Starting { + t.Errorf("Expecting starting, but got %#v\n", status) } if c.State.Health.FailingStreak != 2 { t.Errorf("Expecting FailingStreak=2, but got %d\n", c.State.Health.FailingStreak) @@ -133,15 +133,15 @@ func TestHealthStates(t *testing.T) { c.Config.Healthcheck.StartPeriod = 30 * time.Second handleResult(c.State.StartedAt.Add(20*time.Second), 1) - if c.State.Health.Status != types.Starting { - t.Errorf("Expecting starting, but got %#v\n", c.State.Health.Status) + if status := c.State.Health.Status(); status != types.Starting { + t.Errorf("Expecting starting, but got %#v\n", status) } if c.State.Health.FailingStreak != 0 { t.Errorf("Expecting FailingStreak=0, but got %d\n", c.State.Health.FailingStreak) } handleResult(c.State.StartedAt.Add(50*time.Second), 1) - if c.State.Health.Status != types.Starting { - t.Errorf("Expecting starting, but got %#v\n", c.State.Health.Status) + if status := c.State.Health.Status(); status != types.Starting { + t.Errorf("Expecting starting, but got %#v\n", status) } if c.State.Health.FailingStreak != 1 { t.Errorf("Expecting FailingStreak=1, but got %d\n", c.State.Health.FailingStreak) diff --git a/components/engine/daemon/inspect.go b/components/engine/daemon/inspect.go index 8b0f109e32..20cfa6ce2b 100644 --- a/components/engine/daemon/inspect.go +++ b/components/engine/daemon/inspect.go @@ -139,7 +139,7 @@ func (daemon *Daemon) getInspectData(container *container.Container) (*types.Con var containerHealth *types.Health if container.State.Health != nil { containerHealth = &types.Health{ - Status: container.State.Health.Status, + Status: container.State.Health.Status(), FailingStreak: container.State.Health.FailingStreak, Log: append([]*types.HealthcheckResult{}, container.State.Health.Log...), } From 0db60dff81670e364238b63cfa4b2244158f5a17 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 15 Nov 2017 13:54:56 -0800 Subject: [PATCH 73/94] graphdriver: custom build-time priority list Add a way to specify a custom graphdriver priority list during build. This can be done with something like go build -ldflags "-X github.com/docker/docker/daemon/graphdriver.priority=overlay2,devicemapper" As ldflags are already used by the engine build process, and it seems that only one (last) `-ldflags` argument is taken into account by go, an envoronment variable `DOCKER_LDFLAGS` is introduced in order to be able to append some text to `-ldflags`. With this in place, using the feature becomes make DOCKER_LDFLAGS="-X github.com/docker/docker/daemon/graphdriver.priority=overlay2,devicemapper" dynbinary The idea behind this is, the priority list might be different for different distros, so vendors are now able to change it without patching the source code. Signed-off-by: Kir Kolyshkin Upstream-commit: 17708e72a7ef29fb1d4b03fbded1c5e4c08105fd Component: engine --- components/engine/Makefile | 8 ++++++++ components/engine/daemon/graphdriver/driver.go | 6 ++++-- .../engine/daemon/graphdriver/driver_freebsd.go | 6 ++---- components/engine/daemon/graphdriver/driver_linux.go | 12 ++---------- .../engine/daemon/graphdriver/driver_unsupported.go | 6 ++---- .../engine/daemon/graphdriver/driver_windows.go | 6 ++---- components/engine/hack/make/.binary | 1 + 7 files changed, 21 insertions(+), 24 deletions(-) diff --git a/components/engine/Makefile b/components/engine/Makefile index 7d50afa7d4..db7056f7c9 100644 --- a/components/engine/Makefile +++ b/components/engine/Makefile @@ -16,6 +16,13 @@ export DOCKER_GITCOMMIT # env vars passed through directly to Docker's build scripts # to allow things like `make KEEPBUNDLE=1 binary` easily # `project/PACKAGERS.md` have some limited documentation of some of these +# +# DOCKER_LDFLAGS can be used to pass additional parameters to -ldflags +# option of "go build". For example, a built-in graphdriver priority list +# can be changed during build time like this: +# +# make DOCKER_LDFLAGS="-X github.com/docker/docker/daemon/graphdriver.priority=overlay2,devicemapper" dynbinary +# DOCKER_ENVS := \ -e DOCKER_CROSSPLATFORMS \ -e BUILD_APT_MIRROR \ @@ -31,6 +38,7 @@ DOCKER_ENVS := \ -e DOCKER_GITCOMMIT \ -e DOCKER_GRAPHDRIVER \ -e DOCKER_INCREMENTAL_BINARY \ + -e DOCKER_LDFLAGS \ -e DOCKER_PORT \ -e DOCKER_REMAP_ROOT \ -e DOCKER_STORAGE_OPTS \ diff --git a/components/engine/daemon/graphdriver/driver.go b/components/engine/daemon/graphdriver/driver.go index 68f9022e1c..7a3a0d1892 100644 --- a/components/engine/daemon/graphdriver/driver.go +++ b/components/engine/daemon/graphdriver/driver.go @@ -208,7 +208,9 @@ func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, err // Guess for prior driver driversMap := scanPriorDrivers(config.Root) - for _, name := range priority { + list := strings.Split(priority, ",") + logrus.Debugf("[graphdriver] priority list: %v", list) + for _, name := range list { if name == "vfs" { // don't use vfs even if there is state present. continue @@ -243,7 +245,7 @@ func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, err } // Check for priority drivers first - for _, name := range priority { + for _, name := range list { driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.UIDMaps, config.GIDMaps) if err != nil { if isDriverNotSupported(err) { diff --git a/components/engine/daemon/graphdriver/driver_freebsd.go b/components/engine/daemon/graphdriver/driver_freebsd.go index 53394b738d..f9fded986b 100644 --- a/components/engine/daemon/graphdriver/driver_freebsd.go +++ b/components/engine/daemon/graphdriver/driver_freebsd.go @@ -7,10 +7,8 @@ import ( ) var ( - // Slice of drivers that should be used in an order - priority = []string{ - "zfs", - } + // List of drivers that should be used in an order + priority = "zfs" ) // Mounted checks if the given path is mounted as the fs type diff --git a/components/engine/daemon/graphdriver/driver_linux.go b/components/engine/daemon/graphdriver/driver_linux.go index a2be46b53a..aa3cfc9f79 100644 --- a/components/engine/daemon/graphdriver/driver_linux.go +++ b/components/engine/daemon/graphdriver/driver_linux.go @@ -51,16 +51,8 @@ const ( ) var ( - // Slice of drivers that should be used in an order - priority = []string{ - "btrfs", - "zfs", - "overlay2", - "aufs", - "overlay", - "devicemapper", - "vfs", - } + // List of drivers that should be used in an order + priority = "btrfs,zfs,overlay2,aufs,overlay,devicemapper,vfs" // FsNames maps filesystem id to name of the filesystem. FsNames = map[FsMagic]string{ diff --git a/components/engine/daemon/graphdriver/driver_unsupported.go b/components/engine/daemon/graphdriver/driver_unsupported.go index b3f6857309..f951e7674d 100644 --- a/components/engine/daemon/graphdriver/driver_unsupported.go +++ b/components/engine/daemon/graphdriver/driver_unsupported.go @@ -3,10 +3,8 @@ package graphdriver var ( - // Slice of drivers that should be used in an order - priority = []string{ - "unsupported", - } + // List of drivers that should be used in an order + priority = "unsupported" ) // GetFSMagic returns the filesystem id given the path. diff --git a/components/engine/daemon/graphdriver/driver_windows.go b/components/engine/daemon/graphdriver/driver_windows.go index ffd30c2950..7411089b03 100644 --- a/components/engine/daemon/graphdriver/driver_windows.go +++ b/components/engine/daemon/graphdriver/driver_windows.go @@ -1,10 +1,8 @@ package graphdriver var ( - // Slice of drivers that should be used in order - priority = []string{ - "windowsfilter", - } + // List of drivers that should be used in order + priority = "windowsfilter" ) // GetFSMagic returns the filesystem id given the path. diff --git a/components/engine/hack/make/.binary b/components/engine/hack/make/.binary index ff5cbff722..b3c7a795ab 100644 --- a/components/engine/hack/make/.binary +++ b/components/engine/hack/make/.binary @@ -57,6 +57,7 @@ go build \ -ldflags " $LDFLAGS $LDFLAGS_STATIC_DOCKER + $DOCKER_LDFLAGS " \ $GO_PACKAGE ) From 94aae8ab652e967c6ae513d85df46ea1a51e138a Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 16 Nov 2017 12:54:31 +0100 Subject: [PATCH 74/94] Skip further checks for quota in user namespaces Commit 7a1618ced359a3ac921d8a05903d62f544ff17d0 regresses running Docker in user namespaces. The new check for whether quota are supported calls NewControl() which in turn calls makeBackingFsDev() which tries to mknod(). Skip quota tests when we detect that we are running in a user namespace and return ErrQuotaNotSupported to the caller. This just restores the status quo. Signed-off-by: Christian Brauner Upstream-commit: 7e35df0e0484118740dbf01e7db9b482a1827ef1 Component: engine --- .../engine/daemon/graphdriver/quota/projectquota.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/engine/daemon/graphdriver/quota/projectquota.go b/components/engine/daemon/graphdriver/quota/projectquota.go index 9709588b6b..e25965baf3 100644 --- a/components/engine/daemon/graphdriver/quota/projectquota.go +++ b/components/engine/daemon/graphdriver/quota/projectquota.go @@ -58,6 +58,7 @@ import ( "path/filepath" "unsafe" + rsystem "github.com/opencontainers/runc/libcontainer/system" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -98,6 +99,14 @@ type Control struct { // project ids. // func NewControl(basePath string) (*Control, error) { + // + // If we are running in a user namespace quota won't be supported for + // now since makeBackingFsDev() will try to mknod(). + // + if rsystem.RunningInUserNS() { + return nil, ErrQuotaNotSupported + } + // // create backing filesystem device node // From be9497fbf46c479bad99e47b907acb7370f42d8b Mon Sep 17 00:00:00 2001 From: Asad Saeeduddin Date: Sat, 18 Nov 2017 22:32:56 -0500 Subject: [PATCH 75/94] Fix consumes MIME type for NetworkConnect This route expects `application/json`. Sending a content type header of `application/octet-stream` results in an error. Signed-off-by: Asad Saeeduddin Upstream-commit: 876b32861789a0424557c640622bde47eedd2d98 Component: engine --- components/engine/api/swagger.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/api/swagger.yaml b/components/engine/api/swagger.yaml index 3b532f7c19..a6b5ee5cfd 100644 --- a/components/engine/api/swagger.yaml +++ b/components/engine/api/swagger.yaml @@ -7891,7 +7891,7 @@ paths: summary: "Connect a container to a network" operationId: "NetworkConnect" consumes: - - "application/octet-stream" + - "application/json" responses: 200: description: "No error" From 38076c34e868c3231f8ba42fe0739cea8637962c Mon Sep 17 00:00:00 2001 From: Simon Ferquel Date: Tue, 14 Nov 2017 14:36:25 +0100 Subject: [PATCH 76/94] Added validation of isolation settings on daemon.verifyContainerSettings Signed-off-by: Simon Ferquel Upstream-commit: e6bfe9cdcb32e97f38b53781eb1f9d7bd2ef5971 Component: engine --- components/engine/daemon/container.go | 4 ++++ components/engine/daemon/daemon_linux_test.go | 7 +++++++ components/engine/daemon/daemon_test.go | 9 +++++++++ 3 files changed, 20 insertions(+) diff --git a/components/engine/daemon/container.go b/components/engine/daemon/container.go index 7338514682..26faedfdf9 100644 --- a/components/engine/daemon/container.go +++ b/components/engine/daemon/container.go @@ -329,6 +329,10 @@ func (daemon *Daemon) verifyContainerSettings(platform string, hostConfig *conta return nil, errors.Errorf("invalid restart policy '%s'", p.Name) } + if !hostConfig.Isolation.IsValid() { + return nil, errors.Errorf("invalid isolation '%s' on %s", hostConfig.Isolation, runtime.GOOS) + } + // Now do platform-specific verification return verifyPlatformContainerSettings(daemon, hostConfig, config, update) } diff --git a/components/engine/daemon/daemon_linux_test.go b/components/engine/daemon/daemon_linux_test.go index 36ef52e25a..e027b14dd0 100644 --- a/components/engine/daemon/daemon_linux_test.go +++ b/components/engine/daemon/daemon_linux_test.go @@ -157,3 +157,10 @@ func TestTmpfsDevShmSizeOverride(t *testing.T) { t.Fatal("/dev/shm not found in spec, or size option missing") } } + +func TestValidateContainerIsolationLinux(t *testing.T) { + d := Daemon{} + + _, err := d.verifyContainerSettings("linux", &containertypes.HostConfig{Isolation: containertypes.IsolationHyperV}, nil, false) + assert.EqualError(t, err, "invalid isolation 'hyperv' on linux") +} diff --git a/components/engine/daemon/daemon_test.go b/components/engine/daemon/daemon_test.go index 4044fad836..422be1fd77 100644 --- a/components/engine/daemon/daemon_test.go +++ b/components/engine/daemon/daemon_test.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "testing" containertypes "github.com/docker/docker/api/types/container" @@ -16,6 +17,7 @@ import ( "github.com/docker/docker/volume/local" "github.com/docker/docker/volume/store" "github.com/docker/go-connections/nat" + "github.com/stretchr/testify/assert" ) // @@ -302,3 +304,10 @@ func TestMerge(t *testing.T) { } } } + +func TestValidateContainerIsolation(t *testing.T) { + d := Daemon{} + + _, err := d.verifyContainerSettings(runtime.GOOS, &containertypes.HostConfig{Isolation: containertypes.Isolation("invalid")}, nil, false) + assert.EqualError(t, err, "invalid isolation 'invalid' on "+runtime.GOOS) +} From 276378cfb99af1b62e56bab949c7fb0d28da2ee3 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 20 Nov 2017 11:56:27 +0100 Subject: [PATCH 77/94] Fix "make test" failing on missing "test-unit" Commit dbf580be57a4bb854d7ce20d313e3a22ea337be5 removed this helper script because it's no longer used in CI. However, the "make test" target in the Makefile still called this helper, resulting it to fail. Signed-off-by: Sebastiaan van Stijn Upstream-commit: a17071e88f15625a6be19f80c697ab1c3471381b Component: engine --- components/engine/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/Makefile b/components/engine/Makefile index db7056f7c9..32988150c7 100644 --- a/components/engine/Makefile +++ b/components/engine/Makefile @@ -158,8 +158,8 @@ run: build ## run the docker daemon in a container shell: build ## start a shell inside the build env $(DOCKER_RUN_DOCKER) bash -test: build ## run the unit, integration and docker-py tests - $(DOCKER_RUN_DOCKER) hack/make.sh dynbinary cross test-unit test-integration test-docker-py +test: build test-unit ## run the unit, integration and docker-py tests + $(DOCKER_RUN_DOCKER) hack/make.sh dynbinary cross test-integration test-docker-py test-docker-py: build ## run the docker-py tests $(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-docker-py From 63aefcb844ae2f2e1a45b5c91c557dea993a532a Mon Sep 17 00:00:00 2001 From: Darren Stahl Date: Mon, 20 Nov 2017 16:27:21 -0800 Subject: [PATCH 78/94] Update hcsshim to v0.6.7 for go1.9 support Signed-off-by: Darren Stahl Upstream-commit: 0bee8049c78c635b8a38064483e4c3c788779345 Component: engine --- components/engine/vendor.conf | 2 +- .../github.com/Microsoft/hcsshim/errors.go | 22 ++++++++++++++++ .../Microsoft/hcsshim/hnsendpoint.go | 23 ++++++++++------- .../Microsoft/hcsshim/hnsnetwork.go | 3 +-- .../github.com/Microsoft/hcsshim/hnspolicy.go | 25 +++++++++---------- .../github.com/Microsoft/hcsshim/legacy.go | 19 +++++++++----- .../github.com/Microsoft/hcsshim/legacy18.go | 7 ++++++ .../github.com/Microsoft/hcsshim/legacy19.go | 7 ++++++ 8 files changed, 77 insertions(+), 31 deletions(-) create mode 100644 components/engine/vendor/github.com/Microsoft/hcsshim/legacy18.go create mode 100644 components/engine/vendor/github.com/Microsoft/hcsshim/legacy19.go diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index 22a5d90540..4487925249 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -1,6 +1,6 @@ # the following lines are in sorted order, FYI github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 -github.com/Microsoft/hcsshim v0.6.5 +github.com/Microsoft/hcsshim v0.6.7 github.com/Microsoft/go-winio v0.4.5 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a diff --git a/components/engine/vendor/github.com/Microsoft/hcsshim/errors.go b/components/engine/vendor/github.com/Microsoft/hcsshim/errors.go index d2f9cc8bd2..c0c6cac87c 100644 --- a/components/engine/vendor/github.com/Microsoft/hcsshim/errors.go +++ b/components/engine/vendor/github.com/Microsoft/hcsshim/errors.go @@ -72,6 +72,22 @@ var ( ErrPlatformNotSupported = errors.New("unsupported platform request") ) +type EndpointNotFoundError struct { + EndpointName string +} + +func (e EndpointNotFoundError) Error() string { + return fmt.Sprintf("Endpoint %s not found", e.EndpointName) +} + +type NetworkNotFoundError struct { + NetworkName string +} + +func (e NetworkNotFoundError) Error() string { + return fmt.Sprintf("Network %s not found", e.NetworkName) +} + // ProcessError is an error encountered in HCS during an operation on a Process object type ProcessError struct { Process *process @@ -174,6 +190,12 @@ func makeProcessError(process *process, operation string, extraInfo string, err // will currently return true when the error is ErrElementNotFound or ErrProcNotFound. func IsNotExist(err error) bool { err = getInnerError(err) + if _, ok := err.(EndpointNotFoundError); ok { + return true + } + if _, ok := err.(NetworkNotFoundError); ok { + return true + } return err == ErrComputeSystemDoesNotExist || err == ErrElementNotFound || err == ErrProcNotFound diff --git a/components/engine/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go b/components/engine/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go index 92afc0c249..7e516f8a2e 100644 --- a/components/engine/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go +++ b/components/engine/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go @@ -2,7 +2,6 @@ package hcsshim import ( "encoding/json" - "fmt" "net" "github.com/sirupsen/logrus" @@ -135,7 +134,7 @@ func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { return &hnsEndpoint, nil } } - return nil, fmt.Errorf("Endpoint %v not found", endpointName) + return nil, EndpointNotFoundError{EndpointName: endpointName} } // Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods @@ -192,18 +191,24 @@ func (endpoint *HNSEndpoint) ContainerHotDetach(containerID string) error { return modifyNetworkEndpoint(containerID, endpoint.Id, Remove) } -// ApplyACLPolicy applies Acl Policy on the Endpoint -func (endpoint *HNSEndpoint) ApplyACLPolicy(policy *ACLPolicy) error { +// ApplyACLPolicy applies a set of ACL Policies on the Endpoint +func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error { operation := "ApplyACLPolicy" title := "HCSShim::HNSEndpoint::" + operation logrus.Debugf(title+" id=%s", endpoint.Id) - jsonString, err := json.Marshal(policy) - if err != nil { - return err + for _, policy := range policies { + if policy == nil { + continue + } + jsonString, err := json.Marshal(policy) + if err != nil { + return err + } + endpoint.Policies = append(endpoint.Policies, jsonString) } - endpoint.Policies[0] = jsonString - _, err = endpoint.Update() + + _, err := endpoint.Update() return err } diff --git a/components/engine/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go b/components/engine/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go index 3345bfa3f2..04c1b59196 100644 --- a/components/engine/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go +++ b/components/engine/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go @@ -2,7 +2,6 @@ package hcsshim import ( "encoding/json" - "fmt" "net" "github.com/sirupsen/logrus" @@ -90,7 +89,7 @@ func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) { return &hnsnetwork, nil } } - return nil, fmt.Errorf("Network %v not found", networkName) + return nil, NetworkNotFoundError{NetworkName: networkName} } // Create Network by sending NetworkRequest to HNS. diff --git a/components/engine/vendor/github.com/Microsoft/hcsshim/hnspolicy.go b/components/engine/vendor/github.com/Microsoft/hcsshim/hnspolicy.go index ecfbf0eda3..65b8e93d9b 100644 --- a/components/engine/vendor/github.com/Microsoft/hcsshim/hnspolicy.go +++ b/components/engine/vendor/github.com/Microsoft/hcsshim/hnspolicy.go @@ -75,19 +75,18 @@ const ( ) type ACLPolicy struct { - Type PolicyType `json:"Type"` - Protocol uint16 - InternalPort uint16 - Action ActionType - Direction DirectionType - LocalAddress string - RemoteAddress string - LocalPort uint16 - RemotePort uint16 - RuleType RuleType `json:"RuleType,omitempty"` - - Priority uint16 - ServiceName string + Type PolicyType `json:"Type"` + Protocol uint16 + InternalPort uint16 + Action ActionType + Direction DirectionType + LocalAddresses string + RemoteAddresses string + LocalPort uint16 + RemotePort uint16 + RuleType RuleType `json:"RuleType,omitempty"` + Priority uint16 + ServiceName string } type Policy struct { diff --git a/components/engine/vendor/github.com/Microsoft/hcsshim/legacy.go b/components/engine/vendor/github.com/Microsoft/hcsshim/legacy.go index c7f6073ac3..a0a97d7c72 100644 --- a/components/engine/vendor/github.com/Microsoft/hcsshim/legacy.go +++ b/components/engine/vendor/github.com/Microsoft/hcsshim/legacy.go @@ -472,15 +472,21 @@ func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error { } destFilePath := filepath.Join(destPath, relPath) + fileAttributes := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes // Directories, reparse points, and files that will be mutated during // utility VM import must be copied. All other files can be hard linked. - isReparsePoint := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 - if info.IsDir() || isReparsePoint || mutatedFiles[relPath] { - fi, err := copyFileWithMetadata(srcFilePath, destFilePath, info.IsDir()) + isReparsePoint := fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 + // In go1.9, FileInfo.IsDir() returns false if the directory is also a symlink. + // See: https://github.com/golang/go/commit/1989921aef60c83e6f9127a8448fb5ede10e9acc + // Fixes the problem by checking syscall.FILE_ATTRIBUTE_DIRECTORY directly + isDir := fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 + + if isDir || isReparsePoint || mutatedFiles[relPath] { + fi, err := copyFileWithMetadata(srcFilePath, destFilePath, isDir) if err != nil { return err } - if info.IsDir() && !isReparsePoint { + if isDir && !isReparsePoint { di = append(di, dirInfo{path: destFilePath, fileInfo: *fi}) } } else { @@ -490,8 +496,9 @@ func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error { } } - // Don't recurse on reparse points. - if info.IsDir() && isReparsePoint { + // Don't recurse on reparse points in go1.8 and older. Filepath.Walk + // handles this in go1.9 and newer. + if isDir && isReparsePoint && shouldSkipDirectoryReparse { return filepath.SkipDir } diff --git a/components/engine/vendor/github.com/Microsoft/hcsshim/legacy18.go b/components/engine/vendor/github.com/Microsoft/hcsshim/legacy18.go new file mode 100644 index 0000000000..578552f913 --- /dev/null +++ b/components/engine/vendor/github.com/Microsoft/hcsshim/legacy18.go @@ -0,0 +1,7 @@ +// +build !go1.9 + +package hcsshim + +// Due to a bug in go1.8 and before, directory reparse points need to be skipped +// during filepath.Walk. This is fixed in go1.9 +var shouldSkipDirectoryReparse = true diff --git a/components/engine/vendor/github.com/Microsoft/hcsshim/legacy19.go b/components/engine/vendor/github.com/Microsoft/hcsshim/legacy19.go new file mode 100644 index 0000000000..6aa1dc0584 --- /dev/null +++ b/components/engine/vendor/github.com/Microsoft/hcsshim/legacy19.go @@ -0,0 +1,7 @@ +// +build go1.9 + +package hcsshim + +// Due to a bug in go1.8 and before, directory reparse points need to be skipped +// during filepath.Walk. This is fixed in go1.9 +var shouldSkipDirectoryReparse = false From 69bca6d97b23b23e17e6ede70911c19b15aaaddb Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 10 Nov 2017 15:41:02 -0800 Subject: [PATCH 79/94] container.NetworkMounts(): don't lookup mounts twice The code in question looks up mounts two times: first by using HasMountFor(), and then directly by looking in container.MountPoints. There is no need to do it twice. Signed-off-by: Kir Kolyshkin Upstream-commit: eab3ac3e70a510b97f9399efd13e3dc01a07c413 Component: engine --- components/engine/container/container_unix.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/components/engine/container/container_unix.go b/components/engine/container/container_unix.go index 77851946f3..2e1b95f404 100644 --- a/components/engine/container/container_unix.go +++ b/components/engine/container/container_unix.go @@ -65,12 +65,11 @@ func (container *Container) NetworkMounts() []Mount { 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) } else { - if !container.HasMountFor("/etc/resolv.conf") { - label.Relabel(container.ResolvConfPath, container.MountLabel, shared) - } writable := !container.HostConfig.ReadonlyRootfs if m, exists := container.MountPoints["/etc/resolv.conf"]; exists { writable = m.RW + } else { + label.Relabel(container.ResolvConfPath, container.MountLabel, shared) } mounts = append(mounts, Mount{ Source: container.ResolvConfPath, @@ -84,12 +83,11 @@ func (container *Container) NetworkMounts() []Mount { if _, err := os.Stat(container.HostnamePath); err != nil { logrus.Warnf("HostnamePath set to %q, but can't stat this filename (err = %v); skipping", container.HostnamePath, err) } else { - if !container.HasMountFor("/etc/hostname") { - label.Relabel(container.HostnamePath, container.MountLabel, shared) - } writable := !container.HostConfig.ReadonlyRootfs if m, exists := container.MountPoints["/etc/hostname"]; exists { writable = m.RW + } else { + label.Relabel(container.HostnamePath, container.MountLabel, shared) } mounts = append(mounts, Mount{ Source: container.HostnamePath, @@ -103,12 +101,11 @@ func (container *Container) NetworkMounts() []Mount { if _, err := os.Stat(container.HostsPath); err != nil { logrus.Warnf("HostsPath set to %q, but can't stat this filename (err = %v); skipping", container.HostsPath, err) } else { - if !container.HasMountFor("/etc/hosts") { - label.Relabel(container.HostsPath, container.MountLabel, shared) - } writable := !container.HostConfig.ReadonlyRootfs if m, exists := container.MountPoints["/etc/hosts"]; exists { writable = m.RW + } else { + label.Relabel(container.HostsPath, container.MountLabel, shared) } mounts = append(mounts, Mount{ Source: container.HostsPath, From 6e7cb1931d8fc3b1fe2bcb41e9572394c41accbc Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 10 Nov 2017 15:43:57 -0800 Subject: [PATCH 80/94] Fix "duplicate mount point" when --tmpfs /dev/shm is used This is a fix to the following issue: $ docker run --tmpfs /dev/shm busybox sh docker: Error response from daemon: linux mounts: Duplicate mount point '/dev/shm'. In current code (daemon.createSpec()), tmpfs mount from --tmpfs is added to list of mounts (`ms`), when the mount from IpcMounts() is added. While IpcMounts() is checking for existing mounts first, it does that by using container.HasMountFor() function which only checks container.Mounts but not container.Tmpfs. Ultimately, the solution is to get rid of container.Tmpfs (moving its data to container.Mounts). Current workaround is to add checking of container.Tmpfs into container.HasMountFor(). A unit test case is included. Unfortunately we can't call daemon.createSpec() from a unit test, as the code relies a lot on various daemon structures to be initialized properly, and it is hard to achieve. Therefore, we minimally mimick the code flow of daemon.createSpec() -- barely enough to reproduce the issue. https://github.com/moby/moby/issues/35455 Signed-off-by: Kir Kolyshkin Upstream-commit: 1861abdc4a31efad202a5c3d89a895bb7a62799a Component: engine --- components/engine/container/container_unix.go | 13 ++++- components/engine/daemon/oci_linux_test.go | 50 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 components/engine/daemon/oci_linux_test.go diff --git a/components/engine/container/container_unix.go b/components/engine/container/container_unix.go index 2e1b95f404..28a3722c79 100644 --- a/components/engine/container/container_unix.go +++ b/components/engine/container/container_unix.go @@ -157,7 +157,18 @@ func (container *Container) ShmResourcePath() (string, error) { // HasMountFor checks if path is a mountpoint func (container *Container) HasMountFor(path string) bool { _, exists := container.MountPoints[path] - return exists + if exists { + return true + } + + // Also search among the tmpfs mounts + for dest := range container.HostConfig.Tmpfs { + if dest == path { + return true + } + } + + return false } // UnmountIpcMount uses the provided unmount function to unmount shm if it was mounted diff --git a/components/engine/daemon/oci_linux_test.go b/components/engine/daemon/oci_linux_test.go new file mode 100644 index 0000000000..f2f455f9c6 --- /dev/null +++ b/components/engine/daemon/oci_linux_test.go @@ -0,0 +1,50 @@ +package daemon + +import ( + "testing" + + containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/container" + "github.com/docker/docker/daemon/config" + "github.com/docker/docker/oci" + "github.com/docker/docker/pkg/idtools" + + "github.com/stretchr/testify/assert" +) + +// TestTmpfsDevShmNoDupMount checks that a user-specified /dev/shm tmpfs +// mount (as in "docker run --tmpfs /dev/shm:rw,size=NNN") does not result +// in "Duplicate mount point" error from the engine. +// https://github.com/moby/moby/issues/35455 +func TestTmpfsDevShmNoDupMount(t *testing.T) { + d := Daemon{ + // some empty structs to avoid getting a panic + // caused by a null pointer dereference + idMappings: &idtools.IDMappings{}, + configStore: &config.Config{}, + } + c := &container.Container{ + ShmPath: "foobar", // non-empty, for c.IpcMounts() to work + HostConfig: &containertypes.HostConfig{ + IpcMode: containertypes.IpcMode("shareable"), // default mode + // --tmpfs /dev/shm:rw,exec,size=NNN + Tmpfs: map[string]string{ + "/dev/shm": "rw,exec,size=1g", + }, + }, + } + + // Mimick the code flow of daemon.createSpec(), enough to reproduce the issue + ms, err := d.setupMounts(c) + assert.NoError(t, err) + + ms = append(ms, c.IpcMounts()...) + + tmpfsMounts, err := c.TmpfsMounts() + assert.NoError(t, err) + ms = append(ms, tmpfsMounts...) + + s := oci.DefaultSpec() + err = setMounts(&d, &s, c, ms) + assert.NoError(t, err) +} From d4007aa747b8a59ee75740c0daae7064d5636768 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 19 Nov 2017 00:41:09 +0100 Subject: [PATCH 81/94] Update idtools tests to test non-deprecated functions Signed-off-by: Sebastiaan van Stijn Upstream-commit: 9aba019b72611119578ea32adb03fa49f73da522 Component: engine --- .../engine/pkg/idtools/idtools_unix_test.go | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/components/engine/pkg/idtools/idtools_unix_test.go b/components/engine/pkg/idtools/idtools_unix_test.go index 6d3b591ec1..afefdb34d0 100644 --- a/components/engine/pkg/idtools/idtools_unix_test.go +++ b/components/engine/pkg/idtools/idtools_unix_test.go @@ -25,7 +25,7 @@ type node struct { gid int } -func TestMkdirAllAs(t *testing.T) { +func TestMkdirAllAndChown(t *testing.T) { RequiresRoot(t) dirName, err := ioutil.TempDir("", "mkdirall") if err != nil { @@ -46,7 +46,7 @@ func TestMkdirAllAs(t *testing.T) { } // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid - if err := MkdirAllAs(filepath.Join(dirName, "usr", "share"), 0755, 99, 99); err != nil { + if err := MkdirAllAndChown(filepath.Join(dirName, "usr", "share"), 0755, IDPair{UID: 99, GID: 99}); err != nil { t.Fatal(err) } testTree["usr/share"] = node{99, 99} @@ -59,7 +59,7 @@ func TestMkdirAllAs(t *testing.T) { } // test 2-deep new directories--both should be owned by the uid/gid pair - if err := MkdirAllAs(filepath.Join(dirName, "lib", "some", "other"), 0755, 101, 101); err != nil { + if err := MkdirAllAndChown(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{UID: 101, GID: 101}); err != nil { t.Fatal(err) } testTree["lib/some"] = node{101, 101} @@ -73,7 +73,7 @@ func TestMkdirAllAs(t *testing.T) { } // test a directory that already exists; should be chowned, but nothing else - if err := MkdirAllAs(filepath.Join(dirName, "usr"), 0755, 102, 102); err != nil { + if err := MkdirAllAndChown(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 102, GID: 102}); err != nil { t.Fatal(err) } testTree["usr"] = node{102, 102} @@ -102,7 +102,7 @@ func TestMkdirAllAndChownNew(t *testing.T) { require.NoError(t, buildTree(dirName, testTree)) // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid - err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0755, IDPair{99, 99}) + err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0755, IDPair{UID: 99, GID: 99}) require.NoError(t, err) testTree["usr/share"] = node{99, 99} @@ -111,7 +111,7 @@ func TestMkdirAllAndChownNew(t *testing.T) { require.NoError(t, compareTrees(testTree, verifyTree)) // test 2-deep new directories--both should be owned by the uid/gid pair - err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{101, 101}) + err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{UID: 101, GID: 101}) require.NoError(t, err) testTree["lib/some"] = node{101, 101} testTree["lib/some/other"] = node{101, 101} @@ -120,14 +120,14 @@ func TestMkdirAllAndChownNew(t *testing.T) { require.NoError(t, compareTrees(testTree, verifyTree)) // test a directory that already exists; should NOT be chowned - err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0755, IDPair{102, 102}) + err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 102, GID: 102}) require.NoError(t, err) verifyTree, err = readTree(dirName, "") require.NoError(t, err) require.NoError(t, compareTrees(testTree, verifyTree)) } -func TestMkdirAs(t *testing.T) { +func TestMkdirAndChown(t *testing.T) { RequiresRoot(t) dirName, err := ioutil.TempDir("", "mkdir") if err != nil { @@ -143,7 +143,7 @@ func TestMkdirAs(t *testing.T) { } // test a directory that already exists; should just chown to the requested uid/gid - if err := MkdirAs(filepath.Join(dirName, "usr"), 0755, 99, 99); err != nil { + if err := MkdirAndChown(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 99, GID: 99}); err != nil { t.Fatal(err) } testTree["usr"] = node{99, 99} @@ -156,12 +156,12 @@ func TestMkdirAs(t *testing.T) { } // create a subdir under a dir which doesn't exist--should fail - if err := MkdirAs(filepath.Join(dirName, "usr", "bin", "subdir"), 0755, 102, 102); err == nil { + if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin", "subdir"), 0755, IDPair{UID: 102, GID: 102}); err == nil { t.Fatalf("Trying to create a directory with Mkdir where the parent doesn't exist should have failed") } // create a subdir under an existing dir; should only change the ownership of the new subdir - if err := MkdirAs(filepath.Join(dirName, "usr", "bin"), 0755, 102, 102); err != nil { + if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin"), 0755, IDPair{UID: 102, GID: 102}); err != nil { t.Fatal(err) } testTree["usr/bin"] = node{102, 102} @@ -336,7 +336,7 @@ func TestNewIDMappings(t *testing.T) { assert.NoError(t, err, "Couldn't create temp directory") defer os.RemoveAll(dirName) - err = MkdirAllAs(dirName, 0700, rootUID, rootGID) + err = MkdirAllAndChown(dirName, 0700, IDPair{UID: rootUID, GID: rootGID}) assert.NoError(t, err, "Couldn't change ownership of file path. Got error") assert.True(t, CanAccess(dirName, idMappings.RootPair()), fmt.Sprintf("Unable to access %s directory with user UID:%d and GID:%d", dirName, rootUID, rootGID)) } From 9282ca84049567e71be06f13b337895a0d81da28 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 19 Nov 2017 00:49:17 +0100 Subject: [PATCH 82/94] Minor refactor in idtools Signed-off-by: Sebastiaan van Stijn Upstream-commit: ce66470f5404872db2cbaa7fbd59970700144be2 Component: engine --- components/engine/pkg/idtools/idtools.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/engine/pkg/idtools/idtools.go b/components/engine/pkg/idtools/idtools.go index 68a072db22..c905a64720 100644 --- a/components/engine/pkg/idtools/idtools.go +++ b/components/engine/pkg/idtools/idtools.go @@ -52,21 +52,21 @@ func MkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int) error { // MkdirAllAndChown creates a directory (include any along the path) and then modifies // ownership to the requested uid/gid. If the directory already exists, this // function will still change ownership to the requested uid/gid pair. -func MkdirAllAndChown(path string, mode os.FileMode, ids IDPair) error { - return mkdirAs(path, mode, ids.UID, ids.GID, true, true) +func MkdirAllAndChown(path string, mode os.FileMode, owner IDPair) error { + return mkdirAs(path, mode, owner.UID, owner.GID, true, true) } // MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid. // If the directory already exists, this function still changes ownership -func MkdirAndChown(path string, mode os.FileMode, ids IDPair) error { - return mkdirAs(path, mode, ids.UID, ids.GID, false, true) +func MkdirAndChown(path string, mode os.FileMode, owner IDPair) error { + return mkdirAs(path, mode, owner.UID, owner.GID, false, true) } // MkdirAllAndChownNew creates a directory (include any along the path) and then modifies // ownership ONLY of newly created directories to the requested uid/gid. If the // directories along the path exist, no change of ownership will be performed -func MkdirAllAndChownNew(path string, mode os.FileMode, ids IDPair) error { - return mkdirAs(path, mode, ids.UID, ids.GID, true, false) +func MkdirAllAndChownNew(path string, mode os.FileMode, owner IDPair) error { + return mkdirAs(path, mode, owner.UID, owner.GID, true, false) } // GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps. From d0d7235731bade8e59232759e9a346d520eee3c7 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 19 Nov 2017 00:52:47 +0100 Subject: [PATCH 83/94] Remove deprecated MkdirAllAs(), MkdirAs() Signed-off-by: Sebastiaan van Stijn Upstream-commit: 38b3af567f676c4c35e80e493aa97b7346ae75e4 Component: engine --- .../engine/daemon/graphdriver/aufs/aufs.go | 6 ++--- .../engine/daemon/graphdriver/btrfs/btrfs.go | 6 ++--- .../daemon/graphdriver/devmapper/deviceset.go | 4 ++-- .../daemon/graphdriver/devmapper/driver.go | 6 ++--- .../engine/daemon/graphdriver/lcow/lcow.go | 6 ++--- .../daemon/graphdriver/overlay/overlay.go | 22 ++++++++++--------- .../daemon/graphdriver/overlay2/overlay.go | 14 +++++++----- .../daemon/graphdriver/windows/windows.go | 2 +- .../engine/daemon/graphdriver/zfs/zfs.go | 4 ++-- components/engine/pkg/idtools/idtools.go | 15 ------------- 10 files changed, 37 insertions(+), 48 deletions(-) diff --git a/components/engine/daemon/graphdriver/aufs/aufs.go b/components/engine/daemon/graphdriver/aufs/aufs.go index 8313263ff1..5a1f3d1fdd 100644 --- a/components/engine/daemon/graphdriver/aufs/aufs.go +++ b/components/engine/daemon/graphdriver/aufs/aufs.go @@ -125,7 +125,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap // Create the root aufs driver dir and return // if it already exists // If not populate the dir structure - if err := idtools.MkdirAllAs(root, 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAndChown(root, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil { if os.IsExist(err) { return a, nil } @@ -138,7 +138,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap // Populate the dir structure for _, p := range paths { - if err := idtools.MkdirAllAs(path.Join(root, p), 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAndChown(path.Join(root, p), 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil { return nil, err } } @@ -290,7 +290,7 @@ func (a *Driver) createDirsFor(id string) error { // The path of directories are /mnt/ // and /diff/ for _, p := range paths { - if err := idtools.MkdirAllAs(path.Join(a.rootPath(), p, id), 0755, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAndChown(path.Join(a.rootPath(), p, id), 0755, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil { return err } } diff --git a/components/engine/daemon/graphdriver/btrfs/btrfs.go b/components/engine/daemon/graphdriver/btrfs/btrfs.go index 5a61217341..0dabf711dd 100644 --- a/components/engine/daemon/graphdriver/btrfs/btrfs.go +++ b/components/engine/daemon/graphdriver/btrfs/btrfs.go @@ -64,7 +64,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap if err != nil { return nil, err } - if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAndChown(home, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil { return nil, err } @@ -502,7 +502,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { if err != nil { return err } - if err := idtools.MkdirAllAs(subvolumes, 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAndChown(subvolumes, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil { return err } if parent == "" { @@ -537,7 +537,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { if err := d.setStorageSize(path.Join(subvolumes, id), driver); err != nil { return err } - if err := idtools.MkdirAllAs(quotas, 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil { return err } if err := ioutil.WriteFile(path.Join(quotas, id), []byte(fmt.Sprint(driver.options.size)), 0644); err != nil { diff --git a/components/engine/daemon/graphdriver/devmapper/deviceset.go b/components/engine/daemon/graphdriver/devmapper/deviceset.go index db42aad586..160ba46407 100644 --- a/components/engine/daemon/graphdriver/devmapper/deviceset.go +++ b/components/engine/daemon/graphdriver/devmapper/deviceset.go @@ -268,7 +268,7 @@ func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) { if err != nil { return "", err } - if err := idtools.MkdirAllAs(dirname, 0700, uid, gid); err != nil && !os.IsExist(err) { + if err := idtools.MkdirAllAndChown(dirname, 0700, idtools.IDPair{UID: uid, GID: gid}); err != nil && !os.IsExist(err) { return "", err } @@ -1697,7 +1697,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { if err != nil { return err } - if err := idtools.MkdirAs(devices.root, 0700, uid, gid); err != nil && !os.IsExist(err) { + if err := idtools.MkdirAndChown(devices.root, 0700, idtools.IDPair{UID: uid, GID: gid}); err != nil && !os.IsExist(err) { return err } if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil && !os.IsExist(err) { diff --git a/components/engine/daemon/graphdriver/devmapper/driver.go b/components/engine/daemon/graphdriver/devmapper/driver.go index bdb2336265..288f50126f 100644 --- a/components/engine/daemon/graphdriver/devmapper/driver.go +++ b/components/engine/daemon/graphdriver/devmapper/driver.go @@ -189,11 +189,11 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) { } // Create the target directories if they don't exist - if err := idtools.MkdirAllAs(path.Join(d.home, "mnt"), 0755, uid, gid); err != nil && !os.IsExist(err) { + if err := idtools.MkdirAllAndChown(path.Join(d.home, "mnt"), 0755, idtools.IDPair{UID: uid, GID: gid}); err != nil && !os.IsExist(err) { d.ctr.Decrement(mp) return nil, err } - if err := idtools.MkdirAs(mp, 0755, uid, gid); err != nil && !os.IsExist(err) { + if err := idtools.MkdirAndChown(mp, 0755, idtools.IDPair{UID: uid, GID: gid}); err != nil && !os.IsExist(err) { d.ctr.Decrement(mp) return nil, err } @@ -204,7 +204,7 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) { return nil, err } - if err := idtools.MkdirAllAs(rootFs, 0755, uid, gid); err != nil && !os.IsExist(err) { + if err := idtools.MkdirAllAndChown(rootFs, 0755, idtools.IDPair{UID: uid, GID: gid}); err != nil && !os.IsExist(err) { d.ctr.Decrement(mp) d.DeviceSet.UnmountDevice(id, mp) return nil, err diff --git a/components/engine/daemon/graphdriver/lcow/lcow.go b/components/engine/daemon/graphdriver/lcow/lcow.go index c999d5b145..5ec8b8baaa 100644 --- a/components/engine/daemon/graphdriver/lcow/lcow.go +++ b/components/engine/daemon/graphdriver/lcow/lcow.go @@ -181,17 +181,17 @@ func InitDriver(dataRoot string, options []string, _, _ []idtools.IDMap) (graphd } // Make sure the dataRoot directory is created - if err := idtools.MkdirAllAs(dataRoot, 0700, 0, 0); err != nil { + if err := idtools.MkdirAllAndChown(dataRoot, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil { return nil, fmt.Errorf("%s failed to create '%s': %v", title, dataRoot, err) } // Make sure the cache directory is created under dataRoot - if err := idtools.MkdirAllAs(cd, 0700, 0, 0); err != nil { + if err := idtools.MkdirAllAndChown(cd, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil { return nil, fmt.Errorf("%s failed to create '%s': %v", title, cd, err) } // Make sure the scratch directory is created under dataRoot - if err := idtools.MkdirAllAs(sd, 0700, 0, 0); err != nil { + if err := idtools.MkdirAllAndChown(sd, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil { return nil, fmt.Errorf("%s failed to create '%s': %v", title, sd, err) } diff --git a/components/engine/daemon/graphdriver/overlay/overlay.go b/components/engine/daemon/graphdriver/overlay/overlay.go index 3db84bc229..853318de59 100644 --- a/components/engine/daemon/graphdriver/overlay/overlay.go +++ b/components/engine/daemon/graphdriver/overlay/overlay.go @@ -136,7 +136,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, err } // Create the driver home dir - if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { + if err := idtools.MkdirAllAndChown(home, 0700, idtools.IDPair{rootUID, rootGID}); err != nil && !os.IsExist(err) { return nil, err } @@ -255,10 +255,12 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr if err != nil { return err } - if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil { + root := idtools.IDPair{UID: rootUID, GID: rootGID} + + if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil { return err } - if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAndChown(dir, 0700, root); err != nil { return err } @@ -271,7 +273,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr // Toplevel images are just a "root" dir if parent == "" { - return idtools.MkdirAndChown(path.Join(dir, "root"), 0755, idtools.IDPair{rootUID, rootGID}) + return idtools.MkdirAndChown(path.Join(dir, "root"), 0755, root) } parentDir := d.dir(parent) @@ -285,13 +287,13 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr parentRoot := path.Join(parentDir, "root") if s, err := os.Lstat(parentRoot); err == nil { - if err := idtools.MkdirAs(path.Join(dir, "upper"), s.Mode(), rootUID, rootGID); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, "upper"), s.Mode(), root); err != nil { return err } - if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil { return err } - if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, "merged"), 0700, root); err != nil { return err } if err := ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666); err != nil { @@ -318,13 +320,13 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr } upperDir := path.Join(dir, "upper") - if err := idtools.MkdirAs(upperDir, s.Mode(), rootUID, rootGID); err != nil { + if err := idtools.MkdirAndChown(upperDir, s.Mode(), root); err != nil { return err } - if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil { return err } - if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, "merged"), 0700, root); err != nil { return err } diff --git a/components/engine/daemon/graphdriver/overlay2/overlay.go b/components/engine/daemon/graphdriver/overlay2/overlay.go index 0f252e6ff2..c2023c7252 100644 --- a/components/engine/daemon/graphdriver/overlay2/overlay.go +++ b/components/engine/daemon/graphdriver/overlay2/overlay.go @@ -171,7 +171,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, err } // Create the driver home dir - if err := idtools.MkdirAllAs(path.Join(home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { + if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, idtools.IDPair{rootUID, rootGID}); err != nil && !os.IsExist(err) { return nil, err } @@ -362,10 +362,12 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr if err != nil { return err } - if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil { + root := idtools.IDPair{UID: rootUID, GID: rootGID} + + if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil { return err } - if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAndChown(dir, 0700, root); err != nil { return err } @@ -390,7 +392,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr } } - if err := idtools.MkdirAs(path.Join(dir, "diff"), 0755, rootUID, rootGID); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, "diff"), 0755, root); err != nil { return err } @@ -409,10 +411,10 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr return nil } - if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil { return err } - if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, "merged"), 0700, root); err != nil { return err } diff --git a/components/engine/daemon/graphdriver/windows/windows.go b/components/engine/daemon/graphdriver/windows/windows.go index cc9ca529df..f3d239d76a 100644 --- a/components/engine/daemon/graphdriver/windows/windows.go +++ b/components/engine/daemon/graphdriver/windows/windows.go @@ -95,7 +95,7 @@ func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) return nil, fmt.Errorf("%s is on an ReFS volume - ReFS volumes are not supported", home) } - if err := idtools.MkdirAllAs(home, 0700, 0, 0); err != nil { + if err := idtools.MkdirAllAndChown(home, 0700, idtools.IDPair{UID: 0, GID: 0}); err != nil { return nil, fmt.Errorf("windowsfilter failed to create '%s': %v", home, err) } diff --git a/components/engine/daemon/graphdriver/zfs/zfs.go b/components/engine/daemon/graphdriver/zfs/zfs.go index 52e2aa1d26..51d31bcfc5 100644 --- a/components/engine/daemon/graphdriver/zfs/zfs.go +++ b/components/engine/daemon/graphdriver/zfs/zfs.go @@ -104,7 +104,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri if err != nil { return nil, fmt.Errorf("Failed to get root uid/guid: %v", err) } - if err := idtools.MkdirAllAs(base, 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAndChown(base, 0700, idtools.IDPair{rootUID, rootGID}); err != nil { return nil, fmt.Errorf("Failed to create '%s': %v", base, err) } @@ -373,7 +373,7 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) { return nil, err } // Create the target directories if they don't exist - if err := idtools.MkdirAllAs(mountpoint, 0755, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAndChown(mountpoint, 0755, idtools.IDPair{rootUID, rootGID}); err != nil { d.ctr.Decrement(mountpoint) return nil, err } diff --git a/components/engine/pkg/idtools/idtools.go b/components/engine/pkg/idtools/idtools.go index c905a64720..49cc97c3d6 100644 --- a/components/engine/pkg/idtools/idtools.go +++ b/components/engine/pkg/idtools/idtools.go @@ -34,21 +34,6 @@ const ( subgidFileName string = "/etc/subgid" ) -// MkdirAllAs creates a directory (include any along the path) and then modifies -// ownership to the requested uid/gid. If the directory already exists, this -// function will still change ownership to the requested uid/gid pair. -// Deprecated: Use MkdirAllAndChown -func MkdirAllAs(path string, mode os.FileMode, ownerUID, ownerGID int) error { - return mkdirAs(path, mode, ownerUID, ownerGID, true, true) -} - -// MkdirAs creates a directory and then modifies ownership to the requested uid/gid. -// If the directory already exists, this function still changes ownership -// Deprecated: Use MkdirAndChown with a IDPair -func MkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int) error { - return mkdirAs(path, mode, ownerUID, ownerGID, false, true) -} - // MkdirAllAndChown creates a directory (include any along the path) and then modifies // ownership to the requested uid/gid. If the directory already exists, this // function will still change ownership to the requested uid/gid pair. From 6063e62a1f73efd6cd68bf236af637ff52c09871 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 21 Nov 2017 15:09:23 +0100 Subject: [PATCH 84/94] Remove unused "testutil" package Signed-off-by: Sebastiaan van Stijn Upstream-commit: 47af9f625d3f9e94729ec5aee4abcce813914d07 Component: engine --- components/engine/pkg/testutil/pkg.go | 1 - 1 file changed, 1 deletion(-) delete mode 100644 components/engine/pkg/testutil/pkg.go diff --git a/components/engine/pkg/testutil/pkg.go b/components/engine/pkg/testutil/pkg.go deleted file mode 100644 index 110b2e6a79..0000000000 --- a/components/engine/pkg/testutil/pkg.go +++ /dev/null @@ -1 +0,0 @@ -package testutil From a653d42fdb38484e42d9b7db48ba72a01f6d60bd Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 16 Nov 2017 18:22:22 +0100 Subject: [PATCH 85/94] Skip empty directories on prior graphdriver detection When starting the daemon, the `/var/lib/docker` directory is scanned for existing directories, so that the previously selected graphdriver will automatically be used. In some situations, empty directories are present (those directories can be created during feature detection of graph-drivers), in which case the daemon refuses to start. This patch improves detection, and skips empty directories, so that leftover directories don't cause the daemon to fail. Before this change: $ mkdir /var/lib/docker /var/lib/docker/aufs /var/lib/docker/overlay2 $ dockerd ... Error starting daemon: error initializing graphdriver: /var/lib/docker contains several valid graphdrivers: overlay2, aufs; Please cleanup or explicitly choose storage driver (-s ) With this patch applied: $ mkdir /var/lib/docker /var/lib/docker/aufs /var/lib/docker/overlay2 $ dockerd ... INFO[2017-11-16T17:26:43.207739140Z] Docker daemon commit=ab90bc296 graphdriver(s)=overlay2 version=dev INFO[2017-11-16T17:26:43.208033095Z] Daemon has completed initialization And on restart (prior graphdriver is still picked up): $ dockerd ... INFO[2017-11-16T17:27:52.260361465Z] [graphdriver] using prior storage driver: overlay2 Signed-off-by: Sebastiaan van Stijn Upstream-commit: 1262c57714e694193be6bbcbed83e859dc246c2f Component: engine --- .../engine/daemon/graphdriver/driver.go | 21 ++++++++++- .../engine/daemon/graphdriver/driver_test.go | 37 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 components/engine/daemon/graphdriver/driver_test.go diff --git a/components/engine/daemon/graphdriver/driver.go b/components/engine/daemon/graphdriver/driver.go index 7a3a0d1892..d08c6dc5b7 100644 --- a/components/engine/daemon/graphdriver/driver.go +++ b/components/engine/daemon/graphdriver/driver.go @@ -283,8 +283,27 @@ func scanPriorDrivers(root string) map[string]bool { for driver := range drivers { p := filepath.Join(root, driver) if _, err := os.Stat(p); err == nil && driver != "vfs" { - driversMap[driver] = true + if !isEmptyDir(p) { + driversMap[driver] = true + } } } return driversMap } + +// isEmptyDir checks if a directory is empty. It is used to check if prior +// storage-driver directories exist. If an error occurs, it also assumes the +// directory is not empty (which preserves the behavior _before_ this check +// was added) +func isEmptyDir(name string) bool { + f, err := os.Open(name) + if err != nil { + return false + } + defer f.Close() + + if _, err = f.Readdirnames(1); err == io.EOF { + return true + } + return false +} diff --git a/components/engine/daemon/graphdriver/driver_test.go b/components/engine/daemon/graphdriver/driver_test.go new file mode 100644 index 0000000000..40084be88d --- /dev/null +++ b/components/engine/daemon/graphdriver/driver_test.go @@ -0,0 +1,37 @@ +package graphdriver + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestIsEmptyDir(t *testing.T) { + tmp, err := ioutil.TempDir("", "test-is-empty-dir") + require.NoError(t, err) + defer os.RemoveAll(tmp) + + d := filepath.Join(tmp, "empty-dir") + err = os.Mkdir(d, 0755) + require.NoError(t, err) + empty := isEmptyDir(d) + assert.True(t, empty) + + d = filepath.Join(tmp, "dir-with-subdir") + err = os.MkdirAll(filepath.Join(d, "subdir"), 0755) + require.NoError(t, err) + empty = isEmptyDir(d) + assert.False(t, empty) + + d = filepath.Join(tmp, "dir-with-empty-file") + err = os.Mkdir(d, 0755) + require.NoError(t, err) + _, err = ioutil.TempFile(d, "file") + require.NoError(t, err) + empty = isEmptyDir(d) + assert.False(t, empty) +} From 605885e5180c01af497fba3c7bd25b605bb12341 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 30 Jun 2017 01:29:56 -0700 Subject: [PATCH 86/94] Bump Go to 1.9.2 Signed-off-by: Sebastiaan van Stijn Upstream-commit: d6e1cc32d309ee237342975388e729570343a974 Component: engine --- components/engine/Dockerfile | 2 +- components/engine/Dockerfile.aarch64 | 2 +- components/engine/Dockerfile.armhf | 2 +- components/engine/Dockerfile.e2e | 2 +- components/engine/Dockerfile.ppc64le | 2 +- components/engine/Dockerfile.s390x | 2 +- components/engine/Dockerfile.simple | 2 +- components/engine/Dockerfile.windows | 2 +- .../deb/aarch64/debian-jessie/Dockerfile | 2 +- .../deb/aarch64/debian-stretch/Dockerfile | 2 +- .../deb/aarch64/ubuntu-trusty/Dockerfile | 2 +- .../deb/aarch64/ubuntu-xenial/Dockerfile | 2 +- .../builder/deb/amd64/debian-jessie/Dockerfile | 2 +- .../deb/amd64/debian-stretch/Dockerfile | 2 +- .../builder/deb/amd64/debian-wheezy/Dockerfile | 2 +- .../builder/deb/amd64/ubuntu-trusty/Dockerfile | 2 +- .../builder/deb/amd64/ubuntu-xenial/Dockerfile | 2 +- .../deb/amd64/ubuntu-yakkety/Dockerfile | 2 +- .../builder/deb/amd64/ubuntu-zesty/Dockerfile | 2 +- .../builder/deb/armhf/debian-jessie/Dockerfile | 2 +- .../deb/armhf/raspbian-jessie/Dockerfile | 2 +- .../builder/deb/armhf/ubuntu-trusty/Dockerfile | 2 +- .../builder/deb/armhf/ubuntu-xenial/Dockerfile | 2 +- .../deb/armhf/ubuntu-yakkety/Dockerfile | 2 +- .../deb/ppc64le/ubuntu-trusty/Dockerfile | 2 +- .../deb/ppc64le/ubuntu-xenial/Dockerfile | 2 +- .../deb/ppc64le/ubuntu-yakkety/Dockerfile | 2 +- .../builder/deb/s390x/ubuntu-xenial/Dockerfile | 2 +- .../deb/s390x/ubuntu-yakkety/Dockerfile | 2 +- .../rpm/amd64/amazonlinux-latest/Dockerfile | 4 ++-- .../builder/rpm/amd64/centos-7/Dockerfile | 4 ++-- .../builder/rpm/amd64/fedora-24/Dockerfile | 4 ++-- .../builder/rpm/amd64/fedora-25/Dockerfile | 4 ++-- .../builder/rpm/amd64/opensuse-13.2/Dockerfile | 4 ++-- .../builder/rpm/amd64/oraclelinux-6/Dockerfile | 4 ++-- .../builder/rpm/amd64/oraclelinux-7/Dockerfile | 4 ++-- .../builder/rpm/amd64/photon-1.0/Dockerfile | 4 ++-- .../builder/rpm/armhf/centos-7/Dockerfile | 4 ++-- .../builder/rpm/ppc64le/centos-7/Dockerfile | 6 +++--- .../builder/rpm/ppc64le/fedora-24/Dockerfile | 4 ++-- .../rpm/ppc64le/opensuse-42.1/Dockerfile | 6 +++--- .../rpm/s390x/clefos-base-s390x-7/Dockerfile | 4 ++-- .../rpm/s390x/opensuse-tumbleweed-1/Dockerfile | 4 ++-- .../docker_cli_cp_to_container_test.go | 18 +++++++++++++++++- 44 files changed, 76 insertions(+), 60 deletions(-) diff --git a/components/engine/Dockerfile b/components/engine/Dockerfile index e027d48554..e2c8770117 100644 --- a/components/engine/Dockerfile +++ b/components/engine/Dockerfile @@ -86,7 +86,7 @@ RUN apt-get update && apt-get install -y \ # will need updating, to avoid errors. Ping #docker-maintainers on IRC # with a heads-up. # IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" \ | tar -xzC /usr/local diff --git a/components/engine/Dockerfile.aarch64 b/components/engine/Dockerfile.aarch64 index 876d80935e..58ca40d878 100644 --- a/components/engine/Dockerfile.aarch64 +++ b/components/engine/Dockerfile.aarch64 @@ -73,7 +73,7 @@ RUN apt-get update && apt-get install -y \ # Install Go # IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz" \ | tar -xzC /usr/local diff --git a/components/engine/Dockerfile.armhf b/components/engine/Dockerfile.armhf index 33304b55e4..bc430e8c0b 100644 --- a/components/engine/Dockerfile.armhf +++ b/components/engine/Dockerfile.armhf @@ -63,7 +63,7 @@ RUN apt-get update && apt-get install -y \ # Install Go # IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" \ | tar -xzC /usr/local ENV PATH /go/bin:/usr/local/go/bin:$PATH diff --git a/components/engine/Dockerfile.e2e b/components/engine/Dockerfile.e2e index c57d7d3217..7c6bb45d91 100644 --- a/components/engine/Dockerfile.e2e +++ b/components/engine/Dockerfile.e2e @@ -1,5 +1,5 @@ ## Step 1: Build tests -FROM golang:1.8.5-alpine3.6 as builder +FROM golang:1.9.2-alpine3.6 as builder RUN apk add --update \ bash \ diff --git a/components/engine/Dockerfile.ppc64le b/components/engine/Dockerfile.ppc64le index 4abd8891f7..fa7307b3be 100644 --- a/components/engine/Dockerfile.ppc64le +++ b/components/engine/Dockerfile.ppc64le @@ -64,7 +64,7 @@ RUN apt-get update && apt-get install -y \ # Install Go # NOTE: official ppc64le go binaries weren't available until go 1.6.4 and 1.7.4 # IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" \ | tar -xzC /usr/local diff --git a/components/engine/Dockerfile.s390x b/components/engine/Dockerfile.s390x index 33dfc4319b..e8e78302df 100644 --- a/components/engine/Dockerfile.s390x +++ b/components/engine/Dockerfile.s390x @@ -58,7 +58,7 @@ RUN apt-get update && apt-get install -y \ --no-install-recommends # IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" \ | tar -xzC /usr/local diff --git a/components/engine/Dockerfile.simple b/components/engine/Dockerfile.simple index c84025e67c..2a5d30bac0 100644 --- a/components/engine/Dockerfile.simple +++ b/components/engine/Dockerfile.simple @@ -40,7 +40,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ # will need updating, to avoid errors. Ping #docker-maintainers on IRC # with a heads-up. # IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" \ | tar -xzC /usr/local ENV PATH /go/bin:/usr/local/go/bin:$PATH diff --git a/components/engine/Dockerfile.windows b/components/engine/Dockerfile.windows index 2671aea8cd..519a96124c 100644 --- a/components/engine/Dockerfile.windows +++ b/components/engine/Dockerfile.windows @@ -161,7 +161,7 @@ SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPref # Environment variable notes: # - GO_VERSION must be consistent with 'Dockerfile' used by Linux. # - FROM_DOCKERFILE is used for detection of building within a container. -ENV GO_VERSION=1.8.5 ` +ENV GO_VERSION=1.9.2 ` GIT_VERSION=2.11.1 ` GOPATH=C:\go ` FROM_DOCKERFILE=1 diff --git a/components/engine/contrib/builder/deb/aarch64/debian-jessie/Dockerfile b/components/engine/contrib/builder/deb/aarch64/debian-jessie/Dockerfile index 865d6aaeae..a0a6612b7e 100644 --- a/components/engine/contrib/builder/deb/aarch64/debian-jessie/Dockerfile +++ b/components/engine/contrib/builder/deb/aarch64/debian-jessie/Dockerfile @@ -7,7 +7,7 @@ FROM aarch64/debian:jessie RUN echo deb http://ftp.debian.org/debian jessie-backports main > /etc/apt/sources.list.d/backports.list RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev libseccomp-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/aarch64/debian-stretch/Dockerfile b/components/engine/contrib/builder/deb/aarch64/debian-stretch/Dockerfile index 2b561bee78..90525dbb12 100644 --- a/components/engine/contrib/builder/deb/aarch64/debian-stretch/Dockerfile +++ b/components/engine/contrib/builder/deb/aarch64/debian-stretch/Dockerfile @@ -6,7 +6,7 @@ FROM aarch64/debian:stretch RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-dev libseccomp-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/aarch64/ubuntu-trusty/Dockerfile b/components/engine/contrib/builder/deb/aarch64/ubuntu-trusty/Dockerfile index e1b85ec747..5d2d58ea12 100644 --- a/components/engine/contrib/builder/deb/aarch64/ubuntu-trusty/Dockerfile +++ b/components/engine/contrib/builder/deb/aarch64/ubuntu-trusty/Dockerfile @@ -6,7 +6,7 @@ FROM aarch64/ubuntu:trusty RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/aarch64/ubuntu-xenial/Dockerfile b/components/engine/contrib/builder/deb/aarch64/ubuntu-xenial/Dockerfile index 6f8bc95b1b..cb9b681d42 100644 --- a/components/engine/contrib/builder/deb/aarch64/ubuntu-xenial/Dockerfile +++ b/components/engine/contrib/builder/deb/aarch64/ubuntu-xenial/Dockerfile @@ -6,7 +6,7 @@ FROM aarch64/ubuntu:xenial RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-dev libseccomp-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/amd64/debian-jessie/Dockerfile b/components/engine/contrib/builder/deb/amd64/debian-jessie/Dockerfile index 668fc3c1f4..5b8b7852f9 100644 --- a/components/engine/contrib/builder/deb/amd64/debian-jessie/Dockerfile +++ b/components/engine/contrib/builder/deb/amd64/debian-jessie/Dockerfile @@ -10,7 +10,7 @@ RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/amd64/debian-stretch/Dockerfile b/components/engine/contrib/builder/deb/amd64/debian-stretch/Dockerfile index a23eba39d1..aa96240b9a 100644 --- a/components/engine/contrib/builder/deb/amd64/debian-stretch/Dockerfile +++ b/components/engine/contrib/builder/deb/amd64/debian-stretch/Dockerfile @@ -10,7 +10,7 @@ RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/amd64/debian-wheezy/Dockerfile b/components/engine/contrib/builder/deb/amd64/debian-wheezy/Dockerfile index 2652706f4b..2f4e051af9 100644 --- a/components/engine/contrib/builder/deb/amd64/debian-wheezy/Dockerfile +++ b/components/engine/contrib/builder/deb/amd64/debian-wheezy/Dockerfile @@ -12,7 +12,7 @@ RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list.d RUN apt-get update && apt-get install -y -t wheezy-backports btrfs-tools --no-install-recommends && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y apparmor bash-completion build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/amd64/ubuntu-trusty/Dockerfile b/components/engine/contrib/builder/deb/amd64/ubuntu-trusty/Dockerfile index 4fce6f31f1..a0105663e4 100644 --- a/components/engine/contrib/builder/deb/amd64/ubuntu-trusty/Dockerfile +++ b/components/engine/contrib/builder/deb/amd64/ubuntu-trusty/Dockerfile @@ -6,7 +6,7 @@ FROM ubuntu:trusty RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/amd64/ubuntu-xenial/Dockerfile b/components/engine/contrib/builder/deb/amd64/ubuntu-xenial/Dockerfile index ed9c4a9f36..e2768c33d2 100644 --- a/components/engine/contrib/builder/deb/amd64/ubuntu-xenial/Dockerfile +++ b/components/engine/contrib/builder/deb/amd64/ubuntu-xenial/Dockerfile @@ -6,7 +6,7 @@ FROM ubuntu:xenial RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/amd64/ubuntu-yakkety/Dockerfile b/components/engine/contrib/builder/deb/amd64/ubuntu-yakkety/Dockerfile index a7dd9b7228..419522c138 100644 --- a/components/engine/contrib/builder/deb/amd64/ubuntu-yakkety/Dockerfile +++ b/components/engine/contrib/builder/deb/amd64/ubuntu-yakkety/Dockerfile @@ -6,7 +6,7 @@ FROM ubuntu:yakkety RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/amd64/ubuntu-zesty/Dockerfile b/components/engine/contrib/builder/deb/amd64/ubuntu-zesty/Dockerfile index 5074efe2bb..98314f1ed7 100644 --- a/components/engine/contrib/builder/deb/amd64/ubuntu-zesty/Dockerfile +++ b/components/engine/contrib/builder/deb/amd64/ubuntu-zesty/Dockerfile @@ -6,7 +6,7 @@ FROM ubuntu:zesty RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/armhf/debian-jessie/Dockerfile b/components/engine/contrib/builder/deb/armhf/debian-jessie/Dockerfile index 558d353937..048e7747d8 100644 --- a/components/engine/contrib/builder/deb/armhf/debian-jessie/Dockerfile +++ b/components/engine/contrib/builder/deb/armhf/debian-jessie/Dockerfile @@ -10,7 +10,7 @@ RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/armhf/raspbian-jessie/Dockerfile b/components/engine/contrib/builder/deb/armhf/raspbian-jessie/Dockerfile index 31a6688d1b..c80a3f6a44 100644 --- a/components/engine/contrib/builder/deb/armhf/raspbian-jessie/Dockerfile +++ b/components/engine/contrib/builder/deb/armhf/raspbian-jessie/Dockerfile @@ -10,7 +10,7 @@ RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 # GOARM is the ARM architecture version which is unrelated to the above Golang version ENV GOARM 6 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local diff --git a/components/engine/contrib/builder/deb/armhf/ubuntu-trusty/Dockerfile b/components/engine/contrib/builder/deb/armhf/ubuntu-trusty/Dockerfile index a9899a06db..b6fc393aae 100644 --- a/components/engine/contrib/builder/deb/armhf/ubuntu-trusty/Dockerfile +++ b/components/engine/contrib/builder/deb/armhf/ubuntu-trusty/Dockerfile @@ -6,7 +6,7 @@ FROM armhf/ubuntu:trusty RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/armhf/ubuntu-xenial/Dockerfile b/components/engine/contrib/builder/deb/armhf/ubuntu-xenial/Dockerfile index 2766f330bc..cc9284ffdc 100644 --- a/components/engine/contrib/builder/deb/armhf/ubuntu-xenial/Dockerfile +++ b/components/engine/contrib/builder/deb/armhf/ubuntu-xenial/Dockerfile @@ -6,7 +6,7 @@ FROM armhf/ubuntu:xenial RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/armhf/ubuntu-yakkety/Dockerfile b/components/engine/contrib/builder/deb/armhf/ubuntu-yakkety/Dockerfile index 27edd04d30..57a77acd73 100644 --- a/components/engine/contrib/builder/deb/armhf/ubuntu-yakkety/Dockerfile +++ b/components/engine/contrib/builder/deb/armhf/ubuntu-yakkety/Dockerfile @@ -6,7 +6,7 @@ FROM armhf/ubuntu:yakkety RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config vim-common libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/ppc64le/ubuntu-trusty/Dockerfile b/components/engine/contrib/builder/deb/ppc64le/ubuntu-trusty/Dockerfile index b85a68e4e8..d29ac5138e 100644 --- a/components/engine/contrib/builder/deb/ppc64le/ubuntu-trusty/Dockerfile +++ b/components/engine/contrib/builder/deb/ppc64le/ubuntu-trusty/Dockerfile @@ -6,7 +6,7 @@ FROM ppc64le/ubuntu:trusty RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libsystemd-journal-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/ppc64le/ubuntu-xenial/Dockerfile b/components/engine/contrib/builder/deb/ppc64le/ubuntu-xenial/Dockerfile index abb5b23133..730bacb079 100644 --- a/components/engine/contrib/builder/deb/ppc64le/ubuntu-xenial/Dockerfile +++ b/components/engine/contrib/builder/deb/ppc64le/ubuntu-xenial/Dockerfile @@ -6,7 +6,7 @@ FROM ppc64le/ubuntu:xenial RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libseccomp-dev libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/ppc64le/ubuntu-yakkety/Dockerfile b/components/engine/contrib/builder/deb/ppc64le/ubuntu-yakkety/Dockerfile index d72581659e..27cfd292a0 100644 --- a/components/engine/contrib/builder/deb/ppc64le/ubuntu-yakkety/Dockerfile +++ b/components/engine/contrib/builder/deb/ppc64le/ubuntu-yakkety/Dockerfile @@ -6,7 +6,7 @@ FROM ppc64le/ubuntu:yakkety RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev pkg-config vim-common libseccomp-dev libsystemd-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/s390x/ubuntu-xenial/Dockerfile b/components/engine/contrib/builder/deb/s390x/ubuntu-xenial/Dockerfile index 6d61ed7f5e..2233897375 100644 --- a/components/engine/contrib/builder/deb/s390x/ubuntu-xenial/Dockerfile +++ b/components/engine/contrib/builder/deb/s390x/ubuntu-xenial/Dockerfile @@ -6,7 +6,7 @@ FROM s390x/ubuntu:xenial RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config libsystemd-dev vim-common --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/deb/s390x/ubuntu-yakkety/Dockerfile b/components/engine/contrib/builder/deb/s390x/ubuntu-yakkety/Dockerfile index e30e875047..b2a0cf5735 100644 --- a/components/engine/contrib/builder/deb/s390x/ubuntu-yakkety/Dockerfile +++ b/components/engine/contrib/builder/deb/s390x/ubuntu-yakkety/Dockerfile @@ -6,7 +6,7 @@ FROM s390x/ubuntu:yakkety RUN apt-get update && apt-get install -y apparmor bash-completion btrfs-tools build-essential cmake curl ca-certificates debhelper dh-apparmor dh-systemd git libapparmor-dev libdevmapper-dev libseccomp-dev pkg-config libsystemd-dev vim-common --no-install-recommends && rm -rf /var/lib/apt/lists/* -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/amd64/amazonlinux-latest/Dockerfile b/components/engine/contrib/builder/rpm/amd64/amazonlinux-latest/Dockerfile index 8e755cd0a6..1f0a0b7c09 100644 --- a/components/engine/contrib/builder/rpm/amd64/amazonlinux-latest/Dockerfile +++ b/components/engine/contrib/builder/rpm/amd64/amazonlinux-latest/Dockerfile @@ -5,9 +5,9 @@ FROM amazonlinux:latest RUN yum groupinstall -y "Development Tools" -RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel tar git cmake vim-common +RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel tar git cmake vim-common -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/amd64/centos-7/Dockerfile b/components/engine/contrib/builder/rpm/amd64/centos-7/Dockerfile index 8a220649ea..5767b8cd07 100644 --- a/components/engine/contrib/builder/rpm/amd64/centos-7/Dockerfile +++ b/components/engine/contrib/builder/rpm/amd64/centos-7/Dockerfile @@ -6,9 +6,9 @@ FROM centos:7 RUN yum groupinstall -y "Development Tools" RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs -RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common +RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/amd64/fedora-24/Dockerfile b/components/engine/contrib/builder/rpm/amd64/fedora-24/Dockerfile index e0b369aa4b..1e075238c2 100644 --- a/components/engine/contrib/builder/rpm/amd64/fedora-24/Dockerfile +++ b/components/engine/contrib/builder/rpm/amd64/fedora-24/Dockerfile @@ -6,9 +6,9 @@ FROM fedora:24 RUN dnf -y upgrade RUN dnf install -y @development-tools fedora-packager -RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common +RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/amd64/fedora-25/Dockerfile b/components/engine/contrib/builder/rpm/amd64/fedora-25/Dockerfile index f259a5cea3..23e24638ec 100644 --- a/components/engine/contrib/builder/rpm/amd64/fedora-25/Dockerfile +++ b/components/engine/contrib/builder/rpm/amd64/fedora-25/Dockerfile @@ -6,9 +6,9 @@ FROM fedora:25 RUN dnf -y upgrade RUN dnf install -y @development-tools fedora-packager -RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common +RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/amd64/opensuse-13.2/Dockerfile b/components/engine/contrib/builder/rpm/amd64/opensuse-13.2/Dockerfile index 7f13863992..b55bfee8ed 100644 --- a/components/engine/contrib/builder/rpm/amd64/opensuse-13.2/Dockerfile +++ b/components/engine/contrib/builder/rpm/amd64/opensuse-13.2/Dockerfile @@ -5,9 +5,9 @@ FROM opensuse:13.2 RUN zypper --non-interactive install ca-certificates* curl gzip rpm-build -RUN zypper --non-interactive install libbtrfs-devel device-mapper-devel glibc-static libselinux-devel libtool-ltdl-devel pkg-config selinux-policy selinux-policy-devel systemd-devel tar git cmake vim systemd-rpm-macros +RUN zypper --non-interactive install libbtrfs-devel device-mapper-devel glibc-static libselinux-devel pkg-config selinux-policy selinux-policy-devel systemd-devel tar git cmake vim systemd-rpm-macros -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/amd64/oraclelinux-6/Dockerfile b/components/engine/contrib/builder/rpm/amd64/oraclelinux-6/Dockerfile index b75f2dc3e1..1b395c2c68 100644 --- a/components/engine/contrib/builder/rpm/amd64/oraclelinux-6/Dockerfile +++ b/components/engine/contrib/builder/rpm/amd64/oraclelinux-6/Dockerfile @@ -8,9 +8,9 @@ RUN yum install -y yum-utils && curl -o /etc/yum.repos.d/public-yum-ol6.repo htt RUN yum install -y kernel-uek-devel-4.1.12-32.el6uek RUN yum groupinstall -y "Development Tools" -RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel tar git cmake vim-common +RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel pkgconfig selinux-policy selinux-policy-devel tar git cmake vim-common -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/amd64/oraclelinux-7/Dockerfile b/components/engine/contrib/builder/rpm/amd64/oraclelinux-7/Dockerfile index f4dc894f16..ec6db986ba 100644 --- a/components/engine/contrib/builder/rpm/amd64/oraclelinux-7/Dockerfile +++ b/components/engine/contrib/builder/rpm/amd64/oraclelinux-7/Dockerfile @@ -5,9 +5,9 @@ FROM oraclelinux:7 RUN yum groupinstall -y "Development Tools" -RUN yum install -y --enablerepo=ol7_optional_latest btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common +RUN yum install -y --enablerepo=ol7_optional_latest btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/amd64/photon-1.0/Dockerfile b/components/engine/contrib/builder/rpm/amd64/photon-1.0/Dockerfile index 01d5fea10d..be9463c213 100644 --- a/components/engine/contrib/builder/rpm/amd64/photon-1.0/Dockerfile +++ b/components/engine/contrib/builder/rpm/amd64/photon-1.0/Dockerfile @@ -5,9 +5,9 @@ FROM photon:1.0 RUN tdnf install -y wget curl ca-certificates gzip make rpm-build sed gcc linux-api-headers glibc-devel binutils libseccomp elfutils -RUN tdnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkg-config selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common +RUN tdnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkg-config selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/armhf/centos-7/Dockerfile b/components/engine/contrib/builder/rpm/armhf/centos-7/Dockerfile index 79c2ef1626..8e77e8b098 100644 --- a/components/engine/contrib/builder/rpm/armhf/centos-7/Dockerfile +++ b/components/engine/contrib/builder/rpm/armhf/centos-7/Dockerfile @@ -7,9 +7,9 @@ FROM multiarch/centos:7.2.1511-armhfp-clean RUN yum install -y yum-plugin-ovl RUN yum groupinstall --skip-broken -y "Development Tools" RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs -RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim-common +RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim-common -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/ppc64le/centos-7/Dockerfile b/components/engine/contrib/builder/rpm/ppc64le/centos-7/Dockerfile index ebce3c0229..8bcc4be8cc 100644 --- a/components/engine/contrib/builder/rpm/ppc64le/centos-7/Dockerfile +++ b/components/engine/contrib/builder/rpm/ppc64le/centos-7/Dockerfile @@ -6,10 +6,10 @@ FROM ppc64le/centos:7 RUN yum groupinstall -y "Development Tools" RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs -RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim-common +RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim-common -ENV GO_VERSION 1.8.5 -RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local +ENV GO_VERSION 1.9.2 +RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin ENV AUTO_GOPATH 1 diff --git a/components/engine/contrib/builder/rpm/ppc64le/fedora-24/Dockerfile b/components/engine/contrib/builder/rpm/ppc64le/fedora-24/Dockerfile index 18dd7d4de0..32321fe9c4 100644 --- a/components/engine/contrib/builder/rpm/ppc64le/fedora-24/Dockerfile +++ b/components/engine/contrib/builder/rpm/ppc64le/fedora-24/Dockerfile @@ -6,9 +6,9 @@ FROM ppc64le/fedora:24 RUN dnf -y upgrade RUN dnf install -y @development-tools fedora-packager -RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake +RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim-common -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/ppc64le/opensuse-42.1/Dockerfile b/components/engine/contrib/builder/rpm/ppc64le/opensuse-42.1/Dockerfile index 3343f021c4..04f158cc41 100644 --- a/components/engine/contrib/builder/rpm/ppc64le/opensuse-42.1/Dockerfile +++ b/components/engine/contrib/builder/rpm/ppc64le/opensuse-42.1/Dockerfile @@ -7,10 +7,10 @@ FROM ppc64le/opensuse:42.1 RUN zypper addrepo -n ppc64le-oss -f https://download.opensuse.org/ports/ppc/distribution/leap/42.1/repo/oss/ ppc64le-oss RUN zypper addrepo -n ppc64le-updates -f https://download.opensuse.org/ports/update/42.1/ ppc64le-updates RUN zypper --non-interactive install ca-certificates* curl gzip rpm-build -RUN zypper --non-interactive install libbtrfs-devel device-mapper-devel glibc-static libselinux-devel libtool-ltdl-devel pkg-config selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim +RUN zypper --non-interactive install libbtrfs-devel device-mapper-devel glibc-static libselinux-devel pkg-config selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim -ENV GO_VERSION 1.8.5 -RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local +ENV GO_VERSION 1.9.2 +RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin ENV AUTO_GOPATH 1 diff --git a/components/engine/contrib/builder/rpm/s390x/clefos-base-s390x-7/Dockerfile b/components/engine/contrib/builder/rpm/s390x/clefos-base-s390x-7/Dockerfile index ef875b8fc4..27195d3b4a 100644 --- a/components/engine/contrib/builder/rpm/s390x/clefos-base-s390x-7/Dockerfile +++ b/components/engine/contrib/builder/rpm/s390x/clefos-base-s390x-7/Dockerfile @@ -6,9 +6,9 @@ FROM sinenomine/clefos-base-s390x RUN touch /var/lib/rpm/* && yum groupinstall -y "Development Tools" -RUN touch /var/lib/rpm/* && yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim-common +RUN touch /var/lib/rpm/* && yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel pkgconfig selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim-common -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/contrib/builder/rpm/s390x/opensuse-tumbleweed-1/Dockerfile b/components/engine/contrib/builder/rpm/s390x/opensuse-tumbleweed-1/Dockerfile index a05bee5096..275fab5e93 100644 --- a/components/engine/contrib/builder/rpm/s390x/opensuse-tumbleweed-1/Dockerfile +++ b/components/engine/contrib/builder/rpm/s390x/opensuse-tumbleweed-1/Dockerfile @@ -7,9 +7,9 @@ FROM opensuse/s390x:tumbleweed RUN zypper ar https://download.opensuse.org/ports/zsystems/tumbleweed/repo/oss/ tumbleweed RUN zypper --non-interactive install ca-certificates* curl gzip rpm-build -RUN zypper --non-interactive install libbtrfs-devel device-mapper-devel glibc-static libselinux-devel libtool-ltdl-devel pkg-config selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim systemd-rpm-macros +RUN zypper --non-interactive install libbtrfs-devel device-mapper-devel glibc-static libselinux-devel pkg-config selinux-policy selinux-policy-devel sqlite-devel systemd-devel tar git cmake vim systemd-rpm-macros -ENV GO_VERSION 1.8.5 +ENV GO_VERSION 1.9.2 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" | tar xzC /usr/local ENV PATH $PATH:/usr/local/go/bin diff --git a/components/engine/integration-cli/docker_cli_cp_to_container_test.go b/components/engine/integration-cli/docker_cli_cp_to_container_test.go index 57a850c425..24c1fe2288 100644 --- a/components/engine/integration-cli/docker_cli_cp_to_container_test.go +++ b/components/engine/integration-cli/docker_cli_cp_to_container_test.go @@ -2,6 +2,7 @@ package main import ( "os" + "runtime" "strings" "github.com/docker/docker/integration-cli/checker" @@ -35,7 +36,22 @@ func (s *DockerSuite) TestCpToErrSrcNotExists(c *check.C) { c.Assert(os.IsNotExist(srcStatErr), checker.True) err := runDockerCp(c, srcPath, dstPath, nil) - c.Assert(strings.ToLower(err.Error()), checker.Contains, strings.ToLower(srcStatErr.Error())) + if runtime.GOOS == "windows" { + // Go 1.9+ on Windows returns a different error for `os.Stat()`, see + // https://github.com/golang/go/commit/6144c7270e5812d9de8fb97456ee4e5ae657fcbb#diff-f63e1a4b4377b2fe0b05011db3df9599 + // + // Go 1.8: CreateFile C:\not-exist: The system cannot find the file specified. + // Go 1.9: GetFileAttributesEx C:\not-exist: The system cannot find the file specified. + // + // Due to the CLI using a different version than the daemon, comparing the + // error message won't work, so just hard-code the common part here. + // + // TODO this should probably be a test in the CLI repository instead + c.Assert(strings.ToLower(err.Error()), checker.Contains, "cannot find the file specified") + c.Assert(strings.ToLower(err.Error()), checker.Contains, strings.ToLower(tmpDir)) + } else { + c.Assert(strings.ToLower(err.Error()), checker.Contains, strings.ToLower(srcStatErr.Error())) + } } // Test for error when SRC ends in a trailing From e6fc3af770e74196b77e515799199d395d5cfde5 Mon Sep 17 00:00:00 2001 From: Cheng-mean Liu Date: Tue, 21 Nov 2017 12:00:26 -0800 Subject: [PATCH 87/94] Added support for persisting Windows network driver specific options over reboot or service restart Signed-off-by: Cheng-mean Liu Upstream-commit: cef1578ac46e02d05c86621505631b3be7ca9d72 Component: engine --- components/engine/daemon/daemon_windows.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/components/engine/daemon/daemon_windows.go b/components/engine/daemon/daemon_windows.go index a79ed4f071..8545a2f651 100644 --- a/components/engine/daemon/daemon_windows.go +++ b/components/engine/daemon/daemon_windows.go @@ -350,6 +350,9 @@ func (daemon *Daemon) initNetworkController(config *config.Config, activeSandbox } controller.WalkNetworks(s) + + drvOptions := make(map[string]string) + if n != nil { // global networks should not be deleted by local HNS if n.Info().Scope() == datastore.GlobalScope { @@ -358,14 +361,23 @@ func (daemon *Daemon) initNetworkController(config *config.Config, activeSandbox v.Name = n.Name() // This will not cause network delete from HNS as the network // is not yet populated in the libnetwork windows driver + + // restore option if it existed before + drvOptions = n.Info().DriverOptions() n.Delete() } - netOption := map[string]string{ winlibnetwork.NetworkName: v.Name, winlibnetwork.HNSID: v.Id, } + // add persisted driver options + for k, v := range drvOptions { + if k != winlibnetwork.NetworkName && k != winlibnetwork.HNSID { + netOption[k] = v + } + } + v4Conf := []*libnetwork.IpamConf{} for _, subnet := range v.Subnets { ipamV4Conf := libnetwork.IpamConf{} From 9f84ce63a5441a2d51381a75e4782f4274cca683 Mon Sep 17 00:00:00 2001 From: Christopher Jones Date: Wed, 22 Nov 2017 11:13:32 -0500 Subject: [PATCH 88/94] [integration] add main_test for image test Adds a main_test for the image integration test, so we can download frozen images, and clean up after the image test is ran Signed-off-by: Christopher Jones Upstream-commit: be83f42612e3be42fcd60726d48d7346befc9449 Component: engine --- .../engine/integration/image/main_test.go | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 components/engine/integration/image/main_test.go diff --git a/components/engine/integration/image/main_test.go b/components/engine/integration/image/main_test.go new file mode 100644 index 0000000000..5ef46aaf57 --- /dev/null +++ b/components/engine/integration/image/main_test.go @@ -0,0 +1,33 @@ +package image + +import ( + "fmt" + "os" + "testing" + + "github.com/docker/docker/internal/test/environment" +) + +var testEnv *environment.Execution + +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()) +} + +func setupTest(t *testing.T) func() { + environment.ProtectAll(t, testEnv) + return func() { testEnv.Clean(t) } +} From b88ff8a5c2913e42cc39dedadd623a00bd28a2b4 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Wed, 22 Nov 2017 22:21:40 +0100 Subject: [PATCH 89/94] container: update real-time resources Signed-off-by: Antonio Murdaca Upstream-commit: dca82b9e5c53e96d1e34bec03e78e17feb8d6fdb Component: engine --- components/engine/container/container_unix.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/engine/container/container_unix.go b/components/engine/container/container_unix.go index 28a3722c79..f030232060 100644 --- a/components/engine/container/container_unix.go +++ b/components/engine/container/container_unix.go @@ -332,6 +332,12 @@ func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfi if resources.KernelMemory != 0 { cResources.KernelMemory = resources.KernelMemory } + if resources.CPURealtimePeriod != 0 { + cResources.CPURealtimePeriod = resources.CPURealtimePeriod + } + if resources.CPURealtimeRuntime != 0 { + cResources.CPURealtimeRuntime = resources.CPURealtimeRuntime + } // update HostConfig of container if hostConfig.RestartPolicy.Name != "" { From 12aa8ed3d88e62a8a12c8a09deb0bbc531d823f8 Mon Sep 17 00:00:00 2001 From: Nicolas De loof Date: Thu, 23 Nov 2017 12:37:05 +0100 Subject: [PATCH 90/94] `memory` is an int64 (long) Signed-off-by: Nicolas De Loof Upstream-commit: 5ad85b1d4d69806754d1814f019b03b57e40d6da Component: engine --- components/engine/api/swagger.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/api/swagger.yaml b/components/engine/api/swagger.yaml index a6b5ee5cfd..07ee0674cd 100644 --- a/components/engine/api/swagger.yaml +++ b/components/engine/api/swagger.yaml @@ -332,6 +332,7 @@ definitions: Memory: description: "Memory limit in bytes." type: "integer" + format: "int64" default: 0 # Applicable to UNIX platforms CgroupParent: From a737bf670476c9f4bbd43491baca1d7ff50d22dd Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Wed, 22 Nov 2017 12:55:36 -0500 Subject: [PATCH 91/94] Fix layer DNE with duplicate layers. Signed-off-by: Daniel Nephin Upstream-commit: 936ef0c4c97737066ae1e028f04b5188022a91b8 Component: engine --- components/engine/daemon/build.go | 6 +--- .../engine/integration/build/build_test.go | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/components/engine/daemon/build.go b/components/engine/daemon/build.go index 6a0081484a..3674ad9d28 100644 --- a/components/engine/daemon/build.go +++ b/components/engine/daemon/build.go @@ -71,11 +71,7 @@ func (rl *releaseableLayer) Commit(os string) (builder.ReleaseableLayer, error) if err != nil { return nil, err } - - if layer.IsEmpty(newLayer.DiffID()) { - _, err := rl.layerStore.Release(newLayer) - return &releaseableLayer{layerStore: rl.layerStore}, err - } + // TODO: An optimization woudld be to handle empty layers before returning return &releaseableLayer{layerStore: rl.layerStore, roLayer: newLayer}, nil } diff --git a/components/engine/integration/build/build_test.go b/components/engine/integration/build/build_test.go index cbaa7dc9bb..b447b623cc 100644 --- a/components/engine/integration/build/build_test.go +++ b/components/engine/integration/build/build_test.go @@ -169,3 +169,31 @@ func TestBuildMultiStageParentConfig(t *testing.T) { assert.Equal(t, "/foo/sub2", image.Config.WorkingDir) assert.Contains(t, image.Config.Env, "WHO=parent") } + +func TestBuildWithEmptyLayers(t *testing.T) { + dockerfile := ` + FROM busybox + COPY 1/ /target/ + COPY 2/ /target/ + COPY 3/ /target/ + ` + ctx := context.Background() + source := fakecontext.New(t, "", + fakecontext.WithDockerfile(dockerfile), + fakecontext.WithFile("1/a", "asdf"), + fakecontext.WithFile("2/a", "asdf"), + fakecontext.WithFile("3/a", "asdf")) + defer source.Close() + + apiclient := testEnv.APIClient() + resp, err := apiclient.ImageBuild(ctx, + source.AsTarReader(t), + types.ImageBuildOptions{ + Remove: true, + ForceRemove: true, + }) + require.NoError(t, err) + _, err = io.Copy(ioutil.Discard, resp.Body) + resp.Body.Close() + require.NoError(t, err) +} From c3f989d26bd73706a64b16ee49f5f68345d8df3c Mon Sep 17 00:00:00 2001 From: Igor Karpovich Date: Mon, 27 Nov 2017 14:26:53 +0000 Subject: [PATCH 92/94] This fixes casting of log message []byte into string with --log-opt line-only=true Signed-off-by: Igor Karpovich Upstream-commit: 440e50b6c702b5e13fff9424ef656b6bb6a259f0 Component: engine --- components/engine/daemon/logger/logentries/logentries.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/daemon/logger/logentries/logentries.go b/components/engine/daemon/logger/logentries/logentries.go index e28707c746..1d25843e8c 100644 --- a/components/engine/daemon/logger/logentries/logentries.go +++ b/components/engine/daemon/logger/logentries/logentries.go @@ -76,7 +76,7 @@ func (f *logentries) Log(msg *logger.Message) error { logger.PutMessage(msg) f.writer.Println(f.tag, ts, data) } else { - line := msg.Line + line := string(msg.Line) logger.PutMessage(msg) f.writer.Println(line) } From 016b82fbf3776b768026e9f889e283e5df599b70 Mon Sep 17 00:00:00 2001 From: Dennis Chen Date: Wed, 22 Nov 2017 10:42:58 +0000 Subject: [PATCH 93/94] image: skip the import test on AArch64 The commit '0a13f827a10d3bf61744d9b3f7165c5885a39c5d' introduces an import test for CVE-2017-14992, it uses a 8GB image to make sure we don't revert CVE-2017-14992, but unfortunately this test can't finish in 5-min on AArch64, as a fact, in most cases we have to crate a very big image to make the test effective on AArch64, but this will result in a test panic, so now we skip it order to avoid termination of others tests followed. Signed-off-by: Dennis Chen Upstream-commit: 6395b8b3dcc43be6750e0d90d9bab0a83e4eb20b Component: engine --- components/engine/integration/image/import_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/engine/integration/image/import_test.go b/components/engine/integration/image/import_test.go index 955891f288..2bb7ffbdc0 100644 --- a/components/engine/integration/image/import_test.go +++ b/components/engine/integration/image/import_test.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "io" + "runtime" "testing" "github.com/docker/docker/api/types" @@ -14,12 +15,17 @@ import ( // Ensure we don't regress on CVE-2017-14992. func TestImportExtremelyLargeImageWorks(t *testing.T) { + if runtime.GOARCH == "arm64" { + t.Skip("effective test will be time out") + } + client := request.NewAPIClient(t) // Construct an empty tar archive with about 8GB of junk padding at the // end. This should not cause any crashes (the padding should be mostly // ignored). var tarBuffer bytes.Buffer + tw := tar.NewWriter(&tarBuffer) if err := tw.Close(); err != nil { t.Fatal(err) From b7dabb480c25b813eca0b20a8054836d0fbed13d Mon Sep 17 00:00:00 2001 From: Anusha Ragunathan Date: Tue, 28 Nov 2017 13:04:13 -0800 Subject: [PATCH 94/94] Fix potential panic during plugin set. Plugin config can have Mounts without a 'Source' field. In such cases, performing a 'plugin set' on the mount source will panic the daemon. Its the same case for device paths as well. This detects the case and returns error. Signed-off-by: Anusha Ragunathan Upstream-commit: 6572e27df7f3483cfed7a8294c1f6d9cf157809a Component: engine --- .../docker_cli_plugins_test.go | 28 +++++++++++++++++++ components/engine/plugin/v2/plugin.go | 6 ++++ 2 files changed, 34 insertions(+) diff --git a/components/engine/integration-cli/docker_cli_plugins_test.go b/components/engine/integration-cli/docker_cli_plugins_test.go index 13ae2b0eb6..310067e386 100644 --- a/components/engine/integration-cli/docker_cli_plugins_test.go +++ b/components/engine/integration-cli/docker_cli_plugins_test.go @@ -168,8 +168,19 @@ func (ps *DockerPluginSuite) TestPluginSet(c *check.C) { defer cancel() initialValue := "0" + mntSrc := "foo" + devPath := "/dev/bar" + err = plugin.Create(ctx, client, name, func(cfg *plugin.Config) { cfg.Env = []types.PluginEnv{{Name: "DEBUG", Value: &initialValue, Settable: []string{"value"}}} + cfg.Mounts = []types.PluginMount{ + {Name: "pmount1", Settable: []string{"source"}, Type: "none", Source: &mntSrc}, + {Name: "pmount2", Settable: []string{"source"}, Type: "none"}, // Mount without source is invalid. + } + cfg.Linux.Devices = []types.PluginDevice{ + {Name: "pdev1", Path: &devPath, Settable: []string{"path"}}, + {Name: "pdev2", Settable: []string{"path"}}, // Device without Path is invalid. + } }) c.Assert(err, checker.IsNil, check.Commentf("failed to create test plugin")) @@ -180,6 +191,23 @@ func (ps *DockerPluginSuite) TestPluginSet(c *check.C) { env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", name) c.Assert(strings.TrimSpace(env), checker.Equals, "[DEBUG=1]") + + env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{with $mount := index .Settings.Mounts 0}}{{$mount.Source}}{{end}}", name) + c.Assert(strings.TrimSpace(env), checker.Contains, mntSrc) + + dockerCmd(c, "plugin", "set", name, "pmount1.source=bar") + + env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{with $mount := index .Settings.Mounts 0}}{{$mount.Source}}{{end}}", name) + c.Assert(strings.TrimSpace(env), checker.Contains, "bar") + + out, _, err := dockerCmdWithError("plugin", "set", name, "pmount2.source=bar2") + c.Assert(err, checker.NotNil) + c.Assert(out, checker.Contains, "Plugin config has no mount source") + + out, _, err = dockerCmdWithError("plugin", "set", name, "pdev2.path=/dev/bar2") + c.Assert(err, checker.NotNil) + c.Assert(out, checker.Contains, "Plugin config has no device path") + } func (ps *DockerPluginSuite) TestPluginInstallArgs(c *check.C) { diff --git a/components/engine/plugin/v2/plugin.go b/components/engine/plugin/v2/plugin.go index b77536c986..ce3257c0cb 100644 --- a/components/engine/plugin/v2/plugin.go +++ b/components/engine/plugin/v2/plugin.go @@ -142,6 +142,9 @@ next: } // it is, so lets update the settings in memory + if mount.Source == nil { + return fmt.Errorf("Plugin config has no mount source") + } *mount.Source = s.value continue next } @@ -159,6 +162,9 @@ next: } // it is, so lets update the settings in memory + if device.Path == nil { + return fmt.Errorf("Plugin config has no device path") + } *device.Path = s.value continue next }