|
@@ -1,7 +1,6 @@
|
|
package cluster
|
|
package cluster
|
|
|
|
|
|
import (
|
|
import (
|
|
- "crypto/x509"
|
|
|
|
"encoding/json"
|
|
"encoding/json"
|
|
"fmt"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"io/ioutil"
|
|
@@ -27,6 +26,7 @@ import (
|
|
"github.com/docker/docker/pkg/signal"
|
|
"github.com/docker/docker/pkg/signal"
|
|
"github.com/docker/docker/runconfig"
|
|
"github.com/docker/docker/runconfig"
|
|
swarmapi "github.com/docker/swarmkit/api"
|
|
swarmapi "github.com/docker/swarmkit/api"
|
|
|
|
+ "github.com/docker/swarmkit/manager/encryption"
|
|
swarmnode "github.com/docker/swarmkit/node"
|
|
swarmnode "github.com/docker/swarmkit/node"
|
|
"github.com/pkg/errors"
|
|
"github.com/pkg/errors"
|
|
"golang.org/x/net/context"
|
|
"golang.org/x/net/context"
|
|
@@ -140,6 +140,7 @@ type nodeStartConfig struct {
|
|
forceNewCluster bool
|
|
forceNewCluster bool
|
|
joinToken string
|
|
joinToken string
|
|
lockKey []byte
|
|
lockKey []byte
|
|
|
|
+ autolock bool
|
|
}
|
|
}
|
|
|
|
|
|
// New creates a new Cluster instance using provided config.
|
|
// New creates a new Cluster instance using provided config.
|
|
@@ -172,12 +173,6 @@ func New(config Config) (*Cluster, error) {
|
|
|
|
|
|
n, err := c.startNewNode(*nodeConfig)
|
|
n, err := c.startNewNode(*nodeConfig)
|
|
if err != nil {
|
|
if err != nil {
|
|
- if errors.Cause(err) == ErrSwarmLocked {
|
|
|
|
- logrus.Warnf("swarm component could not be started: %v", err)
|
|
|
|
- c.locked = true
|
|
|
|
- c.lastNodeConfig = nodeConfig
|
|
|
|
- return c, nil
|
|
|
|
- }
|
|
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
|
|
@@ -186,6 +181,10 @@ func New(config Config) (*Cluster, error) {
|
|
logrus.Error("swarm component could not be started before timeout was reached")
|
|
logrus.Error("swarm component could not be started before timeout was reached")
|
|
case <-n.Ready():
|
|
case <-n.Ready():
|
|
case <-n.done:
|
|
case <-n.done:
|
|
|
|
+ if errors.Cause(c.err) == ErrSwarmLocked {
|
|
|
|
+ return c, nil
|
|
|
|
+ }
|
|
|
|
+
|
|
return nil, fmt.Errorf("swarm component could not be started: %v", c.err)
|
|
return nil, fmt.Errorf("swarm component could not be started: %v", c.err)
|
|
}
|
|
}
|
|
go c.reconnectOnFailure(n)
|
|
go c.reconnectOnFailure(n)
|
|
@@ -314,15 +313,10 @@ func (c *Cluster) startNewNode(conf nodeStartConfig) (*node, error) {
|
|
HeartbeatTick: 1,
|
|
HeartbeatTick: 1,
|
|
ElectionTick: 3,
|
|
ElectionTick: 3,
|
|
UnlockKey: conf.lockKey,
|
|
UnlockKey: conf.lockKey,
|
|
|
|
+ AutoLockManagers: conf.autolock,
|
|
})
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
if err != nil {
|
|
- err = detectLockedError(err)
|
|
|
|
- if errors.Cause(err) == ErrSwarmLocked {
|
|
|
|
- c.locked = true
|
|
|
|
- confClone := conf
|
|
|
|
- c.lastNodeConfig = &confClone
|
|
|
|
- }
|
|
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
ctx := context.Background()
|
|
ctx := context.Background()
|
|
@@ -341,13 +335,18 @@ func (c *Cluster) startNewNode(conf nodeStartConfig) (*node, error) {
|
|
|
|
|
|
c.config.Backend.SetClusterProvider(c)
|
|
c.config.Backend.SetClusterProvider(c)
|
|
go func() {
|
|
go func() {
|
|
- err := n.Err(ctx)
|
|
|
|
|
|
+ err := detectLockedError(n.Err(ctx))
|
|
if err != nil {
|
|
if err != nil {
|
|
logrus.Errorf("cluster exited with error: %v", err)
|
|
logrus.Errorf("cluster exited with error: %v", err)
|
|
}
|
|
}
|
|
c.Lock()
|
|
c.Lock()
|
|
c.node = nil
|
|
c.node = nil
|
|
c.err = err
|
|
c.err = err
|
|
|
|
+ if errors.Cause(err) == ErrSwarmLocked {
|
|
|
|
+ c.locked = true
|
|
|
|
+ confClone := conf
|
|
|
|
+ c.lastNodeConfig = &confClone
|
|
|
|
+ }
|
|
c.Unlock()
|
|
c.Unlock()
|
|
close(node.done)
|
|
close(node.done)
|
|
}()
|
|
}()
|
|
@@ -443,18 +442,13 @@ func (c *Cluster) Init(req types.InitRequest) (string, error) {
|
|
localAddr = advertiseIP.String()
|
|
localAddr = advertiseIP.String()
|
|
}
|
|
}
|
|
|
|
|
|
- var key []byte
|
|
|
|
- if len(req.LockKey) > 0 {
|
|
|
|
- key = []byte(req.LockKey)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// todo: check current state existing
|
|
// todo: check current state existing
|
|
n, err := c.startNewNode(nodeStartConfig{
|
|
n, err := c.startNewNode(nodeStartConfig{
|
|
forceNewCluster: req.ForceNewCluster,
|
|
forceNewCluster: req.ForceNewCluster,
|
|
|
|
+ autolock: req.AutoLockManagers,
|
|
LocalAddr: localAddr,
|
|
LocalAddr: localAddr,
|
|
ListenAddr: net.JoinHostPort(listenHost, listenPort),
|
|
ListenAddr: net.JoinHostPort(listenHost, listenPort),
|
|
AdvertiseAddr: net.JoinHostPort(advertiseHost, advertisePort),
|
|
AdvertiseAddr: net.JoinHostPort(advertiseHost, advertisePort),
|
|
- lockKey: key,
|
|
|
|
})
|
|
})
|
|
if err != nil {
|
|
if err != nil {
|
|
c.Unlock()
|
|
c.Unlock()
|
|
@@ -569,8 +563,9 @@ func (c *Cluster) GetUnlockKey() (string, error) {
|
|
|
|
|
|
// UnlockSwarm provides a key to decrypt data that is encrypted at rest.
|
|
// UnlockSwarm provides a key to decrypt data that is encrypted at rest.
|
|
func (c *Cluster) UnlockSwarm(req types.UnlockRequest) error {
|
|
func (c *Cluster) UnlockSwarm(req types.UnlockRequest) error {
|
|
- if len(req.LockKey) == 0 {
|
|
|
|
- return errors.New("unlock key can't be empty")
|
|
|
|
|
|
+ key, err := encryption.ParseHumanReadableKey(req.UnlockKey)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
}
|
|
}
|
|
|
|
|
|
c.Lock()
|
|
c.Lock()
|
|
@@ -580,7 +575,7 @@ func (c *Cluster) UnlockSwarm(req types.UnlockRequest) error {
|
|
}
|
|
}
|
|
|
|
|
|
config := *c.lastNodeConfig
|
|
config := *c.lastNodeConfig
|
|
- config.lockKey = []byte(req.LockKey)
|
|
|
|
|
|
+ config.lockKey = key
|
|
n, err := c.startNewNode(config)
|
|
n, err := c.startNewNode(config)
|
|
if err != nil {
|
|
if err != nil {
|
|
c.Unlock()
|
|
c.Unlock()
|
|
@@ -779,9 +774,10 @@ func (c *Cluster) Update(version uint64, spec types.Spec, flags types.UpdateFlag
|
|
ClusterVersion: &swarmapi.Version{
|
|
ClusterVersion: &swarmapi.Version{
|
|
Index: version,
|
|
Index: version,
|
|
},
|
|
},
|
|
- Rotation: swarmapi.JoinTokenRotation{
|
|
|
|
- RotateWorkerToken: flags.RotateWorkerToken,
|
|
|
|
- RotateManagerToken: flags.RotateManagerToken,
|
|
|
|
|
|
+ Rotation: swarmapi.KeyRotation{
|
|
|
|
+ WorkerJoinToken: flags.RotateWorkerToken,
|
|
|
|
+ ManagerJoinToken: flags.RotateManagerToken,
|
|
|
|
+ ManagerUnlockKey: flags.RotateManagerUnlockKey,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
)
|
|
)
|
|
@@ -1708,7 +1704,7 @@ func initClusterSpec(node *node, spec types.Spec) error {
|
|
}
|
|
}
|
|
|
|
|
|
func detectLockedError(err error) error {
|
|
func detectLockedError(err error) error {
|
|
- if errors.Cause(err) == x509.IncorrectPasswordError || errors.Cause(err).Error() == "tls: failed to parse private key" { // todo: better to export typed error
|
|
|
|
|
|
+ if err == swarmnode.ErrInvalidUnlockKey {
|
|
return errors.WithStack(ErrSwarmLocked)
|
|
return errors.WithStack(ErrSwarmLocked)
|
|
}
|
|
}
|
|
return err
|
|
return err
|