diff --git a/components/engine/daemon/cluster/executor/container/executor.go b/components/engine/daemon/cluster/executor/container/executor.go index 86b957b005..f0c6f998b5 100644 --- a/components/engine/daemon/cluster/executor/container/executor.go +++ b/components/engine/daemon/cluster/executor/container/executor.go @@ -8,6 +8,7 @@ import ( "github.com/docker/docker/api/types/network" executorpkg "github.com/docker/docker/daemon/cluster/executor" clustertypes "github.com/docker/docker/daemon/cluster/provider" + "github.com/docker/docker/plugin" networktypes "github.com/docker/libnetwork/types" "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/agent/secrets" @@ -45,12 +46,39 @@ func (e *executor) Describe(ctx context.Context) (*api.NodeDescription, error) { } } + // add v1 plugins addPlugins("Volume", info.Plugins.Volume) // Add builtin driver "overlay" (the only builtin multi-host driver) to // the plugin list by default. addPlugins("Network", append([]string{"overlay"}, info.Plugins.Network...)) addPlugins("Authorization", info.Plugins.Authorization) + // add v2 plugins + v2Plugins, err := plugin.GetManager().List() + if err == nil { + for _, plgn := range v2Plugins { + for _, typ := range plgn.Config.Interface.Types { + if typ.Prefix != "docker" || !plgn.Enabled { + continue + } + plgnTyp := typ.Capability + if typ.Capability == "volumedriver" { + plgnTyp = "Volume" + } else if typ.Capability == "networkdriver" { + plgnTyp = "Network" + } + plgnName := plgn.Name + if plgn.Tag != "" { + plgnName += ":" + plgn.Tag + } + plugins[api.PluginDescription{ + Type: plgnTyp, + Name: plgnName, + }] = struct{}{} + } + } + } + pluginFields := make([]api.PluginDescription, 0, len(plugins)) for k := range plugins { pluginFields = append(pluginFields, k) diff --git a/components/engine/integration-cli/daemon/daemon_swarm.go b/components/engine/integration-cli/daemon/daemon_swarm.go index 2b7f71908b..5d120d9936 100644 --- a/components/engine/integration-cli/daemon/daemon_swarm.go +++ b/components/engine/integration-cli/daemon/daemon_swarm.go @@ -148,20 +148,28 @@ func (d *Swarm) GetServiceTasks(c *check.C, service string) []swarm.Task { return tasks } -// CheckServiceRunningTasks returns the number of running tasks for the specified service -func (d *Swarm) CheckServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) { +// CheckServiceTasksInState returns the number of tasks with a matching state, +// and optional message substring. +func (d *Swarm) CheckServiceTasksInState(service string, state swarm.TaskState, message string) func(*check.C) (interface{}, check.CommentInterface) { return func(c *check.C) (interface{}, check.CommentInterface) { tasks := d.GetServiceTasks(c, service) - var runningCount int + var count int for _, task := range tasks { - if task.Status.State == swarm.TaskStateRunning { - runningCount++ + if task.Status.State == state { + if message == "" || strings.Contains(task.Status.Message, message) { + count++ + } } } - return runningCount, nil + 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, "") +} + // CheckServiceUpdateState returns the current update state for the specified service func (d *Swarm) CheckServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) { return func(c *check.C) (interface{}, check.CommentInterface) { diff --git a/components/engine/integration-cli/docker_cli_external_volume_driver_unix_test.go b/components/engine/integration-cli/docker_cli_external_volume_driver_unix_test.go index 9b13647a1b..ad055e3d48 100644 --- a/components/engine/integration-cli/docker_cli_external_volume_driver_unix_test.go +++ b/components/engine/integration-cli/docker_cli_external_volume_driver_unix_test.go @@ -282,6 +282,22 @@ func (s *DockerExternalVolumeSuite) TearDownSuite(c *check.C) { c.Assert(err, checker.IsNil) } +func (s *DockerExternalVolumeSuite) TestVolumeCLICreateOptionConflict(c *check.C) { + dockerCmd(c, "volume", "create", "test") + + out, _, err := dockerCmdWithError("volume", "create", "test", "--driver", volumePluginName) + c.Assert(err, check.NotNil, check.Commentf("volume create exception name already in use with another driver")) + c.Assert(out, checker.Contains, "A volume named test already exists") + + out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Driver }}", "test") + _, _, err = dockerCmdWithError("volume", "create", "test", "--driver", strings.TrimSpace(out)) + c.Assert(err, check.IsNil) + + // make sure hidden --name option conflicts with positional arg name + out, _, err = dockerCmdWithError("volume", "create", "--name", "test2", "test2") + c.Assert(err, check.NotNil, check.Commentf("Conflicting options: either specify --name or provide positional arg, not both")) +} + func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverNamed(c *check.C) { s.d.StartWithBusybox(c) diff --git a/components/engine/integration-cli/docker_cli_swarm_unix_test.go b/components/engine/integration-cli/docker_cli_swarm_unix_test.go new file mode 100644 index 0000000000..96e77e258e --- /dev/null +++ b/components/engine/integration-cli/docker_cli_swarm_unix_test.go @@ -0,0 +1,52 @@ +// +build !windows + +package main + +import ( + "encoding/json" + "strings" + + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/pkg/integration/checker" + "github.com/go-check/check" +) + +func (s *DockerSwarmSuite) TestSwarmVolumePlugin(c *check.C) { + d := s.AddDaemon(c, true, true) + + out, err := d.Cmd("service", "create", "--mount", "type=volume,source=my-volume,destination=/foo,volume-driver=customvolumedriver", "--name", "top", "busybox", "top") + 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) + + plugin := newVolumePlugin(c, "customvolumedriver") + defer plugin.Close() + + // create a dummy volume to trigger lazy loading of the plugin + out, err = d.Cmd("volume", "create", "-d", "customvolumedriver", "hello") + + // TODO(aaronl): It will take about 15 seconds for swarm to realize the + // plugin was loaded. Switching the test over to plugin v2 would avoid + // this long delay. + + // make sure task has been deployed. + waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1) + + out, err = d.Cmd("ps", "-q") + c.Assert(err, checker.IsNil) + containerID := strings.TrimSpace(out) + + out, err = d.Cmd("inspect", "-f", "{{json .Mounts}}", containerID) + c.Assert(err, checker.IsNil) + + var mounts []struct { + Name string + Driver string + } + + c.Assert(json.NewDecoder(strings.NewReader(out)).Decode(&mounts), checker.IsNil) + c.Assert(len(mounts), checker.Equals, 1, check.Commentf(out)) + c.Assert(mounts[0].Name, checker.Equals, "my-volume") + c.Assert(mounts[0].Driver, checker.Equals, "customvolumedriver") +} diff --git a/components/engine/integration-cli/docker_cli_volume_test.go b/components/engine/integration-cli/docker_cli_volume_test.go index 0dace5d495..772b2d6359 100644 --- a/components/engine/integration-cli/docker_cli_volume_test.go +++ b/components/engine/integration-cli/docker_cli_volume_test.go @@ -29,21 +29,6 @@ func (s *DockerSuite) TestVolumeCLICreate(c *check.C) { c.Assert(name, check.Equals, "test2") } -func (s *DockerSuite) TestVolumeCLICreateOptionConflict(c *check.C) { - dockerCmd(c, "volume", "create", "test") - out, _, err := dockerCmdWithError("volume", "create", "test", "--driver", "nosuchdriver") - c.Assert(err, check.NotNil, check.Commentf("volume create exception name already in use with another driver")) - c.Assert(out, checker.Contains, "A volume named test already exists") - - out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Driver }}", "test") - _, _, err = dockerCmdWithError("volume", "create", "test", "--driver", strings.TrimSpace(out)) - c.Assert(err, check.IsNil) - - // make sure hidden --name option conflicts with positional arg name - out, _, err = dockerCmdWithError("volume", "create", "--name", "test2", "test2") - c.Assert(err, check.NotNil, check.Commentf("Conflicting options: either specify --name or provide positional arg, not both")) -} - func (s *DockerSuite) TestVolumeCLIInspect(c *check.C) { c.Assert( exec.Command(dockerBinary, "volume", "inspect", "doesntexist").Run(), diff --git a/components/engine/vendor.conf b/components/engine/vendor.conf index 42ea2e9361..412d7a824e 100644 --- a/components/engine/vendor.conf +++ b/components/engine/vendor.conf @@ -100,7 +100,7 @@ github.com/docker/containerd 03e5862ec0d8d3b3f750e19fca3ee367e13c090e github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4 # cluster -github.com/docker/swarmkit 5a6df4b07d83e6dbd72e39e354c325dc9b91850f +github.com/docker/swarmkit 9e4bd71a1690cd27400714fcd98c329b752b5c4c github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9 github.com/gogo/protobuf v0.3 github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a diff --git a/components/engine/vendor/github.com/docker/swarmkit/api/ca.pb.go b/components/engine/vendor/github.com/docker/swarmkit/api/ca.pb.go index d1c13f2c45..619421b3b0 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/api/ca.pb.go +++ b/components/engine/vendor/github.com/docker/swarmkit/api/ca.pb.go @@ -43,9 +43,8 @@ func (*NodeCertificateStatusRequest) ProtoMessage() {} func (*NodeCertificateStatusRequest) Descriptor() ([]byte, []int) { return fileDescriptorCa, []int{0} } type NodeCertificateStatusResponse struct { - Status *IssuanceStatus `protobuf:"bytes,1,opt,name=status" json:"status,omitempty"` - Certificate *Certificate `protobuf:"bytes,2,opt,name=certificate" json:"certificate,omitempty"` - RootCABundle []byte `protobuf:"bytes,3,opt,name=root_ca_bundle,json=rootCaBundle,proto3" json:"root_ca_bundle,omitempty"` + Status *IssuanceStatus `protobuf:"bytes,1,opt,name=status" json:"status,omitempty"` + Certificate *Certificate `protobuf:"bytes,2,opt,name=certificate" json:"certificate,omitempty"` } func (m *NodeCertificateStatusResponse) Reset() { *m = NodeCertificateStatusResponse{} } @@ -184,9 +183,8 @@ func (m *NodeCertificateStatusResponse) Copy() *NodeCertificateStatusResponse { } o := &NodeCertificateStatusResponse{ - Status: m.Status.Copy(), - Certificate: m.Certificate.Copy(), - RootCABundle: m.RootCABundle, + Status: m.Status.Copy(), + Certificate: m.Certificate.Copy(), } return o @@ -279,7 +277,7 @@ func (this *NodeCertificateStatusResponse) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 7) + s := make([]string, 0, 6) s = append(s, "&api.NodeCertificateStatusResponse{") if this.Status != nil { s = append(s, "Status: "+fmt.Sprintf("%#v", this.Status)+",\n") @@ -287,7 +285,6 @@ func (this *NodeCertificateStatusResponse) GoString() string { if this.Certificate != nil { s = append(s, "Certificate: "+fmt.Sprintf("%#v", this.Certificate)+",\n") } - s = append(s, "RootCABundle: "+fmt.Sprintf("%#v", this.RootCABundle)+",\n") s = append(s, "}") return strings.Join(s, "") } @@ -646,12 +643,6 @@ func (m *NodeCertificateStatusResponse) MarshalTo(data []byte) (int, error) { } i += n2 } - if len(m.RootCABundle) > 0 { - data[i] = 0x1a - i++ - i = encodeVarintCa(data, i, uint64(len(m.RootCABundle))) - i += copy(data[i:], m.RootCABundle) - } return i, nil } @@ -1121,10 +1112,6 @@ func (m *NodeCertificateStatusResponse) Size() (n int) { l = m.Certificate.Size() n += 1 + l + sovCa(uint64(l)) } - l = len(m.RootCABundle) - if l > 0 { - n += 1 + l + sovCa(uint64(l)) - } return n } @@ -1225,7 +1212,6 @@ func (this *NodeCertificateStatusResponse) String() string { s := strings.Join([]string{`&NodeCertificateStatusResponse{`, `Status:` + strings.Replace(fmt.Sprintf("%v", this.Status), "IssuanceStatus", "IssuanceStatus", 1) + `,`, `Certificate:` + strings.Replace(fmt.Sprintf("%v", this.Certificate), "Certificate", "Certificate", 1) + `,`, - `RootCABundle:` + fmt.Sprintf("%v", this.RootCABundle) + `,`, `}`, }, "") return s @@ -1475,37 +1461,6 @@ func (m *NodeCertificateStatusResponse) Unmarshal(data []byte) error { return err } iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RootCABundle", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowCa - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := data[iNdEx] - iNdEx++ - byteLen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthCa - } - postIndex := iNdEx + byteLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.RootCABundle = append(m.RootCABundle[:0], data[iNdEx:postIndex]...) - if m.RootCABundle == nil { - m.RootCABundle = []byte{} - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipCa(data[iNdEx:]) @@ -2173,46 +2128,44 @@ var ( func init() { proto.RegisterFile("ca.proto", fileDescriptorCa) } var fileDescriptorCa = []byte{ - // 651 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x54, 0xc1, 0x6e, 0xd3, 0x4a, - 0x14, 0xed, 0xb8, 0x7d, 0x69, 0x7b, 0xe3, 0x97, 0x56, 0xd3, 0x56, 0x0a, 0x69, 0xea, 0x54, 0x66, - 0xd1, 0xb2, 0x20, 0x6d, 0x03, 0x62, 0x01, 0x1b, 0xe2, 0x20, 0x55, 0x15, 0x2a, 0x42, 0x53, 0xc1, - 0x36, 0x9a, 0x38, 0x43, 0xb0, 0xe2, 0x78, 0x8c, 0x67, 0x5c, 0xc8, 0x0e, 0x09, 0xc4, 0x1f, 0x20, - 0xf8, 0x0a, 0xbe, 0xa3, 0x62, 0x85, 0x84, 0x84, 0x58, 0x45, 0xd4, 0x1f, 0x80, 0xf8, 0x04, 0xe4, - 0xb1, 0x43, 0x93, 0xc6, 0x09, 0x65, 0x15, 0xcf, 0xf5, 0x39, 0xe7, 0xde, 0x73, 0x7c, 0x33, 0xb0, - 0x64, 0xd3, 0xaa, 0x1f, 0x70, 0xc9, 0x31, 0x6e, 0x73, 0xbb, 0xcb, 0x82, 0xaa, 0x78, 0x49, 0x83, - 0x5e, 0xd7, 0x91, 0xd5, 0xd3, 0x83, 0x52, 0x5e, 0xf6, 0x7d, 0x26, 0x12, 0x40, 0x29, 0x2f, 0x7c, - 0x66, 0x0f, 0x0f, 0xeb, 0x1d, 0xde, 0xe1, 0xea, 0x71, 0x2f, 0x7e, 0x4a, 0xab, 0x6b, 0xbe, 0x1b, - 0x76, 0x1c, 0x6f, 0x2f, 0xf9, 0x49, 0x8a, 0x66, 0x03, 0xca, 0x8f, 0x78, 0x9b, 0x35, 0x58, 0x20, - 0x9d, 0x67, 0x8e, 0x4d, 0x25, 0x3b, 0x91, 0x54, 0x86, 0x82, 0xb0, 0x17, 0x21, 0x13, 0x12, 0x5f, - 0x87, 0x45, 0x8f, 0xb7, 0x59, 0xd3, 0x69, 0x17, 0xd1, 0x36, 0xda, 0x5d, 0xb6, 0x20, 0x1a, 0x54, - 0x72, 0x31, 0xe5, 0xe8, 0x01, 0xc9, 0xc5, 0xaf, 0x8e, 0xda, 0xe6, 0x37, 0x04, 0x5b, 0x53, 0x54, - 0x84, 0xcf, 0x3d, 0xc1, 0xf0, 0x5d, 0xc8, 0x09, 0x55, 0x51, 0x2a, 0xf9, 0x9a, 0x59, 0x9d, 0x34, - 0x54, 0x3d, 0x12, 0x22, 0xa4, 0x9e, 0x3d, 0xe4, 0xa6, 0x0c, 0x5c, 0x87, 0xbc, 0x7d, 0x21, 0x5c, - 0xd4, 0x94, 0x40, 0x25, 0x4b, 0x60, 0xa4, 0x3f, 0x19, 0xe5, 0xe0, 0x3b, 0x50, 0x08, 0x38, 0x97, - 0x4d, 0x9b, 0x36, 0x5b, 0xa1, 0xd7, 0x76, 0x59, 0x71, 0x7e, 0x1b, 0xed, 0xea, 0xd6, 0x6a, 0x34, - 0xa8, 0xe8, 0x84, 0x73, 0xd9, 0xa8, 0x5b, 0xaa, 0x4e, 0xf4, 0x18, 0xd7, 0xa0, 0xc9, 0xc9, 0xfc, - 0x8a, 0x60, 0x33, 0x9e, 0x8a, 0x5d, 0x72, 0x37, 0x4c, 0xe7, 0x36, 0x2c, 0x04, 0xdc, 0x65, 0xca, - 0x54, 0xa1, 0x56, 0xce, 0x9a, 0x29, 0x66, 0x12, 0xee, 0x32, 0x4b, 0x2b, 0x22, 0xa2, 0xd0, 0xf8, - 0x1a, 0xcc, 0xdb, 0x22, 0x50, 0x46, 0x74, 0x6b, 0x31, 0x1a, 0x54, 0xe6, 0x1b, 0x27, 0x84, 0xc4, - 0x35, 0xbc, 0x0e, 0xff, 0x49, 0xde, 0x65, 0x9e, 0x9a, 0x6f, 0x99, 0x24, 0x07, 0x7c, 0x0c, 0x3a, - 0x3d, 0xa5, 0x8e, 0x4b, 0x5b, 0x8e, 0xeb, 0xc8, 0x7e, 0x71, 0x41, 0xb5, 0xbb, 0x31, 0xad, 0xdd, - 0x89, 0xcf, 0xec, 0x6a, 0x7d, 0x84, 0x40, 0xc6, 0xe8, 0xe6, 0x7b, 0x04, 0xe5, 0x6c, 0x57, 0xe9, - 0xd7, 0xba, 0xca, 0x47, 0xc7, 0x8f, 0x61, 0x45, 0x81, 0x7a, 0xac, 0xd7, 0x62, 0x81, 0x78, 0xee, - 0xf8, 0xca, 0x51, 0xa1, 0xb6, 0x33, 0x73, 0xae, 0xe3, 0x3f, 0x70, 0x52, 0x88, 0xf9, 0x17, 0x67, - 0x73, 0x0b, 0x36, 0x0f, 0x99, 0x4c, 0x3e, 0xc7, 0x64, 0xd8, 0xe6, 0x7d, 0x28, 0x67, 0xbf, 0x4e, - 0xa7, 0xde, 0x1e, 0xdf, 0x93, 0x78, 0x72, 0x7d, 0x6c, 0x0d, 0xcc, 0x0d, 0x58, 0x3b, 0x64, 0xf2, - 0x89, 0xe7, 0x72, 0xbb, 0xfb, 0x90, 0xf5, 0x87, 0xc2, 0x01, 0xac, 0x8f, 0x97, 0x53, 0xc1, 0x2d, - 0x80, 0x50, 0x15, 0x9b, 0x5d, 0xd6, 0x4f, 0xf5, 0x96, 0xc3, 0x21, 0x0c, 0xdf, 0x83, 0xc5, 0x53, - 0x16, 0x08, 0x87, 0x7b, 0xe9, 0x4e, 0x6e, 0x66, 0x19, 0x7f, 0x9a, 0x40, 0xac, 0x85, 0xb3, 0x41, - 0x65, 0x8e, 0x0c, 0x19, 0xb5, 0xb7, 0x1a, 0x68, 0x8d, 0x3a, 0x7e, 0x83, 0x54, 0xef, 0x09, 0x53, - 0x78, 0x2f, 0x4b, 0x6b, 0x46, 0x3a, 0xa5, 0xfd, 0xab, 0x13, 0x12, 0x7b, 0xe6, 0xd2, 0xe7, 0x4f, - 0x3f, 0x3f, 0x6a, 0xda, 0x2a, 0xc2, 0xaf, 0x40, 0x1f, 0x0d, 0x00, 0xef, 0x4c, 0xd1, 0xba, 0x9c, - 0x5c, 0x69, 0xf7, 0xef, 0xc0, 0xb4, 0xd9, 0x86, 0x6a, 0xb6, 0x02, 0xff, 0x2b, 0xe4, 0xcd, 0x1e, - 0xf5, 0x68, 0x87, 0x05, 0xb5, 0x0f, 0x1a, 0xa8, 0xbd, 0x4a, 0xa3, 0xc8, 0xda, 0xca, 0xec, 0x28, - 0x66, 0xfc, 0x2b, 0xb3, 0xa3, 0x98, 0xb5, 0xf0, 0x23, 0x51, 0xbc, 0x43, 0xb0, 0x91, 0x79, 0x95, - 0xe1, 0xfd, 0x69, 0x6b, 0x3d, 0xed, 0xee, 0x2c, 0x1d, 0xfc, 0x03, 0xe3, 0xf2, 0x20, 0x56, 0xf9, - 0xec, 0xdc, 0x98, 0xfb, 0x7e, 0x6e, 0xcc, 0xfd, 0x3a, 0x37, 0xd0, 0xeb, 0xc8, 0x40, 0x67, 0x91, - 0x81, 0xbe, 0x44, 0x06, 0xfa, 0x11, 0x19, 0xa8, 0x95, 0x53, 0xb7, 0xf7, 0xad, 0xdf, 0x01, 0x00, - 0x00, 0xff, 0xff, 0x69, 0xb6, 0x7e, 0x90, 0x22, 0x06, 0x00, 0x00, + // 615 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x54, 0xcd, 0x6e, 0xd3, 0x4c, + 0x14, 0xed, 0xb8, 0xfd, 0xd2, 0xf6, 0x26, 0x5f, 0x8b, 0xa6, 0xad, 0x14, 0xd2, 0xd4, 0xa9, 0xcc, + 0xa2, 0x65, 0x81, 0xd3, 0x06, 0x56, 0xb0, 0x21, 0x09, 0x52, 0x15, 0xa1, 0x22, 0x34, 0x11, 0x6c, + 0x2b, 0xc7, 0x19, 0x82, 0x15, 0xc7, 0x63, 0x3c, 0xe3, 0x40, 0x76, 0x48, 0x20, 0xde, 0x00, 0xc1, + 0x8a, 0x47, 0xe0, 0x39, 0x22, 0x56, 0x48, 0x6c, 0x58, 0x45, 0xc4, 0x0f, 0x80, 0x78, 0x04, 0xe4, + 0xb1, 0x4d, 0xf3, 0xe3, 0x84, 0xb2, 0xf2, 0xcc, 0x9d, 0x73, 0xce, 0xbd, 0xf7, 0xcc, 0xf5, 0xc0, + 0x86, 0x69, 0xe8, 0xae, 0xc7, 0x04, 0xc3, 0xb8, 0xcd, 0xcc, 0x2e, 0xf5, 0x74, 0xfe, 0xd2, 0xf0, + 0x7a, 0x5d, 0x4b, 0xe8, 0xfd, 0xd3, 0x42, 0x56, 0x0c, 0x5c, 0xca, 0x23, 0x40, 0x21, 0xcb, 0x5d, + 0x6a, 0x26, 0x9b, 0xdd, 0x0e, 0xeb, 0x30, 0xb9, 0x2c, 0x87, 0xab, 0x38, 0xba, 0xe3, 0xda, 0x7e, + 0xc7, 0x72, 0xca, 0xd1, 0x27, 0x0a, 0x6a, 0x75, 0x28, 0x3e, 0x62, 0x6d, 0x5a, 0xa7, 0x9e, 0xb0, + 0x9e, 0x59, 0xa6, 0x21, 0x68, 0x53, 0x18, 0xc2, 0xe7, 0x84, 0xbe, 0xf0, 0x29, 0x17, 0xf8, 0x06, + 0xac, 0x3b, 0xac, 0x4d, 0x2f, 0xac, 0x76, 0x1e, 0x1d, 0xa2, 0xe3, 0xcd, 0x1a, 0x04, 0xa3, 0x52, + 0x26, 0xa4, 0x34, 0x1e, 0x90, 0x4c, 0x78, 0xd4, 0x68, 0x6b, 0x9f, 0x10, 0x1c, 0x2c, 0x50, 0xe1, + 0x2e, 0x73, 0x38, 0xc5, 0x77, 0x21, 0xc3, 0x65, 0x44, 0xaa, 0x64, 0x2b, 0x9a, 0x3e, 0xdf, 0x90, + 0xde, 0xe0, 0xdc, 0x37, 0x1c, 0x33, 0xe1, 0xc6, 0x0c, 0x5c, 0x85, 0xac, 0x79, 0x29, 0x9c, 0x57, + 0xa4, 0x40, 0x29, 0x4d, 0x60, 0x22, 0x3f, 0x99, 0xe4, 0x68, 0xdf, 0x10, 0xec, 0x87, 0xea, 0x74, + 0xa6, 0xca, 0xa4, 0xcb, 0x3b, 0xb0, 0xe6, 0x31, 0x9b, 0xca, 0xe2, 0xb6, 0x2a, 0xc5, 0x34, 0xed, + 0x90, 0x49, 0x98, 0x4d, 0x6b, 0x4a, 0x1e, 0x11, 0x89, 0xc6, 0xd7, 0x61, 0xd5, 0xe4, 0x9e, 0x2c, + 0x28, 0x57, 0x5b, 0x0f, 0x46, 0xa5, 0xd5, 0x7a, 0x93, 0x90, 0x30, 0x86, 0x77, 0xe1, 0x3f, 0xc1, + 0xba, 0xd4, 0xc9, 0xaf, 0x86, 0xa6, 0x91, 0x68, 0x83, 0xcf, 0x21, 0x67, 0xf4, 0x0d, 0xcb, 0x36, + 0x5a, 0x96, 0x6d, 0x89, 0x41, 0x7e, 0x4d, 0xa6, 0xbb, 0xb9, 0x28, 0x5d, 0xd3, 0xa5, 0xa6, 0x5e, + 0x9d, 0x20, 0x90, 0x29, 0xba, 0xf6, 0x1e, 0x41, 0x31, 0xbd, 0xab, 0xd8, 0xf5, 0xab, 0x5c, 0x1e, + 0x7e, 0x0c, 0xdb, 0x12, 0xd4, 0xa3, 0xbd, 0x16, 0xf5, 0xf8, 0x73, 0xcb, 0x95, 0x1d, 0x6d, 0x55, + 0x8e, 0x96, 0xd6, 0x75, 0xfe, 0x07, 0x4e, 0xb6, 0x42, 0xfe, 0xe5, 0x5e, 0x3b, 0x80, 0xfd, 0x33, + 0x2a, 0x08, 0x63, 0xa2, 0x5e, 0x9d, 0x37, 0x5b, 0xbb, 0x0f, 0xc5, 0xf4, 0xe3, 0xb8, 0xea, 0xc3, + 0xe9, 0xfb, 0x0e, 0x2b, 0xcf, 0x4d, 0x5f, 0xe7, 0x1e, 0xec, 0x9c, 0x51, 0xf1, 0xc4, 0xb1, 0x99, + 0xd9, 0x7d, 0x48, 0x07, 0x89, 0xb0, 0x07, 0xbb, 0xd3, 0xe1, 0x58, 0xf0, 0x00, 0xc0, 0x97, 0xc1, + 0x8b, 0x2e, 0x1d, 0xc4, 0x7a, 0x9b, 0x7e, 0x02, 0xc3, 0xf7, 0x60, 0xbd, 0x4f, 0x3d, 0x6e, 0x31, + 0x27, 0x9e, 0xad, 0xfd, 0xb4, 0xc6, 0x9f, 0x46, 0x90, 0xda, 0xda, 0x70, 0x54, 0x5a, 0x21, 0x09, + 0xa3, 0xf2, 0x56, 0x01, 0xa5, 0x5e, 0xc5, 0x6f, 0x90, 0xcc, 0x3d, 0xd7, 0x14, 0x2e, 0xa7, 0x69, + 0x2d, 0x71, 0xa7, 0x70, 0x72, 0x75, 0x42, 0xd4, 0x9e, 0xb6, 0xf1, 0xe5, 0xf3, 0xcf, 0x8f, 0x8a, + 0x72, 0x0d, 0xe1, 0x57, 0x90, 0x9b, 0x34, 0x00, 0x1f, 0x2d, 0xd0, 0x9a, 0x75, 0xae, 0x70, 0xfc, + 0x77, 0x60, 0x9c, 0x6c, 0x4f, 0x26, 0xdb, 0x86, 0xff, 0x25, 0xf2, 0x56, 0xcf, 0x70, 0x8c, 0x0e, + 0xf5, 0x2a, 0x1f, 0x14, 0x90, 0x73, 0x15, 0x5b, 0x91, 0x36, 0x95, 0xe9, 0x56, 0x2c, 0xf9, 0x2b, + 0xd3, 0xad, 0x58, 0x36, 0xf0, 0x13, 0x56, 0xbc, 0x43, 0xb0, 0x97, 0xfa, 0x24, 0xe1, 0x93, 0x45, + 0x63, 0xbd, 0xe8, 0x0d, 0x2c, 0x9c, 0xfe, 0x03, 0x63, 0xb6, 0x90, 0x5a, 0x71, 0x38, 0x56, 0x57, + 0xbe, 0x8f, 0xd5, 0x95, 0x5f, 0x63, 0x15, 0xbd, 0x0e, 0x54, 0x34, 0x0c, 0x54, 0xf4, 0x35, 0x50, + 0xd1, 0x8f, 0x40, 0x45, 0xad, 0x8c, 0x7c, 0x85, 0x6f, 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xcf, + 0xc4, 0x68, 0xc2, 0xea, 0x05, 0x00, 0x00, } diff --git a/components/engine/vendor/github.com/docker/swarmkit/api/ca.proto b/components/engine/vendor/github.com/docker/swarmkit/api/ca.proto index 1d1cfa2193..be4b4a58c9 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/api/ca.proto +++ b/components/engine/vendor/github.com/docker/swarmkit/api/ca.proto @@ -36,7 +36,6 @@ message NodeCertificateStatusRequest { message NodeCertificateStatusResponse { IssuanceStatus status = 1; Certificate certificate = 2; - bytes root_ca_bundle = 3 [(gogoproto.customname) = "RootCABundle"]; } message IssueNodeCertificateRequest { diff --git a/components/engine/vendor/github.com/docker/swarmkit/ca/certificates.go b/components/engine/vendor/github.com/docker/swarmkit/ca/certificates.go index 75b604a69e..6eb6e4dd74 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/ca/certificates.go +++ b/components/engine/vendor/github.com/docker/swarmkit/ca/certificates.go @@ -102,15 +102,13 @@ type RootCA struct { // Key will only be used by the original manager to put the private // key-material in raft, no signing operations depend on it. Key []byte - // Cert includes the PEM encoded Certificate bundle for the Root CA + // Cert includes the PEM encoded Certificate for the Root CA Cert []byte Pool *x509.CertPool // Digest of the serialized bytes of the certificate Digest digest.Digest // This signer will be nil if the node doesn't have the appropriate key material Signer cfsigner.Signer - // Path stores the location on disk where the RootCA lives - Path CertPaths } // CanSign ensures that the signer has all three necessary elements needed to operate @@ -165,9 +163,9 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWrit // Get the remote manager to issue a CA signed certificate for this node // Retry up to 5 times in case the manager we first try to contact isn't // responding properly (for example, it may have just been demoted). - var response *api.NodeCertificateStatusResponse + var signedCert []byte for i := 0; i != 5; i++ { - response, err = GetRemoteSignedCertificate(ctx, csr, rca.Pool, config) + signedCert, err = GetRemoteSignedCertificate(ctx, csr, rca.Pool, config) if err == nil { break } @@ -179,7 +177,7 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWrit // Доверяй, но проверяй. // Before we overwrite our local key + certificate, let's make sure the server gave us one that is valid // Create an X509Cert so we can .Verify() - certBlock, _ := pem.Decode(response.Certificate.Certificate) + certBlock, _ := pem.Decode(signedCert) if certBlock == nil { return nil, errors.New("failed to parse certificate PEM") } @@ -187,34 +185,17 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWrit if err != nil { return nil, err } - // We retrieve the certificate with the current root pool, so we know this was issued by a legitimate manager. - // However, there might have been a server-side root rotation, so we verify this cert with a new pool. - // If we got a valid response.RootCABundle, turn it into a Pool, and verify the newly minted certificate using it. - var ( - newRootErr error - newRootCA RootCA - ) - rootCAPool := rca.Pool - if response.RootCABundle != nil { - newRootCA, newRootErr = NewRootCA(response.RootCABundle, nil, rca.Path, time.Minute) - if newRootErr == nil { - // The response.RootCABundle we got from the remote server seems to be good, use it - rootCAPool = newRootCA.Pool - } - } - - // Create VerifyOptions with either the new certificate bundle, or the old pool + // Include our current root pool opts := x509.VerifyOptions{ - Roots: rootCAPool, + Roots: rca.Pool, } - - // Check to see if this certificate was signed by one of the CAs, and isn't expired + // Check to see if this certificate was signed by our CA, and isn't expired if _, err := X509Cert.Verify(opts); err != nil { return nil, err } // Create a valid TLSKeyPair out of the PEM encoded private key and certificate - tlsKeyPair, err := tls.X509KeyPair(response.Certificate.Certificate, key) + tlsKeyPair, err := tls.X509KeyPair(signedCert, key) if err != nil { return nil, err } @@ -230,16 +211,7 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWrit return nil, err } - // If a CA certificate bundle exists it has been validated before. If it's different, let's write it to disk. - // Root rotation should always happen by appending a new CA cert, and later removing the old one, - // so it's safer to do it in this order of operations (write root, write certificate) - if newRootErr == nil && !bytes.Equal(rca.Cert, response.RootCABundle) { - if err := newRootCA.saveCertificate(); err != nil { - return nil, err - } - } - - if err := kw.Write(response.Certificate.Certificate, key, kekUpdate); err != nil { + if err := kw.Write(signedCert, key, kekUpdate); err != nil { return nil, err } @@ -344,28 +316,10 @@ func (rca *RootCA) AppendFirstRootPEM(cert []byte) ([]byte, error) { return certChain, nil } -func (rca *RootCA) saveCertificate() error { - if rca.Cert == nil { - return errors.New("no valid certificate bundle found") - - } - if rca.Path.Cert == "" { - return errors.New("no path found for this root CA") - } - - // Make sure the necessary dirs exist and they are writable - err := os.MkdirAll(filepath.Dir(rca.Path.Cert), 0755) - if err != nil { - return err - } - - return ioutils.AtomicWriteFile(rca.Path.Cert, rca.Cert, 0644) -} - // NewRootCA creates a new RootCA object from unparsed PEM cert bundle and key byte // slices. key may be nil, and in this case NewRootCA will return a RootCA // without a signer. -func NewRootCA(certBytes, keyBytes []byte, paths CertPaths, certExpiry time.Duration) (RootCA, error) { +func NewRootCA(certBytes, keyBytes []byte, certExpiry time.Duration) (RootCA, error) { // Parse all the certificates in the cert bundle parsedCerts, err := helpers.ParseCertificatesPEM(certBytes) if err != nil { @@ -391,7 +345,7 @@ func NewRootCA(certBytes, keyBytes []byte, paths CertPaths, certExpiry time.Dura if len(keyBytes) == 0 { // This RootCA does not have a valid signer. - return RootCA{Cert: certBytes, Digest: digest, Pool: pool, Path: paths}, nil + return RootCA{Cert: certBytes, Digest: digest, Pool: pool}, nil } var ( @@ -433,7 +387,7 @@ func NewRootCA(certBytes, keyBytes []byte, paths CertPaths, certExpiry time.Dura keyBlock, _ := pem.Decode(keyBytes) if keyBlock == nil { // This RootCA does not have a valid signer. - return RootCA{Cert: certBytes, Digest: digest, Pool: pool, Path: paths}, nil + return RootCA{Cert: certBytes, Digest: digest, Pool: pool}, nil } if passphraseStr != "" && !x509.IsEncryptedPEMBlock(keyBlock) { keyBytes, err = EncryptECPrivateKey(keyBytes, passphraseStr) @@ -442,7 +396,7 @@ func NewRootCA(certBytes, keyBytes []byte, paths CertPaths, certExpiry time.Dura } } - return RootCA{Signer: signer, Key: keyBytes, Digest: digest, Cert: certBytes, Pool: pool, Path: paths}, nil + return RootCA{Signer: signer, Key: keyBytes, Digest: digest, Cert: certBytes, Pool: pool}, nil } func ensureCertKeyMatch(cert *x509.Certificate, key crypto.PublicKey) error { @@ -460,7 +414,8 @@ func ensureCertKeyMatch(cert *x509.Certificate, key crypto.PublicKey) error { return errors.New("certificate key mismatch") } -// GetLocalRootCA returns the PEM-encoded root CA Certificate if it exists +// GetLocalRootCA validates if the contents of the file are a valid self-signed +// CA certificate, and returns the PEM-encoded Certificate if so func GetLocalRootCA(paths CertPaths) (RootCA, error) { // Check if we have a Certificate file cert, err := ioutil.ReadFile(paths.Cert) @@ -472,7 +427,17 @@ func GetLocalRootCA(paths CertPaths) (RootCA, error) { return RootCA{}, err } - return NewRootCA(cert, nil, paths, DefaultNodeCertExpiration) + key, err := ioutil.ReadFile(paths.Key) + if err != nil { + if !os.IsNotExist(err) { + return RootCA{}, err + } + // There may not be a local key. It's okay to pass in a nil + // key. We'll get a root CA without a signer. + key = nil + } + + return NewRootCA(cert, key, DefaultNodeCertExpiration) } func getGRPCConnection(creds credentials.TransportCredentials, r remotes.Remotes) (*grpc.ClientConn, api.Peer, error) { @@ -565,13 +530,13 @@ func CreateRootCA(rootCN string, paths CertPaths) (RootCA, error) { return RootCA{}, err } - rootCA, err := NewRootCA(cert, key, paths, DefaultNodeCertExpiration) + rootCA, err := NewRootCA(cert, key, DefaultNodeCertExpiration) if err != nil { return RootCA{}, err } // save the cert to disk - if err := rootCA.saveCertificate(); err != nil { + if err := saveRootCA(rootCA, paths); err != nil { return RootCA{}, err } @@ -580,7 +545,7 @@ func CreateRootCA(rootCN string, paths CertPaths) (RootCA, error) { // GetRemoteSignedCertificate submits a CSR to a remote CA server address, // and that is part of a CA identified by a specific certificate pool. -func GetRemoteSignedCertificate(ctx context.Context, csr []byte, rootCAPool *x509.CertPool, config CertificateRequestConfig) (*api.NodeCertificateStatusResponse, error) { +func GetRemoteSignedCertificate(ctx context.Context, csr []byte, rootCAPool *x509.CertPool, config CertificateRequestConfig) ([]byte, error) { if rootCAPool == nil { return nil, errors.New("valid root CA pool required") } @@ -629,7 +594,7 @@ func GetRemoteSignedCertificate(ctx context.Context, csr []byte, rootCAPool *x50 } // If the certificate was issued, return - if statusResponse.Status != nil && statusResponse.Status.State == api.IssuanceStateIssued { + if statusResponse.Status.State == api.IssuanceStateIssued { if statusResponse.Certificate == nil { return nil, errors.New("no certificate in CertificateStatus response") } @@ -641,7 +606,7 @@ func GetRemoteSignedCertificate(ctx context.Context, csr []byte, rootCAPool *x50 // current request. if bytes.Equal(statusResponse.Certificate.CSR, csr) { config.Remotes.Observe(peer, remotes.DefaultObservationWeight) - return statusResponse, nil + return statusResponse.Certificate.Certificate, nil } } @@ -675,6 +640,17 @@ func readCertValidity(kr KeyReader) (time.Time, time.Time, error) { } +func saveRootCA(rootCA RootCA, paths CertPaths) error { + // Make sure the necessary dirs exist and they are writable + err := os.MkdirAll(filepath.Dir(paths.Cert), 0755) + if err != nil { + return err + } + + // If the root certificate got returned successfully, save the rootCA to disk. + return ioutils.AtomicWriteFile(paths.Cert, rootCA.Cert, 0644) +} + // GenerateNewCSR returns a newly generated key and CSR signed with said key func GenerateNewCSR() (csr, key []byte, err error) { req := &cfcsr.CertificateRequest{ diff --git a/components/engine/vendor/github.com/docker/swarmkit/ca/config.go b/components/engine/vendor/github.com/docker/swarmkit/ca/config.go index db5ba02236..e15454b5b4 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/ca/config.go +++ b/components/engine/vendor/github.com/docker/swarmkit/ca/config.go @@ -120,15 +120,8 @@ func (s *SecurityConfig) UpdateRootCA(cert, key []byte, certExpiry time.Duration s.mu.Lock() defer s.mu.Unlock() - // Create a new RootCA, keeping the path of the old RootCA - rootCA, err := NewRootCA(cert, key, s.rootCA.Path, certExpiry) - if err != nil { - return err - } - // Attempt to write the new certificate to disk - err = rootCA.saveCertificate() + rootCA, err := NewRootCA(cert, key, certExpiry) if err == nil { - // No errors, save the current rootCA s.rootCA = &rootCA } @@ -239,8 +232,7 @@ func DownloadRootCA(ctx context.Context, paths CertPaths, token string, r remote } // Save root CA certificate to disk - rootCA.Path = paths - if err = rootCA.saveCertificate(); err != nil { + if err = saveRootCA(rootCA, paths); err != nil { return RootCA{}, err } @@ -462,6 +454,7 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, remotes remotes.Remo // Since the expiration of the certificate is managed remotely we should update our // retry timer on every iteration of this loop. + // Retrieve the current certificate expiration information. validFrom, validUntil, err := readCertValidity(s.KeyReader()) if err != nil { // We failed to read the expiration, let's stick with the starting default diff --git a/components/engine/vendor/github.com/docker/swarmkit/ca/server.go b/components/engine/vendor/github.com/docker/swarmkit/ca/server.go index 51f78f4512..fa55d38534 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/ca/server.go +++ b/components/engine/vendor/github.com/docker/swarmkit/ca/server.go @@ -142,9 +142,8 @@ func (s *Server) NodeCertificateStatus(ctx context.Context, request *api.NodeCer // If this certificate has a final state, return it immediately (both pending and renew are transition states) if isFinalState(node.Certificate.Status) { return &api.NodeCertificateStatusResponse{ - Status: &node.Certificate.Status, - Certificate: &node.Certificate, - RootCABundle: s.securityConfig.RootCA().Cert, + Status: &node.Certificate.Status, + Certificate: &node.Certificate, }, nil } @@ -165,9 +164,8 @@ func (s *Server) NodeCertificateStatus(ctx context.Context, request *api.NodeCer if isFinalState(v.Node.Certificate.Status) { cert := v.Node.Certificate.Copy() return &api.NodeCertificateStatusResponse{ - Status: &cert.Status, - Certificate: cert, - RootCABundle: s.securityConfig.RootCA().Cert, + Status: &cert.Status, + Certificate: cert, }, nil } } diff --git a/components/engine/vendor/github.com/docker/swarmkit/manager/allocator/networkallocator/networkallocator.go b/components/engine/vendor/github.com/docker/swarmkit/manager/allocator/networkallocator/networkallocator.go index 62751b290a..f16b0d4175 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/manager/allocator/networkallocator/networkallocator.go +++ b/components/engine/vendor/github.com/docker/swarmkit/manager/allocator/networkallocator/networkallocator.go @@ -746,7 +746,7 @@ func (na *NetworkAllocator) allocatePools(n *api.Network) (map[string]string, er } for i, ic := range ipamConfigs { - poolID, poolIP, _, err := ipam.RequestPool(asName, ic.Subnet, ic.Range, nil, false) + poolID, poolIP, _, err := ipam.RequestPool(asName, ic.Subnet, ic.Range, dOptions, false) if err != nil { // Rollback by releasing all the resources allocated so far. releasePools(ipam, ipamConfigs[:i], pools) 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 1ecbb7d08a..b2b64578b4 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 @@ -2,6 +2,7 @@ package scheduler import ( "fmt" + "strings" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/manager/constraint" @@ -93,6 +94,15 @@ type PluginFilter struct { t *api.Task } +func referencesVolumePlugin(mount api.Mount) bool { + return mount.Type == api.MountTypeVolume && + mount.VolumeOptions != nil && + mount.VolumeOptions.DriverConfig != nil && + mount.VolumeOptions.DriverConfig.Name != "" && + mount.VolumeOptions.DriverConfig.Name != "local" + +} + // SetTask returns true when the filter is enabled for a given task. func (f *PluginFilter) SetTask(t *api.Task) bool { c := t.Spec.GetContainer() @@ -100,12 +110,9 @@ func (f *PluginFilter) SetTask(t *api.Task) bool { var volumeTemplates bool if c != nil { for _, mount := range c.Mounts { - if mount.Type == api.MountTypeVolume && - mount.VolumeOptions != nil && - mount.VolumeOptions.DriverConfig != nil && - mount.VolumeOptions.DriverConfig.Name != "" && - mount.VolumeOptions.DriverConfig.Name != "local" { + if referencesVolumePlugin(mount) { volumeTemplates = true + break } } } @@ -128,7 +135,7 @@ func (f *PluginFilter) Check(n *NodeInfo) bool { container := f.t.Spec.GetContainer() if container != nil { for _, mount := range container.Mounts { - if mount.VolumeOptions != nil && mount.VolumeOptions.DriverConfig != nil { + if referencesVolumePlugin(mount) { if !f.pluginExistsOnNode("Volume", mount.VolumeOptions.DriverConfig.Name, nodePlugins) { return false } @@ -138,16 +145,30 @@ func (f *PluginFilter) Check(n *NodeInfo) bool { // Check if all network plugins required by task are installed on node for _, tn := range f.t.Networks { - if !f.pluginExistsOnNode("Network", tn.Network.DriverState.Name, nodePlugins) { - return false + if tn.Network != nil && tn.Network.DriverState != nil && tn.Network.DriverState.Name != "" { + if !f.pluginExistsOnNode("Network", tn.Network.DriverState.Name, nodePlugins) { + return false + } } } return true } +// pluginExistsOnNode returns true if the (pluginName, pluginType) pair is present in nodePlugins func (f *PluginFilter) pluginExistsOnNode(pluginType string, pluginName string, nodePlugins []api.PluginDescription) bool { for _, np := range nodePlugins { - if pluginType == np.Type && pluginName == np.Name { + if pluginType != np.Type { + continue + } + if pluginName == np.Name { + return true + } + // This does not use the reference package to avoid the + // overhead of parsing references as part of the scheduling + // loop. This is okay only because plugin names are a very + // strict subset of the reference grammar that is always + // name:tag. + if strings.HasPrefix(np.Name, pluginName) && np.Name[len(pluginName):] == ":latest" { return true } } diff --git a/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/pipeline.go b/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/pipeline.go index d9981aa125..00fd36c5c7 100644 --- a/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/pipeline.go +++ b/components/engine/vendor/github.com/docker/swarmkit/manager/scheduler/pipeline.go @@ -11,12 +11,7 @@ var ( // Always check for readiness first. &ReadyFilter{}, &ResourceFilter{}, - - // TODO(stevvooe): Do not filter based on plugins since they are lazy - // loaded in the engine. We can add this back when we can schedule - // plugins in the future. - // &PluginFilter{}, - + &PluginFilter{}, &ConstraintFilter{}, } ) 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 de1a607991..98b3f451cd 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 @@ -1259,8 +1259,6 @@ func (n *Node) saveToStorage( } if err := n.raftLogger.SaveEntries(hardState, entries); err != nil { - // TODO(aaronl): These error types should really wrap more - // detailed errors. return errors.Wrap(err, "failed to save raft log entries") } diff --git a/components/engine/volume/store/errors.go b/components/engine/volume/store/errors.go index db7c3ce243..980175f29c 100644 --- a/components/engine/volume/store/errors.go +++ b/components/engine/volume/store/errors.go @@ -14,7 +14,7 @@ var ( // errInvalidName is a typed error returned when creating a volume with a name that is not valid on the platform errInvalidName = errors.New("volume name is not valid on this platform") // errNameConflict is a typed error returned on create when a volume exists with the given name, but for a different driver - errNameConflict = errors.New("conflict: volume name must be unique") + errNameConflict = errors.New("volume name must be unique") ) // OpErr is the error type returned by functions in the store package. It describes diff --git a/components/engine/volume/store/store.go b/components/engine/volume/store/store.go index ef7b2b971b..a5b97c9785 100644 --- a/components/engine/volume/store/store.go +++ b/components/engine/volume/store/store.go @@ -310,8 +310,17 @@ func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, err vDriverName := v.DriverName() var conflict bool - if driverName != "" && vDriverName != driverName { - conflict = true + if driverName != "" { + // Retrieve canonical driver name to avoid inconsistencies (for example + // "plugin" vs. "plugin:latest") + vd, err := volumedrivers.GetDriver(driverName) + if err != nil { + return nil, err + } + + if vDriverName != vd.Name() { + conflict = true + } } // let's check if the found volume ref