Browse code

Merge pull request #29378 from aaronlehmann/swarm-plugins

Support v2 plugins in swarm mode

Vincent Demeester authored on 2016/12/19 19:07:06
Showing 17 changed files
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"github.com/docker/docker/api/types/network"
9 9
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
10 10
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
11
+	"github.com/docker/docker/plugin"
11 12
 	networktypes "github.com/docker/libnetwork/types"
12 13
 	"github.com/docker/swarmkit/agent/exec"
13 14
 	"github.com/docker/swarmkit/agent/secrets"
... ...
@@ -45,12 +46,39 @@ func (e *executor) Describe(ctx context.Context) (*api.NodeDescription, error) {
45 45
 		}
46 46
 	}
47 47
 
48
+	// add v1 plugins
48 49
 	addPlugins("Volume", info.Plugins.Volume)
49 50
 	// Add builtin driver "overlay" (the only builtin multi-host driver) to
50 51
 	// the plugin list by default.
51 52
 	addPlugins("Network", append([]string{"overlay"}, info.Plugins.Network...))
52 53
 	addPlugins("Authorization", info.Plugins.Authorization)
53 54
 
55
+	// add v2 plugins
56
+	v2Plugins, err := plugin.GetManager().List()
57
+	if err == nil {
58
+		for _, plgn := range v2Plugins {
59
+			for _, typ := range plgn.Config.Interface.Types {
60
+				if typ.Prefix != "docker" || !plgn.Enabled {
61
+					continue
62
+				}
63
+				plgnTyp := typ.Capability
64
+				if typ.Capability == "volumedriver" {
65
+					plgnTyp = "Volume"
66
+				} else if typ.Capability == "networkdriver" {
67
+					plgnTyp = "Network"
68
+				}
69
+				plgnName := plgn.Name
70
+				if plgn.Tag != "" {
71
+					plgnName += ":" + plgn.Tag
72
+				}
73
+				plugins[api.PluginDescription{
74
+					Type: plgnTyp,
75
+					Name: plgnName,
76
+				}] = struct{}{}
77
+			}
78
+		}
79
+	}
80
+
54 81
 	pluginFields := make([]api.PluginDescription, 0, len(plugins))
55 82
 	for k := range plugins {
56 83
 		pluginFields = append(pluginFields, k)
... ...
@@ -148,20 +148,28 @@ func (d *Swarm) GetServiceTasks(c *check.C, service string) []swarm.Task {
148 148
 	return tasks
149 149
 }
150 150
 
151
-// CheckServiceRunningTasks returns the number of running tasks for the specified service
152
-func (d *Swarm) CheckServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
151
+// CheckServiceTasksInState returns the number of tasks with a matching state,
152
+// and optional message substring.
153
+func (d *Swarm) CheckServiceTasksInState(service string, state swarm.TaskState, message string) func(*check.C) (interface{}, check.CommentInterface) {
153 154
 	return func(c *check.C) (interface{}, check.CommentInterface) {
154 155
 		tasks := d.GetServiceTasks(c, service)
155
-		var runningCount int
156
+		var count int
156 157
 		for _, task := range tasks {
157
-			if task.Status.State == swarm.TaskStateRunning {
158
-				runningCount++
158
+			if task.Status.State == state {
159
+				if message == "" || strings.Contains(task.Status.Message, message) {
160
+					count++
161
+				}
159 162
 			}
160 163
 		}
161
-		return runningCount, nil
164
+		return count, nil
162 165
 	}
163 166
 }
164 167
 
168
+// CheckServiceRunningTasks returns the number of running tasks for the specified service
169
+func (d *Swarm) CheckServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
170
+	return d.CheckServiceTasksInState(service, swarm.TaskStateRunning, "")
171
+}
172
+
165 173
 // CheckServiceUpdateState returns the current update state for the specified service
166 174
 func (d *Swarm) CheckServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) {
167 175
 	return func(c *check.C) (interface{}, check.CommentInterface) {
... ...
@@ -282,6 +282,22 @@ func (s *DockerExternalVolumeSuite) TearDownSuite(c *check.C) {
282 282
 	c.Assert(err, checker.IsNil)
283 283
 }
284 284
 
285
+func (s *DockerExternalVolumeSuite) TestVolumeCLICreateOptionConflict(c *check.C) {
286
+	dockerCmd(c, "volume", "create", "test")
287
+
288
+	out, _, err := dockerCmdWithError("volume", "create", "test", "--driver", volumePluginName)
289
+	c.Assert(err, check.NotNil, check.Commentf("volume create exception name already in use with another driver"))
290
+	c.Assert(out, checker.Contains, "A volume named test already exists")
291
+
292
+	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Driver }}", "test")
293
+	_, _, err = dockerCmdWithError("volume", "create", "test", "--driver", strings.TrimSpace(out))
294
+	c.Assert(err, check.IsNil)
295
+
296
+	// make sure hidden --name option conflicts with positional arg name
297
+	out, _, err = dockerCmdWithError("volume", "create", "--name", "test2", "test2")
298
+	c.Assert(err, check.NotNil, check.Commentf("Conflicting options: either specify --name or provide positional arg, not both"))
299
+}
300
+
285 301
 func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverNamed(c *check.C) {
286 302
 	s.d.StartWithBusybox(c)
287 303
 
288 304
new file mode 100644
... ...
@@ -0,0 +1,52 @@
0
+// +build !windows
1
+
2
+package main
3
+
4
+import (
5
+	"encoding/json"
6
+	"strings"
7
+
8
+	"github.com/docker/docker/api/types/swarm"
9
+	"github.com/docker/docker/pkg/integration/checker"
10
+	"github.com/go-check/check"
11
+)
12
+
13
+func (s *DockerSwarmSuite) TestSwarmVolumePlugin(c *check.C) {
14
+	d := s.AddDaemon(c, true, true)
15
+
16
+	out, err := d.Cmd("service", "create", "--mount", "type=volume,source=my-volume,destination=/foo,volume-driver=customvolumedriver", "--name", "top", "busybox", "top")
17
+	c.Assert(err, checker.IsNil, check.Commentf(out))
18
+
19
+	// Make sure task stays pending before plugin is available
20
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckServiceTasksInState("top", swarm.TaskStatePending, "missing plugin on 1 node"), checker.Equals, 1)
21
+
22
+	plugin := newVolumePlugin(c, "customvolumedriver")
23
+	defer plugin.Close()
24
+
25
+	// create a dummy volume to trigger lazy loading of the plugin
26
+	out, err = d.Cmd("volume", "create", "-d", "customvolumedriver", "hello")
27
+
28
+	// TODO(aaronl): It will take about 15 seconds for swarm to realize the
29
+	// plugin was loaded. Switching the test over to plugin v2 would avoid
30
+	// this long delay.
31
+
32
+	// make sure task has been deployed.
33
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
34
+
35
+	out, err = d.Cmd("ps", "-q")
36
+	c.Assert(err, checker.IsNil)
37
+	containerID := strings.TrimSpace(out)
38
+
39
+	out, err = d.Cmd("inspect", "-f", "{{json .Mounts}}", containerID)
40
+	c.Assert(err, checker.IsNil)
41
+
42
+	var mounts []struct {
43
+		Name   string
44
+		Driver string
45
+	}
46
+
47
+	c.Assert(json.NewDecoder(strings.NewReader(out)).Decode(&mounts), checker.IsNil)
48
+	c.Assert(len(mounts), checker.Equals, 1, check.Commentf(out))
49
+	c.Assert(mounts[0].Name, checker.Equals, "my-volume")
50
+	c.Assert(mounts[0].Driver, checker.Equals, "customvolumedriver")
51
+}
... ...
@@ -29,21 +29,6 @@ func (s *DockerSuite) TestVolumeCLICreate(c *check.C) {
29 29
 	c.Assert(name, check.Equals, "test2")
30 30
 }
