Support v2 plugins in swarm mode
| ... | ... |
@@ -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 |