github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/controlapi/node.go (about)

     1  package controlapi
     2  
     3  import (
     4  	"context"
     5  	"crypto/x509"
     6  	"encoding/pem"
     7  
     8  	"github.com/docker/swarmkit/api"
     9  	"github.com/docker/swarmkit/manager/state/raft/membership"
    10  	"github.com/docker/swarmkit/manager/state/store"
    11  	gogotypes "github.com/gogo/protobuf/types"
    12  	"google.golang.org/grpc/codes"
    13  	"google.golang.org/grpc/status"
    14  )
    15  
    16  func validateNodeSpec(spec *api.NodeSpec) error {
    17  	if spec == nil {
    18  		return status.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
    19  	}
    20  	return nil
    21  }
    22  
    23  // GetNode returns a Node given a NodeID.
    24  // - Returns `InvalidArgument` if NodeID is not provided.
    25  // - Returns `NotFound` if the Node is not found.
    26  func (s *Server) GetNode(ctx context.Context, request *api.GetNodeRequest) (*api.GetNodeResponse, error) {
    27  	if request.NodeID == "" {
    28  		return nil, status.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
    29  	}
    30  
    31  	var node *api.Node
    32  	s.store.View(func(tx store.ReadTx) {
    33  		node = store.GetNode(tx, request.NodeID)
    34  	})
    35  	if node == nil {
    36  		return nil, status.Errorf(codes.NotFound, "node %s not found", request.NodeID)
    37  	}
    38  
    39  	if s.raft != nil {
    40  		memberlist := s.raft.GetMemberlist()
    41  		for _, member := range memberlist {
    42  			if member.NodeID == node.ID {
    43  				node.ManagerStatus = &api.ManagerStatus{
    44  					RaftID:       member.RaftID,
    45  					Addr:         member.Addr,
    46  					Leader:       member.Status.Leader,
    47  					Reachability: member.Status.Reachability,
    48  				}
    49  				break
    50  			}
    51  		}
    52  	}
    53  
    54  	return &api.GetNodeResponse{
    55  		Node: node,
    56  	}, nil
    57  }
    58  
    59  func filterNodes(candidates []*api.Node, filters ...func(*api.Node) bool) []*api.Node {
    60  	result := []*api.Node{}
    61  
    62  	for _, c := range candidates {
    63  		match := true
    64  		for _, f := range filters {
    65  			if !f(c) {
    66  				match = false
    67  				break
    68  			}
    69  		}
    70  		if match {
    71  			result = append(result, c)
    72  		}
    73  	}
    74  
    75  	return result
    76  }
    77  
    78  // ListNodes returns a list of all nodes.
    79  func (s *Server) ListNodes(ctx context.Context, request *api.ListNodesRequest) (*api.ListNodesResponse, error) {
    80  	var (
    81  		nodes []*api.Node
    82  		err   error
    83  	)
    84  	s.store.View(func(tx store.ReadTx) {
    85  		switch {
    86  		case request.Filters != nil && len(request.Filters.Names) > 0:
    87  			nodes, err = store.FindNodes(tx, buildFilters(store.ByName, request.Filters.Names))
    88  		case request.Filters != nil && len(request.Filters.NamePrefixes) > 0:
    89  			nodes, err = store.FindNodes(tx, buildFilters(store.ByNamePrefix, request.Filters.NamePrefixes))
    90  		case request.Filters != nil && len(request.Filters.IDPrefixes) > 0:
    91  			nodes, err = store.FindNodes(tx, buildFilters(store.ByIDPrefix, request.Filters.IDPrefixes))
    92  		case request.Filters != nil && len(request.Filters.Roles) > 0:
    93  			filters := make([]store.By, 0, len(request.Filters.Roles))
    94  			for _, v := range request.Filters.Roles {
    95  				filters = append(filters, store.ByRole(v))
    96  			}
    97  			nodes, err = store.FindNodes(tx, store.Or(filters...))
    98  		case request.Filters != nil && len(request.Filters.Memberships) > 0:
    99  			filters := make([]store.By, 0, len(request.Filters.Memberships))
   100  			for _, v := range request.Filters.Memberships {
   101  				filters = append(filters, store.ByMembership(v))
   102  			}
   103  			nodes, err = store.FindNodes(tx, store.Or(filters...))
   104  		default:
   105  			nodes, err = store.FindNodes(tx, store.All)
   106  		}
   107  	})
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	if request.Filters != nil {
   113  		nodes = filterNodes(nodes,
   114  			func(e *api.Node) bool {
   115  				if len(request.Filters.Names) == 0 {
   116  					return true
   117  				}
   118  				if e.Description == nil {
   119  					return false
   120  				}
   121  				return filterContains(e.Description.Hostname, request.Filters.Names)
   122  			},
   123  			func(e *api.Node) bool {
   124  				if len(request.Filters.NamePrefixes) == 0 {
   125  					return true
   126  				}
   127  				if e.Description == nil {
   128  					return false
   129  				}
   130  				return filterContainsPrefix(e.Description.Hostname, request.Filters.NamePrefixes)
   131  			},
   132  			func(e *api.Node) bool {
   133  				return filterContainsPrefix(e.ID, request.Filters.IDPrefixes)
   134  			},
   135  			func(e *api.Node) bool {
   136  				if len(request.Filters.Labels) == 0 {
   137  					return true
   138  				}
   139  				if e.Description == nil {
   140  					return false
   141  				}
   142  				return filterMatchLabels(e.Description.Engine.Labels, request.Filters.Labels)
   143  			},
   144  			func(e *api.Node) bool {
   145  				if len(request.Filters.NodeLabels) == 0 {
   146  					return true
   147  				}
   148  				return filterMatchLabels(e.Spec.Annotations.Labels, request.Filters.NodeLabels)
   149  			},
   150  			func(e *api.Node) bool {
   151  				if len(request.Filters.Roles) == 0 {
   152  					return true
   153  				}
   154  				for _, c := range request.Filters.Roles {
   155  					if c == e.Role {
   156  						return true
   157  					}
   158  				}
   159  				return false
   160  			},
   161  			func(e *api.Node) bool {
   162  				if len(request.Filters.Memberships) == 0 {
   163  					return true
   164  				}
   165  				for _, c := range request.Filters.Memberships {
   166  					if c == e.Spec.Membership {
   167  						return true
   168  					}
   169  				}
   170  				return false
   171  			},
   172  		)
   173  	}
   174  
   175  	// Add in manager information on nodes that are managers
   176  	if s.raft != nil {
   177  		memberlist := s.raft.GetMemberlist()
   178  
   179  		for _, node := range nodes {
   180  			for _, member := range memberlist {
   181  				if member.NodeID == node.ID {
   182  					node.ManagerStatus = &api.ManagerStatus{
   183  						RaftID:       member.RaftID,
   184  						Addr:         member.Addr,
   185  						Leader:       member.Status.Leader,
   186  						Reachability: member.Status.Reachability,
   187  					}
   188  					break
   189  				}
   190  			}
   191  		}
   192  	}
   193  
   194  	return &api.ListNodesResponse{
   195  		Nodes: nodes,
   196  	}, nil
   197  }
   198  
   199  // UpdateNode updates a Node referenced by NodeID with the given NodeSpec.
   200  // - Returns `NotFound` if the Node is not found.
   201  // - Returns `InvalidArgument` if the NodeSpec is malformed.
   202  // - Returns an error if the update fails.
   203  func (s *Server) UpdateNode(ctx context.Context, request *api.UpdateNodeRequest) (*api.UpdateNodeResponse, error) {
   204  	if request.NodeID == "" || request.NodeVersion == nil {
   205  		return nil, status.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
   206  	}
   207  	if err := validateNodeSpec(request.Spec); err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	var (
   212  		node   *api.Node
   213  		member *membership.Member
   214  	)
   215  
   216  	err := s.store.Update(func(tx store.Tx) error {
   217  		node = store.GetNode(tx, request.NodeID)
   218  		if node == nil {
   219  			return status.Errorf(codes.NotFound, "node %s not found", request.NodeID)
   220  		}
   221  
   222  		// Demotion sanity checks.
   223  		if node.Spec.DesiredRole == api.NodeRoleManager && request.Spec.DesiredRole == api.NodeRoleWorker {
   224  			// Check for manager entries in Store.
   225  			managers, err := store.FindNodes(tx, store.ByRole(api.NodeRoleManager))
   226  			if err != nil {
   227  				return status.Errorf(codes.Internal, "internal store error: %v", err)
   228  			}
   229  			if len(managers) == 1 && managers[0].ID == node.ID {
   230  				return status.Errorf(codes.FailedPrecondition, "attempting to demote the last manager of the swarm")
   231  			}
   232  
   233  			// Check for node in memberlist
   234  			if member = s.raft.GetMemberByNodeID(request.NodeID); member == nil {
   235  				return status.Errorf(codes.NotFound, "can't find manager in raft memberlist")
   236  			}
   237  
   238  			// Quorum safeguard
   239  			if !s.raft.CanRemoveMember(member.RaftID) {
   240  				return status.Errorf(codes.FailedPrecondition, "can't remove member from the raft: this would result in a loss of quorum")
   241  			}
   242  		}
   243  
   244  		node.Meta.Version = *request.NodeVersion
   245  		node.Spec = *request.Spec.Copy()
   246  		return store.UpdateNode(tx, node)
   247  	})
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  
   252  	return &api.UpdateNodeResponse{
   253  		Node: node,
   254  	}, nil
   255  }
   256  
   257  func orphanNodeTasks(tx store.Tx, nodeID string) error {
   258  	// when a node is deleted, all of its tasks are irrecoverably removed.
   259  	// additionally, the Dispatcher can no longer be relied on to update the
   260  	// task status. Therefore, when the node is removed, we must additionally
   261  	// move all of its assigned tasks to the Orphaned state, so that their
   262  	// resources can be cleaned up.
   263  	tasks, err := store.FindTasks(tx, store.ByNodeID(nodeID))
   264  	if err != nil {
   265  		return err
   266  	}
   267  	for _, task := range tasks {
   268  		// this operation must occur within the same transaction boundary. If
   269  		// we cannot accomplish this task orphaning in the same transaction, we
   270  		// could crash or die between transactions and not get a chance to do
   271  		// this. however, in cases were there is an exceptionally large number
   272  		// of tasks for a node, this may cause the transaction to exceed the
   273  		// max message size.
   274  		//
   275  		// therefore, we restrict updating to only tasks in a non-terminal
   276  		// state. Tasks in a terminal state do not need to be updated.
   277  		if task.Status.State < api.TaskStateCompleted {
   278  			task.Status = api.TaskStatus{
   279  				Timestamp: gogotypes.TimestampNow(),
   280  				State:     api.TaskStateOrphaned,
   281  				Message:   "Task belonged to a node that has been deleted",
   282  			}
   283  			store.UpdateTask(tx, task)
   284  		}
   285  	}
   286  	return nil
   287  }
   288  
   289  // RemoveNode removes a Node referenced by NodeID with the given NodeSpec.
   290  // - Returns NotFound if the Node is not found.
   291  // - Returns FailedPrecondition if the Node has manager role (and is part of the memberlist) or is not shut down.
   292  // - Returns InvalidArgument if NodeID or NodeVersion is not valid.
   293  // - Returns an error if the delete fails.
   294  func (s *Server) RemoveNode(ctx context.Context, request *api.RemoveNodeRequest) (*api.RemoveNodeResponse, error) {
   295  	if request.NodeID == "" {
   296  		return nil, status.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
   297  	}
   298  
   299  	err := s.store.Update(func(tx store.Tx) error {
   300  		node := store.GetNode(tx, request.NodeID)
   301  		if node == nil {
   302  			return status.Errorf(codes.NotFound, "node %s not found", request.NodeID)
   303  		}
   304  		if node.Spec.DesiredRole == api.NodeRoleManager {
   305  			if s.raft == nil {
   306  				return status.Errorf(codes.FailedPrecondition, "node %s is a manager but cannot access node information from the raft memberlist", request.NodeID)
   307  			}
   308  			if member := s.raft.GetMemberByNodeID(request.NodeID); member != nil {
   309  				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)
   310  			}
   311  		}
   312  		if !request.Force && node.Status.State == api.NodeStatus_READY {
   313  			return status.Errorf(codes.FailedPrecondition, "node %s is not down and can't be removed", request.NodeID)
   314  		}
   315  
   316  		// lookup the cluster
   317  		clusters, err := store.FindClusters(tx, store.ByName(store.DefaultClusterName))
   318  		if err != nil {
   319  			return err
   320  		}
   321  		if len(clusters) != 1 {
   322  			return status.Errorf(codes.Internal, "could not fetch cluster object")
   323  		}
   324  		cluster := clusters[0]
   325  
   326  		blacklistedCert := &api.BlacklistedCertificate{}
   327  
   328  		// Set an expiry time for this RemovedNode if a certificate
   329  		// exists and can be parsed.
   330  		if len(node.Certificate.Certificate) != 0 {
   331  			certBlock, _ := pem.Decode(node.Certificate.Certificate)
   332  			if certBlock != nil {
   333  				X509Cert, err := x509.ParseCertificate(certBlock.Bytes)
   334  				if err == nil && !X509Cert.NotAfter.IsZero() {
   335  					expiry, err := gogotypes.TimestampProto(X509Cert.NotAfter)
   336  					if err == nil {
   337  						blacklistedCert.Expiry = expiry
   338  					}
   339  				}
   340  			}
   341  		}
   342  
   343  		if cluster.BlacklistedCertificates == nil {
   344  			cluster.BlacklistedCertificates = make(map[string]*api.BlacklistedCertificate)
   345  		}
   346  		cluster.BlacklistedCertificates[node.ID] = blacklistedCert
   347  
   348  		expireBlacklistedCerts(cluster)
   349  
   350  		if err := store.UpdateCluster(tx, cluster); err != nil {
   351  			return err
   352  		}
   353  
   354  		if err := orphanNodeTasks(tx, request.NodeID); err != nil {
   355  			return err
   356  		}
   357  
   358  		return store.DeleteNode(tx, request.NodeID)
   359  	})
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  	return &api.RemoveNodeResponse{}, nil
   364  }