31 31
 
32
-func (s *DockerSuite) TestVolumeCLICreateOptionConflict(c *check.C) {
33
-	dockerCmd(c, "volume", "create", "test")
34
-	out, _, err := dockerCmdWithError("volume", "create", "test", "--driver", "nosuchdriver")
35
-	c.Assert(err, check.NotNil, check.Commentf("volume create exception name already in use with another driver"))
36
-	c.Assert(out, checker.Contains, "A volume named test already exists")
37
-
38
-	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Driver }}", "test")
39
-	_, _, err = dockerCmdWithError("volume", "create", "test", "--driver", strings.TrimSpace(out))
40
-	c.Assert(err, check.IsNil)
41
-
42
-	// make sure hidden --name option conflicts with positional arg name
43
-	out, _, err = dockerCmdWithError("volume", "create", "--name", "test2", "test2")
44
-	c.Assert(err, check.NotNil, check.Commentf("Conflicting options: either specify --name or provide positional arg, not both"))
45
-}
46
-
47 32
 func (s *DockerSuite) TestVolumeCLIInspect(c *check.C) {
48 33
 	c.Assert(
49 34
 		exec.Command(dockerBinary, "volume", "inspect", "doesntexist").Run(),
... ...
@@ -100,7 +100,7 @@ github.com/docker/containerd 03e5862ec0d8d3b3f750e19fca3ee367e13c090e
100 100
 github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
101 101
 
102 102
 # cluster
103
-github.com/docker/swarmkit 5a6df4b07d83e6dbd72e39e354c325dc9b91850f
103
+github.com/docker/swarmkit 9e4bd71a1690cd27400714fcd98c329b752b5c4c
104 104
 github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9
105 105
 github.com/gogo/protobuf v0.3
106 106
 github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
... ...
@@ -43,9 +43,8 @@ func (*NodeCertificateStatusRequest) ProtoMessage()               {}
43 43
 func (*NodeCertificateStatusRequest) Descriptor() ([]byte, []int) { return fileDescriptorCa, []int{0} }
44 44
 
45 45
 type NodeCertificateStatusResponse struct {
46
-	Status       *IssuanceStatus `protobuf:"bytes,1,opt,name=status" json:"status,omitempty"`
47
-	Certificate  *Certificate    `protobuf:"bytes,2,opt,name=certificate" json:"certificate,omitempty"`
48
-	RootCABundle []byte          `protobuf:"bytes,3,opt,name=root_ca_bundle,json=rootCaBundle,proto3" json:"root_ca_bundle,omitempty"`
46
+	Status      *IssuanceStatus `protobuf:"bytes,1,opt,name=status" json:"status,omitempty"`
47
+	Certificate *Certificate    `protobuf:"bytes,2,opt,name=certificate" json:"certificate,omitempty"`
49 48
 }
50 49
 
51 50
 func (m *NodeCertificateStatusResponse) Reset()                    { *m = NodeCertificateStatusResponse{} }
... ...
@@ -184,9 +183,8 @@ func (m *NodeCertificateStatusResponse) Copy() *NodeCertificateStatusResponse {
184 184
 	}
185 185
 
186 186
 	o := &NodeCertificateStatusResponse{
187
-		Status:       m.Status.Copy(),
188
-		Certificate:  m.Certificate.Copy(),
189
-		RootCABundle: m.RootCABundle,
187
+		Status:      m.Status.Copy(),
188
+		Certificate: m.Certificate.Copy(),
190 189
 	}
191 190
 
192 191
 	return o
... ...
@@ -279,7 +277,7 @@ func (this *NodeCertificateStatusResponse) GoString() string {
279 279
 	if this == nil {
280 280
 		return "nil"
281 281
 	}
282
-	s := make([]string, 0, 7)
282
+	s := make([]string, 0, 6)
283 283
 	s = append(s, "&api.NodeCertificateStatusResponse{")
284 284
 	if this.Status != nil {
285 285
 		s = append(s, "Status: "+fmt.Sprintf("%#v", this.Status)+",\n")
... ...
@@ -287,7 +285,6 @@ func (this *NodeCertificateStatusResponse) GoString() string {
287 287
 	if this.Certificate != nil {
288 288
 		s = append(s, "Certificate: "+fmt.Sprintf("%#v", this.Certificate)+",\n")
289 289
 	}
290
-	s = append(s, "RootCABundle: "+fmt.Sprintf("%#v", this.RootCABundle)+",\n")
291 290
 	s = append(s, "}")
292 291
 	return strings.Join(s, "")
293 292
 }
... ...
@@ -646,12 +643,6 @@ func (m *NodeCertificateStatusResponse) MarshalTo(data []byte) (int, error) {
646 646
 		}
647 647
 		i += n2
648 648
 	}
649
-	if len(m.RootCABundle) > 0 {
650
-		data[i] = 0x1a
651
-		i++
652
-		i = encodeVarintCa(data, i, uint64(len(m.RootCABundle)))
653
-		i += copy(data[i:], m.RootCABundle)
654
-	}
655 649
 	return i, nil
656 650
 }
657 651
 
... ...
@@ -1121,10 +1112,6 @@ func (m *NodeCertificateStatusResponse) Size() (n int) {
1121 1121
 		l = m.Certificate.Size()
1122 1122
 		n += 1 + l + sovCa(uint64(l))
1123 1123
 	}
1124
-	l = len(m.RootCABundle)
1125
-	if l > 0 {
1126
-		n += 1 + l + sovCa(uint64(l))
1127
-	}
1128 1124
 	return n
1129 1125
 }
1130 1126
 
... ...
@@ -1225,7 +1212,6 @@ func (this *NodeCertificateStatusResponse) String() string {
1225 1225
 	s := strings.Join([]string{`&NodeCertificateStatusResponse{`,
1226 1226
 		`Status:` + strings.Replace(fmt.Sprintf("%v", this.Status), "IssuanceStatus", "IssuanceStatus", 1) + `,`,
1227 1227
 		`Certificate:` + strings.Replace(fmt.Sprintf("%v", this.Certificate), "Certificate", "Certificate", 1) + `,`,
1228
-		`RootCABundle:` + fmt.Sprintf("%v", this.RootCABundle) + `,`,
1229 1228
 		`}`,
1230 1229
 	}, "")
1231 1230
 	return s
... ...
@@ -1475,37 +1461,6 @@ func (m *NodeCertificateStatusResponse) Unmarshal(data []byte) error {
1475 1475
 				return err
1476 1476
 			}
1477 1477
 			iNdEx = postIndex
1478
-		case 3:
1479
-			if wireType != 2 {
1480
-				return fmt.Errorf("proto: wrong wireType = %d for field RootCABundle", wireType)
1481
-			}
1482
-			var byteLen int
1483
-			for shift := uint(0); ; shift += 7 {
1484
-				if shift >= 64 {
1485
-					return ErrIntOverflowCa
1486
-				}
1487
-				if iNdEx >= l {
1488
-					return io.ErrUnexpectedEOF
1489
-				}
1490
-				b := data[iNdEx]
1491
-				iNdEx++
1492
-				byteLen |= (int(b) & 0x7F) << shift
1493
-				if b < 0x80 {
1494
-					break
1495
-				}
1496
-			}
1497
-			if byteLen < 0 {
1498
-				return ErrInvalidLengthCa
1499
-			}
1500
-			postIndex := iNdEx + byteLen
1501
-			if postIndex > l {
1502
-				return io.ErrUnexpectedEOF
1503
-			}
1504
-			m.RootCABundle = append(m.RootCABundle[:0], data[iNdEx:postIndex]...)
1505
-			if m.RootCABundle == nil {
1506
-				m.RootCABundle = []byte{}
1507
-			}
1508
-			iNdEx = postIndex
1509 1478
 		default:
1510 1479
 			iNdEx = preIndex
1511 1480
 			skippy, err := skipCa(data[iNdEx:])
... ...
@@ -2173,46 +2128,44 @@ var (
2173 2173
 func init() { proto.RegisterFile("ca.proto", fileDescriptorCa) }
2174 2174
 
2175 2175
 var fileDescriptorCa = []byte{
2176
-	// 651 bytes of a gzipped FileDescriptorProto
2177
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x54, 0xc1, 0x6e, 0xd3, 0x4a,
2178
-	0x14, 0xed, 0xb8, 0x7d, 0x69, 0x7b, 0xe3, 0x97, 0x56, 0xd3, 0x56, 0x0a, 0x69, 0xea, 0x54, 0x66,
2179
-	0xd1, 0xb2, 0x20, 0x6d, 0x03, 0x62, 0x01, 0x1b, 0xe2, 0x20, 0x55, 0x15, 0x2a, 0x42, 0x53, 0xc1,
2180
-	0x36, 0x9a, 0x38, 0x43, 0xb0, 0xe2, 0x78, 0x8c, 0x67, 0x5c, 0xc8, 0x0e, 0x09, 0xc4, 0x1f, 0x20,
2181
-	0xf8, 0x0a, 0xbe, 0xa3, 0x62, 0x85, 0x84, 0x84, 0x58, 0x45, 0xd4, 0x1f, 0x80, 0xf8, 0x04, 0xe4,
2182
-	0xb1, 0x43, 0x93, 0xc6, 0x09, 0x65, 0x15, 0xcf, 0xf5, 0x39, 0xe7, 0xde, 0x73, 0x7c, 0x33, 0xb0,
2183
-	0x64, 0xd3, 0xaa, 0x1f, 0x70, 0xc9, 0x31, 0x6e, 0x73, 0xbb, 0xcb, 0x82, 0xaa, 0x78, 0x49, 0x83,
2184
-	0x5e, 0xd7, 0x91, 0xd5, 0xd3, 0x83, 0x52, 0x5e, 0xf6, 0x7d, 0x26, 0x12, 0x40, 0x29, 0x2f, 0x7c,
2185
-	0x66, 0x0f, 0x0f, 0xeb, 0x1d, 0xde, 0xe1, 0xea, 0x71, 0x2f, 0x7e, 0x4a, 0xab, 0x6b, 0xbe, 0x1b,
2186
-	0x76, 0x1c, 0x6f, 0x2f, 0xf9, 0x49, 0x8a, 0x66, 0x03, 0xca, 0x8f, 0x78, 0x9b, 0x35, 0x58, 0x20,
2187
-	0x9d, 0x67, 0x8e, 0x4d, 0x25, 0x3b, 0x91, 0x54, 0x86, 0x82, 0xb0, 0x17, 0x21, 0x13, 0x12, 0x5f,
2188
-	0x87, 0x45, 0x8f, 0xb7, 0x59, 0xd3, 0x69, 0x17, 0xd1, 0x36, 0xda, 0x5d, 0xb6, 0x20, 0x1a, 0x54,
2189
-	0x72, 0x31, 0xe5, 0xe8, 0x01, 0xc9, 0xc5, 0xaf, 0x8e, 0xda, 0xe6, 0x37, 0x04, 0x5b, 0x53, 0x54,
2190
-	0x84, 0xcf, 0x3d, 0xc1, 0xf0, 0x5d, 0xc8, 0x09, 0x55, 0x51, 0x2a, 0xf9, 0x9a, 0x59, 0x9d, 0x34,
2191
-	0x54, 0x3d, 0x12, 0x22, 0xa4, 0x9e, 0x3d, 0xe4, 0xa6, 0x0c, 0x5c, 0x87, 0xbc, 0x7d, 0x21, 0x5c,
2192
-	0xd4, 0x94, 0x40, 0x25, 0x4b, 0x60, 0xa4, 0x3f, 0x19, 0xe5, 0xe0, 0x3b, 0x50, 0x08, 0x38, 0x97,
2193
-	0x4d, 0x9b, 0x36, 0x5b, 0xa1, 0xd7, 0x76, 0x59, 0x71, 0x7e, 0x1b, 0xed, 0xea, 0xd6, 0x6a, 0x34,
2194
-	0xa8, 0xe8, 0x84, 0x73, 0xd9, 0xa8, 0x5b, 0xaa, 0x4e, 0xf4, 0x18, 0xd7, 0xa0, 0xc9, 0xc9, 0xfc,
2195
-	0x8a, 0x60, 0x33, 0x9e, 0x8a, 0x5d, 0x72, 0x37, 0x4c, 0xe7, 0x36, 0x2c, 0x04, 0xdc, 0x65, 0xca,
2196
-	0x54, 0xa1, 0x56, 0xce, 0x9a, 0x29, 0x66, 0x12, 0xee, 0x32, 0x4b, 0x2b, 0x22, 0xa2, 0xd0, 0xf8,
2197
-	0x1a, 0xcc, 0xdb, 0x22, 0x50, 0x46, 0x74, 0x6b, 0x31, 0x1a, 0x54, 0xe6, 0x1b, 0x27, 0x84, 0xc4,
2198
-	0x35, 0xbc, 0x0e, 0xff, 0x49, 0xde, 0x65, 0x9e, 0x9a, 0x6f, 0x99, 0x24, 0x07, 0x7c, 0x0c, 0x3a,
2199
-	0x3d, 0xa5, 0x8e, 0x4b, 0x5b, 0x8e, 0xeb, 0xc8, 0x7e, 0x71, 0x41, 0xb5, 0xbb, 0x31, 0xad, 0xdd,
2200
-	0x89, 0xcf, 0xec, 0x6a, 0x7d, 0x84, 0x40, 0xc6, 0xe8, 0xe6, 0x7b, 0x04, 0xe5, 0x6c, 0x57, 0xe9,
2201
-	0xd7, 0xba, 0xca, 0x47, 0xc7, 0x8f, 0x61, 0x45, 0x81, 0x7a, 0xac, 0xd7, 0x62, 0x81, 0x78, 0xee,
2202
-	0xf8, 0xca, 0x51, 0xa1, 0xb6, 0x33, 0x73, 0xae, 0xe3, 0x3f, 0x70, 0x52, 0x88, 0xf9, 0x17, 0x67,
2203
-	0x73, 0x0b, 0x36, 0x0f, 0x99, 0x4c, 0x3e, 0xc7, 0x64, 0xd8, 0xe6, 0x7d, 0x28, 0x67, 0xbf, 0x4e,
2204
-	0xa7, 0xde, 0x1e, 0xdf, 0x93, 0x78, 0x72, 0x7d, 0x6c, 0x0d, 0xcc, 0x0d, 0x58, 0x3b, 0x64, 0xf2,
2205
-	0x89, 0xe7, 0x72, 0xbb, 0xfb, 0x90, 0xf5, 0x87, 0xc2, 0x01, 0xac, 0x8f, 0x97, 0x53, 0xc1, 0x2d,
2206
-	0x80, 0x50, 0x15, 0x9b, 0x5d, 0xd6, 0x4f, 0xf5, 0x96, 0xc3, 0x21, 0x0c, 0xdf, 0x83, 0xc5, 0x53,
2207
-	0x16, 0x08, 0x87, 0x7b, 0xe9, 0x4e, 0x6e, 0x66, 0x19, 0x7f, 0x9a, 0x40, 0xac, 0x85, 0xb3, 0x41,
2208
-	0x65, 0x8e, 0x0c, 0x19, 0xb5, 0xb7, 0x1a, 0x68, 0x8d, 0x3a, 0x7e, 0x83, 0x54, 0xef, 0x09, 0x53,
2209
-	0x78, 0x2f, 0x4b, 0x6b, 0x46, 0x3a, 0xa5, 0xfd, 0xab, 0x13, 0x12, 0x7b, 0xe6, 0xd2, 0xe7, 0x4f,
2210
-	0x3f, 0x3f, 0x6a, 0xda, 0x2a, 0xc2, 0xaf, 0x40, 0x1f, 0x0d, 0x00, 0xef, 0x4c, 0xd1, 0xba, 0x9c,
2211
-	0x5c, 0x69, 0xf7, 0xef, 0xc0, 0xb4, 0xd9, 0x86, 0x6a, 0xb6, 0x02, 0xff, 0x2b, 0xe4, 0xcd, 0x1e,
2212
-	0xf5, 0x68, 0x87, 0x05, 0xb5, 0x0f, 0x1a, 0xa8, 0xbd, 0x4a, 0xa3, 0xc8, 0xda, 0xca, 0xec, 0x28,
2213
-	0x66, 0xfc, 0x2b, 0xb3, 0xa3, 0x98, 0xb5, 0xf0, 0x23, 0x51, 0xbc, 0x43, 0xb0, 0x91, 0x79, 0x95,
2214
-	0xe1, 0xfd, 0x69, 0x6b, 0x3d, 0xed, 0xee, 0x2c, 0x1d, 0xfc, 0x03, 0xe3, 0xf2, 0x20, 0x56, 0xf9,
2215
-	0xec, 0xdc, 0x98, 0xfb, 0x7e, 0x6e, 0xcc, 0xfd, 0x3a, 0x37, 0xd0, 0xeb, 0xc8, 0x40, 0x67, 0x91,
2216
-	0x81, 0xbe, 0x44, 0x06, 0xfa, 0x11, 0x19, 0xa8, 0x95, 0x53, 0xb7, 0xf7, 0xad, 0xdf, 0x01, 0x00,
2217
-	0x00, 0xff, 0xff, 0x69, 0xb6, 0x7e, 0x90, 0x22, 0x06, 0x00, 0x00,
2176
+	// 615 bytes of a gzipped FileDescriptorProto
2177
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x54, 0xcd, 0x6e, 0xd3, 0x4c,
2178
+	0x14, 0xed, 0xb8, 0xfd, 0xd2, 0xf6, 0x26, 0x5f, 0x8b, 0xa6, 0xad, 0x14, 0xd2, 0xd4, 0xa9, 0xcc,
2179
+	0xa2, 0x65, 0x81, 0xd3, 0x06, 0x56, 0xb0, 0x21, 0x09, 0x52, 0x15, 0xa1, 0x22, 0x34, 0x11, 0x6c,
2180
+	0x2b, 0xc7, 0x19, 0x82, 0x15, 0xc7, 0x63, 0x3c, 0xe3, 0x40, 0x76, 0x48, 0x20, 0xde, 0x00, 0xc1,
2181
+	0x8a, 0x47, 0xe0, 0x39, 0x22, 0x56, 0x48, 0x6c, 0x58, 0x45, 0xc4, 0x0f, 0x80, 0x78, 0x04, 0xe4,
2182
+	0xb1, 0x4d, 0xf3, 0xe3, 0x84, 0xb2, 0xf2, 0xcc, 0x9d, 0x73, 0xce, 0xbd, 0xf7, 0xcc, 0xf5, 0xc0,
2183
+	0x86, 0x69, 0xe8, 0xae, 0xc7, 0x04, 0xc3, 0xb8, 0xcd, 0xcc, 0x2e, 0xf5, 0x74, 0xfe, 0xd2, 0xf0,
2184
+	0x7a, 0x5d, 0x4b, 0xe8, 0xfd, 0xd3, 0x42, 0x56, 0x0c, 0x5c, 0xca, 0x23, 0x40, 0x21, 0xcb, 0x5d,
2185
+	0x6a, 0x26, 0x9b, 0xdd, 0x0e, 0xeb, 0x30, 0xb9, 0x2c, 0x87, 0xab, 0x38, 0xba, 0xe3, 0xda, 0x7e,
2186
+	0xc7, 0x72, 0xca, 0xd1, 0x27, 0x0a, 0x6a, 0x75, 0x28, 0x3e, 0x62, 0x6d, 0x5a, 0xa7, 0x9e, 0xb0,
2187
+	0x9e, 0x59, 0xa6, 0x21, 0x68, 0x53, 0x18, 0xc2, 0xe7, 0x84, 0xbe, 0xf0, 0x29, 0x17, 0xf8, 0x06,
2188
+	0xac, 0x3b, 0xac, 0x4d, 0x2f, 0xac, 0x76, 0x1e, 0x1d, 0xa2, 0xe3, 0xcd, 0x1a, 0x04, 0xa3, 0x52,
2189
+	0x26, 0xa4, 0x34, 0x1e, 0x90, 0x4c, 0x78, 0xd4, 0x68, 0x6b, 0x9f, 0x10, 0x1c, 0x2c, 0x50, 0xe1,
2190
+	0x2e, 0x73, 0x38, 0xc5, 0x77, 0x21, 0xc3, 0x65, 0x44, 0xaa, 0x64, 0x2b, 0x9a, 0x3e, 0xdf, 0x90,
2191
+	0xde, 0xe0, 0xdc, 0x37, 0x1c, 0x33, 0xe1, 0xc6, 0x0c, 0x5c, 0x85, 0xac, 0x79, 0x29, 0x9c, 0x57,
2192
+	0xa4, 0x40, 0x29, 0x4d, 0x60, 0x22, 0x3f, 0x99, 0xe4, 0x68, 0xdf, 0x10, 0xec, 0x87, 0xea, 0x74,
2193
+	0xa6, 0xca, 0xa4, 0xcb, 0x3b, 0xb0, 0xe6, 0x31, 0x9b, 0xca, 0xe2, 0xb6, 0x2a, 0xc5, 0x34, 0xed,
2194
+	0x90, 0x49, 0x98, 0x4d, 0x6b, 0x4a, 0x1e, 0x11, 0x89, 0xc6, 0xd7, 0x61, 0xd5, 0xe4, 0x9e, 0x2c,
2195
+	0x28, 0x57, 0x5b, 0x0f, 0x46, 0xa5, 0xd5, 0x7a, 0x93, 0x90, 0x30, 0x86, 0x77, 0xe1, 0x3f, 0xc1,
2196
+	0xba, 0xd4, 0xc9, 0xaf, 0x86, 0xa6, 0x91, 0x68, 0x83, 0xcf, 0x21, 0x67, 0xf4, 0x0d, 0xcb, 0x36,
2197
+	0x5a, 0x96, 0x6d, 0x89, 0x41, 0x7e, 0x4d, 0xa6, 0xbb, 0xb9, 0x28, 0x5d, 0xd3, 0xa5, 0xa6, 0x5e,
2198
+	0x9d, 0x20, 0x90, 0x29, 0xba, 0xf6, 0x1e, 0x41, 0x31, 0xbd, 0xab, 0xd8, 0xf5, 0xab, 0x5c, 0x1e,
2199
+	0x7e, 0x0c, 0xdb, 0x12, 0xd4, 0xa3, 0xbd, 0x16, 0xf5, 0xf8, 0x73, 0xcb, 0x95, 0x1d, 0x6d, 0x55,
2200
+	0x8e, 0x96, 0xd6, 0x75, 0xfe, 0x07, 0x4e, 0xb6, 0x42, 0xfe, 0xe5, 0x5e, 0x3b, 0x80, 0xfd, 0x33,
2201
+	0x2a, 0x08, 0x63, 0xa2, 0x5e, 0x9d, 0x37, 0x5b, 0xbb, 0x0f, 0xc5, 0xf4, 0xe3, 0xb8, 0xea, 0xc3,
2202
+	0xe9, 0xfb, 0x0e, 0x2b, 0xcf, 0x4d, 0x5f, 0xe7, 0x1e, 0xec, 0x9c, 0x51, 0xf1, 0xc4, 0xb1, 0x99,
2203
+	0xd9, 0x7d, 0x48, 0x07, 0x89, 0xb0, 0x07, 0xbb, 0xd3, 0xe1, 0x58, 0xf0, 0x00, 0xc0, 0x97, 0xc1,
2204
+	0x8b, 0x2e, 0x1d, 0xc4, 0x7a, 0x9b, 0x7e, 0x02, 0xc3, 0xf7, 0x60, 0xbd, 0x4f, 0x3d, 0x6e, 0x31,
2205
+	0x27, 0x9e, 0xad, 0xfd, 0xb4, 0xc6, 0x9f, 0x46, 0x90, 0xda, 0xda, 0x70, 0x54, 0x5a, 0x21, 0x09,
2206
+	0xa3, 0xf2, 0x56, 0x01, 0xa5, 0x5e, 0xc5, 0x6f, 0x90, 0xcc, 0x3d, 0xd7, 0x14, 0x2e, 0xa7, 0x69,
2207
+	0x2d, 0x71, 0xa7, 0x70, 0x72, 0x75, 0x42, 0xd4, 0x9e, 0xb6, 0xf1, 0xe5, 0xf3, 0xcf, 0x8f, 0x8a,
2208
+	0x72, 0x0d, 0xe1, 0x57, 0x90, 0x9b, 0x34, 0x00, 0x1f, 0x2d, 0xd0, 0x9a, 0x75, 0xae, 0x70, 0xfc,
2209
+	0x77, 0x60, 0x9c, 0x6c, 0x4f, 0x26, 0xdb, 0x86, 0xff, 0x25, 0xf2, 0x56, 0xcf, 0x70, 0x8c, 0x0e,
2210
+	0xf5, 0x2a, 0x1f, 0x14, 0x90, 0x73, 0x15, 0x5b, 0x91, 0x36, 0x95, 0xe9, 0x56, 0x2c, 0xf9, 0x2b,
2211
+	0xd3, 0xad, 0x58, 0x36, 0xf0, 0x13, 0x56, 0xbc, 0x43, 0xb0, 0x97, 0xfa, 0x24, 0xe1, 0x93, 0x45,
2212
+	0x63, 0xbd, 0xe8, 0x0d, 0x2c, 0x9c, 0xfe, 0x03, 0x63, 0xb6, 0x90, 0x5a, 0x71, 0x38, 0x56, 0x57,
2213
+	0xbe, 0x8f, 0xd5, 0x95, 0x5f, 0x63, 0x15, 0xbd, 0x0e, 0x54, 0x34, 0x0c, 0x54, 0xf4, 0x35, 0x50,
2214
+	0xd1, 0x8f, 0x40, 0x45, 0xad, 0x8c, 0x7c, 0x85, 0x6f, 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xcf,
2215
+	0xc4, 0x68, 0xc2, 0xea, 0x05, 0x00, 0x00,
2218 2216
 }
... ...
@@ -36,7 +36,6 @@ message NodeCertificateStatusRequest {
36 36
 message NodeCertificateStatusResponse {
37 37
 	IssuanceStatus status = 1;
38 38
 	Certificate certificate = 2;
39
-	bytes root_ca_bundle = 3 [(gogoproto.customname) = "RootCABundle"];
40 39
 }
41 40
 
42 41
 message IssueNodeCertificateRequest {
... ...
@@ -102,15 +102,13 @@ type RootCA struct {
102 102
 	// Key will only be used by the original manager to put the private
103 103
 	// key-material in raft, no signing operations depend on it.
104 104
 	Key []byte
105
-	// Cert includes the PEM encoded Certificate bundle for the Root CA
105
+	// Cert includes the PEM encoded Certificate for the Root CA
106 106
 	Cert []byte
107 107
 	Pool *x509.CertPool
108 108
 	// Digest of the serialized bytes of the certificate
109 109
 	Digest digest.Digest
110 110
 	// This signer will be nil if the node doesn't have the appropriate key material
111 111
 	Signer cfsigner.Signer
112
-	// Path stores the location on disk where the RootCA lives
113
-	Path CertPaths
114 112
 }
115 113
 
116 114
 // 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
165 165
 	// Get the remote manager to issue a CA signed certificate for this node
166 166
 	// Retry up to 5 times in case the manager we first try to contact isn't
167 167
 	// responding properly (for example, it may have just been demoted).
168
-	var response *api.NodeCertificateStatusResponse
168
+	var signedCert []byte
169 169
 	for i := 0; i != 5; i++ {
170
-		response, err = GetRemoteSignedCertificate(ctx, csr, rca.Pool, config)
170
+		signedCert, err = GetRemoteSignedCertificate(ctx, csr, rca.Pool, config)
171 171
 		if err == nil {
172 172
 			break
173 173
 		}
... ...
@@ -179,7 +177,7 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWrit
179 179
 	// Доверяй, но проверяй.
180 180
 	// Before we overwrite our local key + certificate, let's make sure the server gave us one that is valid
181 181
 	// Create an X509Cert so we can .Verify()
182
-	certBlock, _ := pem.Decode(response.Certificate.Certificate)
182
+	certBlock, _ := pem.Decode(signedCert)
183 183
 	if certBlock == nil {
184 184
 		return nil, errors.New("failed to parse certificate PEM")
185 185
 	}
... ...
@@ -187,34 +185,17 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWrit
187 187
 	if err != nil {
188 188
 		return nil, err
189 189
 	}
190
-	// We retrieve the certificate with the current root pool, so we know this was issued by a legitimate manager.
191
-	// However, there might have been a server-side root rotation, so we verify this cert with a new pool.
192
-	// If we got a valid response.RootCABundle, turn it into a Pool, and verify the newly minted certificate using it.
193
-	var (
194
-		newRootErr error
195
-		newRootCA  RootCA
196
-	)
197
-	rootCAPool := rca.Pool
198
-	if response.RootCABundle != nil {
199
-		newRootCA, newRootErr = NewRootCA(response.RootCABundle, nil, rca.Path, time.Minute)
200
-		if newRootErr == nil {
201
-			// The response.RootCABundle we got from the remote server seems to be good, use it
202
-			rootCAPool = newRootCA.Pool
203
-		}
204
-	}
205
-
206
-	// Create VerifyOptions with either the new certificate bundle, or the old pool
190
+	// Include our current root pool
207 191
 	opts := x509.VerifyOptions{
208
-		Roots: rootCAPool,
192
+		Roots: rca.Pool,
209 193
 	}
210
-
211
-	// Check to see if this certificate was signed by one of the CAs, and isn't expired
194
+	// Check to see if this certificate was signed by our CA, and isn't expired
212 195
 	if _, err := X509Cert.Verify(opts); err != nil {
213 196
 		return nil, err
214 197
 	}
215 198
 
216 199
 	// Create a valid TLSKeyPair out of the PEM encoded private key and certificate
217
-	tlsKeyPair, err := tls.X509KeyPair(response.Certificate.Certificate, key)
200
+	tlsKeyPair, err := tls.X509KeyPair(signedCert, key)
218 201
 	if err != nil {
219 202
 		return nil, err
220 203
 	}
... ...
@@ -230,16 +211,7 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWrit
230 230
 		return nil, err
231 231
 	}
232 232
 
233
-	// If a CA certificate bundle exists it has been validated before. If it's different, let's write it to disk.
234
-	// Root rotation should always happen by appending a new CA cert, and later removing the old one,
235
-	// so it's safer to do it in this order of operations (write root, write certificate)
236
-	if newRootErr == nil && !bytes.Equal(rca.Cert, response.RootCABundle) {
237
-		if err := newRootCA.saveCertificate(); err != nil {
238
-			return nil, err
239
-		}
240
-	}
241
-
242
-	if err := kw.Write(response.Certificate.Certificate, key, kekUpdate); err != nil {
233
+	if err := kw.Write(signedCert, key, kekUpdate); err != nil {
243 234
 		return nil, err
244 235
 	}
245 236
 
... ...
@@ -344,28 +316,10 @@ func (rca *RootCA) AppendFirstRootPEM(cert []byte) ([]byte, error) {
344 344
 	return certChain, nil
345 345
 }
346 346
 
347
-func (rca *RootCA) saveCertificate() error {
348
-	if rca.Cert == nil {
349
-		return errors.New("no valid certificate bundle found")
350
-
351
-	}
352
-	if rca.Path.Cert == "" {
353
-		return errors.New("no path found for this root CA")
354
-	}
355
-
356
-	// Make sure the necessary dirs exist and they are writable
357
-	err := os.MkdirAll(filepath.Dir(rca.Path.Cert), 0755)
358
-	if err != nil {
359
-		return err
360
-	}
361
-
362
-	return ioutils.AtomicWriteFile(rca.Path.Cert, rca.Cert, 0644)
363
-}
364
-
365 347
 // NewRootCA creates a new RootCA object from unparsed PEM cert bundle and key byte
366 348
 // slices. key may be nil, and in this case NewRootCA will return a RootCA
367 349
 // without a signer.
368
-func NewRootCA(certBytes, keyBytes []byte, paths CertPaths, certExpiry time.Duration) (RootCA, error) {
350
+func NewRootCA(certBytes, keyBytes []byte, certExpiry time.Duration) (RootCA, error) {
369 351
 	// Parse all the certificates in the cert bundle
370 352
 	parsedCerts, err := helpers.ParseCertificatesPEM(certBytes)
371 353
 	if err != nil {
... ...
@@ -391,7 +345,7 @@ func NewRootCA(certBytes, keyBytes []byte, paths CertPaths, certExpiry time.Dura
391 391
 
392 392
 	if len(keyBytes) == 0 {
393 393
 		// This RootCA does not have a valid signer.
394
-		return RootCA{Cert: certBytes, Digest: digest, Pool: pool, Path: paths}, nil
394
+		return RootCA{Cert: certBytes, Digest: digest, Pool: pool}, nil
395 395
 	}
396 396
 
397 397
 	var (
... ...
@@ -433,7 +387,7 @@ func NewRootCA(certBytes, keyBytes []byte, paths CertPaths, certExpiry time.Dura
433 433
 	keyBlock, _ := pem.Decode(keyBytes)
434 434
 	if keyBlock == nil {
435 435
 		// This RootCA does not have a valid signer.
436
-		return RootCA{Cert: certBytes, Digest: digest, Pool: pool, Path: paths}, nil
436
+		return RootCA{Cert: certBytes, Digest: digest, Pool: pool}, nil
437 437
 	}
438 438
 	if passphraseStr != "" && !x509.IsEncryptedPEMBlock(keyBlock) {
439 439
 		keyBytes, err = EncryptECPrivateKey(keyBytes, passphraseStr)
... ...
@@ -442,7 +396,7 @@ func NewRootCA(certBytes, keyBytes []byte, paths CertPaths, certExpiry time.Dura
442 442
 		}
443 443
 	}
444 444
 
445
-	return RootCA{Signer: signer, Key: keyBytes, Digest: digest, Cert: certBytes, Pool: pool, Path: paths}, nil
445
+	return RootCA{Signer: signer, Key: keyBytes, Digest: digest, Cert: certBytes, Pool: pool}, nil
446 446
 }
447 447
 
448 448
 func ensureCertKeyMatch(cert *x509.Certificate, key crypto.PublicKey) error {
... ...
@@ -460,7 +414,8 @@ func ensureCertKeyMatch(cert *x509.Certificate, key crypto.PublicKey) error {
460 460
 	return errors.New("certificate key mismatch")
461 461
 }
462 462
 
463
-// GetLocalRootCA returns the PEM-encoded root CA Certificate if it exists
463
+// GetLocalRootCA validates if the contents of the file are a valid self-signed
464
+// CA certificate, and returns the PEM-encoded Certificate if so
464 465
 func GetLocalRootCA(paths CertPaths) (RootCA, error) {
465 466
 	// Check if we have a Certificate file
466 467
 	cert, err := ioutil.ReadFile(paths.Cert)
... ...
@@ -472,7 +427,17 @@ func GetLocalRootCA(paths CertPaths) (RootCA, error) {
472 472
 		return RootCA{}, err
473 473
 	}
474 474
 
475
-	return NewRootCA(cert, nil, paths, DefaultNodeCertExpiration)
475
+	key, err := ioutil.ReadFile(paths.Key)
476
+	if err != nil {
477
+		if !os.IsNotExist(err) {
478
+			return RootCA{}, err
479
+		}
480
+		// There may not be a local key. It's okay to pass in a nil
481
+		// key. We'll get a root CA without a signer.
482
+		key = nil
483
+	}
484
+
485
+	return NewRootCA(cert, key, DefaultNodeCertExpiration)
476 486
 }
477 487
 
478 488
 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) {
565 565
 		return RootCA{}, err
566 566
 	}
567 567
 
568
-	rootCA, err := NewRootCA(cert, key, paths, DefaultNodeCertExpiration)
568
+	rootCA, err := NewRootCA(cert, key, DefaultNodeCertExpiration)
569 569
 	if err != nil {
570 570
 		return RootCA{}, err
571 571
 	}
572 572
 
573 573
 	// save the cert to disk
574
-	if err := rootCA.saveCertificate(); err != nil {
574
+	if err := saveRootCA(rootCA, paths); err != nil {
575 575
 		return RootCA{}, err
576 576
 	}
577 577
 
... ...
@@ -580,7 +545,7 @@ func CreateRootCA(rootCN string, paths CertPaths) (RootCA, error) {
580 580
 
581 581
 // GetRemoteSignedCertificate submits a CSR to a remote CA server address,
582 582
 // and that is part of a CA identified by a specific certificate pool.
583
-func GetRemoteSignedCertificate(ctx context.Context, csr []byte, rootCAPool *x509.CertPool, config CertificateRequestConfig) (*api.NodeCertificateStatusResponse, error) {
583
+func GetRemoteSignedCertificate(ctx context.Context, csr []byte, rootCAPool *x509.CertPool, config CertificateRequestConfig) ([]byte, error) {
584 584
 	if rootCAPool == nil {
585 585
 		return nil, errors.New("valid root CA pool required")
586 586
 	}
... ...
@@ -629,7 +594,7 @@ func GetRemoteSignedCertificate(ctx context.Context, csr []byte, rootCAPool *x50
629 629
 		}
630 630
 
631 631
 		// If the certificate was issued, return
632
-		if statusResponse.Status != nil && statusResponse.Status.State == api.IssuanceStateIssued {
632
+		if statusResponse.Status.State == api.IssuanceStateIssued {
633 633
 			if statusResponse.Certificate == nil {
634 634
 				return nil, errors.New("no certificate in CertificateStatus response")
635 635
 			}
... ...
@@ -641,7 +606,7 @@ func GetRemoteSignedCertificate(ctx context.Context, csr []byte, rootCAPool *x50
641 641
 			// current request.
642 642
 			if bytes.Equal(statusResponse.Certificate.CSR, csr) {
643 643
 				config.Remotes.Observe(peer, remotes.DefaultObservationWeight)
644
-				return statusResponse, nil
644
+				return statusResponse.Certificate.Certificate, nil
645 645
 			}
646 646
 		}
647 647
 
... ...
@@ -675,6 +640,17 @@ func readCertValidity(kr KeyReader) (time.Time, time.Time, error) {
675 675
 
676 676
 }
677 677
 
678
+func saveRootCA(rootCA RootCA, paths CertPaths) error {
679
+	// Make sure the necessary dirs exist and they are writable
680
+	err := os.MkdirAll(filepath.Dir(paths.Cert), 0755)
681
+	if err != nil {
682
+		return err
683
+	}
684
+
685
+	// If the root certificate got returned successfully, save the rootCA to disk.
686
+	return ioutils.AtomicWriteFile(paths.Cert, rootCA.Cert, 0644)
687
+}
688
+
678 689
 // GenerateNewCSR returns a newly generated key and CSR signed with said key
679 690
 func GenerateNewCSR() (csr, key []byte, err error) {
680 691
 	req := &cfcsr.CertificateRequest{
... ...
@@ -120,15 +120,8 @@ func (s *SecurityConfig) UpdateRootCA(cert, key []byte, certExpiry time.Duration
120 120
 	s.mu.Lock()
121 121
 	defer s.mu.Unlock()
122 122
 
123
-	// Create a new RootCA, keeping the path of the old RootCA
124
-	rootCA, err := NewRootCA(cert, key, s.rootCA.Path, certExpiry)
125
-	if err != nil {
126
-		return err
127
-	}
128
-	// Attempt to write the new certificate to disk
129
-	err = rootCA.saveCertificate()
123
+	rootCA, err := NewRootCA(cert, key, certExpiry)
130 124
 	if err == nil {
131
-		// No errors, save the current rootCA
132 125
 		s.rootCA = &rootCA
133 126
 	}
134 127
 
... ...
@@ -239,8 +232,7 @@ func DownloadRootCA(ctx context.Context, paths CertPaths, token string, r remote
239 239
 	}
240 240
 
241 241
 	// Save root CA certificate to disk
242
-	rootCA.Path = paths
243
-	if err = rootCA.saveCertificate(); err != nil {
242
+	if err = saveRootCA(rootCA, paths); err != nil {
244 243
 		return RootCA{}, err
245 244
 	}
246 245
 
... ...
@@ -462,6 +454,7 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, remotes remotes.Remo
462 462
 
463 463
 			// Since the expiration of the certificate is managed remotely we should update our
464 464
 			// retry timer on every iteration of this loop.
465
+			// Retrieve the current certificate expiration information.
465 466
 			validFrom, validUntil, err := readCertValidity(s.KeyReader())
466 467
 			if err != nil {
467 468
 				// We failed to read the expiration, let's stick with the starting default
... ...
@@ -142,9 +142,8 @@ func (s *Server) NodeCertificateStatus(ctx context.Context, request *api.NodeCer
142 142
 	// If this certificate has a final state, return it immediately (both pending and renew are transition states)
143 143
 	if isFinalState(node.Certificate.Status) {
144 144
 		return &api.NodeCertificateStatusResponse{
145
-			Status:       &node.Certificate.Status,
146
-			Certificate:  &node.Certificate,
147
-			RootCABundle: s.securityConfig.RootCA().Cert,
145
+			Status:      &node.Certificate.Status,
146
+			Certificate: &node.Certificate,
148 147
 		}, nil
149 148
 	}
150 149
 
... ...
@@ -165,9 +164,8 @@ func (s *Server) NodeCertificateStatus(ctx context.Context, request *api.NodeCer
165 165
 				if isFinalState(v.Node.Certificate.Status) {
166 166
 					cert := v.Node.Certificate.Copy()
167 167
 					return &api.NodeCertificateStatusResponse{
168
-						Status:       &cert.Status,
169
-						Certificate:  cert,
170
-						RootCABundle: s.securityConfig.RootCA().Cert,
168
+						Status:      &cert.Status,
169
+						Certificate: cert,
171 170
 					}, nil
172 171
 				}
173 172
 			}
... ...
@@ -746,7 +746,7 @@ func (na *NetworkAllocator) allocatePools(n *api.Network) (map[string]string, er
746 746
 	}
747 747
 
748 748
 	for i, ic := range ipamConfigs {
749
-		poolID, poolIP, _, err := ipam.RequestPool(asName, ic.Subnet, ic.Range, nil, false)
749
+		poolID, poolIP, _, err := ipam.RequestPool(asName, ic.Subnet, ic.Range, dOptions, false)
750 750
 		if err != nil {
751 751
 			// Rollback by releasing all the resources allocated so far.
752 752
 			releasePools(ipam, ipamConfigs[:i], pools)
... ...
@@ -2,6 +2,7 @@ package scheduler
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"strings"
5 6
 
6 7
 	"github.com/docker/swarmkit/api"
7 8
 	"github.com/docker/swarmkit/manager/constraint"
... ...
@@ -93,6 +94,15 @@ type PluginFilter struct {
93 93
 	t *api.Task
94 94
 }
95 95
 
96
+func referencesVolumePlugin(mount api.Mount) bool {
97
+	return mount.Type == api.MountTypeVolume &&
98
+		mount.VolumeOptions != nil &&
99
+		mount.VolumeOptions.DriverConfig != nil &&
100
+		mount.VolumeOptions.DriverConfig.Name != "" &&
101
+		mount.VolumeOptions.DriverConfig.Name != "local"
102
+
103
+}
104
+
96 105
 // SetTask returns true when the filter is enabled for a given task.
97 106
 func (f *PluginFilter) SetTask(t *api.Task) bool {
98 107
 	c := t.Spec.GetContainer()
... ...
@@ -100,12 +110,9 @@ func (f *PluginFilter) SetTask(t *api.Task) bool {
100 100
 	var volumeTemplates bool
101 101
 	if c != nil {
102 102
 		for _, mount := range c.Mounts {
103
-			if mount.Type == api.MountTypeVolume &&
104
-				mount.VolumeOptions != nil &&
105
-				mount.VolumeOptions.DriverConfig != nil &&
106
-				mount.VolumeOptions.DriverConfig.Name != "" &&
107
-				mount.VolumeOptions.DriverConfig.Name != "local" {
103
+			if referencesVolumePlugin(mount) {
108 104
 				volumeTemplates = true
105
+				break
109 106
 			}
110 107
 		}
111 108
 	}
... ...
@@ -128,7 +135,7 @@ func (f *PluginFilter) Check(n *NodeInfo) bool {
128 128
 	container := f.t.Spec.GetContainer()
129 129
 	if container != nil {
130 130
 		for _, mount := range container.Mounts {
131
-			if mount.VolumeOptions != nil && mount.VolumeOptions.DriverConfig != nil {
131
+			if referencesVolumePlugin(mount) {
132 132
 				if !f.pluginExistsOnNode("Volume", mount.VolumeOptions.DriverConfig.Name, nodePlugins) {
133 133
 					return false
134 134
 				}
... ...
@@ -138,16 +145,30 @@ func (f *PluginFilter) Check(n *NodeInfo) bool {
138 138
 
139 139
 	// Check if all network plugins required by task are installed on node
140 140
 	for _, tn := range f.t.Networks {
141
-		if !f.pluginExistsOnNode("Network", tn.Network.DriverState.Name, nodePlugins) {
142
-			return false
141
+		if tn.Network != nil && tn.Network.DriverState != nil && tn.Network.DriverState.Name != "" {
142
+			if !f.pluginExistsOnNode("Network", tn.Network.DriverState.Name, nodePlugins) {
143
+				return false
144
+			}
143 145
 		}
144 146
 	}
145 147
 	return true
146 148
 }
147 149
 
150
+// pluginExistsOnNode returns true if the (pluginName, pluginType) pair is present in nodePlugins
148 151
 func (f *PluginFilter) pluginExistsOnNode(pluginType string, pluginName string, nodePlugins []api.PluginDescription) bool {
149 152
 	for _, np := range nodePlugins {
150
-		if pluginType == np.Type && pluginName == np.Name {
153
+		if pluginType != np.Type {
154
+			continue
155
+		}
156
+		if pluginName == np.Name {
157
+			return true
158
+		}
159
+		// This does not use the reference package to avoid the
160
+		// overhead of parsing references as part of the scheduling
161
+		// loop. This is okay only because plugin names are a very
162
+		// strict subset of the reference grammar that is always
163
+		// name:tag.
164
+		if strings.HasPrefix(np.Name, pluginName) && np.Name[len(pluginName):] == ":latest" {
151 165
 			return true
152 166
 		}
153 167
 	}
... ...
@@ -11,12 +11,7 @@ var (
11 11
 		// Always check for readiness first.
12 12
 		&ReadyFilter{},
13 13
 		&ResourceFilter{},
14
-
15
-		// TODO(stevvooe): Do not filter based on plugins since they are lazy
16
-		// loaded in the engine. We can add this back when we can schedule
17
-		// plugins in the future.
18
-		// &PluginFilter{},
19
-
14
+		&PluginFilter{},
20 15
 		&ConstraintFilter{},
21 16
 	}
22 17
 )
... ...
@@ -1259,8 +1259,6 @@ func (n *Node) saveToStorage(
1259 1259
 	}
1260 1260
 
1261 1261
 	if err := n.raftLogger.SaveEntries(hardState, entries); err != nil {
1262
-		// TODO(aaronl): These error types should really wrap more
1263
-		// detailed errors.
1264 1262
 		return errors.Wrap(err, "failed to save raft log entries")
1265 1263
 	}
1266 1264
 
... ...
@@ -14,7 +14,7 @@ var (
14 14
 	// errInvalidName is a typed error returned when creating a volume with a name that is not valid on the platform
15 15
 	errInvalidName = errors.New("volume name is not valid on this platform")
16 16
 	// errNameConflict is a typed error returned on create when a volume exists with the given name, but for a different driver
17
-	errNameConflict = errors.New("conflict: volume name must be unique")
17
+	errNameConflict = errors.New("volume name must be unique")
18 18
 )
19 19
 
20 20
 // OpErr is the error type returned by functions in the store package. It describes
... ...
@@ -310,8 +310,17 @@ func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, err
310 310
 
311 311
 	vDriverName := v.DriverName()
312 312
 	var conflict bool
313
-	if driverName != "" && vDriverName != driverName {
314
-		conflict = true
313
+	if driverName != "" {
314
+		// Retrieve canonical driver name to avoid inconsistencies (for example
315
+		// "plugin" vs. "plugin:latest")
316
+		vd, err := volumedrivers.GetDriver(driverName)
317
+		if err != nil {
318
+			return nil, err
319
+		}
320
+
321
+		if vDriverName != vd.Name() {
322
+			conflict = true
323
+		}
315 324
 	}
316 325
 
317 326
 	// let's check if the found volume ref