From 7bcbc59dea49f1dbe7f76e5a49b69340cd9d4fdc Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Thu, 19 Nov 2020 15:27:31 -0600 Subject: [PATCH] command: when generating envoy bootstrap configs use the datacenter returned from the agent services endpoint (#9229) Fixes #9215 --- .changelog/9229.txt | 3 +++ agent/agent_endpoint.go | 19 ++++++++++++++----- agent/agent_endpoint_test.go | 10 ++++++---- api/agent.go | 2 ++ api/agent_test.go | 5 +++-- command/connect/envoy/envoy.go | 13 +++++-------- command/connect/envoy/envoy_test.go | 8 +++++--- website/pages/api-docs/agent/service.mdx | 6 ++++++ 8 files changed, 44 insertions(+), 22 deletions(-) create mode 100644 .changelog/9229.txt diff --git a/.changelog/9229.txt b/.changelog/9229.txt new file mode 100644 index 000000000..36af2f2cd --- /dev/null +++ b/.changelog/9229.txt @@ -0,0 +1,3 @@ +```release-note:bug +command: when generating envoy bootstrap configs use the datacenter returned from the agent services endpoint +``` diff --git a/agent/agent_endpoint.go b/agent/agent_endpoint.go index 49721f912..3c2735c92 100644 --- a/agent/agent_endpoint.go +++ b/agent/agent_endpoint.go @@ -170,7 +170,7 @@ func (s *HTTPHandlers) AgentReload(resp http.ResponseWriter, req *http.Request) return nil, s.agent.ReloadConfig() } -func buildAgentService(s *structs.NodeService) api.AgentService { +func buildAgentService(s *structs.NodeService, dc string) api.AgentService { weights := api.AgentWeights{Passing: 1, Warning: 1} if s.Weights != nil { if s.Weights.Passing > 0 { @@ -200,6 +200,7 @@ func buildAgentService(s *structs.NodeService) api.AgentService { CreateIndex: s.CreateIndex, ModifyIndex: s.ModifyIndex, Weights: weights, + Datacenter: dc, } if as.Tags == nil { @@ -253,9 +254,11 @@ func (s *HTTPHandlers) AgentServices(resp http.ResponseWriter, req *http.Request // anyway. agentSvcs := make(map[string]*api.AgentService) + dc := s.agent.config.Datacenter + // Use empty list instead of nil for id, s := range services { - agentService := buildAgentService(s) + agentService := buildAgentService(s, dc) agentSvcs[id.ID] = &agentService } @@ -303,6 +306,8 @@ func (s *HTTPHandlers) AgentService(resp http.ResponseWriter, req *http.Request) sid := structs.NewServiceID(id, &entMeta) + dc := s.agent.config.Datacenter + resultHash, service, err := s.agent.LocalBlockingQuery(false, hash, queryOpts.MaxQueryTime, func(ws memdb.WatchSet) (string, interface{}, error) { @@ -330,7 +335,7 @@ func (s *HTTPHandlers) AgentService(resp http.ResponseWriter, req *http.Request) } // Calculate the content hash over the response, minus the hash field - aSvc := buildAgentService(svc) + aSvc := buildAgentService(svc, dc) reply := &aSvc rawHash, err := hashstructure.Hash(reply, nil) @@ -768,6 +773,8 @@ func (s *HTTPHandlers) AgentHealthServiceByID(resp http.ResponseWriter, req *htt sid := structs.NewServiceID(serviceID, &entMeta) + dc := s.agent.config.Datacenter + if service := s.agent.State.Service(sid); service != nil { if authz != nil && authz.ServiceRead(service.Service, &authzContext) != acl.Allow { return nil, acl.ErrPermissionDenied @@ -776,7 +783,7 @@ func (s *HTTPHandlers) AgentHealthServiceByID(resp http.ResponseWriter, req *htt if returnTextPlain(req) { return status, CodeWithPayloadError{StatusCode: code, Reason: status, ContentType: "text/plain"} } - serviceInfo := buildAgentService(service) + serviceInfo := buildAgentService(service, dc) result := &api.AgentServiceChecksInfo{ AggregatedStatus: status, Checks: healthChecks, @@ -822,6 +829,8 @@ func (s *HTTPHandlers) AgentHealthServiceByName(resp http.ResponseWriter, req *h return nil, acl.ErrPermissionDenied } + dc := s.agent.config.Datacenter + code := http.StatusNotFound status := fmt.Sprintf("ServiceName %s Not Found", serviceName) services := s.agent.State.Services(&entMeta) @@ -831,7 +840,7 @@ func (s *HTTPHandlers) AgentHealthServiceByName(resp http.ResponseWriter, req *h sid := structs.NewServiceID(service.ID, &entMeta) scode, sstatus, healthChecks := agentHealthService(sid, s) - serviceInfo := buildAgentService(service) + serviceInfo := buildAgentService(service, dc) res := api.AgentServiceChecksInfo{ AggregatedStatus: sstatus, Checks: healthChecks, diff --git a/agent/agent_endpoint_test.go b/agent/agent_endpoint_test.go index 5f8600a93..bd251ee5f 100644 --- a/agent/agent_endpoint_test.go +++ b/agent/agent_endpoint_test.go @@ -365,8 +365,9 @@ func TestAgent_Service(t *testing.T) { Passing: 1, Warning: 1, }, - Meta: map[string]string{}, - Tags: []string{}, + Meta: map[string]string{}, + Tags: []string{}, + Datacenter: "dc1", } fillAgentServiceEnterpriseMeta(expectedResponse, structs.DefaultEnterpriseMeta()) @@ -391,8 +392,9 @@ func TestAgent_Service(t *testing.T) { Port: 1818, }, }, - Meta: map[string]string{}, - Tags: []string{}, + Meta: map[string]string{}, + Tags: []string{}, + Datacenter: "dc1", } fillAgentServiceEnterpriseMeta(expectWebResponse, structs.DefaultEnterpriseMeta()) diff --git a/api/agent.go b/api/agent.go index d392d0897..a4cc143f0 100644 --- a/api/agent.go +++ b/api/agent.go @@ -93,6 +93,8 @@ type AgentService struct { // to include the Namespace in the hash. When we do, then we are in for lots of fun with tests. // For now though, ignoring it works well enough. Namespace string `json:",omitempty" bexpr:"-" hash:"ignore"` + // Datacenter is only ever returned and is ignored if presented. + Datacenter string `json:",omitempty" bexpr:"-" hash:"ignore"` } // AgentServiceChecksInfo returns information about a Service and its checks diff --git a/api/agent_test.go b/api/agent_test.go index b0b7701a2..6bcd7868c 100644 --- a/api/agent_test.go +++ b/api/agent_test.go @@ -726,8 +726,9 @@ func TestAPI_AgentService(t *testing.T) { Passing: 1, Warning: 1, }, - Meta: map[string]string{}, - Namespace: defaultNamespace, + Meta: map[string]string{}, + Namespace: defaultNamespace, + Datacenter: "dc1", } require.Equal(expect, got) require.Equal(expect.ContentHash, qm.LastContentHash) diff --git a/command/connect/envoy/envoy.go b/command/connect/envoy/envoy.go index 6e2953ae3..5bd617f30 100644 --- a/command/connect/envoy/envoy.go +++ b/command/connect/envoy/envoy.go @@ -478,6 +478,7 @@ func (c *cmd) templateArgs() (*BootstrapTplArgs, error) { LocalAgentClusterName: xds.LocalAgentClusterName, Namespace: httpCfg.Namespace, EnvoyVersion: c.envoyVersion, + Datacenter: httpCfg.Datacenter, }, nil } @@ -529,15 +530,11 @@ func (c *cmd) generateConfig() ([]byte, error) { // cluster is using namespaces regardless. args.Namespace = svc.Namespace } - agent, err := c.client.Agent().Self() - if err != nil { - return nil, fmt.Errorf("failed to fetch agent config: %v", err) + + if svc.Datacenter != "" { + // The agent will definitely have the definitive answer here. + args.Datacenter = svc.Datacenter } - dc, ok := agent["Config"]["Datacenter"].(string) - if !ok { - return nil, fmt.Errorf("failed to fetch datacenter from agent. DC is: %T", agent["Config"]["Datacenter"]) - } - args.Datacenter = dc if !c.disableCentralConfig { // Parse the bootstrap config diff --git a/command/connect/envoy/envoy_test.go b/command/connect/envoy/envoy_test.go index 9d0b03704..24cd3659a 100644 --- a/command/connect/envoy/envoy_test.go +++ b/command/connect/envoy/envoy_test.go @@ -991,9 +991,10 @@ func testMockAgentGatewayConfig(namespacesEnabled bool) http.HandlerFunc { svc := map[string]*api.AgentService{ string(kind): { - Kind: kind, - ID: string(kind), - Service: string(kind), + Kind: kind, + ID: string(kind), + Service: string(kind), + Datacenter: "dc1", }, } @@ -1036,6 +1037,7 @@ func testMockAgentProxyConfig(cfg map[string]interface{}, namespacesEnabled bool DestinationServiceID: serviceID, Config: cfg, }, + Datacenter: "dc1", } if namespacesEnabled { diff --git a/website/pages/api-docs/agent/service.mdx b/website/pages/api-docs/agent/service.mdx index 20e166f1c..f370082bc 100644 --- a/website/pages/api-docs/agent/service.mdx +++ b/website/pages/api-docs/agent/service.mdx @@ -74,6 +74,7 @@ $ curl \ "Port": 8000, "Address": "", "EnableTagOverride": false, + "Datacenter": "dc1", "Weights": { "Passing": 10, "Warning": 1 @@ -185,6 +186,7 @@ $ curl \ "Warning": 1 }, "EnableTagOverride": false, + "Datacenter": "dc1", "ContentHash": "4ecd29c7bc647ca8", "Proxy": { "DestinationServiceName": "web", @@ -304,6 +306,7 @@ curl localhost:8500/v1/agent/health/service/name/web "Meta": null, "Port": 80, "EnableTagOverride": false, + "Datacenter": "dc1", "Connect": { "Native": false, "Proxy": null @@ -331,6 +334,7 @@ curl localhost:8500/v1/agent/health/service/name/web "Meta": null, "Port": 80, "EnableTagOverride": false, + "Datacenter": "dc1", "Connect": { "Native": false, "Proxy": null @@ -380,6 +384,7 @@ curl localhost:8500/v1/agent/health/service/id/web2 "Meta": null, "Port": 80, "EnableTagOverride": false, + "Datacenter": "dc1", "Connect": { "Native": false, "Proxy": null @@ -425,6 +430,7 @@ curl localhost:8500/v1/agent/health/service/id/web1 "Meta": null, "Port": 80, "EnableTagOverride": false, + "Datacenter": "dc1", "Connect": { "Native": false, "Proxy": null