Skip to content

Commit

Permalink
add logic for a/b testing
Browse files Browse the repository at this point in the history
Signed-off-by: Sanskar Jaiswal <[email protected]>
  • Loading branch information
aryan9600 committed Feb 15, 2022
1 parent 255a399 commit badabfe
Showing 1 changed file with 90 additions and 35 deletions.
125 changes: 90 additions & 35 deletions pkg/router/gateway_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error {
for _, host := range canary.Spec.Service.Hosts {
hostNames = append(hostNames, v1alpha2.Hostname(host))
}
requestMatches := canary.Spec.Service.Match
matches, err := gwr.mapRouteMatches(requestMatches)
matches, err := gwr.mapRouteMatches(canary.Spec.Service.Match)
if err != nil {
return fmt.Errorf("Invalid request matching selectors: %w", err)
}
Expand All @@ -96,32 +95,31 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error {
Matches: matches,
BackendRefs: []v1alpha2.HTTPBackendRef{
{
BackendRef: v1alpha2.BackendRef{
BackendObjectReference: v1alpha2.BackendObjectReference{
Group: (*v1alpha2.Group)(&backendRefGroup),
Kind: (*v1alpha2.Kind)(&backendRefKind),
Name: v1alpha2.ObjectName(primarySvcName),
Port: (*v1alpha2.PortNumber)(&canary.Spec.Service.Port),
},
Weight: &initialPrimaryWeight,
},
BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port),
},
{
BackendRef: v1alpha2.BackendRef{
BackendObjectReference: v1alpha2.BackendObjectReference{
Group: (*v1alpha2.Group)(&backendRefGroup),
Kind: (*v1alpha2.Kind)(&backendRefKind),
Name: v1alpha2.ObjectName(canarySvcName),
Port: (*v1alpha2.PortNumber)(&canary.Spec.Service.Port),
},
Weight: &initialCanaryWeight,
},
BackendRef: gwr.makeBackendRef(canarySvcName, initialCanaryWeight, canary.Spec.Service.Port),
},
},
},
},
}

// A/B testing
if len(canary.GetAnalysis().Match) > 0 {
analysisMatches, _ := gwr.mapRouteMatches(canary.GetAnalysis().Match)
// serviceMatches, _ := gwr.mapRouteMatches(canary.Spec.Service.Match)
httpRouteSpec.Rules[0].Matches = analysisMatches
httpRouteSpec.Rules = append(httpRouteSpec.Rules, v1alpha2.HTTPRouteRule{
Matches: matches,
BackendRefs: []v1alpha2.HTTPBackendRef{
{
BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port),
},
},
})
}

httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).Get(
context.TODO(), apexSvcName, metav1.GetOptions{},
)
Expand Down Expand Up @@ -200,14 +198,18 @@ func (gwr *GatewayAPIRouter) GetRoutes(canary *flaggerv1.Canary) (
return
}
for _, rule := range httpRoute.Spec.Rules {
for _, backendRef := range rule.BackendRefs {
if backendRef.Name == v1alpha2.ObjectName(primarySvcName) {
primaryWeight = int(*backendRef.Weight)
}
if backendRef.Name == v1alpha2.ObjectName(canarySvcName) {
canaryWeight = int(*backendRef.Weight)
// A/B testing: Avoid reading the rule with only for backendRef.
if len(rule.BackendRefs) == 2 {
for _, backendRef := range rule.BackendRefs {
if backendRef.Name == v1alpha2.ObjectName(primarySvcName) {
primaryWeight = int(*backendRef.Weight)
}
if backendRef.Name == v1alpha2.ObjectName(canarySvcName) {
canaryWeight = int(*backendRef.Weight)
}
}
}

}
return
}
Expand All @@ -230,16 +232,57 @@ func (gwr *GatewayAPIRouter) SetRoutes(
return fmt.Errorf("HTTPRoute %s.%s get error: %w", apexSvcName, hrNamespace, err)
}
hrClone := httpRoute.DeepCopy()
for i, rule := range hrClone.Spec.Rules {
for j, backendRef := range rule.BackendRefs {
if backendRef.Name == v1alpha2.ObjectName(primarySvcName) {
hrClone.Spec.Rules[i].BackendRefs[j].Weight = &pWeight
}
if backendRef.Name == v1alpha2.ObjectName(canarySvcName) {
hrClone.Spec.Rules[i].BackendRefs[j].Weight = &cWeight
}
}
hostNames := []v1alpha2.Hostname{}
for _, host := range canary.Spec.Service.Hosts {
hostNames = append(hostNames, v1alpha2.Hostname(host))
}
matches, err := gwr.mapRouteMatches(canary.Spec.Service.Match)
if err != nil {
return fmt.Errorf("Invalid request matching selectors: %w", err)
}
if len(matches) == 0 {
matches = append(matches, v1alpha2.HTTPRouteMatch{
Path: &v1alpha2.HTTPPathMatch{
Type: &pathMatchType,
Value: &pathMatchValue,
},
})
}
httpRouteSpec := v1alpha2.HTTPRouteSpec{
CommonRouteSpec: v1alpha2.CommonRouteSpec{
ParentRefs: canary.Spec.Service.GatewayRefs,
},
Hostnames: hostNames,
Rules: []v1alpha2.HTTPRouteRule{
{
Matches: matches,
BackendRefs: []v1alpha2.HTTPBackendRef{
{
BackendRef: gwr.makeBackendRef(primarySvcName, pWeight, canary.Spec.Service.Port),
},
{
BackendRef: gwr.makeBackendRef(canarySvcName, cWeight, canary.Spec.Service.Port),
},
},
},
},
}
hrClone.Spec = httpRouteSpec

// A/B testing
if len(canary.GetAnalysis().Match) > 0 {
analysisMatches, _ := gwr.mapRouteMatches(canary.GetAnalysis().Match)
hrClone.Spec.Rules[0].Matches = analysisMatches
hrClone.Spec.Rules = append(hrClone.Spec.Rules, v1alpha2.HTTPRouteRule{
Matches: matches,
BackendRefs: []v1alpha2.HTTPBackendRef{
{
BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port),
},
},
})
}

_, err = gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).Update(context.TODO(), hrClone, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("HTTPRoute %s.%s update error: %w", hrClone.GetName(), hrNamespace, err)
Expand Down Expand Up @@ -328,3 +371,15 @@ func (gwr *GatewayAPIRouter) mapRouteMatches(requestMatches []v1alpha3.HTTPMatch

return matches, nil
}

func (gwr *GatewayAPIRouter) makeBackendRef(svcName string, weight, port int32) v1alpha2.BackendRef {
return v1alpha2.BackendRef{
BackendObjectReference: v1alpha2.BackendObjectReference{
Group: (*v1alpha2.Group)(&backendRefGroup),
Kind: (*v1alpha2.Kind)(&backendRefKind),
Name: v1alpha2.ObjectName(svcName),
Port: (*v1alpha2.PortNumber)(&port),
},
Weight: &weight,
}
}

0 comments on commit badabfe

Please sign in to comment.