123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- package controlapi
- import (
- "crypto/x509"
- "encoding/pem"
- "github.com/docker/swarmkit/api"
- "github.com/docker/swarmkit/manager/state/raft/membership"
- "github.com/docker/swarmkit/manager/state/store"
- "github.com/docker/swarmkit/protobuf/ptypes"
- "golang.org/x/net/context"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- )
- func validateNodeSpec(spec *api.NodeSpec) error {
- if spec == nil {
- return grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
- }
- 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 == "" {
- return nil, grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
- }
- var node *api.Node
- s.store.View(func(tx store.ReadTx) {
- node = store.GetNode(tx, request.NodeID)
- })
- if node == nil {
- return nil, grpc.Errorf(codes.NotFound, "node %s not found", request.NodeID)
- }
- if s.raft != nil {
- memberlist := s.raft.GetMemberlist()
- 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.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))
- 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
- }
- // 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
- demote bool
- )
- 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.Role == api.NodeRoleManager && request.Spec.Role == api.NodeRoleWorker {
- demote = true
- // 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
- }
- if demote && s.raft != nil {
- // TODO(abronan): the remove can potentially fail and leave the node with
- // an incorrect role (worker rather than manager), we need to reconcile the
- // memberlist with the desired state rather than attempting to remove the
- // member once.
- if err := s.raft.RemoveMember(ctx, member.RaftID); err != nil {
- return nil, grpc.Errorf(codes.Internal, "cannot demote manager to worker: %v", err)
- }
- }
- return &api.UpdateNodeResponse{
- Node: node,
- }, nil
- }
- // RemoveNode removes a Node referenced by NodeID with the given NodeSpec.
- // - Returns NotFound if the Node is not found.
- // - Returns FailedPrecondition if the Node has manager role (and is part of the memberlist) or is not shut down.
- // - 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 == "" {
- return nil, grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
- }
- 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)
- }
- if node.Spec.Role == api.NodeRoleManager {
- if s.raft == nil {
- return grpc.Errorf(codes.FailedPrecondition, "node %s is a manager but cannot access node information from the raft memberlist", request.NodeID)
- }
- if member := s.raft.GetMemberByNodeID(request.NodeID); member != nil {
- return grpc.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)
- }
- }
- if !request.Force && node.Status.State == api.NodeStatus_READY {
- return grpc.Errorf(codes.FailedPrecondition, "node %s is not down and can't be removed", request.NodeID)
- }
- // lookup the cluster
- clusters, err := store.FindClusters(tx, store.ByName("default"))
- if err != nil {
- return err
- }
- if len(clusters) != 1 {
- return grpc.Errorf(codes.Internal, "could not fetch cluster object")
- }
- cluster := clusters[0]
- blacklistedCert := &api.BlacklistedCertificate{}
- // 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() {
- expiry, err := ptypes.TimestampProto(X509Cert.NotAfter)
- if err == nil {
- blacklistedCert.Expiry = expiry
- }
- }
- }
- }
- if cluster.BlacklistedCertificates == nil {
- cluster.BlacklistedCertificates = make(map[string]*api.BlacklistedCertificate)
- }
- cluster.BlacklistedCertificates[node.ID] = blacklistedCert
- expireBlacklistedCerts(cluster)
- if err := store.UpdateCluster(tx, cluster); err != nil {
- return err
- }
- return store.DeleteNode(tx, request.NodeID)
- })
- if err != nil {
- return nil, err
- }
- return &api.RemoveNodeResponse{}, nil
- }
|