consul/agent/catalog_endpoint.go

608 lines
22 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
[COMPLIANCE] License changes (#18443) * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
2013-12-23 22:26:34 +00:00
package agent
import (
2013-12-24 00:20:51 +00:00
"fmt"
2013-12-23 22:26:34 +00:00
"net/http"
2022-06-01 17:17:14 +00:00
"strings"
"github.com/armon/go-metrics"
"github.com/armon/go-metrics/prometheus"
cachetype "github.com/hashicorp/consul/agent/cache-types"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/internal/dnsutil"
2013-12-23 22:26:34 +00:00
)
var CatalogCounters = []prometheus.CounterDefinition{
{
Name: []string{"client", "api", "catalog_register"},
Help: "Increments whenever a Consul agent receives a catalog register request.",
},
{
Name: []string{"client", "rpc", "error", "catalog_register"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives an RPC error for a catalog register request.",
},
{
Name: []string{"client", "api", "success", "catalog_register"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent successfully responds to a catalog register request.",
},
{
Name: []string{"client", "api", "catalog_deregister"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives a catalog deregister request.",
},
{
Name: []string{"client", "api", "catalog_datacenters"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives a request to list datacenters in the catalog.",
},
{
Name: []string{"client", "rpc", "error", "catalog_deregister"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives an RPC error for a catalog deregister request.",
},
{
Name: []string{"client", "api", "success", "catalog_nodes"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent successfully responds to a request to list nodes.",
},
{
Name: []string{"client", "rpc", "error", "catalog_nodes"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives an RPC error for a request to list nodes.",
},
{
Name: []string{"client", "api", "success", "catalog_deregister"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent successfully responds to a catalog deregister request.",
},
{
Name: []string{"client", "rpc", "error", "catalog_datacenters"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives an RPC error for a request to list datacenters.",
},
{
Name: []string{"client", "api", "success", "catalog_datacenters"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent successfully responds to a request to list datacenters.",
},
{
Name: []string{"client", "api", "catalog_nodes"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives a request to list nodes from the catalog.",
},
{
Name: []string{"client", "api", "catalog_services"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives a request to list services from the catalog.",
},
{
Name: []string{"client", "rpc", "error", "catalog_services"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives an RPC error for a request to list services.",
},
{
Name: []string{"client", "api", "success", "catalog_services"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent successfully responds to a request to list services.",
},
{
Name: []string{"client", "api", "catalog_service_nodes"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives a request to list nodes offering a service.",
},
{
Name: []string{"client", "rpc", "error", "catalog_service_nodes"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives an RPC error for a request to list nodes offering a service.",
},
{
Name: []string{"client", "api", "success", "catalog_service_nodes"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent successfully responds to a request to list nodes offering a service.",
},
{
Name: []string{"client", "api", "error", "catalog_service_nodes"},
Help: "Increments whenever a Consul agent receives an RPC error for request to list nodes offering a service.",
},
{
Name: []string{"client", "api", "catalog_node_services"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent successfully responds to a request to list nodes offering a service.",
},
{
Name: []string{"client", "api", "success", "catalog_node_services"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent successfully responds to a request to list services in a node.",
},
{
Name: []string{"client", "rpc", "error", "catalog_node_services"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives an RPC error for a request to list services in a node.",
},
{
Name: []string{"client", "api", "catalog_node_service_list"},
Help: "Increments whenever a Consul agent receives a request to list a node's registered services.",
},
{
Name: []string{"client", "rpc", "error", "catalog_node_service_list"},
Help: "Increments whenever a Consul agent receives an RPC error for request to list a node's registered services.",
},
{
Name: []string{"client", "api", "success", "catalog_node_service_list"},
Help: "Increments whenever a Consul agent successfully responds to a request to list a node's registered services.",
},
{
Name: []string{"client", "api", "catalog_gateway_services"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives a request to list services associated with a gateway.",
},
{
Name: []string{"client", "rpc", "error", "catalog_gateway_services"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent receives an RPC error for a request to list services associated with a gateway.",
},
{
Name: []string{"client", "api", "success", "catalog_gateway_services"},
2020-11-16 19:02:11 +00:00
Help: "Increments whenever a Consul agent successfully responds to a request to list services associated with a gateway.",
},
}
func (s *HTTPHandlers) CatalogRegister(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "catalog_register"}, 1,
s.nodeMetricsLabels())
2013-12-24 00:20:51 +00:00
var args structs.RegisterRequest
if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil {
return nil, err
}
if err := s.rewordUnknownEnterpriseFieldError(decodeBody(req.Body, &args)); err != nil {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: fmt.Sprintf("Request decode failed: %v", err)}
2013-12-24 00:20:51 +00:00
}
// Setup the default DC if not provided
if args.Datacenter == "" {
args.Datacenter = s.agent.config.Datacenter
}
s.parseToken(req, &args.Token)
2013-12-24 00:20:51 +00:00
// Forward to the servers
var out struct{}
if err := s.agent.RPC(req.Context(), "Catalog.Register", &args, &out); err != nil {
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_register"}, 1,
s.nodeMetricsLabels())
2013-12-24 00:20:51 +00:00
return nil, err
}
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "success", "catalog_register"}, 1,
s.nodeMetricsLabels())
2013-12-24 00:20:51 +00:00
return true, nil
}
func (s *HTTPHandlers) CatalogDeregister(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "catalog_deregister"}, 1,
s.nodeMetricsLabels())
var args structs.DeregisterRequest
if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil {
return nil, err
}
if err := s.rewordUnknownEnterpriseFieldError(decodeBody(req.Body, &args)); err != nil {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: fmt.Sprintf("Request decode failed: %v", err)}
}
// Setup the default DC if not provided
if args.Datacenter == "" {
args.Datacenter = s.agent.config.Datacenter
}
s.parseToken(req, &args.Token)
// Forward to the servers
var out struct{}
if err := s.agent.RPC(req.Context(), "Catalog.Deregister", &args, &out); err != nil {
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_deregister"}, 1,
s.nodeMetricsLabels())
return nil, err
}
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "success", "catalog_deregister"}, 1,
s.nodeMetricsLabels())
return true, nil
}
func (s *HTTPHandlers) CatalogDatacenters(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "catalog_datacenters"}, 1,
s.nodeMetricsLabels())
args := structs.DatacentersRequest{}
s.parseConsistency(resp, req, &args.QueryOptions)
parseCacheControl(resp, req, &args.QueryOptions)
2013-12-23 22:26:34 +00:00
var out []string
if args.QueryOptions.UseCache {
raw, m, err := s.agent.cache.Get(req.Context(), cachetype.CatalogDatacentersName, &args)
if err != nil {
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_datacenters"}, 1,
s.nodeMetricsLabels())
return nil, err
}
reply, ok := raw.(*[]string)
if !ok {
// This should never happen, but we want to protect against panics
return nil, fmt.Errorf("internal error: response type not correct")
}
defer setCacheMeta(resp, &m)
out = *reply
} else {
if err := s.agent.RPC(req.Context(), "Catalog.ListDatacenters", &args, &out); err != nil {
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_datacenters"}, 1,
s.nodeMetricsLabels())
return nil, err
}
2013-12-23 22:26:34 +00:00
}
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "success", "catalog_datacenters"}, 1,
s.nodeMetricsLabels())
2013-12-23 22:26:34 +00:00
return out, nil
}
2013-12-24 00:20:51 +00:00
func (s *HTTPHandlers) CatalogNodes(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "catalog_nodes"}, 1,
s.nodeMetricsLabels())
// Setup the request
args := structs.DCSpecificRequest{}
s.parseSource(req, &args.Source)
if err := s.parseEntMetaPartition(req, &args.EnterpriseMeta); err != nil {
return nil, err
}
args.NodeMetaFilters = s.parseMetaFilter(req)
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_nodes"}, 1,
s.nodeMetricsLabels())
return nil, nil
2013-12-24 00:20:51 +00:00
}
var out structs.IndexedNodes
defer setMeta(resp, &out.QueryMeta)
RETRY_ONCE:
if err := s.agent.RPC(req.Context(), "Catalog.ListNodes", &args, &out); err != nil {
return nil, err
2013-12-24 00:20:51 +00:00
}
if args.QueryOptions.AllowStale && args.MaxStaleDuration > 0 && args.MaxStaleDuration < out.LastContact {
args.AllowStale = false
args.MaxStaleDuration = 0
goto RETRY_ONCE
}
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
s.agent.TranslateAddresses(args.Datacenter, out.Nodes, dnsutil.TranslateAddressAcceptAny)
// Use empty list instead of nil
if out.Nodes == nil {
out.Nodes = make(structs.Nodes, 0)
}
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "success", "catalog_nodes"}, 1,
s.nodeMetricsLabels())
return out.Nodes, nil
2013-12-24 00:20:51 +00:00
}
func (s *HTTPHandlers) CatalogServices(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "catalog_services"}, 1,
s.nodeMetricsLabels())
args := structs.DCSpecificRequest{}
if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil {
return nil, err
}
args.NodeMetaFilters = s.parseMetaFilter(req)
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil
}
var out structs.IndexedServices
defer setMeta(resp, &out.QueryMeta)
if args.QueryOptions.UseCache {
raw, m, err := s.agent.cache.Get(req.Context(), cachetype.CatalogListServicesName, &args)
if err != nil {
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_services"}, 1,
s.nodeMetricsLabels())
return nil, err
}
reply, ok := raw.(*structs.IndexedServices)
if !ok {
// This should never happen, but we want to protect against panics
return nil, fmt.Errorf("internal error: response type not correct")
}
defer setCacheMeta(resp, &m)
out = *reply
} else {
RETRY_ONCE:
if err := s.agent.RPC(req.Context(), "Catalog.ListServices", &args, &out); err != nil {
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_services"}, 1,
s.nodeMetricsLabels())
return nil, err
}
if args.QueryOptions.AllowStale && args.MaxStaleDuration > 0 && args.MaxStaleDuration < out.LastContact {
args.AllowStale = false
args.MaxStaleDuration = 0
goto RETRY_ONCE
}
}
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
2017-04-28 01:22:07 +00:00
// Use empty map instead of nil
if out.Services == nil {
out.Services = make(structs.Services)
2017-04-28 01:22:07 +00:00
}
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "success", "catalog_services"}, 1,
s.nodeMetricsLabels())
return out.Services, nil
}
func (s *HTTPHandlers) CatalogConnectServiceNodes(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
return s.catalogServiceNodes(resp, req, true)
}
func (s *HTTPHandlers) CatalogServiceNodes(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
return s.catalogServiceNodes(resp, req, false)
}
func (s *HTTPHandlers) catalogServiceNodes(resp http.ResponseWriter, req *http.Request, connect bool) (interface{}, error) {
metricsKey := "catalog_service_nodes"
pathPrefix := "/v1/catalog/service/"
if connect {
metricsKey = "catalog_connect_service_nodes"
pathPrefix = "/v1/catalog/connect/"
}
metrics.IncrCounterWithLabels([]string{"client", "api", metricsKey}, 1,
s.nodeMetricsLabels())
args := structs.ServiceSpecificRequest{Connect: connect}
if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil {
return nil, err
}
s.parseSource(req, &args.Source)
args.NodeMetaFilters = s.parseMetaFilter(req)
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil
}
// Check for a tag
params := req.URL.Query()
if _, ok := params["tag"]; ok {
args.ServiceTags = params["tag"]
args.TagFilter = true
}
if _, ok := params["merge-central-config"]; ok {
args.MergeCentralConfig = true
}
// Pull out the service name
2022-06-01 17:17:14 +00:00
args.ServiceName = strings.TrimPrefix(req.URL.Path, pathPrefix)
if args.ServiceName == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing service name"}
}
// Make the RPC request
var out structs.IndexedServiceNodes
defer setMeta(resp, &out.QueryMeta)
if args.QueryOptions.UseCache {
raw, m, err := s.agent.cache.Get(req.Context(), cachetype.CatalogServicesName, &args)
if err != nil {
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_service_nodes"}, 1,
s.nodeMetricsLabels())
return nil, err
}
defer setCacheMeta(resp, &m)
reply, ok := raw.(*structs.IndexedServiceNodes)
if !ok {
// This should never happen, but we want to protect against panics
return nil, fmt.Errorf("internal error: response type not correct")
}
out = *reply
} else {
RETRY_ONCE:
if err := s.agent.RPC(req.Context(), "Catalog.ServiceNodes", &args, &out); err != nil {
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_service_nodes"}, 1,
s.nodeMetricsLabels())
return nil, err
}
if args.QueryOptions.AllowStale && args.MaxStaleDuration > 0 && args.MaxStaleDuration < out.LastContact {
args.AllowStale = false
args.MaxStaleDuration = 0
goto RETRY_ONCE
}
}
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
s.agent.TranslateAddresses(args.Datacenter, out.ServiceNodes, dnsutil.TranslateAddressAcceptAny)
// Use empty list instead of nil
if out.ServiceNodes == nil {
out.ServiceNodes = make(structs.ServiceNodes, 0)
}
for i, s := range out.ServiceNodes {
2017-04-28 01:22:07 +00:00
if s.ServiceTags == nil {
clone := *s
clone.ServiceTags = make([]string, 0)
out.ServiceNodes[i] = &clone
2017-04-28 01:22:07 +00:00
}
}
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "success", "catalog_service_nodes"}, 1,
s.nodeMetricsLabels())
return out.ServiceNodes, nil
}
func (s *HTTPHandlers) CatalogNodeServices(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "catalog_node_services"}, 1,
s.nodeMetricsLabels())
// Set default Datacenter
args := structs.NodeSpecificRequest{}
if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil {
return nil, err
}
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil
}
// Pull out the node name
2022-06-01 17:17:14 +00:00
args.Node = strings.TrimPrefix(req.URL.Path, "/v1/catalog/node/")
if args.Node == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"}
}
// Make the RPC request
var out structs.IndexedNodeServices
defer setMeta(resp, &out.QueryMeta)
RETRY_ONCE:
if err := s.agent.RPC(req.Context(), "Catalog.NodeServices", &args, &out); err != nil {
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_node_services"}, 1,
s.nodeMetricsLabels())
return nil, err
}
if args.QueryOptions.AllowStale && args.MaxStaleDuration > 0 && args.MaxStaleDuration < out.LastContact {
args.AllowStale = false
args.MaxStaleDuration = 0
goto RETRY_ONCE
}
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
if out.NodeServices != nil {
s.agent.TranslateAddresses(args.Datacenter, out.NodeServices, dnsutil.TranslateAddressAcceptAny)
}
// TODO: The NodeServices object in IndexedNodeServices is a pointer to
// something that's created for each request by the state store way down
// in https://github.com/hashicorp/consul/blob/v1.0.4/agent/consul/state/catalog.go#L953-L963.
// Since this isn't a pointer to a real state store object, it's safe to
// modify out.NodeServices.Services in the loop below without making a
// copy here. Same for the Tags in each service entry, since that was
// created by .ToNodeService() which made a copy. This is safe as-is but
// this whole business is tricky and subtle. See #3867 for more context.
2017-04-28 01:22:07 +00:00
// Use empty list instead of nil
if out.NodeServices != nil {
for _, s := range out.NodeServices.Services {
if s.Tags == nil {
s.Tags = make([]string, 0)
}
}
}
2017-12-22 04:30:29 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "success", "catalog_node_services"}, 1,
s.nodeMetricsLabels())
return out.NodeServices, nil
}
func (s *HTTPHandlers) CatalogNodeServiceList(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
metrics.IncrCounterWithLabels([]string{"client", "api", "catalog_node_service_list"}, 1,
s.nodeMetricsLabels())
// Set default Datacenter
args := structs.NodeSpecificRequest{}
if err := s.parseEntMeta(req, &args.EnterpriseMeta); err != nil {
return nil, err
}
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil
}
// Pull out the node name
2022-06-01 17:17:14 +00:00
args.Node = strings.TrimPrefix(req.URL.Path, "/v1/catalog/node-services/")
if args.Node == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing node name"}
}
if _, ok := req.URL.Query()["merge-central-config"]; ok {
args.MergeCentralConfig = true
}
// Make the RPC request
var out structs.IndexedNodeServiceList
defer setMeta(resp, &out.QueryMeta)
RETRY_ONCE:
if err := s.agent.RPC(req.Context(), "Catalog.NodeServiceList", &args, &out); err != nil {
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_node_service_list"}, 1,
s.nodeMetricsLabels())
return nil, err
}
if args.QueryOptions.AllowStale && args.MaxStaleDuration > 0 && args.MaxStaleDuration < out.LastContact {
args.AllowStale = false
args.MaxStaleDuration = 0
goto RETRY_ONCE
}
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
s.agent.TranslateAddresses(args.Datacenter, &out.NodeServices, dnsutil.TranslateAddressAcceptAny)
// Use empty list instead of nil
for _, s := range out.NodeServices.Services {
if s.Tags == nil {
s.Tags = make([]string, 0)
}
}
metrics.IncrCounterWithLabels([]string{"client", "api", "success", "catalog_node_service_list"}, 1,
s.nodeMetricsLabels())
return &out.NodeServices, nil
}
2020-06-12 05:30:21 +00:00
func (s *HTTPHandlers) CatalogGatewayServices(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
2020-06-12 05:30:21 +00:00
metrics.IncrCounterWithLabels([]string{"client", "api", "catalog_gateway_services"}, 1,
s.nodeMetricsLabels())
2020-06-12 05:30:21 +00:00
var args structs.ServiceSpecificRequest
if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil {
return nil, err
}
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil
}
// Pull out the gateway's service name
2022-06-01 17:17:14 +00:00
args.ServiceName = strings.TrimPrefix(req.URL.Path, "/v1/catalog/gateway-services/")
2020-06-12 05:30:21 +00:00
if args.ServiceName == "" {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "Missing gateway name"}
2020-06-12 05:30:21 +00:00
}
// Make the RPC request
var out structs.IndexedGatewayServices
defer setMeta(resp, &out.QueryMeta)
RETRY_ONCE:
if err := s.agent.RPC(req.Context(), "Catalog.GatewayServices", &args, &out); err != nil {
2020-06-12 05:30:21 +00:00
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_gateway_services"}, 1,
s.nodeMetricsLabels())
2020-06-12 05:30:21 +00:00
return nil, err
}
if args.QueryOptions.AllowStale && args.MaxStaleDuration > 0 && args.MaxStaleDuration < out.LastContact {
args.AllowStale = false
args.MaxStaleDuration = 0
goto RETRY_ONCE
}
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
metrics.IncrCounterWithLabels([]string{"client", "api", "success", "catalog_gateway_services"}, 1,
s.nodeMetricsLabels())
2020-06-12 05:30:21 +00:00
return out.Services, nil
}
func (s *HTTPHandlers) AssignManualServiceVIPs(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
metrics.IncrCounterWithLabels([]string{"client", "api", "service_virtual_ips"}, 1,
s.nodeMetricsLabels())
var args structs.AssignServiceManualVIPsRequest
if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil {
return nil, err
}
if err := s.rewordUnknownEnterpriseFieldError(decodeBody(req.Body, &args)); err != nil {
return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: fmt.Sprintf("Request decode failed: %v", err)}
}
// Setup the default DC if not provided
if args.Datacenter == "" {
args.Datacenter = s.agent.config.Datacenter
}
s.parseToken(req, &args.Token)
// Forward to the servers
var out structs.AssignServiceManualVIPsResponse
if err := s.agent.RPC(req.Context(), "Internal.AssignManualServiceVIPs", &args, &out); err != nil {
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "service_virtual_ips"}, 1,
s.nodeMetricsLabels())
return nil, err
}
metrics.IncrCounterWithLabels([]string{"client", "api", "success", "service_virtual_ips"}, 1,
s.nodeMetricsLabels())
return out, nil
}