Skip to content

Commit 14f4a64

Browse files
Backport of Abhishek/fix api gw peering into release/2.0.x (#23500)
backport of commit 34b661a Co-authored-by: Abhishek Yadav <abhishek.yadav@hashicorp.com>
1 parent 649b070 commit 14f4a64

29 files changed

Lines changed: 1283 additions & 35 deletions

File tree

.changelog/23454.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
xds: Fixed XDS package to generate correct endpoints and cluster configurations for API Gateways when peered, and updated the API Gateway update handler to propogate mesh gateway config to its upstreams.
3+
```

agent/proxycfg/api_gateway.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,19 @@ func (h *handlerAPIGateway) handleRouteConfigUpdate(ctx context.Context, u Updat
367367
for _, rule := range route.Rules {
368368
for _, service := range rule.Services {
369369
effectiveLimits := apiGatewayEffectiveUpstreamLimits(defaultLimits, service.Limits)
370+
371+
// Retrieving the meshGatewayConfig from handlerAPIGateway instance.
372+
// `handlerAPIGateway` embeds `handlerState`, which exposes `serviceInstance.proxyCfg`.
373+
// serviceInstance.proxyCfg.MeshGateway is replicated from NodeService during state setup/update.
374+
// and NodeService populated for all gateway's during service resistration `AgentRegisterService`.
375+
//
376+
// So, Whenever any change happens in NodeService, proxyCfg manager will recreate
377+
// the state where it copies NodeService to serviceInstance and
378+
// then calls this api_gateway handleUpdates method.
379+
// which will update the Mesh-Gateway config to api_gateway upstreams (below).
380+
// h.service = <name of api-gateway>
381+
meshGatewayConfig := h.proxyCfg.MeshGateway
382+
370383
for _, listener := range snap.APIGateway.Listeners {
371384
shouldBind := false
372385
for _, parent := range route.Parents {
@@ -393,6 +406,11 @@ func (h *handlerAPIGateway) handleRouteConfigUpdate(ctx context.Context, u Updat
393406
// Pass the protocol that was configured on the listener in order
394407
// to force that protocol on the Envoy listener.
395408
Config: upstreamCfg,
409+
410+
// Propogate the meshGatewayConfig in api gateway upstreams
411+
// so that meshGatewayMode can be used in XDS for
412+
// endpoints and cluster config generation.
413+
MeshGateway: meshGatewayConfig,
396414
}
397415

398416
listenerKey := APIGatewayListenerKeyFromListener(listener)
@@ -425,6 +443,7 @@ func (h *handlerAPIGateway) handleRouteConfigUpdate(ctx context.Context, u Updat
425443

426444
for _, service := range route.Services {
427445
effectiveLimits := apiGatewayEffectiveUpstreamLimits(defaultLimits, service.Limits)
446+
meshGatewayConfig := h.proxyCfg.MeshGateway
428447
upstreamID := NewUpstreamIDFromServiceName(service.ServiceName())
429448
seenUpstreamIDs.add(upstreamID)
430449

@@ -454,7 +473,8 @@ func (h *handlerAPIGateway) handleRouteConfigUpdate(ctx context.Context, u Updat
454473
LocalBindPort: listener.Port,
455474
// Pass the protocol that was configured on the ingress listener in order
456475
// to force that protocol on the Envoy listener.
457-
Config: upstreamCfg,
476+
Config: upstreamCfg,
477+
MeshGateway: meshGatewayConfig,
458478
}
459479

460480
listenerKey := APIGatewayListenerKeyFromListener(listener)

agent/proxycfg/state.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ func newKindHandler(config stateConfig, s serviceInstance, ch chan UpdateEvent)
226226
case structs.ServiceKindIngressGateway:
227227
handler = &handlerIngressGateway{handlerState: h}
228228
case structs.ServiceKindAPIGateway:
229+
h.logger = config.logger.Named(logging.APIGateway)
229230
handler = &handlerAPIGateway{handlerState: h}
230231
default:
231232
return nil, errors.New("not a connect-proxy, terminating-gateway, mesh-gateway, or ingress-gateway")

agent/proxycfg/state_test.go

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
"github.com/hashicorp/consul/sdk/testutil"
2727
)
2828

29-
func TestStateChanged(t *testing.T) {
29+
func TestStateChangedConnectProxy(t *testing.T) {
3030
tests := []struct {
3131
name string
3232
ns *structs.NodeService
@@ -108,6 +108,131 @@ func TestStateChanged(t *testing.T) {
108108
},
109109
want: true,
110110
},
111+
{
112+
name: "different proxy mesh gateway mode",
113+
ns: structs.TestNodeServiceAPIGateway(t),
114+
token: "foo",
115+
mutate: func(ns structs.NodeService, token string) (*structs.NodeService, string) {
116+
ns.Proxy.MeshGateway.Mode = structs.MeshGatewayModeLocal
117+
return &ns, token
118+
},
119+
want: true,
120+
},
121+
}
122+
123+
for _, tt := range tests {
124+
t.Run(tt.name, func(t *testing.T) {
125+
proxyID := ProxyID{ServiceID: tt.ns.CompoundServiceID()}
126+
state, err := newState(proxyID, tt.ns, testSource, tt.token, stateConfig{logger: hclog.New(nil)}, rate.NewLimiter(rate.Inf, 1))
127+
require.NoError(t, err)
128+
otherNS, otherToken := tt.mutate(*tt.ns, tt.token)
129+
require.Equal(t, tt.want, state.Changed(otherNS, otherToken))
130+
})
131+
}
132+
}
133+
134+
func TestStateChangedAPIGateway(t *testing.T) {
135+
tests := []struct {
136+
name string
137+
ns *structs.NodeService
138+
token string
139+
mutate func(ns structs.NodeService, token string) (*structs.NodeService, string)
140+
want bool
141+
}{
142+
{
143+
name: "nil node service",
144+
ns: structs.TestNodeServiceAPIGateway(t),
145+
mutate: func(ns structs.NodeService, token string) (*structs.NodeService, string) {
146+
return nil, token
147+
},
148+
want: true,
149+
},
150+
{
151+
name: "same service",
152+
ns: structs.TestNodeServiceAPIGateway(t),
153+
mutate: func(ns structs.NodeService, token string) (*structs.NodeService, string) {
154+
return &ns, token
155+
}, want: false,
156+
},
157+
{
158+
name: "same service, different token",
159+
ns: structs.TestNodeServiceAPIGateway(t),
160+
token: "foo",
161+
mutate: func(ns structs.NodeService, token string) (*structs.NodeService, string) {
162+
return &ns, "bar"
163+
},
164+
want: true,
165+
},
166+
{
167+
name: "different address",
168+
ns: structs.TestNodeServiceAPIGateway(t),
169+
token: "foo",
170+
mutate: func(ns structs.NodeService, token string) (*structs.NodeService, string) {
171+
ns.Address = "10.10.10.10"
172+
return &ns, token
173+
},
174+
want: true,
175+
},
176+
{
177+
name: "different port",
178+
ns: structs.TestNodeServiceAPIGateway(t),
179+
token: "foo",
180+
mutate: func(ns structs.NodeService, token string) (*structs.NodeService, string) {
181+
ns.Port = 12345
182+
return &ns, token
183+
},
184+
want: true,
185+
},
186+
{
187+
name: "different service kind",
188+
ns: structs.TestNodeServiceAPIGateway(t),
189+
token: "foo",
190+
mutate: func(ns structs.NodeService, token string) (*structs.NodeService, string) {
191+
ns.Kind = ""
192+
return &ns, token
193+
},
194+
want: true,
195+
},
196+
{
197+
name: "different proxy target",
198+
ns: structs.TestNodeServiceAPIGateway(t),
199+
token: "foo",
200+
mutate: func(ns structs.NodeService, token string) (*structs.NodeService, string) {
201+
ns.Proxy.DestinationServiceName = "badger"
202+
return &ns, token
203+
},
204+
want: true,
205+
},
206+
{
207+
name: "different proxy upstreams",
208+
ns: structs.TestNodeServiceAPIGateway(t),
209+
token: "foo",
210+
mutate: func(ns structs.NodeService, token string) (*structs.NodeService, string) {
211+
ns.Proxy.Upstreams = nil
212+
return &ns, token
213+
},
214+
want: true,
215+
},
216+
{
217+
name: "different mesh gateway mode (local)",
218+
ns: structs.TestNodeServiceAPIGateway(t),
219+
token: "foo",
220+
mutate: func(ns structs.NodeService, token string) (*structs.NodeService, string) {
221+
ns.Proxy.MeshGateway.Mode = structs.MeshGatewayModeLocal
222+
return &ns, token
223+
},
224+
want: true,
225+
},
226+
{
227+
name: "different mesh gateway mode (remote)",
228+
ns: structs.TestNodeServiceAPIGateway(t),
229+
token: "foo",
230+
mutate: func(ns structs.NodeService, token string) (*structs.NodeService, string) {
231+
ns.Proxy.MeshGateway.Mode = structs.MeshGatewayModeRemote
232+
return &ns, token
233+
},
234+
want: true,
235+
},
111236
}
112237

113238
for _, tt := range tests {

agent/structs/testing_catalog.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,35 @@ func TestNodeServiceMeshGateway(t testing.T) *NodeService {
176176
}
177177

178178
func TestNodeServiceAPIGateway(t testing.T) *NodeService {
179+
entMeta := DefaultEnterpriseMetaInPartition("")
179180
return &NodeService{
180181
Kind: ServiceKindAPIGateway,
181182
Service: "api-gateway",
182183
Address: "1.1.1.1",
184+
185+
// ---------------------------------------
186+
// Adding TestConnectProxyConfig to the proxy field here within TestNodeServiceAPIGateway
187+
// to test whether APIGateway is able to handle state changes within ConnectProxyConfig (TestStateChangedAPIGateway).
188+
// Please note:
189+
// The naming may suggest that ConnectProxyConfig should only be used for ConnectProxy,
190+
// but "APIGateway state" uses serviceInstance, which embeds ConnectProxyConfig as part of its state,
191+
// so any changes to ConnectProxyConfig will also impact APIGateway, such as change in "mesh gateway mode".
192+
//
193+
// For example, let's say a user updates the mesh gateway mode of an API gateway,
194+
// First NodeService.Proxy will be updated and then proxyCfg manager detects change in config
195+
// and it recreates the state for api_gateway which would copy the
196+
// NodeService.Proxy.MeshGateway to serviceInstance.proxyCfg.MeshGateway in `newServiceInstanceFromNodeService` (serviceInstance is part of state)
197+
// and then proxyCfg manager calls the api_gateway handleUpdates method which would
198+
// update the api_gateway upstreams with new meshGateway config.
199+
//
200+
// Now, this serviceInstance.proxyCfg and NodeService.Proxy
201+
// refers to same proxy configuration, which is of type ConnectProxyConfig.
202+
203+
// So, we need to test it as well.
204+
// ---------------------------------------
205+
206+
Proxy: TestConnectProxyConfig(t),
207+
EnterpriseMeta: *entMeta,
183208
}
184209
}
185210

0 commit comments

Comments
 (0)