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 }