// 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 { return nil, grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error()) } if err := validateNodeSpec(request.Spec); err != nil { return nil, err } var ( node *api.Node demote bool ) err := s.store.Update(func(tx store.Tx) error { node = store.GetNode(tx, request.NodeID) if node == nil { return nil } // Demotion sanity checks. if node.Spec.Role == api.NodeRoleManager && request.Spec.Role == api.NodeRoleWorker { demote = true managers, err := store.FindNodes(tx, store.ByRole(api.NodeRoleManager)) if err != nil { return grpc.Errorf(codes.Internal, "internal store error: %v", err) } if len(managers) == 1 && managers[0].ID == node.ID { return grpc.Errorf(codes.FailedPrecondition, "attempting to demote the last manager of the swarm") } } node.Meta.Version = *request.NodeVersion node.Spec = *request.Spec.Copy() return store.UpdateNode(tx, node) }) if err != nil { return nil, err } if node == nil { return nil, grpc.Errorf(codes.NotFound, "node %s not found", request.NodeID) } if demote && s.raft != nil { memberlist := s.raft.GetMemberlist() for raftID, member := range memberlist { if member.NodeID == request.NodeID { if err := s.raft.RemoveMember(ctx, raftID); err != nil { return nil, err } break } } } return &api.UpdateNodeResponse{ Node: node, }, 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 { return nil, grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error()) } if err := validateNodeSpec(request.Spec); err != nil { return nil, err } var ( node *api.Node member *membership.Member ) err := s.store.Update(func(tx store.Tx) error { node = store.GetNode(tx, request.NodeID) if node == nil { return grpc.Errorf(codes.NotFound, "node %s not found", request.NodeID) } // Demotion sanity checks. if node.Spec.DesiredRole == api.NodeRoleManager && request.Spec.DesiredRole == api.NodeRoleWorker { // Check for manager entries in Store. managers, err := store.FindNodes(tx, store.ByRole(api.NodeRoleManager)) if err != nil { return grpc.Errorf(codes.Internal, "internal store error: %v", err) } if len(managers) == 1 && managers[0].ID == node.ID { return grpc.Errorf(codes.FailedPrecondition, "attempting to demote the last manager of the swarm") } // Check for node in memberlist if member = s.raft.GetMemberByNodeID(request.NodeID); member == nil { return grpc.Errorf(codes.NotFound, "can't find manager in raft memberlist") } // Quorum safeguard if !s.raft.CanRemoveMember(member.RaftID) { return grpc.Errorf(codes.FailedPrecondition, "can't remove member from the raft: this would result in a loss of quorum") } } node.Meta.Version = *request.NodeVersion node.Spec = *request.Spec.Copy() return store.UpdateNode(tx, node) }) if err != nil { return nil, err } return &api.UpdateNodeResponse{ Node: node, }, nil }
// 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)) case request.Filters != nil && len(request.Filters.NamePrefixes) > 0: nodes, err = store.FindNodes(tx, buildFilters(store.ByNamePrefix, request.Filters.NamePrefixes)) 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) }, 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) }, 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) }, func(e *api.Node) bool { if len(request.Filters.Roles) == 0 { return true } for _, c := range request.Filters.Roles { if c == e.Spec.Role { 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() 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 } } } } return &api.ListNodesResponse{ Nodes: nodes, }, nil }