// NodesByMeta is used to return all nodes with the given metadata key/value pairs. func (s *StateStore) NodesByMeta(filters map[string]string) (uint64, structs.Nodes, error) { tx := s.db.Txn(false) defer tx.Abort() // Get the table index. idx := maxIndexTxn(tx, s.getWatchTables("Nodes")...) // Retrieve all of the nodes var args []interface{} for key, value := range filters { args = append(args, key, value) break } nodes, err := tx.Get("nodes", "meta", args...) if err != nil { return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) } // Create and return the nodes list. var results structs.Nodes for node := nodes.Next(); node != nil; node = nodes.Next() { n := node.(*structs.Node) if len(filters) <= 1 || structs.SatisfiesMetaFilters(n.Meta, filters) { results = append(results, n) } } return idx, results, nil }
// ServiceNodes returns all the nodes registered as part of a service including health info func (h *Health) ServiceNodes(args *structs.ServiceSpecificRequest, reply *structs.IndexedCheckServiceNodes) error { if done, err := h.srv.forward("Health.ServiceNodes", args, args, reply); done { return err } // Verify the arguments if args.ServiceName == "" { return fmt.Errorf("Must provide service name") } // Get the nodes state := h.srv.fsm.State() err := h.srv.blockingRPC( &args.QueryOptions, &reply.QueryMeta, state.GetQueryWatch("CheckServiceNodes"), func() error { var index uint64 var nodes structs.CheckServiceNodes var err error if args.TagFilter { index, nodes, err = state.CheckServiceTagNodes(args.ServiceName, args.ServiceTag) } else { index, nodes, err = state.CheckServiceNodes(args.ServiceName) } if err != nil { return err } reply.Index, reply.Nodes = index, nodes if len(args.NodeMetaFilters) > 0 { var filtered structs.CheckServiceNodes for _, node := range nodes { if structs.SatisfiesMetaFilters(node.Node.Meta, args.NodeMetaFilters) { filtered = append(filtered, node) } } reply.Nodes = filtered } if err := h.srv.filterACL(args.Token, reply); err != nil { return err } return h.srv.sortNodesByDistanceFrom(args.Source, reply.Nodes) }) // Provide some metrics if err == nil { metrics.IncrCounter([]string{"consul", "health", "service", "query", args.ServiceName}, 1) if args.ServiceTag != "" { metrics.IncrCounter([]string{"consul", "health", "service", "query-tag", args.ServiceName, args.ServiceTag}, 1) } if len(reply.Nodes) == 0 { metrics.IncrCounter([]string{"consul", "health", "service", "not-found", args.ServiceName}, 1) } } return err }
// ServicesByNodeMeta returns all services, filtered by the given node metadata. func (s *StateStore) ServicesByNodeMeta(filters map[string]string) (uint64, structs.Services, error) { tx := s.db.Txn(false) defer tx.Abort() // Get the table index. idx := maxIndexTxn(tx, s.getWatchTables("ServiceNodes")...) // Retrieve all of the nodes with the meta k/v pair var args []interface{} for key, value := range filters { args = append(args, key, value) break } nodes, err := tx.Get("nodes", "meta", args...) if err != nil { return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) } // Populate the services map unique := make(map[string]map[string]struct{}) for node := nodes.Next(); node != nil; node = nodes.Next() { n := node.(*structs.Node) if len(filters) > 1 && !structs.SatisfiesMetaFilters(n.Meta, filters) { continue } // List all the services on the node services, err := tx.Get("services", "node", n.Node) if err != nil { return 0, nil, fmt.Errorf("failed querying services: %s", err) } // Rip through the services and enumerate them and their unique set of // tags. for service := services.Next(); service != nil; service = services.Next() { svc := service.(*structs.ServiceNode) tags, ok := unique[svc.ServiceName] if !ok { unique[svc.ServiceName] = make(map[string]struct{}) tags = unique[svc.ServiceName] } for _, tag := range svc.ServiceTags { tags[tag] = struct{}{} } } } // Generate the output structure. var results = make(structs.Services) for service, tags := range unique { results[service] = make([]string, 0) for tag, _ := range tags { results[service] = append(results[service], tag) } } return idx, results, nil }
// parseChecksByNodeMeta is a helper function used to deduplicate some // repetitive code for returning health checks filtered by node metadata fields. func (s *StateStore) parseChecksByNodeMeta(idx uint64, iter memdb.ResultIterator, tx *memdb.Txn, filters map[string]string) (uint64, structs.HealthChecks, error) { var results structs.HealthChecks for check := iter.Next(); check != nil; check = iter.Next() { healthCheck := check.(*structs.HealthCheck) node, err := tx.First("nodes", "id", healthCheck.Node) if err != nil { return 0, nil, fmt.Errorf("failed node lookup: %s", err) } if node == nil { return 0, nil, ErrMissingNode } if structs.SatisfiesMetaFilters(node.(*structs.Node).Meta, filters) { results = append(results, healthCheck) } } return idx, results, nil }