Skip to content

Commit

Permalink
gatewayapi: add support for b/g mirroring
Browse files Browse the repository at this point in the history
Add support for mirroring requests while performing B/G deployments with
Gateway API. A `RequestMirror` filter pointing to the canary service is
added to the HTTPRoute during a Canary run. During the Canary run, drift
correction for `.spec.rules[].filters` is disabled to avoid removing the
mirror filter.

Signed-off-by: Sanskar Jaiswal <[email protected]>
  • Loading branch information
aryan9600 committed Sep 27, 2023
1 parent cadce1a commit de627e3
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 0 deletions.
33 changes: 33 additions & 0 deletions pkg/router/gateway_api_v1beta1.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,16 @@ func (gwr *GatewayAPIV1Beta1Router) Reconcile(canary *flaggerv1.Canary) error {
ignoreCmpOptions = append(ignoreCmpOptions, cmpopts.IgnoreFields(v1beta1.HTTPBackendRef{}, "Filters"))
}

if canary.GetAnalysis().Mirror {
// If a Canary run is in progress, the HTTPRoute rule will have an extra filter of type RequestMirror
// which needs to be ignored so that the requests are mirrored to the canary deployment.
inProgress := canary.Status.Phase == flaggerv1.CanaryPhaseWaiting || canary.Status.Phase == flaggerv1.CanaryPhaseProgressing ||
canary.Status.Phase == flaggerv1.CanaryPhaseWaitingPromotion
if inProgress {
ignoreCmpOptions = append(ignoreCmpOptions, cmpopts.IgnoreFields(v1beta1.HTTPRouteRule{}, "Filters"))
}
}

if httpRoute != nil {
specDiff := cmp.Diff(
httpRoute.Spec, httpRouteSpec,
Expand Down Expand Up @@ -249,6 +259,12 @@ func (gwr *GatewayAPIV1Beta1Router) GetRoutes(canary *flaggerv1.Canary) (
}
}
}
for _, filter := range rule.Filters {
if filter.Type == v1beta1.HTTPRouteFilterRequestMirror && filter.RequestMirror != nil &&
string(filter.RequestMirror.BackendRef.Name) == canarySvcName {
mirrored = true
}
}
}

if weightedRule != nil {
Expand Down Expand Up @@ -307,6 +323,23 @@ func (gwr *GatewayAPIV1Beta1Router) SetRoutes(
},
},
}

// If B/G mirroring is enabled, then add a route filter which mirrors the traffic
// to the canary service.
if mirrored && canary.GetAnalysis().Iterations > 0 {
weightedRouteRule.Filters = append(weightedRouteRule.Filters, v1beta1.HTTPRouteFilter{
Type: v1beta1.HTTPRouteFilterRequestMirror,
RequestMirror: &v1beta1.HTTPRequestMirrorFilter{
BackendRef: v1beta1.BackendObjectReference{
Group: (*v1beta1.Group)(&backendRefGroup),
Kind: (*v1beta1.Kind)(&backendRefKind),
Name: v1beta1.ObjectName(canarySvcName),
Port: (*v1beta1.PortNumber)(&canary.Spec.Service.Port),
},
},
})
}

httpRouteSpec := v1beta1.HTTPRouteSpec{
CommonRouteSpec: v1beta1.CommonRouteSpec{
ParentRefs: canary.Spec.Service.GatewayRefs,
Expand Down
38 changes: 38 additions & 0 deletions pkg/router/gateway_api_v1beta1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,44 @@ func TestGatewayAPIV1Beta1Router_Routes(t *testing.T) {
}
assert.True(t, found)
})

t.Run("b/g mirror", func(t *testing.T) {
canary := mocks.canary.DeepCopy()
canary.Spec.Analysis.Mirror = true
canary.Spec.Analysis.Iterations = 5
_, _, cSvcName := canary.GetServiceNames()

err = router.SetRoutes(canary, 100, 0, true)
hr, err := mocks.meshClient.GatewayapiV1beta1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
assert.Len(t, hr.Spec.Rules, 1)

rule := hr.Spec.Rules[0]
var found bool
for _, filter := range rule.Filters {
if filter.Type == v1beta1.HTTPRouteFilterRequestMirror && filter.RequestMirror != nil &&
string(filter.RequestMirror.BackendRef.Name) == cSvcName {
found = true
}
}
assert.True(t, found, "could not find request mirror filter in HTTPRoute")

// Mark the status as progressing to assert that request mirror filter is ignored.
canary.Status.Phase = flaggerv1.CanaryPhaseProgressing
err = router.Reconcile(canary)
require.NoError(t, err)

hr, err = mocks.meshClient.GatewayapiV1beta1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
assert.Len(t, hr.Spec.Rules, 1)
assert.Empty(t, cmp.Diff(hr.Spec.Rules[0], rule))

err = router.SetRoutes(canary, 100, 0, false)
hr, err = mocks.meshClient.GatewayapiV1beta1().HTTPRoutes("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)
assert.Len(t, hr.Spec.Rules, 1)
assert.Len(t, hr.Spec.Rules[0].Filters, 0)
})
}

func TestGatewayAPIV1Beta1Router_getSessionAffinityRouteRules(t *testing.T) {
Expand Down

0 comments on commit de627e3

Please sign in to comment.