2016-06-07 21:28:28 +00:00
package controlapi
import (
2018-10-11 21:03:18 +00:00
"context"
2016-10-20 18:26:04 +00:00
"crypto/x509"
"encoding/pem"
2016-06-07 21:28:28 +00:00
"github.com/docker/swarmkit/api"
2016-07-07 11:58:43 +00:00
"github.com/docker/swarmkit/manager/state/raft/membership"
2016-06-07 21:28:28 +00:00
"github.com/docker/swarmkit/manager/state/store"
2017-01-23 23:50:10 +00:00
gogotypes "github.com/gogo/protobuf/types"
2016-06-07 21:28:28 +00:00
"google.golang.org/grpc/codes"
2017-12-05 00:38:37 +00:00
"google.golang.org/grpc/status"
2016-06-07 21:28:28 +00:00
)
func validateNodeSpec ( spec * api . NodeSpec ) error {
if spec == nil {
2017-12-05 00:38:37 +00:00
return status . Errorf ( codes . InvalidArgument , errInvalidArgument . Error ( ) )
2016-06-07 21:28:28 +00:00
}
return nil
}
// GetNode returns a Node given a NodeID.
// - Returns `InvalidArgument` if NodeID is not provided.
// - Returns `NotFound` if the Node is not found.
func ( s * Server ) GetNode ( ctx context . Context , request * api . GetNodeRequest ) ( * api . GetNodeResponse , error ) {
if request . NodeID == "" {
2017-12-05 00:38:37 +00:00
return nil , status . Errorf ( codes . InvalidArgument , errInvalidArgument . Error ( ) )
2016-06-07 21:28:28 +00:00
}
var node * api . Node
s . store . View ( func ( tx store . ReadTx ) {
node = store . GetNode ( tx , request . NodeID )
} )
if node == nil {
2017-12-05 00:38:37 +00:00
return nil , status . Errorf ( codes . NotFound , "node %s not found" , request . NodeID )
2016-06-07 21:28:28 +00:00
}
if s . raft != nil {
memberlist := s . raft . GetMemberlist ( )
2016-06-15 00:07:14 +00:00
for _ , member := range memberlist {
if member . NodeID == node . ID {
node . ManagerStatus = & api . ManagerStatus {
RaftID : member . RaftID ,
Addr : member . Addr ,
Leader : member . Status . Leader ,
Reachability : member . Status . Reachability ,
}
break
}
2016-06-07 21:28:28 +00:00
}
}
return & api . GetNodeResponse {
Node : node ,
} , nil
}
func filterNodes ( candidates [ ] * api . Node , filters ... func ( * api . Node ) bool ) [ ] * api . Node {
result := [ ] * api . Node { }
for _ , c := range candidates {
match := true
for _ , f := range filters {
if ! f ( c ) {
match = false
break
}
}
if match {
result = append ( result , c )
}
}
return result
}
// ListNodes returns a list of all nodes.
func ( s * Server ) ListNodes ( ctx context . Context , request * api . ListNodesRequest ) ( * api . ListNodesResponse , error ) {
var (
nodes [ ] * api . Node
err error
)
s . store . View ( func ( tx store . ReadTx ) {
switch {
case request . Filters != nil && len ( request . Filters . Names ) > 0 :
nodes , err = store . FindNodes ( tx , buildFilters ( store . ByName , request . Filters . Names ) )
2016-07-20 15:16:54 +00:00
case request . Filters != nil && len ( request . Filters . NamePrefixes ) > 0 :
nodes , err = store . FindNodes ( tx , buildFilters ( store . ByNamePrefix , request . Filters . NamePrefixes ) )
2016-06-07 21:28:28 +00:00
case request . Filters != nil && len ( request . Filters . IDPrefixes ) > 0 :
nodes , err = store . FindNodes ( tx , buildFilters ( store . ByIDPrefix , request . Filters . IDPrefixes ) )
case request . Filters != nil && len ( request . Filters . Roles ) > 0 :
filters := make ( [ ] store . By , 0 , len ( request . Filters . Roles ) )
for _ , v := range request . Filters . Roles {
filters = append ( filters , store . ByRole ( v ) )
}
nodes , err = store . FindNodes ( tx , store . Or ( filters ... ) )
case request . Filters != nil && len ( request . Filters . Memberships ) > 0 :
filters := make ( [ ] store . By , 0 , len ( request . Filters . Memberships ) )
for _ , v := range request . Filters . Memberships {
filters = append ( filters , store . ByMembership ( v ) )
}
nodes , err = store . FindNodes ( tx , store . Or ( filters ... ) )
default :
nodes , err = store . FindNodes ( tx , store . All )
}
} )
if err != nil {
return nil , err
}
if request . Filters != nil {
nodes = filterNodes ( nodes ,
func ( e * api . Node ) bool {
if len ( request . Filters . Names ) == 0 {
return true
}
if e . Description == nil {
return false
}
return filterContains ( e . Description . Hostname , request . Filters . Names )
} ,
2016-07-20 15:16:54 +00:00
func ( e * api . Node ) bool {
if len ( request . Filters . NamePrefixes ) == 0 {
return true
}
if e . Description == nil {
return false
}
return filterContainsPrefix ( e . Description . Hostname , request . Filters . NamePrefixes )
} ,
2016-06-07 21:28:28 +00:00
func ( e * api . Node ) bool {
return filterContainsPrefix ( e . ID , request . Filters . IDPrefixes )
} ,
func ( e * api . Node ) bool {
if len ( request . Filters . Labels ) == 0 {
return true
}
if e . Description == nil {
return false
}
return filterMatchLabels ( e . Description . Engine . Labels , request . Filters . Labels )
} ,
2018-07-30 15:25:02 +00:00
func ( e * api . Node ) bool {
if len ( request . Filters . NodeLabels ) == 0 {
return true
}
return filterMatchLabels ( e . Spec . Annotations . Labels , request . Filters . NodeLabels )
} ,
2016-06-07 21:28:28 +00:00
func ( e * api . Node ) bool {
if len ( request . Filters . Roles ) == 0 {
return true
}
for _ , c := range request . Filters . Roles {
2017-01-07 01:30:33 +00:00
if c == e . Role {
2016-06-07 21:28:28 +00:00
return true
}
}
return false
} ,
func ( e * api . Node ) bool {
if len ( request . Filters . Memberships ) == 0 {
return true
}
for _ , c := range request . Filters . Memberships {
if c == e . Spec . Membership {
return true
}
}
return false
} ,
)
}
// Add in manager information on nodes that are managers
if s . raft != nil {
memberlist := s . raft . GetMemberlist ( )
2016-06-15 00:07:14 +00:00
for _ , node := range nodes {
for _ , member := range memberlist {
if member . NodeID == node . ID {
node . ManagerStatus = & api . ManagerStatus {
RaftID : member . RaftID ,
Addr : member . Addr ,
Leader : member . Status . Leader ,
Reachability : member . Status . Reachability ,
}
break
}
2016-06-07 21:28:28 +00:00
}
}
}
return & api . ListNodesResponse {
Nodes : nodes ,
} , nil
}
// UpdateNode updates a Node referenced by NodeID with the given NodeSpec.
// - Returns `NotFound` if the Node is not found.
// - Returns `InvalidArgument` if the NodeSpec is malformed.
// - Returns an error if the update fails.
func ( s * Server ) UpdateNode ( ctx context . Context , request * api . UpdateNodeRequest ) ( * api . UpdateNodeResponse , error ) {
if request . NodeID == "" || request . NodeVersion == nil {
2017-12-05 00:38:37 +00:00
return nil , status . Errorf ( codes . InvalidArgument , errInvalidArgument . Error ( ) )
2016-06-07 21:28:28 +00:00
}
if err := validateNodeSpec ( request . Spec ) ; err != nil {
return nil , err
}
2016-06-15 00:07:14 +00:00
var (
node * api . Node
2016-07-07 11:58:43 +00:00
member * membership . Member
2016-06-15 00:07:14 +00:00
)
2016-07-07 11:58:43 +00:00
2016-06-07 21:28:28 +00:00
err := s . store . Update ( func ( tx store . Tx ) error {
node = store . GetNode ( tx , request . NodeID )
if node == nil {
2017-12-05 00:38:37 +00:00
return status . Errorf ( codes . NotFound , "node %s not found" , request . NodeID )
2016-06-07 21:28:28 +00:00
}
// Demotion sanity checks.
2017-01-07 01:30:33 +00:00
if node . Spec . DesiredRole == api . NodeRoleManager && request . Spec . DesiredRole == api . NodeRoleWorker {
2016-07-07 11:58:43 +00:00
// Check for manager entries in Store.
2016-06-07 21:28:28 +00:00
managers , err := store . FindNodes ( tx , store . ByRole ( api . NodeRoleManager ) )
if err != nil {
2017-12-05 00:38:37 +00:00
return status . Errorf ( codes . Internal , "internal store error: %v" , err )
2016-06-07 21:28:28 +00:00
}
if len ( managers ) == 1 && managers [ 0 ] . ID == node . ID {
2017-12-05 00:38:37 +00:00
return status . Errorf ( codes . FailedPrecondition , "attempting to demote the last manager of the swarm" )
2016-06-07 21:28:28 +00:00
}
2016-07-07 11:58:43 +00:00
// Check for node in memberlist
if member = s . raft . GetMemberByNodeID ( request . NodeID ) ; member == nil {
2017-12-05 00:38:37 +00:00
return status . Errorf ( codes . NotFound , "can't find manager in raft memberlist" )
2016-07-07 11:58:43 +00:00
}
// Quorum safeguard
if ! s . raft . CanRemoveMember ( member . RaftID ) {
2017-12-05 00:38:37 +00:00
return status . Errorf ( codes . FailedPrecondition , "can't remove member from the raft: this would result in a loss of quorum" )
2016-07-07 11:58:43 +00:00
}
2016-06-07 21:28:28 +00:00
}
node . Meta . Version = * request . NodeVersion
node . Spec = * request . Spec . Copy ( )
return store . UpdateNode ( tx , node )
} )
if err != nil {
return nil , err
}
2016-06-15 00:07:14 +00:00
2016-06-07 21:28:28 +00:00
return & api . UpdateNodeResponse {
Node : node ,
} , nil
}
2019-03-26 13:57:19 +00:00
func orphanNodeTasks ( tx store . Tx , nodeID string ) error {
// when a node is deleted, all of its tasks are irrecoverably removed.
// additionally, the Dispatcher can no longer be relied on to update the
// task status. Therefore, when the node is removed, we must additionally
// move all of its assigned tasks to the Orphaned state, so that their
// resources can be cleaned up.
2017-10-28 14:23:03 +00:00
tasks , err := store . FindTasks ( tx , store . ByNodeID ( nodeID ) )
if err != nil {
return err
}
for _ , task := range tasks {
2019-12-13 18:16:44 +00:00
// this operation must occur within the same transaction boundary. If
// we cannot accomplish this task orphaning in the same transaction, we
// could crash or die between transactions and not get a chance to do
// this. however, in cases were there is an exceptionally large number
// of tasks for a node, this may cause the transaction to exceed the
// max message size.
//
// therefore, we restrict updating to only tasks in a non-terminal
// state. Tasks in a terminal state do not need to be updated.
if task . Status . State < api . TaskStateCompleted {
task . Status = api . TaskStatus {
Timestamp : gogotypes . TimestampNow ( ) ,
State : api . TaskStateOrphaned ,
Message : "Task belonged to a node that has been deleted" ,
}
store . UpdateTask ( tx , task )
2017-10-28 14:23:03 +00:00
}
}
return nil
}
2016-07-20 15:16:54 +00:00
// RemoveNode removes a Node referenced by NodeID with the given NodeSpec.
2016-06-07 21:28:28 +00:00
// - Returns NotFound if the Node is not found.
2016-07-20 15:16:54 +00:00
// - Returns FailedPrecondition if the Node has manager role (and is part of the memberlist) or is not shut down.
2016-06-07 21:28:28 +00:00
// - Returns InvalidArgument if NodeID or NodeVersion is not valid.
// - Returns an error if the delete fails.
func ( s * Server ) RemoveNode ( ctx context . Context , request * api . RemoveNodeRequest ) ( * api . RemoveNodeResponse , error ) {
if request . NodeID == "" {
2017-12-05 00:38:37 +00:00
return nil , status . Errorf ( codes . InvalidArgument , errInvalidArgument . Error ( ) )
2016-06-07 21:28:28 +00:00
}
err := s . store . Update ( func ( tx store . Tx ) error {
node := store . GetNode ( tx , request . NodeID )
if node == nil {
2017-12-05 00:38:37 +00:00
return status . Errorf ( codes . NotFound , "node %s not found" , request . NodeID )
2016-06-07 21:28:28 +00:00
}
2017-01-07 01:30:33 +00:00
if node . Spec . DesiredRole == api . NodeRoleManager {
2016-07-20 15:16:54 +00:00
if s . raft == nil {
2017-12-05 00:38:37 +00:00
return status . Errorf ( codes . FailedPrecondition , "node %s is a manager but cannot access node information from the raft memberlist" , request . NodeID )
2016-07-20 15:16:54 +00:00
}
if member := s . raft . GetMemberByNodeID ( request . NodeID ) ; member != nil {
2017-12-05 00:38:37 +00:00
return status . Errorf ( codes . FailedPrecondition , "node %s is a cluster manager and is a member of the raft cluster. It must be demoted to worker before removal" , request . NodeID )
2016-07-20 15:16:54 +00:00
}
2016-06-07 21:28:28 +00:00
}
2016-07-28 04:17:00 +00:00
if ! request . Force && node . Status . State == api . NodeStatus_READY {
2017-12-05 00:38:37 +00:00
return status . Errorf ( codes . FailedPrecondition , "node %s is not down and can't be removed" , request . NodeID )
2016-06-07 21:28:28 +00:00
}
2016-10-20 18:26:04 +00:00
// lookup the cluster
2017-12-05 00:38:37 +00:00
clusters , err := store . FindClusters ( tx , store . ByName ( store . DefaultClusterName ) )
2016-10-20 18:26:04 +00:00
if err != nil {
return err
}
if len ( clusters ) != 1 {
2017-12-05 00:38:37 +00:00
return status . Errorf ( codes . Internal , "could not fetch cluster object" )
2016-10-20 18:26:04 +00:00
}
cluster := clusters [ 0 ]
2016-10-26 13:35:48 +00:00
blacklistedCert := & api . BlacklistedCertificate { }
2016-10-20 18:26:04 +00:00
// Set an expiry time for this RemovedNode if a certificate
// exists and can be parsed.
if len ( node . Certificate . Certificate ) != 0 {
certBlock , _ := pem . Decode ( node . Certificate . Certificate )
if certBlock != nil {
X509Cert , err := x509 . ParseCertificate ( certBlock . Bytes )
if err == nil && ! X509Cert . NotAfter . IsZero ( ) {
2017-01-23 23:50:10 +00:00
expiry , err := gogotypes . TimestampProto ( X509Cert . NotAfter )
2016-10-20 18:26:04 +00:00
if err == nil {
2016-10-26 13:35:48 +00:00
blacklistedCert . Expiry = expiry
2016-10-20 18:26:04 +00:00
}
}
}
}
2016-10-26 13:35:48 +00:00
if cluster . BlacklistedCertificates == nil {
cluster . BlacklistedCertificates = make ( map [ string ] * api . BlacklistedCertificate )
}
cluster . BlacklistedCertificates [ node . ID ] = blacklistedCert
expireBlacklistedCerts ( cluster )
2016-10-20 18:26:04 +00:00
if err := store . UpdateCluster ( tx , cluster ) ; err != nil {
return err
}
2019-03-26 13:57:19 +00:00
if err := orphanNodeTasks ( tx , request . NodeID ) ; err != nil {
2017-10-28 14:23:03 +00:00
return err
}
2016-06-07 21:28:28 +00:00
return store . DeleteNode ( tx , request . NodeID )
} )
if err != nil {
return nil , err
}
return & api . RemoveNodeResponse { } , nil
}