diff --git a/vendor.conf b/vendor.conf index 04984f11f3..b624fc0597 100644 --- a/vendor.conf +++ b/vendor.conf @@ -102,7 +102,7 @@ github.com/docker/containerd 78fb8f45890a601e0fd9051cf9f9f74923e950fd github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4 # cluster -github.com/docker/swarmkit 78ae345f449ac69aa741c762df7e5f0020f70275 +github.com/docker/swarmkit 3ca4775ba4a5519e2225c3337c7db8901ec39d26 github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9 github.com/gogo/protobuf 8d70fb3182befc465c4a1eac8ad4d38ff49778e2 github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a diff --git a/vendor/github.com/docker/swarmkit/agent/agent.go b/vendor/github.com/docker/swarmkit/agent/agent.go index d8cc595474..efaf7b1350 100644 --- a/vendor/github.com/docker/swarmkit/agent/agent.go +++ b/vendor/github.com/docker/swarmkit/agent/agent.go @@ -39,6 +39,7 @@ type Agent struct { ready chan struct{} leaving chan struct{} leaveOnce sync.Once + left chan struct{} // closed after "run" processes "leaving" and will no longer accept new assignments stopped chan struct{} // requests shutdown stopOnce sync.Once // only allow stop to be called once closed chan struct{} // only closed in run @@ -56,6 +57,7 @@ func New(config *Config) (*Agent, error) { sessionq: make(chan sessionOperation), started: make(chan struct{}), leaving: make(chan struct{}), + left: make(chan struct{}), stopped: make(chan struct{}), closed: make(chan struct{}), ready: make(chan struct{}), @@ -96,6 +98,16 @@ func (a *Agent) Leave(ctx context.Context) error { close(a.leaving) }) + // Do not call Wait until we have confirmed that the agent is no longer + // accepting assignments. Starting a worker might race with Wait. + select { + case <-a.left: + case <-a.closed: + return ErrClosed + case <-ctx.Done(): + return ctx.Err() + } + // agent could be closed while Leave is in progress var err error ch := make(chan struct{}) @@ -215,6 +227,8 @@ func (a *Agent) run(ctx context.Context) { if err := a.worker.Assign(ctx, nil); err != nil { log.G(ctx).WithError(err).Error("failed removing all assignments") } + + close(a.left) case msg := <-session.assignments: // if we have left, accept no more assignments if leaving == nil { diff --git a/vendor/github.com/docker/swarmkit/agent/exec/controller.go b/vendor/github.com/docker/swarmkit/agent/exec/controller.go index f8d000a233..1cafb47fd1 100644 --- a/vendor/github.com/docker/swarmkit/agent/exec/controller.go +++ b/vendor/github.com/docker/swarmkit/agent/exec/controller.go @@ -104,17 +104,21 @@ func Resolve(ctx context.Context, task *api.Task, executor Executor) (Controller // depending on the tasks state, a failed controller resolution has varying // impact. The following expresses that impact. - if task.Status.State < api.TaskStateStarting { - if err != nil { - // before the task has been started, we consider it a rejection. - status.Message = "resolving controller failed" - status.Err = err.Error() + if err != nil { + status.Message = "resolving controller failed" + status.Err = err.Error() + // before the task has been started, we consider it a rejection. + // if task is running, consider the task has failed + // otherwise keep the existing state + if task.Status.State < api.TaskStateStarting { status.State = api.TaskStateRejected - } else if task.Status.State < api.TaskStateAccepted { - // we always want to proceed to accepted when we resolve the contoller - status.Message = "accepted" - status.State = api.TaskStateAccepted + } else if task.Status.State <= api.TaskStateRunning { + status.State = api.TaskStateFailed } + } else if task.Status.State < api.TaskStateAccepted { + // we always want to proceed to accepted when we resolve the controller + status.Message = "accepted" + status.State = api.TaskStateAccepted } return ctlr, status, err diff --git a/vendor/github.com/docker/swarmkit/api/ca.proto b/vendor/github.com/docker/swarmkit/api/ca.proto index be4b4a58c9..84ec7d3f2d 100644 --- a/vendor/github.com/docker/swarmkit/api/ca.proto +++ b/vendor/github.com/docker/swarmkit/api/ca.proto @@ -30,7 +30,7 @@ service NodeCA { } message NodeCertificateStatusRequest { - string node_id = 1 [(gogoproto.customname) = "NodeID"]; + string node_id = 1; } message NodeCertificateStatusResponse { @@ -54,7 +54,7 @@ message IssueNodeCertificateRequest { } message IssueNodeCertificateResponse { - string node_id = 1 [(gogoproto.customname) = "NodeID"]; + string node_id = 1; NodeSpec.Membership node_membership = 2; } diff --git a/vendor/github.com/docker/swarmkit/api/control.proto b/vendor/github.com/docker/swarmkit/api/control.proto index e0b37a9156..cf46a37431 100644 --- a/vendor/github.com/docker/swarmkit/api/control.proto +++ b/vendor/github.com/docker/swarmkit/api/control.proto @@ -119,7 +119,7 @@ service Control { } message GetNodeRequest { - string node_id = 1 [(gogoproto.customname) = "NodeID"]; + string node_id = 1; } message GetNodeResponse { @@ -129,7 +129,7 @@ message GetNodeResponse { message ListNodesRequest { message Filters { repeated string names = 1; - repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; + repeated string id_prefixes = 2; map labels = 3; repeated NodeSpec.Membership memberships = 4; repeated NodeRole roles = 5; @@ -148,7 +148,7 @@ message ListNodesResponse { // to request a new availability for a node, such as PAUSE. Invalid updates // will be denied and cause an error. message UpdateNodeRequest { - string node_id = 1 [(gogoproto.customname) = "NodeID"]; + string node_id = 1; Version node_version = 2; NodeSpec spec = 3; } @@ -159,7 +159,7 @@ message UpdateNodeResponse { // RemoveNodeRequest requests to delete the specified node from store. message RemoveNodeRequest { - string node_id = 1 [(gogoproto.customname) = "NodeID"]; + string node_id = 1; bool force = 2; } @@ -167,7 +167,7 @@ message RemoveNodeResponse { } message GetTaskRequest { - string task_id = 1 [(gogoproto.customname) = "TaskID"]; + string task_id = 1; } message GetTaskResponse { @@ -175,7 +175,7 @@ message GetTaskResponse { } message RemoveTaskRequest { - string task_id = 1 [(gogoproto.customname) = "TaskID"]; + string task_id = 1; } message RemoveTaskResponse { @@ -184,10 +184,10 @@ message RemoveTaskResponse { message ListTasksRequest { message Filters { repeated string names = 1; - repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; + repeated string id_prefixes = 2; map labels = 3; - repeated string service_ids = 4 [(gogoproto.customname) = "ServiceIDs"]; - repeated string node_ids = 5 [(gogoproto.customname) = "NodeIDs"]; + repeated string service_ids = 4; + repeated string node_ids = 5; repeated docker.swarmkit.v1.TaskState desired_states = 6; // NamePrefixes matches all objects with the given prefixes repeated string name_prefixes = 7; @@ -209,7 +209,7 @@ message CreateServiceResponse { } message GetServiceRequest { - string service_id = 1 [(gogoproto.customname) = "ServiceID"]; + string service_id = 1; } message GetServiceResponse { @@ -217,7 +217,7 @@ message GetServiceResponse { } message UpdateServiceRequest { - string service_id = 1 [(gogoproto.customname) = "ServiceID"]; + string service_id = 1; Version service_version = 2; ServiceSpec spec = 3; } @@ -227,7 +227,7 @@ message UpdateServiceResponse { } message RemoveServiceRequest { - string service_id = 1 [(gogoproto.customname) = "ServiceID"]; + string service_id = 1; } message RemoveServiceResponse { @@ -236,7 +236,7 @@ message RemoveServiceResponse { message ListServicesRequest { message Filters { repeated string names = 1; - repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; + repeated string id_prefixes = 2; map labels = 3; // NamePrefixes matches all objects with the given prefixes repeated string name_prefixes = 4; @@ -259,7 +259,7 @@ message CreateNetworkResponse { message GetNetworkRequest { string name = 1; - string network_id = 2 [(gogoproto.customname) = "NetworkID"]; + string network_id = 2; } message GetNetworkResponse { @@ -268,7 +268,7 @@ message GetNetworkResponse { message RemoveNetworkRequest { string name = 1; - string network_id = 2 [(gogoproto.customname) = "NetworkID"]; + string network_id = 2; } message RemoveNetworkResponse {} @@ -276,7 +276,7 @@ message RemoveNetworkResponse {} message ListNetworksRequest { message Filters { repeated string names = 1; - repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; + repeated string id_prefixes = 2; map labels = 3; // NamePrefixes matches all objects with the given prefixes repeated string name_prefixes = 4; @@ -290,7 +290,7 @@ message ListNetworksResponse { } message GetClusterRequest { - string cluster_id = 1 [(gogoproto.customname) = "ClusterID"]; + string cluster_id = 1; } message GetClusterResponse { @@ -300,7 +300,7 @@ message GetClusterResponse { message ListClustersRequest { message Filters { repeated string names = 1; - repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; + repeated string id_prefixes = 2; map labels = 3; // NamePrefixes matches all objects with the given prefixes repeated string name_prefixes = 4; @@ -328,7 +328,7 @@ message KeyRotation { message UpdateClusterRequest { // ClusterID is the cluster ID to update. - string cluster_id = 1 [(gogoproto.customname) = "ClusterID"]; + string cluster_id = 1; // ClusterVersion is the version of the cluster being updated. Version cluster_version = 2; @@ -346,7 +346,7 @@ message UpdateClusterResponse { // GetSecretRequest is the request to get a `Secret` object given a secret id. message GetSecretRequest { - string secret_id = 1 [(gogoproto.customname) = "SecretID"]; + string secret_id = 1; } // GetSecretResponse contains the Secret corresponding to the id in @@ -358,7 +358,7 @@ message GetSecretResponse { message UpdateSecretRequest { // SecretID is the secret ID to update. - string secret_id = 1 [(gogoproto.customname) = "SecretID"]; + string secret_id = 1; // SecretVersion is the version of the secret being updated. Version secret_version = 2; @@ -378,7 +378,7 @@ message UpdateSecretResponse { message ListSecretsRequest { message Filters { repeated string names = 1; - repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"]; + repeated string id_prefixes = 2; map labels = 3; repeated string name_prefixes = 4; } @@ -410,7 +410,7 @@ message CreateSecretResponse { // RemoveSecretRequest contains the ID of the secret that should be removed. This // removes all versions of the secret. message RemoveSecretRequest { - string secret_id = 1 [(gogoproto.customname) = "SecretID"]; + string secret_id = 1; } // RemoveSecretResponse is an empty object indicating the successful removal of diff --git a/vendor/github.com/docker/swarmkit/api/dispatcher.proto b/vendor/github.com/docker/swarmkit/api/dispatcher.proto index d47566b186..ffa2464a48 100644 --- a/vendor/github.com/docker/swarmkit/api/dispatcher.proto +++ b/vendor/github.com/docker/swarmkit/api/dispatcher.proto @@ -66,7 +66,7 @@ message SessionRequest { // SessionID is empty or invalid, a new SessionID will be assigned. // // See SessionMessage.SessionID for details. - string session_id = 2 [(gogoproto.customname) = "SessionID"]; + string session_id = 2; } // SessionMessage instructs an agent on various actions as part of the current @@ -115,7 +115,7 @@ message SessionMessage { // We considered placing this field in a GRPC header. Because this is a // critical feature of the protocol, we thought it should be represented // directly in the RPC message set. - string session_id = 1 [(gogoproto.customname) = "SessionID"]; + string session_id = 1; // Node identifies the registering node. Node node = 2; @@ -130,7 +130,7 @@ message SessionMessage { // HeartbeatRequest provides identifying properties for a single heartbeat. message HeartbeatRequest { - string session_id = 1 [(gogoproto.customname) = "SessionID"]; + string session_id = 1; } message HeartbeatResponse { @@ -142,10 +142,10 @@ message HeartbeatResponse { message UpdateTaskStatusRequest { // Tasks should contain all statuses for running tasks. Only the status // field must be set. The spec is not required. - string session_id = 1 [(gogoproto.customname) = "SessionID"]; + string session_id = 1; message TaskStatusUpdate { - string task_id = 1 [(gogoproto.customname) = "TaskID"]; + string task_id = 1; TaskStatus status = 2; } @@ -157,7 +157,7 @@ message UpdateTaskStatusResponse{ } message TasksRequest { - string session_id = 1 [(gogoproto.customname) = "SessionID"]; + string session_id = 1; } message TasksMessage { @@ -167,7 +167,7 @@ message TasksMessage { } message AssignmentsRequest { - string session_id = 1 [(gogoproto.customname) = "SessionID"]; + string session_id = 1; } message Assignment { diff --git a/vendor/github.com/docker/swarmkit/api/logbroker.proto b/vendor/github.com/docker/swarmkit/api/logbroker.proto index d3885b8209..f9c1c92539 100644 --- a/vendor/github.com/docker/swarmkit/api/logbroker.proto +++ b/vendor/github.com/docker/swarmkit/api/logbroker.proto @@ -54,16 +54,16 @@ message LogSubscriptionOptions { // possible. For example, if they want to listen to all the tasks of a service, // they should use the service id, rather than specifying the individual tasks. message LogSelector { - repeated string service_ids = 1 [(gogoproto.customname) = "ServiceIDs"]; - repeated string node_ids = 2 [(gogoproto.customname) = "NodeIDs"]; - repeated string task_ids = 3 [(gogoproto.customname) = "TaskIDs"]; + repeated string service_ids = 1; + repeated string node_ids = 2; + repeated string task_ids = 3; } // LogContext marks the context from which a log message was generated. message LogContext { - string service_id = 1 [(gogoproto.customname) = "ServiceID"]; - string node_id = 2 [(gogoproto.customname) = "NodeID"]; - string task_id = 3 [(gogoproto.customname) = "TaskID"]; + string service_id = 1; + string node_id = 2; + string task_id = 3; } // LogMessage @@ -147,7 +147,7 @@ message ListenSubscriptionsRequest { } // If Options.Follow == false, the worker should end the subscription on its own. message SubscriptionMessage { // ID identifies the subscription. - string id = 1 [(gogoproto.customname) = "ID"]; + string id = 1; // Selector defines which sources should be sent for the subscription. LogSelector selector = 2; @@ -163,7 +163,7 @@ message SubscriptionMessage { message PublishLogsMessage { // SubscriptionID identifies which subscription the set of messages should // be sent to. We can think of this as a "mail box" for the subscription. - string subscription_id = 1 [(gogoproto.customname) = "SubscriptionID"]; + string subscription_id = 1; // Messages is the log message for publishing. repeated LogMessage messages = 2 [(gogoproto.nullable) = false]; diff --git a/vendor/github.com/docker/swarmkit/api/objects.proto b/vendor/github.com/docker/swarmkit/api/objects.proto index f056314082..36b7a4addb 100644 --- a/vendor/github.com/docker/swarmkit/api/objects.proto +++ b/vendor/github.com/docker/swarmkit/api/objects.proto @@ -25,7 +25,7 @@ message Meta { // Node provides the internal node state as seen by the cluster. message Node { // ID specifies the identity of the node. - string id = 1 [(gogoproto.customname) = "ID"]; + string id = 1; Meta meta = 2 [(gogoproto.nullable) = false]; @@ -63,7 +63,7 @@ message Node { } message Service { - string id = 1 [(gogoproto.customname) = "ID"]; + string id = 1; Meta meta = 2 [(gogoproto.nullable) = false]; @@ -101,7 +101,7 @@ message Endpoint { // and the IP addresses the target service will be made available under. message VirtualIP { // NetworkID for which this endpoint attachment was created. - string network_id = 1 [(gogoproto.customname) = "NetworkID"]; + string network_id = 1; // A virtual IP is used to address this service in IP // layer that the client can use to send requests to @@ -123,7 +123,7 @@ message Endpoint { // immutable and idempotent. Once it is dispatched to a node, it will not be // dispatched to another node. message Task { - string id = 1 [(gogoproto.customname) = "ID"]; + string id = 1; Meta meta = 2 [(gogoproto.nullable) = false]; @@ -133,7 +133,7 @@ message Task { // ServiceID indicates the service under which this task is orchestrated. This // should almost always be set. - string service_id = 4 [(gogoproto.customname) = "ServiceID"]; + string service_id = 4; // Slot is the service slot number for a task. // For example, if a replicated service has replicas = 2, there will be a @@ -142,7 +142,7 @@ message Task { // NodeID indicates the node to which the task is assigned. If this field // is empty or not set, the task is unassigned. - string node_id = 6 [(gogoproto.customname) = "NodeID"]; + string node_id = 6; // Annotations defines the names and labels for the runtime, as set by // the cluster manager. @@ -204,7 +204,7 @@ message NetworkAttachment { } message Network { - string id = 1 [(gogoproto.customname) = "ID"]; + string id = 1; Meta meta = 2 [(gogoproto.nullable) = false]; @@ -220,7 +220,7 @@ message Network { // Cluster provides global cluster settings. message Cluster { - string id = 1 [(gogoproto.customname) = "ID"]; + string id = 1; Meta meta = 2 [(gogoproto.nullable) = false]; @@ -256,7 +256,7 @@ message Cluster { // information that is generated from the secret data in the `spec`, such as // the digest and size of the secret data. message Secret { - string id = 1 [(gogoproto.customname) = "ID"]; + string id = 1; Meta meta = 2 [(gogoproto.nullable) = false]; diff --git a/vendor/github.com/docker/swarmkit/api/raft.proto b/vendor/github.com/docker/swarmkit/api/raft.proto index e1d9c19568..5f75c679cd 100644 --- a/vendor/github.com/docker/swarmkit/api/raft.proto +++ b/vendor/github.com/docker/swarmkit/api/raft.proto @@ -40,10 +40,10 @@ service RaftMembership { message RaftMember { // RaftID specifies the internal ID used by the manager in a raft context, it can never be modified // and is used only for information purposes - uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"]; + uint64 raft_id = 1; // NodeID is the node's ID. - string node_id = 2 [(gogoproto.customname) = "NodeID"]; + string node_id = 2; // Addr specifies the address of the member string addr = 3; @@ -59,7 +59,7 @@ message JoinRequest { message JoinResponse { // RaftID is the ID assigned to the new member. - uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"]; + uint64 raft_id = 1; // Members is the membership set of the cluster. repeated RaftMember members = 2; @@ -84,7 +84,7 @@ message ProcessRaftMessageResponse {} message ResolveAddressRequest { // raft_id is the ID to resolve to an address. - uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"]; + uint64 raft_id = 1; } message ResolveAddressResponse { @@ -96,7 +96,7 @@ message ResolveAddressResponse { // over the raft backend with a request ID to track when the // action is effectively applied message InternalRaftRequest { - uint64 id = 1 [(gogoproto.customname) = "ID"]; + uint64 id = 1; repeated StoreAction action = 2; } diff --git a/vendor/github.com/docker/swarmkit/api/resource.proto b/vendor/github.com/docker/swarmkit/api/resource.proto index 402777fb22..92a8302f46 100644 --- a/vendor/github.com/docker/swarmkit/api/resource.proto +++ b/vendor/github.com/docker/swarmkit/api/resource.proto @@ -20,15 +20,15 @@ service ResourceAllocator { message AttachNetworkRequest { NetworkAttachmentConfig config = 1; - string container_id = 2 [(gogoproto.customname) = "ContainerID"]; + string container_id = 2; } message AttachNetworkResponse { - string attachment_id = 1 [(gogoproto.customname) = "AttachmentID"]; + string attachment_id = 1; } message DetachNetworkRequest { - string attachment_id = 1 [(gogoproto.customname) = "AttachmentID"]; + string attachment_id = 1; } message DetachNetworkResponse {} diff --git a/vendor/github.com/docker/swarmkit/api/specs.proto b/vendor/github.com/docker/swarmkit/api/specs.proto index c51c8a2f06..a71ffa112d 100644 --- a/vendor/github.com/docker/swarmkit/api/specs.proto +++ b/vendor/github.com/docker/swarmkit/api/specs.proto @@ -130,7 +130,7 @@ message TaskSpec { message NetworkAttachmentSpec { // ContainerID spcifies a unique ID of the container for which // this attachment is for. - string container_id = 1 [(gogoproto.customname) = "ContainerID"]; + string container_id = 1; } diff --git a/vendor/github.com/docker/swarmkit/api/types.proto b/vendor/github.com/docker/swarmkit/api/types.proto index af22600c00..fd7d6253e5 100644 --- a/vendor/github.com/docker/swarmkit/api/types.proto +++ b/vendor/github.com/docker/swarmkit/api/types.proto @@ -410,7 +410,7 @@ enum TaskState { // Container specific status. message ContainerStatus { - string container_id = 1 [(gogoproto.customname) = "ContainerID"]; + string container_id = 1; int32 pid = 2 [(gogoproto.customname) = "PID"]; int32 exit_code = 3; @@ -574,7 +574,7 @@ message IPAMOptions { // Peer should be used anywhere where we are describing a remote peer. message Peer { - string node_id = 1 [(gogoproto.customname) = "NodeID"]; + string node_id = 1; string addr = 2; } @@ -787,7 +787,7 @@ message EncryptionKey { message ManagerStatus { // RaftID specifies the internal ID used by the manager in a raft context, it can never be modified // and is used only for information purposes - uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"]; + uint64 raft_id = 1; // Addr is the address advertised to raft. string addr = 2; @@ -805,7 +805,7 @@ message SecretReference { // SecretID represents the ID of the specific Secret that we're // referencing. This identifier exists so that SecretReferences don't leak // any information about the secret contents. - string secret_id = 1 [(gogoproto.customname) = "SecretID"]; + string secret_id = 1; // SecretName is the name of the secret that this references, but this is just provided for // lookup/display purposes. The secret in the reference will be identified by its ID. diff --git a/vendor/github.com/docker/swarmkit/ca/certificates.go b/vendor/github.com/docker/swarmkit/ca/certificates.go index 7258e30593..63fa323fb5 100644 --- a/vendor/github.com/docker/swarmkit/ca/certificates.go +++ b/vendor/github.com/docker/swarmkit/ca/certificates.go @@ -151,6 +151,25 @@ func (rca *RootCA) IssueAndSaveNewCertificates(kw KeyWriter, cn, ou, org string) return &tlsKeyPair, nil } +// Normally we can just call cert.Verify(opts), but since we actually want more information about +// whether a certificate is not yet valid or expired, we also need to perform the expiry checks ourselves. +func verifyCertificate(cert *x509.Certificate, opts x509.VerifyOptions, allowExpired bool) error { + _, err := cert.Verify(opts) + if invalidErr, ok := err.(x509.CertificateInvalidError); ok && invalidErr.Reason == x509.Expired { + now := time.Now().UTC() + if now.Before(cert.NotBefore) { + return errors.Wrapf(err, "certificate not valid before %s, and it is currently %s", + cert.NotBefore.UTC().Format(time.RFC1123), now.Format(time.RFC1123)) + } + if allowExpired { + return nil + } + return errors.Wrapf(err, "certificate expires at %s, and it is currently %s", + cert.NotAfter.UTC().Format(time.RFC1123), now.Format(time.RFC1123)) + } + return err +} + // RequestAndSaveNewCertificates gets new certificates issued, either by signing them locally if a signer is // available, or by requesting them from the remote server at remoteAddr. func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWriter, config CertificateRequestConfig) (*tls.Certificate, error) { @@ -199,7 +218,7 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWrit Roots: rca.Pool, } // Check to see if this certificate was signed by our CA, and isn't expired - if _, err := X509Cert.Verify(opts); err != nil { + if err := verifyCertificate(X509Cert, opts, false); err != nil { return nil, err } diff --git a/vendor/github.com/docker/swarmkit/ca/config.go b/vendor/github.com/docker/swarmkit/ca/config.go index d2664bd635..1d0e528559 100644 --- a/vendor/github.com/docker/swarmkit/ca/config.go +++ b/vendor/github.com/docker/swarmkit/ca/config.go @@ -15,6 +15,7 @@ import ( "github.com/Sirupsen/logrus" cfconfig "github.com/cloudflare/cfssl/config" + events "github.com/docker/go-events" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/connectionbroker" "github.com/docker/swarmkit/identity" @@ -50,6 +51,13 @@ const ( base36DigestLen = 50 ) +// RenewTLSExponentialBackoff sets the exponential backoff when trying to renew TLS certificates that have expired +var RenewTLSExponentialBackoff = events.ExponentialBackoffConfig{ + Base: time.Second * 5, + Factor: time.Minute, + Max: 1 * time.Hour, +} + // SecurityConfig is used to represent a node's security configuration. It includes information about // the RootCA and ServerTLSCreds/ClientTLSCreds transport authenticators to be used for MTLS type SecurityConfig struct { @@ -189,7 +197,7 @@ func GenerateJoinToken(rootCA *RootCA) string { func getCAHashFromToken(token string) (digest.Digest, error) { split := strings.Split(token, "-") - if len(split) != 4 || split[0] != "SWMTKN" || split[1] != "1" { + if len(split) != 4 || split[0] != "SWMTKN" || split[1] != "1" || len(split[2]) != base36DigestLen || len(split[3]) != maxGeneratedSecretLength { return "", errors.New("invalid join token") } @@ -242,7 +250,7 @@ func DownloadRootCA(ctx context.Context, paths CertPaths, token string, connBrok // LoadSecurityConfig loads TLS credentials from disk, or returns an error if // these credentials do not exist or are unusable. -func LoadSecurityConfig(ctx context.Context, rootCA RootCA, krw *KeyReadWriter) (*SecurityConfig, error) { +func LoadSecurityConfig(ctx context.Context, rootCA RootCA, krw *KeyReadWriter, allowExpired bool) (*SecurityConfig, error) { ctx = log.WithModule(ctx, "tls") // At this point we've successfully loaded the CA details from disk, or @@ -273,7 +281,7 @@ func LoadSecurityConfig(ctx context.Context, rootCA RootCA, krw *KeyReadWriter) } // Check to see if this certificate was signed by our CA, and isn't expired - if _, err := X509Cert.Verify(opts); err != nil { + if err := verifyCertificate(X509Cert, opts, allowExpired); err != nil { return nil, err } @@ -447,6 +455,7 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti go func() { var retry time.Duration + expBackoff := events.NewExponentialBackoff(RenewTLSExponentialBackoff) defer close(updates) for { ctx = log.WithModule(ctx, "tls") @@ -472,18 +481,12 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti return } } else { - // If we have an expired certificate, we let's stick with the starting default in - // the hope that this is a temporary clock skew. + // If we have an expired certificate, try to renew immediately: the hope that this is a temporary clock skew, or + // we can issue our own TLS certs. if validUntil.Before(time.Now()) { - log.WithError(err).Errorf("failed to create a new client TLS config") - - select { - case updates <- CertificateUpdate{Err: errors.New("TLS certificate is expired")}: - case <-ctx.Done(): - log.Info("shutting down certificate renewal routine") - return - } - + log.Warn("the current TLS certificate is expired, so an attempt to renew it will be made immediately") + // retry immediately(ish) with exponential backoff + retry = expBackoff.Proceed(nil) } else { // Random retry time between 50% and 80% of the total time to expiration retry = calculateRandomExpiry(validFrom, validUntil) @@ -492,7 +495,7 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti log.WithFields(logrus.Fields{ "time": time.Now().Add(retry), - }).Debugf("next certificate renewal scheduled") + }).Debugf("next certificate renewal scheduled for %v from now", retry) select { case <-time.After(retry): @@ -508,8 +511,10 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti var certUpdate CertificateUpdate if err := RenewTLSConfigNow(ctx, s, connBroker); err != nil { certUpdate.Err = err + expBackoff.Failure(nil, nil) } else { certUpdate.Role = s.ClientTLSCreds.Role() + expBackoff = events.NewExponentialBackoff(RenewTLSExponentialBackoff) } select { diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/service.go b/vendor/github.com/docker/swarmkit/manager/controlapi/service.go index 892e5086d3..80d8b897ab 100644 --- a/vendor/github.com/docker/swarmkit/manager/controlapi/service.go +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/service.go @@ -110,7 +110,7 @@ func validateContainerSpec(container *api.ContainerSpec) error { return grpc.Errorf(codes.InvalidArgument, "ContainerSpec: image reference must be provided") } - if _, err := reference.ParseNamed(container.Image); err != nil { + if _, err := reference.ParseNormalizedNamed(container.Image); err != nil { return grpc.Errorf(codes.InvalidArgument, "ContainerSpec: %q is not a valid repository/tag", container.Image) } @@ -275,6 +275,21 @@ func (s *Server) validateNetworks(networks []*api.NetworkAttachmentConfig) error return nil } +func validateMode(s *api.ServiceSpec) error { + m := s.GetMode() + switch m.(type) { + case *api.ServiceSpec_Replicated: + if int64(m.(*api.ServiceSpec_Replicated).Replicated.Replicas) < 0 { + return grpc.Errorf(codes.InvalidArgument, "Number of replicas must be non-negative") + } + case *api.ServiceSpec_Global: + default: + return grpc.Errorf(codes.InvalidArgument, "Unrecognized service mode") + } + + return nil +} + func validateServiceSpec(spec *api.ServiceSpec) error { if spec == nil { return grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error()) @@ -291,6 +306,9 @@ func validateServiceSpec(spec *api.ServiceSpec) error { if err := validateEndpointSpec(spec.Endpoint); err != nil { return err } + if err := validateMode(spec); err != nil { + return err + } // Check to see if the Secret Reference portion of the spec is valid if err := validateSecretRefsSpec(spec); err != nil { return err diff --git a/vendor/github.com/docker/swarmkit/manager/logbroker/subscription.go b/vendor/github.com/docker/swarmkit/manager/logbroker/subscription.go index 6b3295ae62..3beeb2aaf0 100644 --- a/vendor/github.com/docker/swarmkit/manager/logbroker/subscription.go +++ b/vendor/github.com/docker/swarmkit/manager/logbroker/subscription.go @@ -1,7 +1,6 @@ package logbroker import ( - "context" "fmt" "strings" "sync" @@ -12,6 +11,7 @@ import ( "github.com/docker/swarmkit/manager/state" "github.com/docker/swarmkit/manager/state/store" "github.com/docker/swarmkit/watch" + "golang.org/x/net/context" ) type subscription struct { diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/services.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/services.go index eee840c814..5dabd311ea 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/services.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/services.go @@ -102,15 +102,15 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) { } deploy := service.Spec.GetMode().(*api.ServiceSpec_Replicated) - specifiedSlots := int(deploy.Replicated.Replicas) + specifiedSlots := deploy.Replicated.Replicas switch { - case specifiedSlots > numSlots: + case specifiedSlots > uint64(numSlots): log.G(ctx).Debugf("Service %s was scaled up from %d to %d instances", service.ID, numSlots, specifiedSlots) // Update all current tasks then add missing tasks r.updater.Update(ctx, r.cluster, service, slotsSlice) _, err = r.store.Batch(func(batch *store.Batch) error { - r.addTasks(ctx, batch, service, runningSlots, deadSlots, specifiedSlots-numSlots) + r.addTasks(ctx, batch, service, runningSlots, deadSlots, specifiedSlots-uint64(numSlots)) r.deleteTasksMap(ctx, batch, deadSlots) return nil }) @@ -118,7 +118,7 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) { log.G(ctx).WithError(err).Errorf("reconcile batch failed") } - case specifiedSlots < numSlots: + case specifiedSlots < uint64(numSlots): // Update up to N tasks then remove the extra log.G(ctx).Debugf("Service %s was scaled down from %d to %d instances", service.ID, numSlots, specifiedSlots) @@ -165,7 +165,7 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) { log.G(ctx).WithError(err).Errorf("reconcile batch failed") } - case specifiedSlots == numSlots: + case specifiedSlots == uint64(numSlots): _, err = r.store.Batch(func(batch *store.Batch) error { r.deleteTasksMap(ctx, batch, deadSlots) return nil @@ -178,9 +178,9 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) { } } -func (r *Orchestrator) addTasks(ctx context.Context, batch *store.Batch, service *api.Service, runningSlots map[uint64]orchestrator.Slot, deadSlots map[uint64]orchestrator.Slot, count int) { +func (r *Orchestrator) addTasks(ctx context.Context, batch *store.Batch, service *api.Service, runningSlots map[uint64]orchestrator.Slot, deadSlots map[uint64]orchestrator.Slot, count uint64) { slot := uint64(0) - for i := 0; i < count; i++ { + for i := uint64(0); i < count; i++ { // Find a slot number that is missing a running task for { slot++ diff --git a/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go b/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go index 688d8321a9..ba77ae0ef6 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go +++ b/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go @@ -1193,9 +1193,13 @@ func (n *Node) ProcessRaftMessage(ctx context.Context, msg *api.ProcessRaftMessa ctx, cancel := n.WithContext(ctx) defer cancel() - if err := n.reportNewAddress(ctx, msg.Message.From); err != nil { - log.G(ctx).WithError(err).Errorf("failed to report new address of %x to transport", msg.Message.From) - } + // TODO(aaronl): Address changes are temporarily disabled. + // See https://github.com/docker/docker/issues/30455. + // This should be reenabled in the future with additional + // safeguards (perhaps storing multiple addresses per node). + //if err := n.reportNewAddress(ctx, msg.Message.From); err != nil { + // log.G(ctx).WithError(err).Errorf("failed to report new address of %x to transport", msg.Message.From) + //} // Reject vote requests from unreachable peers if msg.Message.Type == raftpb.MsgVote { diff --git a/vendor/github.com/docker/swarmkit/manager/state/raft/storage/walwrap.go b/vendor/github.com/docker/swarmkit/manager/state/raft/storage/walwrap.go index 5a6c71ae61..0610c0deba 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/raft/storage/walwrap.go +++ b/vendor/github.com/docker/swarmkit/manager/state/raft/storage/walwrap.go @@ -1,7 +1,6 @@ package storage import ( - "context" "io" "io/ioutil" "os" @@ -15,6 +14,7 @@ import ( "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/encryption" "github.com/pkg/errors" + "golang.org/x/net/context" ) // This package wraps the github.com/coreos/etcd/wal package, and encrypts diff --git a/vendor/github.com/docker/swarmkit/node/node.go b/vendor/github.com/docker/swarmkit/node/node.go index d36d90a5a3..45b178b4dc 100644 --- a/vendor/github.com/docker/swarmkit/node/node.go +++ b/vendor/github.com/docker/swarmkit/node/node.go @@ -568,7 +568,8 @@ func (n *Node) loadSecurityConfig(ctx context.Context) (*ca.SecurityConfig, erro return nil, err } if err == nil { - securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw) + // if forcing a new cluster, we allow the certificates to be expired - a new set will be generated + securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw, n.config.ForceNewCluster) if err != nil { _, isInvalidKEK := errors.Cause(err).(ca.ErrInvalidKEK) if isInvalidKEK { @@ -606,7 +607,7 @@ func (n *Node) loadSecurityConfig(ctx context.Context) (*ca.SecurityConfig, erro // - We wait for CreateSecurityConfig to finish since we need a certificate to operate. // Attempt to load certificate from disk - securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw) + securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw, n.config.ForceNewCluster) if err == nil { log.G(ctx).WithFields(logrus.Fields{ "node.id": securityConfig.ClientTLSCreds.NodeID(),