github.com/clly/consul@v1.4.5/agent/consul/catalog_endpoint.go (about)

     1  package consul
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"time"
     7  
     8  	"github.com/armon/go-metrics"
     9  	"github.com/hashicorp/consul/acl"
    10  	"github.com/hashicorp/consul/agent/consul/state"
    11  	"github.com/hashicorp/consul/agent/structs"
    12  	"github.com/hashicorp/consul/ipaddr"
    13  	"github.com/hashicorp/consul/types"
    14  	"github.com/hashicorp/go-memdb"
    15  	"github.com/hashicorp/go-uuid"
    16  )
    17  
    18  // Catalog endpoint is used to manipulate the service catalog
    19  type Catalog struct {
    20  	srv *Server
    21  }
    22  
    23  // nodePreApply does the verification of a node before it is applied to Raft.
    24  func nodePreApply(nodeName, nodeID string) error {
    25  	if nodeName == "" {
    26  		return fmt.Errorf("Must provide node")
    27  	}
    28  	if nodeID != "" {
    29  		if _, err := uuid.ParseUUID(nodeID); err != nil {
    30  			return fmt.Errorf("Bad node ID: %v", err)
    31  		}
    32  	}
    33  
    34  	return nil
    35  }
    36  
    37  func servicePreApply(service *structs.NodeService, rule acl.Authorizer) error {
    38  	// Validate the service. This is in addition to the below since
    39  	// the above just hasn't been moved over yet. We should move it over
    40  	// in time.
    41  	if err := service.Validate(); err != nil {
    42  		return err
    43  	}
    44  
    45  	// If no service id, but service name, use default
    46  	if service.ID == "" && service.Service != "" {
    47  		service.ID = service.Service
    48  	}
    49  
    50  	// Verify ServiceName provided if ID.
    51  	if service.ID != "" && service.Service == "" {
    52  		return fmt.Errorf("Must provide service name with ID")
    53  	}
    54  
    55  	// Check the service address here and in the agent endpoint
    56  	// since service registration isn't synchronous.
    57  	if ipaddr.IsAny(service.Address) {
    58  		return fmt.Errorf("Invalid service address")
    59  	}
    60  
    61  	// Apply the ACL policy if any. The 'consul' service is excluded
    62  	// since it is managed automatically internally (that behavior
    63  	// is going away after version 0.8). We check this same policy
    64  	// later if version 0.8 is enabled, so we can eventually just
    65  	// delete this and do all the ACL checks down there.
    66  	if service.Service != structs.ConsulServiceName {
    67  		if rule != nil && !rule.ServiceWrite(service.Service, nil) {
    68  			return acl.ErrPermissionDenied
    69  		}
    70  	}
    71  
    72  	// Proxies must have write permission on their destination
    73  	if service.Kind == structs.ServiceKindConnectProxy {
    74  		if rule != nil && !rule.ServiceWrite(service.Proxy.DestinationServiceName, nil) {
    75  			return acl.ErrPermissionDenied
    76  		}
    77  	}
    78  
    79  	return nil
    80  }
    81  
    82  // checkPreApply does the verification of a check before it is applied to Raft.
    83  func checkPreApply(check *structs.HealthCheck) {
    84  	if check.CheckID == "" && check.Name != "" {
    85  		check.CheckID = types.CheckID(check.Name)
    86  	}
    87  }
    88  
    89  // Register is used register that a node is providing a given service.
    90  func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error {
    91  	if done, err := c.srv.forward("Catalog.Register", args, args, reply); done {
    92  		return err
    93  	}
    94  	defer metrics.MeasureSince([]string{"catalog", "register"}, time.Now())
    95  
    96  	// Fetch the ACL token, if any.
    97  	rule, err := c.srv.ResolveToken(args.Token)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	// Verify the args.
   103  	if err := nodePreApply(args.Node, string(args.ID)); err != nil {
   104  		return err
   105  	}
   106  	if args.Address == "" && !args.SkipNodeUpdate {
   107  		return fmt.Errorf("Must provide address if SkipNodeUpdate is not set")
   108  	}
   109  
   110  	// Handle a service registration.
   111  	if args.Service != nil {
   112  		if err := servicePreApply(args.Service, rule); err != nil {
   113  			return err
   114  		}
   115  	}
   116  
   117  	// Move the old format single check into the slice, and fixup IDs.
   118  	if args.Check != nil {
   119  		args.Checks = append(args.Checks, args.Check)
   120  		args.Check = nil
   121  	}
   122  	for _, check := range args.Checks {
   123  		if check.Node == "" {
   124  			check.Node = args.Node
   125  		}
   126  		checkPreApply(check)
   127  	}
   128  
   129  	// Check the complete register request against the given ACL policy.
   130  	if rule != nil && c.srv.config.ACLEnforceVersion8 {
   131  		state := c.srv.fsm.State()
   132  		_, ns, err := state.NodeServices(nil, args.Node)
   133  		if err != nil {
   134  			return fmt.Errorf("Node lookup failed: %v", err)
   135  		}
   136  		if err := vetRegisterWithACL(rule, args, ns); err != nil {
   137  			return err
   138  		}
   139  	}
   140  
   141  	resp, err := c.srv.raftApply(structs.RegisterRequestType, args)
   142  	if err != nil {
   143  		return err
   144  	}
   145  	if respErr, ok := resp.(error); ok {
   146  		return respErr
   147  	}
   148  	return nil
   149  }
   150  
   151  // Deregister is used to remove a service registration for a given node.
   152  func (c *Catalog) Deregister(args *structs.DeregisterRequest, reply *struct{}) error {
   153  	if done, err := c.srv.forward("Catalog.Deregister", args, args, reply); done {
   154  		return err
   155  	}
   156  	defer metrics.MeasureSince([]string{"catalog", "deregister"}, time.Now())
   157  
   158  	// Verify the args
   159  	if args.Node == "" {
   160  		return fmt.Errorf("Must provide node")
   161  	}
   162  
   163  	// Fetch the ACL token, if any.
   164  	rule, err := c.srv.ResolveToken(args.Token)
   165  	if err != nil {
   166  		return err
   167  	}
   168  
   169  	// Check the complete deregister request against the given ACL policy.
   170  	if rule != nil && c.srv.config.ACLEnforceVersion8 {
   171  		state := c.srv.fsm.State()
   172  
   173  		var ns *structs.NodeService
   174  		if args.ServiceID != "" {
   175  			_, ns, err = state.NodeService(args.Node, args.ServiceID)
   176  			if err != nil {
   177  				return fmt.Errorf("Service lookup failed: %v", err)
   178  			}
   179  		}
   180  
   181  		var nc *structs.HealthCheck
   182  		if args.CheckID != "" {
   183  			_, nc, err = state.NodeCheck(args.Node, args.CheckID)
   184  			if err != nil {
   185  				return fmt.Errorf("Check lookup failed: %v", err)
   186  			}
   187  		}
   188  
   189  		if err := vetDeregisterWithACL(rule, args, ns, nc); err != nil {
   190  			return err
   191  		}
   192  
   193  	}
   194  
   195  	if _, err := c.srv.raftApply(structs.DeregisterRequestType, args); err != nil {
   196  		return err
   197  	}
   198  	return nil
   199  }
   200  
   201  // ListDatacenters is used to query for the list of known datacenters
   202  func (c *Catalog) ListDatacenters(args *struct{}, reply *[]string) error {
   203  	dcs, err := c.srv.router.GetDatacentersByDistance()
   204  	if err != nil {
   205  		return err
   206  	}
   207  
   208  	if len(dcs) == 0 { // no WAN federation, so return the local data center name
   209  		dcs = []string{c.srv.config.Datacenter}
   210  	}
   211  
   212  	*reply = dcs
   213  	return nil
   214  }
   215  
   216  // ListNodes is used to query the nodes in a DC
   217  func (c *Catalog) ListNodes(args *structs.DCSpecificRequest, reply *structs.IndexedNodes) error {
   218  	if done, err := c.srv.forward("Catalog.ListNodes", args, args, reply); done {
   219  		return err
   220  	}
   221  
   222  	return c.srv.blockingQuery(
   223  		&args.QueryOptions,
   224  		&reply.QueryMeta,
   225  		func(ws memdb.WatchSet, state *state.Store) error {
   226  			var index uint64
   227  			var nodes structs.Nodes
   228  			var err error
   229  			if len(args.NodeMetaFilters) > 0 {
   230  				index, nodes, err = state.NodesByMeta(ws, args.NodeMetaFilters)
   231  			} else {
   232  				index, nodes, err = state.Nodes(ws)
   233  			}
   234  			if err != nil {
   235  				return err
   236  			}
   237  
   238  			reply.Index, reply.Nodes = index, nodes
   239  			if err := c.srv.filterACL(args.Token, reply); err != nil {
   240  				return err
   241  			}
   242  			return c.srv.sortNodesByDistanceFrom(args.Source, reply.Nodes)
   243  		})
   244  }
   245  
   246  // ListServices is used to query the services in a DC
   247  func (c *Catalog) ListServices(args *structs.DCSpecificRequest, reply *structs.IndexedServices) error {
   248  	if done, err := c.srv.forward("Catalog.ListServices", args, args, reply); done {
   249  		return err
   250  	}
   251  
   252  	return c.srv.blockingQuery(
   253  		&args.QueryOptions,
   254  		&reply.QueryMeta,
   255  		func(ws memdb.WatchSet, state *state.Store) error {
   256  			var index uint64
   257  			var services structs.Services
   258  			var err error
   259  			if len(args.NodeMetaFilters) > 0 {
   260  				index, services, err = state.ServicesByNodeMeta(ws, args.NodeMetaFilters)
   261  			} else {
   262  				index, services, err = state.Services(ws)
   263  			}
   264  			if err != nil {
   265  				return err
   266  			}
   267  
   268  			reply.Index, reply.Services = index, services
   269  			return c.srv.filterACL(args.Token, reply)
   270  		})
   271  }
   272  
   273  // ServiceNodes returns all the nodes registered as part of a service
   274  func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *structs.IndexedServiceNodes) error {
   275  	if done, err := c.srv.forward("Catalog.ServiceNodes", args, args, reply); done {
   276  		return err
   277  	}
   278  
   279  	// Verify the arguments
   280  	if args.ServiceName == "" && args.ServiceAddress == "" {
   281  		return fmt.Errorf("Must provide service name")
   282  	}
   283  
   284  	// Determine the function we'll call
   285  	var f func(memdb.WatchSet, *state.Store) (uint64, structs.ServiceNodes, error)
   286  	switch {
   287  	case args.Connect:
   288  		f = func(ws memdb.WatchSet, s *state.Store) (uint64, structs.ServiceNodes, error) {
   289  			return s.ConnectServiceNodes(ws, args.ServiceName)
   290  		}
   291  
   292  	default:
   293  		f = func(ws memdb.WatchSet, s *state.Store) (uint64, structs.ServiceNodes, error) {
   294  			if args.ServiceAddress != "" {
   295  				return s.ServiceAddressNodes(ws, args.ServiceAddress)
   296  			}
   297  
   298  			if args.TagFilter {
   299  				tags := args.ServiceTags
   300  				// DEPRECATED (singular-service-tag) - remove this when backwards RPC compat
   301  				// with 1.2.x is not required.
   302  				// Agents < v1.3.0 populate the ServiceTag field. In this case,
   303  				// use ServiceTag instead of the ServiceTags field.
   304  				if args.ServiceTag != "" {
   305  					tags = []string{args.ServiceTag}
   306  				}
   307  
   308  				return s.ServiceTagNodes(ws, args.ServiceName, tags)
   309  			}
   310  
   311  			return s.ServiceNodes(ws, args.ServiceName)
   312  		}
   313  	}
   314  
   315  	// If we're doing a connect query, we need read access to the service
   316  	// we're trying to find proxies for, so check that.
   317  	if args.Connect {
   318  		// Fetch the ACL token, if any.
   319  		rule, err := c.srv.ResolveToken(args.Token)
   320  		if err != nil {
   321  			return err
   322  		}
   323  
   324  		if rule != nil && !rule.ServiceRead(args.ServiceName) {
   325  			// Just return nil, which will return an empty response (tested)
   326  			return nil
   327  		}
   328  	}
   329  
   330  	err := c.srv.blockingQuery(
   331  		&args.QueryOptions,
   332  		&reply.QueryMeta,
   333  		func(ws memdb.WatchSet, state *state.Store) error {
   334  			index, services, err := f(ws, state)
   335  			if err != nil {
   336  				return err
   337  			}
   338  
   339  			reply.Index, reply.ServiceNodes = index, services
   340  			if len(args.NodeMetaFilters) > 0 {
   341  				var filtered structs.ServiceNodes
   342  				for _, service := range services {
   343  					if structs.SatisfiesMetaFilters(service.NodeMeta, args.NodeMetaFilters) {
   344  						filtered = append(filtered, service)
   345  					}
   346  				}
   347  				reply.ServiceNodes = filtered
   348  			}
   349  			if err := c.srv.filterACL(args.Token, reply); err != nil {
   350  				return err
   351  			}
   352  			return c.srv.sortNodesByDistanceFrom(args.Source, reply.ServiceNodes)
   353  		})
   354  
   355  	// Provide some metrics
   356  	if err == nil {
   357  		// For metrics, we separate Connect-based lookups from non-Connect
   358  		key := "service"
   359  		if args.Connect {
   360  			key = "connect"
   361  		}
   362  
   363  		metrics.IncrCounterWithLabels([]string{"catalog", key, "query"}, 1,
   364  			[]metrics.Label{{Name: "service", Value: args.ServiceName}})
   365  		// DEPRECATED (singular-service-tag) - remove this when backwards RPC compat
   366  		// with 1.2.x is not required.
   367  		if args.ServiceTag != "" {
   368  			metrics.IncrCounterWithLabels([]string{"catalog", key, "query-tag"}, 1,
   369  				[]metrics.Label{{Name: "service", Value: args.ServiceName}, {Name: "tag", Value: args.ServiceTag}})
   370  		}
   371  		if len(args.ServiceTags) > 0 {
   372  			// Sort tags so that the metric is the same even if the request
   373  			// tags are in a different order
   374  			sort.Strings(args.ServiceTags)
   375  
   376  			// Build metric labels
   377  			labels := []metrics.Label{{Name: "service", Value: args.ServiceName}}
   378  			for _, tag := range args.ServiceTags {
   379  				labels = append(labels, metrics.Label{Name: "tag", Value: tag})
   380  			}
   381  			metrics.IncrCounterWithLabels([]string{"catalog", key, "query-tags"}, 1, labels)
   382  		}
   383  		if len(reply.ServiceNodes) == 0 {
   384  			metrics.IncrCounterWithLabels([]string{"catalog", key, "not-found"}, 1,
   385  				[]metrics.Label{{Name: "service", Value: args.ServiceName}})
   386  		}
   387  	}
   388  
   389  	return err
   390  }
   391  
   392  // NodeServices returns all the services registered as part of a node
   393  func (c *Catalog) NodeServices(args *structs.NodeSpecificRequest, reply *structs.IndexedNodeServices) error {
   394  	if done, err := c.srv.forward("Catalog.NodeServices", args, args, reply); done {
   395  		return err
   396  	}
   397  
   398  	// Verify the arguments
   399  	if args.Node == "" {
   400  		return fmt.Errorf("Must provide node")
   401  	}
   402  
   403  	return c.srv.blockingQuery(
   404  		&args.QueryOptions,
   405  		&reply.QueryMeta,
   406  		func(ws memdb.WatchSet, state *state.Store) error {
   407  			index, services, err := state.NodeServices(ws, args.Node)
   408  			if err != nil {
   409  				return err
   410  			}
   411  
   412  			reply.Index, reply.NodeServices = index, services
   413  			return c.srv.filterACL(args.Token, reply)
   414  		})
   415  }