From 78c67479dde7bd1ce008a6a1725c2a5e01149875 Mon Sep 17 00:00:00 2001 From: Philipp Plotnikov Date: Mon, 29 Jan 2024 22:47:13 +0500 Subject: [PATCH 01/11] feat: Add setHeader functionality for httpRoutes Signed-off-by: Philipp Plotnikov --- pkg/plugin/httproute.go | 152 ++++++++++++++++++++++++++++++++++++++++ pkg/plugin/plugin.go | 67 +++++++++++++++--- pkg/plugin/types.go | 8 +++ 3 files changed, 219 insertions(+), 8 deletions(-) diff --git a/pkg/plugin/httproute.go b/pkg/plugin/httproute.go index 61e6780..3553f6c 100644 --- a/pkg/plugin/httproute.go +++ b/pkg/plugin/httproute.go @@ -4,10 +4,23 @@ import ( "context" "errors" "fmt" + "sync" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" pluginTypes "github.com/argoproj/argo-rollouts/utils/plugin/types" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +var ( + httpHeaderRoute = HTTPHeaderRoute{ + mu: sync.Mutex{}, + httpHeaderManagedRouteMap: make(map[string]int), + httpHeaderRouteRule: v1beta1.HTTPRouteRule{ + Matches: []v1beta1.HTTPRouteMatch{}, + BackendRefs: []v1beta1.HTTPBackendRef{}, + }, + } ) func (r *RpcPlugin) setHTTPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight int32, additionalDestinations []v1alpha1.WeightDestination, gatewayAPIConfig *GatewayAPITrafficRouting) pluginTypes.RpcError { @@ -58,6 +71,145 @@ func (r *RpcPlugin) setHTTPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight return pluginTypes.RpcError{} } +// TODO: Add tests +func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting *v1alpha1.SetHeaderRoute, gatewayAPIConfig *GatewayAPITrafficRouting) pluginTypes.RpcError { + if headerRouting.Match == nil { + managedRouteList := []v1alpha1.MangedRoutes{ + { + Name: headerRouting.Name, + }, + } + return r.removeHTTPManagedRoutes(managedRouteList, gatewayAPIConfig) + } + ctx := context.TODO() + httpRouteClient := r.HTTPRouteClient + if !r.IsTest { + gatewayV1beta1 := r.Client.GatewayV1beta1() + httpRouteClient = gatewayV1beta1.HTTPRoutes(gatewayAPIConfig.Namespace) + } + httpRoute, err := httpRouteClient.Get(ctx, gatewayAPIConfig.HTTPRoute, metav1.GetOptions{}) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } + canaryServiceName := v1beta1.ObjectName(rollout.Spec.Strategy.Canary.CanaryService) + canaryServiceKind := v1beta1.Kind("Service") + canaryServiceGroup := v1beta1.Group("") + httpHeaderRouteRuleList := []v1beta1.HTTPHeaderMatch{} + for _, headerRule := range headerRouting.Match { + httpHeaderRouteRule := v1beta1.HTTPHeaderMatch{ + Name: v1beta1.HTTPHeaderName(headerRule.HeaderName), + } + switch { + case headerRule.HeaderValue.Exact != "": + headerMatchType := v1beta1.HeaderMatchType(v1beta1.HeaderMatchExact) + httpHeaderRouteRule.Type = &headerMatchType + httpHeaderRouteRule.Value = headerRule.HeaderValue.Exact + case headerRule.HeaderValue.Prefix != "": + headerMatchType := v1beta1.HeaderMatchType(v1beta1.HeaderMatchRegularExpression) + httpHeaderRouteRule.Type = &headerMatchType + httpHeaderRouteRule.Value = headerRule.HeaderValue.Prefix + "*" + case headerRule.HeaderValue.Regex != "": + headerMatchType := v1beta1.HeaderMatchType(v1beta1.HeaderMatchRegularExpression) + httpHeaderRouteRule.Type = &headerMatchType + httpHeaderRouteRule.Value = headerRule.HeaderValue.Regex + default: + return pluginTypes.RpcError{ + ErrorString: "Not found header match type", + } + } + httpHeaderRouteRuleList = append(httpHeaderRouteRuleList, httpHeaderRouteRule) + } + routeRuleList := HTTPRouteRuleList(httpRoute.Spec.Rules) + backendRefList, err := getBackendRefList[HTTPBackendRefList](routeRuleList) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } + canaryBackendRef, err := getBackendRef[*HTTPBackendRef](string(canaryServiceName), backendRefList) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } + httpHeaderRouteRule := &httpHeaderRoute.httpHeaderRouteRule + httpHeaderRouteRule.Matches = []v1beta1.HTTPRouteMatch{ + { + Path: routeRuleList[0].Matches[0].Path, + Headers: httpHeaderRouteRuleList, + }, + } + httpHeaderRouteRule.BackendRefs = []v1beta1.HTTPBackendRef{ + { + BackendRef: v1beta1.BackendRef{ + BackendObjectReference: v1beta1.BackendObjectReference{ + Group: &canaryServiceGroup, + Kind: &canaryServiceKind, + Name: canaryServiceName, + Port: canaryBackendRef.Port, + }, + }, + }, + } + routeRuleList = append(routeRuleList, *httpHeaderRouteRule) + httpRoute.Spec.Rules = routeRuleList + updatedHTTPRoute, err := httpRouteClient.Update(ctx, httpRoute, metav1.UpdateOptions{}) + if r.IsTest { + r.UpdatedHTTPRouteMock = updatedHTTPRoute + } + if err != nil { + msg := fmt.Sprintf("Error updating Gateway API %q: %s", httpRoute.GetName(), err) + r.LogCtx.Error(msg) + } else { + httpHeaderRoute.httpHeaderManagedRouteMap[headerRouting.Name] = len(routeRuleList) - 1 + } + return pluginTypes.RpcError{} +} + +// TODO: Add tests +func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.MangedRoutes, gatewayAPIConfig *GatewayAPITrafficRouting) pluginTypes.RpcError { + ctx := context.TODO() + httpRouteClient := r.HTTPRouteClient + if !r.IsTest { + gatewayV1beta1 := r.Client.GatewayV1beta1() + httpRouteClient = gatewayV1beta1.HTTPRoutes(gatewayAPIConfig.Namespace) + } + httpRoute, err := httpRouteClient.Get(ctx, gatewayAPIConfig.HTTPRoute, metav1.GetOptions{}) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } + httpRouteRuleList := HTTPRouteRuleList(httpRoute.Spec.Rules) + httpHeaderManagedRouteMap := httpHeaderRoute.httpHeaderManagedRouteMap + for _, managedRoute := range managedRouteNameList { + managedRouteName := managedRoute.Name + httpRouteRuleListIndex, isOk := httpHeaderManagedRouteMap[managedRouteName] + if !isOk { + r.LogCtx.Logger.Info(fmt.Sprintf("%s is not in httpHeaderManagedRouteMap", managedRouteName)) + return pluginTypes.RpcError{} + } + updatedHTTPRouteRuleList := httpRouteRuleList[:httpRouteRuleListIndex] + if httpRouteRuleListIndex+1 < len(httpRouteRuleList) { + updatedHTTPRouteRuleList = append(updatedHTTPRouteRuleList, httpRouteRuleList[httpRouteRuleListIndex+1:]...) + } + httpRoute.Spec.Rules = updatedHTTPRouteRuleList + updatedHTTPRoute, err := httpRouteClient.Update(ctx, httpRoute, metav1.UpdateOptions{}) + if r.IsTest { + r.UpdatedHTTPRouteMock = updatedHTTPRoute + } + if err != nil { + msg := fmt.Sprintf("Error updating Gateway API %q: %s", httpRoute.GetName(), err) + r.LogCtx.Error(msg) + } else { + delete(httpHeaderManagedRouteMap, managedRouteName) + } + } + return pluginTypes.RpcError{} +} + func (r HTTPRouteRuleList) Iterator() (GatewayAPIRouteRuleIterator[HTTPBackendRefList], bool) { ruleList := r index := 0 diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index d79d0eb..8549d0e 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -35,6 +35,7 @@ func (r *RpcPlugin) InitPlugin() pluginTypes.RpcError { } } r.Client = clientset + return pluginTypes.RpcError{} } @@ -50,18 +51,47 @@ func (r *RpcPlugin) SetWeight(rollout *v1alpha1.Rollout, desiredWeight int32, ad ErrorString: err.Error(), } } - if gatewayAPIConfig.HTTPRoute != "" { - return r.setHTTPRouteWeight(rollout, desiredWeight, additionalDestinations, &gatewayAPIConfig) - } - if gatewayAPIConfig.TCPRoute != "" { - return r.setTCPRouteWeight(rollout, desiredWeight, additionalDestinations, &gatewayAPIConfig) - } - return pluginTypes.RpcError{ - ErrorString: GatewayAPIManifestError, + switch { + case gatewayAPIConfig.HTTPRoute != "": + rpcError := r.setHTTPRouteWeight(rollout, desiredWeight, additionalDestinations, &gatewayAPIConfig) + if rpcError.HasError() { + return rpcError + } + case gatewayAPIConfig.TCPRoute != "": + rpcError := r.setTCPRouteWeight(rollout, desiredWeight, additionalDestinations, &gatewayAPIConfig) + if rpcError.HasError() { + return rpcError + } + default: + return pluginTypes.RpcError{ + ErrorString: GatewayAPIManifestError, + } } + return pluginTypes.RpcError{} } func (r *RpcPlugin) SetHeaderRoute(rollout *v1alpha1.Rollout, headerRouting *v1alpha1.SetHeaderRoute) pluginTypes.RpcError { + gatewayAPIConfig := GatewayAPITrafficRouting{} + err := json.Unmarshal(rollout.Spec.Strategy.Canary.TrafficRouting.Plugins[PluginName], &gatewayAPIConfig) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } + switch { + case gatewayAPIConfig.HTTPRoute != "": + httpHeaderRoute.mu.Lock() + rpcError := r.setHTTPHeaderRoute(rollout, headerRouting, &gatewayAPIConfig) + if rpcError.HasError() { + httpHeaderRoute.mu.Unlock() + return rpcError + } + httpHeaderRoute.mu.Unlock() + default: + return pluginTypes.RpcError{ + ErrorString: "httpRoute field is empty. It has to be set to use setHeadRoute functionality", + } + } return pluginTypes.RpcError{} } @@ -74,6 +104,27 @@ func (r *RpcPlugin) VerifyWeight(rollout *v1alpha1.Rollout, desiredWeight int32, } func (r *RpcPlugin) RemoveManagedRoutes(rollout *v1alpha1.Rollout) pluginTypes.RpcError { + gatewayAPIConfig := GatewayAPITrafficRouting{} + err := json.Unmarshal(rollout.Spec.Strategy.Canary.TrafficRouting.Plugins[PluginName], &gatewayAPIConfig) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } + switch { + case gatewayAPIConfig.HTTPRoute != "": + httpHeaderRoute.mu.Lock() + rpcError := r.removeHTTPManagedRoutes(rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes, &gatewayAPIConfig) + if rpcError.HasError() { + httpHeaderRoute.mu.Unlock() + return rpcError + } + httpHeaderRoute.mu.Unlock() + default: + return pluginTypes.RpcError{ + ErrorString: "httpRoute field is empty. It has to be set to remove managed routes", + } + } return pluginTypes.RpcError{} } diff --git a/pkg/plugin/types.go b/pkg/plugin/types.go index bdb7561..4f2d7e8 100644 --- a/pkg/plugin/types.go +++ b/pkg/plugin/types.go @@ -1,6 +1,8 @@ package plugin import ( + "sync" + "github.com/sirupsen/logrus" "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/apis/v1beta1" @@ -30,6 +32,12 @@ type GatewayAPITrafficRouting struct { Namespace string `json:"namespace"` } +type HTTPHeaderRoute struct { + mu sync.Mutex + httpHeaderManagedRouteMap map[string]int + httpHeaderRouteRule v1beta1.HTTPRouteRule +} + type HTTPBackendRef v1beta1.HTTPBackendRef type TCPBackendRef v1beta1.BackendRef From 567863a61403e6e73bf643c6d6d3ffadc48cbb07 Mon Sep 17 00:00:00 2001 From: Philipp Plotnikov Date: Mon, 29 Jan 2024 22:52:27 +0500 Subject: [PATCH 02/11] refactor: fix lint Signed-off-by: Philipp Plotnikov --- pkg/plugin/httproute.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/plugin/httproute.go b/pkg/plugin/httproute.go index 3553f6c..3ffbb4a 100644 --- a/pkg/plugin/httproute.go +++ b/pkg/plugin/httproute.go @@ -103,15 +103,15 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting } switch { case headerRule.HeaderValue.Exact != "": - headerMatchType := v1beta1.HeaderMatchType(v1beta1.HeaderMatchExact) + headerMatchType := v1beta1.HeaderMatchExact httpHeaderRouteRule.Type = &headerMatchType httpHeaderRouteRule.Value = headerRule.HeaderValue.Exact case headerRule.HeaderValue.Prefix != "": - headerMatchType := v1beta1.HeaderMatchType(v1beta1.HeaderMatchRegularExpression) + headerMatchType := v1beta1.HeaderMatchRegularExpression httpHeaderRouteRule.Type = &headerMatchType httpHeaderRouteRule.Value = headerRule.HeaderValue.Prefix + "*" case headerRule.HeaderValue.Regex != "": - headerMatchType := v1beta1.HeaderMatchType(v1beta1.HeaderMatchRegularExpression) + headerMatchType := v1beta1.HeaderMatchRegularExpression httpHeaderRouteRule.Type = &headerMatchType httpHeaderRouteRule.Value = headerRule.HeaderValue.Regex default: From 880151230f595966e42d14dc70ee0525d945533a Mon Sep 17 00:00:00 2001 From: Philipp Plotnikov Date: Tue, 30 Jan 2024 13:15:07 +0500 Subject: [PATCH 03/11] refactor: seperate logic setHTTPHeaderRoute to the different functions Signed-off-by: Philipp Plotnikov --- pkg/plugin/httproute.go | 89 ++++++++++++++++++++--------------------- pkg/plugin/plugin.go | 12 ++++-- pkg/plugin/tcproute.go | 12 ++---- pkg/plugin/types.go | 16 ++++---- 4 files changed, 63 insertions(+), 66 deletions(-) diff --git a/pkg/plugin/httproute.go b/pkg/plugin/httproute.go index 3ffbb4a..c1d6127 100644 --- a/pkg/plugin/httproute.go +++ b/pkg/plugin/httproute.go @@ -14,9 +14,9 @@ import ( var ( httpHeaderRoute = HTTPHeaderRoute{ - mu: sync.Mutex{}, - httpHeaderManagedRouteMap: make(map[string]int), - httpHeaderRouteRule: v1beta1.HTTPRouteRule{ + mu: sync.Mutex{}, + managedRouteMap: make(map[string]int), + rule: v1beta1.HTTPRouteRule{ Matches: []v1beta1.HTTPRouteMatch{}, BackendRefs: []v1beta1.HTTPBackendRef{}, }, @@ -39,20 +39,14 @@ func (r *RpcPlugin) setHTTPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight canaryServiceName := rollout.Spec.Strategy.Canary.CanaryService stableServiceName := rollout.Spec.Strategy.Canary.StableService routeRuleList := HTTPRouteRuleList(httpRoute.Spec.Rules) - backendRefList, err := getBackendRefList[HTTPBackendRefList](routeRuleList) - if err != nil { - return pluginTypes.RpcError{ - ErrorString: err.Error(), - } - } - canaryBackendRef, err := getBackendRef[*HTTPBackendRef](canaryServiceName, backendRefList) + canaryBackendRef, err := getBackendRef[*HTTPBackendRef, HTTPBackendRefList](canaryServiceName, routeRuleList) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), } } canaryBackendRef.Weight = &desiredWeight - stableBackendRef, err := getBackendRef[*HTTPBackendRef](stableServiceName, backendRefList) + stableBackendRef, err := getBackendRef[*HTTPBackendRef, HTTPBackendRefList](stableServiceName, routeRuleList) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -96,45 +90,18 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting canaryServiceName := v1beta1.ObjectName(rollout.Spec.Strategy.Canary.CanaryService) canaryServiceKind := v1beta1.Kind("Service") canaryServiceGroup := v1beta1.Group("") - httpHeaderRouteRuleList := []v1beta1.HTTPHeaderMatch{} - for _, headerRule := range headerRouting.Match { - httpHeaderRouteRule := v1beta1.HTTPHeaderMatch{ - Name: v1beta1.HTTPHeaderName(headerRule.HeaderName), - } - switch { - case headerRule.HeaderValue.Exact != "": - headerMatchType := v1beta1.HeaderMatchExact - httpHeaderRouteRule.Type = &headerMatchType - httpHeaderRouteRule.Value = headerRule.HeaderValue.Exact - case headerRule.HeaderValue.Prefix != "": - headerMatchType := v1beta1.HeaderMatchRegularExpression - httpHeaderRouteRule.Type = &headerMatchType - httpHeaderRouteRule.Value = headerRule.HeaderValue.Prefix + "*" - case headerRule.HeaderValue.Regex != "": - headerMatchType := v1beta1.HeaderMatchRegularExpression - httpHeaderRouteRule.Type = &headerMatchType - httpHeaderRouteRule.Value = headerRule.HeaderValue.Regex - default: - return pluginTypes.RpcError{ - ErrorString: "Not found header match type", - } - } - httpHeaderRouteRuleList = append(httpHeaderRouteRuleList, httpHeaderRouteRule) + httpHeaderRouteRuleList, rpcError := getHTTPHeaderRouteRuleList(headerRouting) + if rpcError.HasError() { + return rpcError } routeRuleList := HTTPRouteRuleList(httpRoute.Spec.Rules) - backendRefList, err := getBackendRefList[HTTPBackendRefList](routeRuleList) - if err != nil { - return pluginTypes.RpcError{ - ErrorString: err.Error(), - } - } - canaryBackendRef, err := getBackendRef[*HTTPBackendRef](string(canaryServiceName), backendRefList) + canaryBackendRef, err := getBackendRef[*HTTPBackendRef, HTTPBackendRefList](string(canaryServiceName), routeRuleList) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), } } - httpHeaderRouteRule := &httpHeaderRoute.httpHeaderRouteRule + httpHeaderRouteRule := &httpHeaderRoute.rule httpHeaderRouteRule.Matches = []v1beta1.HTTPRouteMatch{ { Path: routeRuleList[0].Matches[0].Path, @@ -163,11 +130,41 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting msg := fmt.Sprintf("Error updating Gateway API %q: %s", httpRoute.GetName(), err) r.LogCtx.Error(msg) } else { - httpHeaderRoute.httpHeaderManagedRouteMap[headerRouting.Name] = len(routeRuleList) - 1 + httpHeaderRoute.managedRouteMap[headerRouting.Name] = len(routeRuleList) - 1 } return pluginTypes.RpcError{} } +// TODO: Add tests +func getHTTPHeaderRouteRuleList(headerRouting *v1alpha1.SetHeaderRoute) ([]v1beta1.HTTPHeaderMatch, pluginTypes.RpcError) { + httpHeaderRouteRuleList := []v1beta1.HTTPHeaderMatch{} + for _, headerRule := range headerRouting.Match { + httpHeaderRouteRule := v1beta1.HTTPHeaderMatch{ + Name: v1beta1.HTTPHeaderName(headerRule.HeaderName), + } + switch { + case headerRule.HeaderValue.Exact != "": + headerMatchType := v1beta1.HeaderMatchExact + httpHeaderRouteRule.Type = &headerMatchType + httpHeaderRouteRule.Value = headerRule.HeaderValue.Exact + case headerRule.HeaderValue.Prefix != "": + headerMatchType := v1beta1.HeaderMatchRegularExpression + httpHeaderRouteRule.Type = &headerMatchType + httpHeaderRouteRule.Value = headerRule.HeaderValue.Prefix + ".*" + case headerRule.HeaderValue.Regex != "": + headerMatchType := v1beta1.HeaderMatchRegularExpression + httpHeaderRouteRule.Type = &headerMatchType + httpHeaderRouteRule.Value = headerRule.HeaderValue.Regex + default: + return nil, pluginTypes.RpcError{ + ErrorString: "Not found header match type", + } + } + httpHeaderRouteRuleList = append(httpHeaderRouteRuleList, httpHeaderRouteRule) + } + return httpHeaderRouteRuleList, pluginTypes.RpcError{} +} + // TODO: Add tests func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.MangedRoutes, gatewayAPIConfig *GatewayAPITrafficRouting) pluginTypes.RpcError { ctx := context.TODO() @@ -183,7 +180,7 @@ func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.Mang } } httpRouteRuleList := HTTPRouteRuleList(httpRoute.Spec.Rules) - httpHeaderManagedRouteMap := httpHeaderRoute.httpHeaderManagedRouteMap + httpHeaderManagedRouteMap := httpHeaderRoute.managedRouteMap for _, managedRoute := range managedRouteNameList { managedRouteName := managedRoute.Name httpRouteRuleListIndex, isOk := httpHeaderManagedRouteMap[managedRouteName] @@ -210,7 +207,7 @@ func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.Mang return pluginTypes.RpcError{} } -func (r HTTPRouteRuleList) Iterator() (GatewayAPIRouteRuleIterator[HTTPBackendRefList], bool) { +func (r HTTPRouteRuleList) Iterator() (GatewayAPIRouteRuleIterator[*HTTPBackendRef, HTTPBackendRefList], bool) { ruleList := r index := 0 next := func() (HTTPBackendRefList, bool) { diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 8549d0e..022fe2c 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -132,8 +132,8 @@ func (r *RpcPlugin) Type() string { return Type } -func getBackendRefList[T GatewayAPIBackendRefList](ruleList GatewayAPIRouteRuleCollection[T]) (T, error) { - var backendRefList T +func getBackendRefList[T1 GatewayAPIBackendRef, T2 GatewayAPIBackendRefList[T1]](ruleList GatewayAPIRouteRuleCollection[T1, T2]) (T2, error) { + var backendRefList T2 for next, hasNext := ruleList.Iterator(); hasNext; { backendRefList, hasNext = next() if backendRefList == nil { @@ -144,8 +144,12 @@ func getBackendRefList[T GatewayAPIBackendRefList](ruleList GatewayAPIRouteRuleC return nil, ruleList.Error() } -func getBackendRef[T GatewayAPIBackendRef](backendRefName string, backendRefList GatewayAPIBackendRefCollection[T]) (T, error) { - var selectedService, backendRef T +func getBackendRef[T1 GatewayAPIBackendRef, T2 GatewayAPIBackendRefList[T1]](backendRefName string, ruleList GatewayAPIRouteRuleCollection[T1, T2]) (T1, error) { + var selectedService, backendRef T1 + backendRefList, err := getBackendRefList[T1, T2](ruleList) + if err != nil { + return nil, err + } for next, hasNext := backendRefList.Iterator(); hasNext; { backendRef, hasNext = next() nameOfCurrentService := backendRef.GetName() diff --git a/pkg/plugin/tcproute.go b/pkg/plugin/tcproute.go index c9d6790..4202ae9 100644 --- a/pkg/plugin/tcproute.go +++ b/pkg/plugin/tcproute.go @@ -26,20 +26,14 @@ func (r *RpcPlugin) setTCPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight i canaryServiceName := rollout.Spec.Strategy.Canary.CanaryService stableServiceName := rollout.Spec.Strategy.Canary.StableService routeRuleList := TCPRouteRuleList(tcpRoute.Spec.Rules) - backendRefList, err := getBackendRefList[TCPBackendRefList](routeRuleList) - if err != nil { - return pluginTypes.RpcError{ - ErrorString: err.Error(), - } - } - canaryBackendRef, err := getBackendRef[*TCPBackendRef](canaryServiceName, backendRefList) + canaryBackendRef, err := getBackendRef[*TCPBackendRef, TCPBackendRefList](canaryServiceName, routeRuleList) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), } } canaryBackendRef.Weight = &desiredWeight - stableBackendRef, err := getBackendRef[*TCPBackendRef](stableServiceName, backendRefList) + stableBackendRef, err := getBackendRef[*TCPBackendRef, TCPBackendRefList](stableServiceName, routeRuleList) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -58,7 +52,7 @@ func (r *RpcPlugin) setTCPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight i return pluginTypes.RpcError{} } -func (r TCPRouteRuleList) Iterator() (GatewayAPIRouteRuleIterator[TCPBackendRefList], bool) { +func (r TCPRouteRuleList) Iterator() (GatewayAPIRouteRuleIterator[*TCPBackendRef, TCPBackendRefList], bool) { ruleList := r index := 0 next := func() (TCPBackendRefList, bool) { diff --git a/pkg/plugin/types.go b/pkg/plugin/types.go index 4f2d7e8..d791995 100644 --- a/pkg/plugin/types.go +++ b/pkg/plugin/types.go @@ -33,9 +33,9 @@ type GatewayAPITrafficRouting struct { } type HTTPHeaderRoute struct { - mu sync.Mutex - httpHeaderManagedRouteMap map[string]int - httpHeaderRouteRule v1beta1.HTTPRouteRule + mu sync.Mutex + managedRouteMap map[string]int + rule v1beta1.HTTPRouteRule } type HTTPBackendRef v1beta1.HTTPBackendRef @@ -55,12 +55,14 @@ type GatewayAPIBackendRef interface { GetName() string } -type GatewayAPIBackendRefList interface { +type GatewayAPIBackendRefList[T GatewayAPIBackendRef] interface { HTTPBackendRefList | TCPBackendRefList + Iterator() (GatewayAPIBackendRefIterator[T], bool) + Error() error } -type GatewayAPIRouteRuleCollection[T GatewayAPIBackendRefList] interface { - Iterator() (GatewayAPIRouteRuleIterator[T], bool) +type GatewayAPIRouteRuleCollection[T1 GatewayAPIBackendRef, T2 GatewayAPIBackendRefList[T1]] interface { + Iterator() (GatewayAPIRouteRuleIterator[T1, T2], bool) Error() error } @@ -69,6 +71,6 @@ type GatewayAPIBackendRefCollection[T GatewayAPIBackendRef] interface { Error() error } -type GatewayAPIRouteRuleIterator[T GatewayAPIBackendRefList] func() (T, bool) +type GatewayAPIRouteRuleIterator[T1 GatewayAPIBackendRef, T2 GatewayAPIBackendRefList[T1]] func() (T2, bool) type GatewayAPIBackendRefIterator[T GatewayAPIBackendRef] func() (T, bool) From 6f70f8a8b45bf7507a23380bdb61c8118bedd2bb Mon Sep 17 00:00:00 2001 From: Philipp Plotnikov Date: Tue, 30 Jan 2024 14:51:12 +0500 Subject: [PATCH 04/11] refactor: move error messages into seperate file Signed-off-by: Philipp Plotnikov --- pkg/plugin/errors.go | 12 ++++++++++++ pkg/plugin/httproute.go | 12 ++++++------ pkg/plugin/plugin.go | 8 ++------ pkg/plugin/tcproute.go | 6 +++--- 4 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 pkg/plugin/errors.go diff --git a/pkg/plugin/errors.go b/pkg/plugin/errors.go new file mode 100644 index 0000000..583e7dc --- /dev/null +++ b/pkg/plugin/errors.go @@ -0,0 +1,12 @@ +package plugin + +const ( + GatewayAPIUpdateError = "Error updating Gateway API %q: %s" + GatewayAPIManifestError = "httpRoute and tcpRoute fields are empty. tcpRoute or httpRoute should be set" + HTTPRouteFieldIsEmptyError = "httpRoute field is empty. It has to be set to remove managed routes" + InvalidHeaderMatchTypeError = "Invalid header match type" + BackendRefWasNotFoundInHTTPRouteError = "backendRef was not found in httpRoute" + BackendRefListWasNotFoundInHTTPRouteError = "backendRef list was not found in httpRoute" + BackendRefWasNotFoundInTCPRouteError = "backendRef was not found in tcpRoute" + BackendRefListWasNotFoundInTCPRouteError = "backendRef list was not found in tcpRoute" +) diff --git a/pkg/plugin/httproute.go b/pkg/plugin/httproute.go index c1d6127..0d78143 100644 --- a/pkg/plugin/httproute.go +++ b/pkg/plugin/httproute.go @@ -59,7 +59,7 @@ func (r *RpcPlugin) setHTTPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight r.UpdatedHTTPRouteMock = updatedHTTPRoute } if err != nil { - msg := fmt.Sprintf("Error updating Gateway API %q: %s", httpRoute.GetName(), err) + msg := fmt.Sprintf(GatewayAPIUpdateError, httpRoute.GetName(), err) r.LogCtx.Error(msg) } return pluginTypes.RpcError{} @@ -127,7 +127,7 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting r.UpdatedHTTPRouteMock = updatedHTTPRoute } if err != nil { - msg := fmt.Sprintf("Error updating Gateway API %q: %s", httpRoute.GetName(), err) + msg := fmt.Sprintf(GatewayAPIUpdateError, httpRoute.GetName(), err) r.LogCtx.Error(msg) } else { httpHeaderRoute.managedRouteMap[headerRouting.Name] = len(routeRuleList) - 1 @@ -157,7 +157,7 @@ func getHTTPHeaderRouteRuleList(headerRouting *v1alpha1.SetHeaderRoute) ([]v1bet httpHeaderRouteRule.Value = headerRule.HeaderValue.Regex default: return nil, pluginTypes.RpcError{ - ErrorString: "Not found header match type", + ErrorString: InvalidHeaderMatchTypeError, } } httpHeaderRouteRuleList = append(httpHeaderRouteRuleList, httpHeaderRouteRule) @@ -198,7 +198,7 @@ func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.Mang r.UpdatedHTTPRouteMock = updatedHTTPRoute } if err != nil { - msg := fmt.Sprintf("Error updating Gateway API %q: %s", httpRoute.GetName(), err) + msg := fmt.Sprintf(GatewayAPIUpdateError, httpRoute.GetName(), err) r.LogCtx.Error(msg) } else { delete(httpHeaderManagedRouteMap, managedRouteName) @@ -222,7 +222,7 @@ func (r HTTPRouteRuleList) Iterator() (GatewayAPIRouteRuleIterator[*HTTPBackendR } func (r HTTPRouteRuleList) Error() error { - return errors.New("backendRefs was not found in httpRoute") + return errors.New(BackendRefListWasNotFoundInHTTPRouteError) } func (r HTTPBackendRefList) Iterator() (GatewayAPIBackendRefIterator[*HTTPBackendRef], bool) { @@ -240,7 +240,7 @@ func (r HTTPBackendRefList) Iterator() (GatewayAPIBackendRefIterator[*HTTPBacken } func (r HTTPBackendRefList) Error() error { - return errors.New("backendRef was not found in httpRoute") + return errors.New(BackendRefWasNotFoundInHTTPRouteError) } func (r *HTTPBackendRef) GetName() string { diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 022fe2c..0f2cb00 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -10,12 +10,8 @@ import ( ) const ( - // Type holds this controller type Type = "GatewayAPI" PluginName = "argoproj-labs/gatewayAPI" - - GatewayAPIUpdateError = "GatewayAPIUpdateError" - GatewayAPIManifestError = "httpRoute and tcpRoute fields are empty. tcpRoute or httpRoute should be set" ) func (r *RpcPlugin) InitPlugin() pluginTypes.RpcError { @@ -89,7 +85,7 @@ func (r *RpcPlugin) SetHeaderRoute(rollout *v1alpha1.Rollout, headerRouting *v1a httpHeaderRoute.mu.Unlock() default: return pluginTypes.RpcError{ - ErrorString: "httpRoute field is empty. It has to be set to use setHeadRoute functionality", + ErrorString: HTTPRouteFieldIsEmptyError, } } return pluginTypes.RpcError{} @@ -122,7 +118,7 @@ func (r *RpcPlugin) RemoveManagedRoutes(rollout *v1alpha1.Rollout) pluginTypes.R httpHeaderRoute.mu.Unlock() default: return pluginTypes.RpcError{ - ErrorString: "httpRoute field is empty. It has to be set to remove managed routes", + ErrorString: HTTPRouteFieldIsEmptyError, } } return pluginTypes.RpcError{} diff --git a/pkg/plugin/tcproute.go b/pkg/plugin/tcproute.go index 4202ae9..e80e512 100644 --- a/pkg/plugin/tcproute.go +++ b/pkg/plugin/tcproute.go @@ -46,7 +46,7 @@ func (r *RpcPlugin) setTCPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight i r.UpdatedTCPRouteMock = updatedTCPRoute } if err != nil { - msg := fmt.Sprintf("Error updating Gateway API %q: %s", tcpRoute.GetName(), err) + msg := fmt.Sprintf(GatewayAPIUpdateError, tcpRoute.GetName(), err) r.LogCtx.Error(msg) } return pluginTypes.RpcError{} @@ -67,7 +67,7 @@ func (r TCPRouteRuleList) Iterator() (GatewayAPIRouteRuleIterator[*TCPBackendRef } func (r TCPRouteRuleList) Error() error { - return errors.New("backendRefs was not found in tcpRoute") + return errors.New(BackendRefListWasNotFoundInTCPRouteError) } func (r TCPBackendRefList) Iterator() (GatewayAPIBackendRefIterator[*TCPBackendRef], bool) { @@ -85,7 +85,7 @@ func (r TCPBackendRefList) Iterator() (GatewayAPIBackendRefIterator[*TCPBackendR } func (r TCPBackendRefList) Error() error { - return errors.New("backendRef was not found in tcpRoute") + return errors.New(BackendRefWasNotFoundInTCPRouteError) } func (r *TCPBackendRef) GetName() string { From 613a7c7a74e3b26da2422b5cd16c373e1e7a3156 Mon Sep 17 00:00:00 2001 From: Philipp Plotnikov Date: Tue, 30 Jan 2024 14:53:52 +0500 Subject: [PATCH 05/11] refactor: rename mu field to the mutex field Signed-off-by: Philipp Plotnikov --- pkg/plugin/httproute.go | 2 +- pkg/plugin/plugin.go | 13 ++++++------- pkg/plugin/types.go | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pkg/plugin/httproute.go b/pkg/plugin/httproute.go index 0d78143..1af87d9 100644 --- a/pkg/plugin/httproute.go +++ b/pkg/plugin/httproute.go @@ -14,7 +14,7 @@ import ( var ( httpHeaderRoute = HTTPHeaderRoute{ - mu: sync.Mutex{}, + mutex: sync.Mutex{}, managedRouteMap: make(map[string]int), rule: v1beta1.HTTPRouteRule{ Matches: []v1beta1.HTTPRouteMatch{}, diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 0f2cb00..ddfe164 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -31,7 +31,6 @@ func (r *RpcPlugin) InitPlugin() pluginTypes.RpcError { } } r.Client = clientset - return pluginTypes.RpcError{} } @@ -76,13 +75,13 @@ func (r *RpcPlugin) SetHeaderRoute(rollout *v1alpha1.Rollout, headerRouting *v1a } switch { case gatewayAPIConfig.HTTPRoute != "": - httpHeaderRoute.mu.Lock() + httpHeaderRoute.mutex.Lock() rpcError := r.setHTTPHeaderRoute(rollout, headerRouting, &gatewayAPIConfig) if rpcError.HasError() { - httpHeaderRoute.mu.Unlock() + httpHeaderRoute.mutex.Unlock() return rpcError } - httpHeaderRoute.mu.Unlock() + httpHeaderRoute.mutex.Unlock() default: return pluginTypes.RpcError{ ErrorString: HTTPRouteFieldIsEmptyError, @@ -109,13 +108,13 @@ func (r *RpcPlugin) RemoveManagedRoutes(rollout *v1alpha1.Rollout) pluginTypes.R } switch { case gatewayAPIConfig.HTTPRoute != "": - httpHeaderRoute.mu.Lock() + httpHeaderRoute.mutex.Lock() rpcError := r.removeHTTPManagedRoutes(rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes, &gatewayAPIConfig) if rpcError.HasError() { - httpHeaderRoute.mu.Unlock() + httpHeaderRoute.mutex.Unlock() return rpcError } - httpHeaderRoute.mu.Unlock() + httpHeaderRoute.mutex.Unlock() default: return pluginTypes.RpcError{ ErrorString: HTTPRouteFieldIsEmptyError, diff --git a/pkg/plugin/types.go b/pkg/plugin/types.go index d791995..6167003 100644 --- a/pkg/plugin/types.go +++ b/pkg/plugin/types.go @@ -33,7 +33,7 @@ type GatewayAPITrafficRouting struct { } type HTTPHeaderRoute struct { - mu sync.Mutex + mutex sync.Mutex managedRouteMap map[string]int rule v1beta1.HTTPRouteRule } From f8e4773b7a2d0b91e96bd5eee901839bf8621a11 Mon Sep 17 00:00:00 2001 From: Philipp Plotnikov Date: Tue, 30 Jan 2024 17:05:36 +0500 Subject: [PATCH 06/11] test: add tests for setHeaderRoute functionality Signed-off-by: Philipp Plotnikov --- pkg/mocks/plugin.go | 16 ++++++++++++++-- pkg/plugin/httproute.go | 3 --- pkg/plugin/plugin_test.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/pkg/mocks/plugin.go b/pkg/mocks/plugin.go index 560df0a..876a17f 100644 --- a/pkg/mocks/plugin.go +++ b/pkg/mocks/plugin.go @@ -16,11 +16,18 @@ const ( HTTPRouteName = "argo-rollouts-http-route" TCPRouteName = "argo-rollouts-tcp-route" Namespace = "default" + ManagedRouteName = "test-http-header-route" ) var ( - port = v1beta1.PortNumber(80) - weight int32 = 0 + port = v1beta1.PortNumber(80) + weight int32 = 0 + httpPathMatchType = v1beta1.PathMatchPathPrefix + httpPathMatchValue = "/" + httpPathMatch = v1beta1.HTTPPathMatch{ + Type: &httpPathMatchType, + Value: &httpPathMatchValue, + } ) var HTTPRouteObj = v1beta1.HTTPRoute{ @@ -58,6 +65,11 @@ var HTTPRouteObj = v1beta1.HTTPRoute{ }, }, }, + Matches: []v1beta1.HTTPRouteMatch{ + { + Path: &httpPathMatch, + }, + }, }, }, }, diff --git a/pkg/plugin/httproute.go b/pkg/plugin/httproute.go index 1af87d9..24947d0 100644 --- a/pkg/plugin/httproute.go +++ b/pkg/plugin/httproute.go @@ -65,7 +65,6 @@ func (r *RpcPlugin) setHTTPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight return pluginTypes.RpcError{} } -// TODO: Add tests func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting *v1alpha1.SetHeaderRoute, gatewayAPIConfig *GatewayAPITrafficRouting) pluginTypes.RpcError { if headerRouting.Match == nil { managedRouteList := []v1alpha1.MangedRoutes{ @@ -135,7 +134,6 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting return pluginTypes.RpcError{} } -// TODO: Add tests func getHTTPHeaderRouteRuleList(headerRouting *v1alpha1.SetHeaderRoute) ([]v1beta1.HTTPHeaderMatch, pluginTypes.RpcError) { httpHeaderRouteRuleList := []v1beta1.HTTPHeaderMatch{} for _, headerRule := range headerRouting.Match { @@ -165,7 +163,6 @@ func getHTTPHeaderRouteRuleList(headerRouting *v1alpha1.SetHeaderRoute) ([]v1bet return httpHeaderRouteRuleList, pluginTypes.RpcError{} } -// TODO: Add tests func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.MangedRoutes, gatewayAPIConfig *GatewayAPITrafficRouting) pluginTypes.RpcError { ctx := context.TODO() httpRouteClient := r.HTTPRouteClient diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index 9d5baf6..701c514 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -12,6 +12,7 @@ import ( rolloutsPlugin "github.com/argoproj/argo-rollouts/rollout/trafficrouting/plugin/rpc" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/gateway-api/apis/v1beta1" log "github.com/sirupsen/logrus" gwFake "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/fake" @@ -118,6 +119,36 @@ func TestRunSuccessfully(t *testing.T) { assert.Equal(t, 100-desiredWeight, *(rpcPluginImp.UpdatedTCPRouteMock.Spec.Rules[0].BackendRefs[0].Weight)) assert.Equal(t, desiredWeight, *(rpcPluginImp.UpdatedTCPRouteMock.Spec.Rules[0].BackendRefs[1].Weight)) }) + t.Run("SetHTTPHeaderRoute", func(t *testing.T) { + headerName := "X-Test" + headerValue := "test" + headerValueType := v1beta1.HeaderMatchRegularExpression + prefixedHeaderValue := headerValue + ".*" + headerMatch := v1alpha1.StringMatch{ + Prefix: headerValue, + } + headerRouting := v1alpha1.SetHeaderRoute{ + Name: mocks.ManagedRouteName, + Match: []v1alpha1.HeaderRoutingMatch{ + { + HeaderName: headerName, + HeaderValue: &headerMatch, + }, + }, + } + err := pluginInstance.SetHeaderRoute(newRollout(mocks.StableServiceName, mocks.CanaryServiceName, mocks.HTTPRoute, mocks.HTTPRouteName), &headerRouting) + + assert.Empty(t, err.Error()) + assert.Equal(t, headerName, string(rpcPluginImp.UpdatedHTTPRouteMock.Spec.Rules[1].Matches[0].Headers[0].Name)) + assert.Equal(t, prefixedHeaderValue, rpcPluginImp.UpdatedHTTPRouteMock.Spec.Rules[1].Matches[0].Headers[0].Value) + assert.Equal(t, headerValueType, *rpcPluginImp.UpdatedHTTPRouteMock.Spec.Rules[1].Matches[0].Headers[0].Type) + }) + t.Run("RemoveHTTPManagedRoutes", func(t *testing.T) { + err := pluginInstance.RemoveManagedRoutes(newRollout(mocks.StableServiceName, mocks.CanaryServiceName, mocks.HTTPRoute, mocks.HTTPRouteName)) + + assert.Empty(t, err.Error()) + assert.Equal(t, 1, len(rpcPluginImp.UpdatedHTTPRouteMock.Spec.Rules)) + }) // Canceling should cause an exit cancel() @@ -149,6 +180,11 @@ func newRollout(stableSvc, canarySvc, routeType, routeName string) *v1alpha1.Rol StableService: stableSvc, CanaryService: canarySvc, TrafficRouting: &v1alpha1.RolloutTrafficRouting{ + ManagedRoutes: []v1alpha1.MangedRoutes{ + { + Name: mocks.ManagedRouteName, + }, + }, Plugins: map[string]json.RawMessage{ PluginName: encodedGatewayAPIConfig, }, From 47f875a6904c47c594595bc6ac4313b97ace3001 Mon Sep 17 00:00:00 2001 From: Philipp Plotnikov Date: Thu, 1 Feb 2024 18:19:12 +0500 Subject: [PATCH 07/11] feat: store data about httpManagedRoutes in configMap Signed-off-by: Philipp Plotnikov --- pkg/mocks/plugin.go | 25 +++-- pkg/plugin/httproute.go | 208 ++++++++++++++++++++++++++++++++------ pkg/plugin/plugin.go | 13 ++- pkg/plugin/plugin_test.go | 11 +- pkg/plugin/tcproute.go | 2 +- pkg/plugin/types.go | 10 +- utils/common.go | 70 +++++++++++++ utils/types.go | 23 +++++ 8 files changed, 312 insertions(+), 50 deletions(-) create mode 100644 utils/types.go diff --git a/pkg/mocks/plugin.go b/pkg/mocks/plugin.go index 876a17f..061a055 100644 --- a/pkg/mocks/plugin.go +++ b/pkg/mocks/plugin.go @@ -1,6 +1,7 @@ package mocks import ( + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" @@ -9,14 +10,15 @@ import ( ) const ( - HTTPRoute = "HTTPRoute" - TCPRoute = "TCPRoute" - StableServiceName = "argo-rollouts-stable-service" - CanaryServiceName = "argo-rollouts-canary-service" - HTTPRouteName = "argo-rollouts-http-route" - TCPRouteName = "argo-rollouts-tcp-route" - Namespace = "default" - ManagedRouteName = "test-http-header-route" + HTTPRoute = "HTTPRoute" + TCPRoute = "TCPRoute" + StableServiceName = "argo-rollouts-stable-service" + CanaryServiceName = "argo-rollouts-canary-service" + HTTPRouteName = "argo-rollouts-http-route" + TCPRouteName = "argo-rollouts-tcp-route" + Namespace = "default" + ConfigMapName = "test-config" + HTTPManagedRouteName = "test-http-header-route" ) var ( @@ -110,3 +112,10 @@ var TCPPRouteObj = v1alpha2.TCPRoute{ }, }, } + +var ConfigMapObj = v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: ConfigMapName, + Namespace: Namespace, + }, +} diff --git a/pkg/plugin/httproute.go b/pkg/plugin/httproute.go index 24947d0..4a01f31 100644 --- a/pkg/plugin/httproute.go +++ b/pkg/plugin/httproute.go @@ -6,12 +6,17 @@ import ( "fmt" "sync" + "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/utils" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" pluginTypes "github.com/argoproj/argo-rollouts/utils/plugin/types" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/gateway-api/apis/v1beta1" ) +const ( + HTTPConfigMapKey = "httpManagedRoutes" +) + var ( httpHeaderRoute = HTTPHeaderRoute{ mutex: sync.Mutex{}, @@ -27,7 +32,7 @@ func (r *RpcPlugin) setHTTPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight ctx := context.TODO() httpRouteClient := r.HTTPRouteClient if !r.IsTest { - gatewayV1beta1 := r.Client.GatewayV1beta1() + gatewayV1beta1 := r.GatewayAPIClientset.GatewayV1beta1() httpRouteClient = gatewayV1beta1.HTTPRoutes(gatewayAPIConfig.Namespace) } httpRoute, err := httpRouteClient.Get(ctx, gatewayAPIConfig.HTTPRoute, metav1.GetOptions{}) @@ -76,9 +81,27 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting } ctx := context.TODO() httpRouteClient := r.HTTPRouteClient + managedRouteMap := httpHeaderRoute.managedRouteMap + clientset := r.TestClientset if !r.IsTest { - gatewayV1beta1 := r.Client.GatewayV1beta1() + gatewayV1beta1 := r.GatewayAPIClientset.GatewayV1beta1() httpRouteClient = gatewayV1beta1.HTTPRoutes(gatewayAPIConfig.Namespace) + clientset = r.Clientset.CoreV1().ConfigMaps(gatewayAPIConfig.Namespace) + } + configMap, err := utils.CreateConfigMap(gatewayAPIConfig.ConfigMap, utils.CreateConfigMapOptions{ + Clientset: clientset, + Ctx: ctx, + }) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } + err = utils.SetConfigMapData(configMap, HTTPConfigMapKey, &managedRouteMap) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } } httpRoute, err := httpRouteClient.Get(ctx, gatewayAPIConfig.HTTPRoute, metav1.GetOptions{}) if err != nil { @@ -93,8 +116,8 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting if rpcError.HasError() { return rpcError } - routeRuleList := HTTPRouteRuleList(httpRoute.Spec.Rules) - canaryBackendRef, err := getBackendRef[*HTTPBackendRef, HTTPBackendRefList](string(canaryServiceName), routeRuleList) + httpRouteRuleList := HTTPRouteRuleList(httpRoute.Spec.Rules) + canaryBackendRef, err := getBackendRef[*HTTPBackendRef, HTTPBackendRefList](string(canaryServiceName), httpRouteRuleList) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -103,7 +126,7 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting httpHeaderRouteRule := &httpHeaderRoute.rule httpHeaderRouteRule.Matches = []v1beta1.HTTPRouteMatch{ { - Path: routeRuleList[0].Matches[0].Path, + Path: httpRouteRuleList[0].Matches[0].Path, Headers: httpHeaderRouteRuleList, }, } @@ -119,17 +142,71 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting }, }, } - routeRuleList = append(routeRuleList, *httpHeaderRouteRule) - httpRoute.Spec.Rules = routeRuleList - updatedHTTPRoute, err := httpRouteClient.Update(ctx, httpRoute, metav1.UpdateOptions{}) - if r.IsTest { - r.UpdatedHTTPRouteMock = updatedHTTPRoute + httpRouteRuleList = append(httpRouteRuleList, *httpHeaderRouteRule) + oldHTTPRuleList := httpRoute.Spec.Rules + httpRoute.Spec.Rules = httpRouteRuleList + oldConfigMapData := make(map[string]int) + err = utils.SetConfigMapData(configMap, HTTPConfigMapKey, &oldConfigMapData) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } + taskList := []utils.Task{ + { + Action: func() error { + updatedHTTPRoute, err := httpRouteClient.Update(ctx, httpRoute, metav1.UpdateOptions{}) + if r.IsTest { + r.UpdatedHTTPRouteMock = updatedHTTPRoute + } + if err != nil { + return err + } + return nil + }, + ReverseAction: func() error { + httpRoute.Spec.Rules = oldHTTPRuleList + updatedHTTPRoute, err := httpRouteClient.Update(ctx, httpRoute, metav1.UpdateOptions{}) + if r.IsTest { + r.UpdatedHTTPRouteMock = updatedHTTPRoute + } + if err != nil { + return err + } + return nil + }, + }, + { + Action: func() error { + managedRouteMap[headerRouting.Name] = len(httpRouteRuleList) - 1 + err = utils.UpdateConfigMapData(configMap, managedRouteMap, utils.UpdateConfigMapOptions{ + Clientset: clientset, + ConfigMapKey: HTTPConfigMapKey, + Ctx: ctx, + }) + if err != nil { + return err + } + return nil + }, + ReverseAction: func() error { + err = utils.UpdateConfigMapData(configMap, oldConfigMapData, utils.UpdateConfigMapOptions{ + Clientset: clientset, + ConfigMapKey: HTTPConfigMapKey, + Ctx: ctx, + }) + if err != nil { + return err + } + return nil + }, + }, } + err = utils.DoTransaction(r.LogCtx, taskList...) if err != nil { - msg := fmt.Sprintf(GatewayAPIUpdateError, httpRoute.GetName(), err) - r.LogCtx.Error(msg) - } else { - httpHeaderRoute.managedRouteMap[headerRouting.Name] = len(routeRuleList) - 1 + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } } return pluginTypes.RpcError{} } @@ -166,9 +243,24 @@ func getHTTPHeaderRouteRuleList(headerRouting *v1alpha1.SetHeaderRoute) ([]v1bet func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.MangedRoutes, gatewayAPIConfig *GatewayAPITrafficRouting) pluginTypes.RpcError { ctx := context.TODO() httpRouteClient := r.HTTPRouteClient + clientset := r.TestClientset + managedRouteMap := httpHeaderRoute.managedRouteMap if !r.IsTest { - gatewayV1beta1 := r.Client.GatewayV1beta1() + gatewayV1beta1 := r.GatewayAPIClientset.GatewayV1beta1() httpRouteClient = gatewayV1beta1.HTTPRoutes(gatewayAPIConfig.Namespace) + clientset = r.Clientset.CoreV1().ConfigMaps(gatewayAPIConfig.Namespace) + } + configMap, err := clientset.Get(ctx, gatewayAPIConfig.ConfigMap, metav1.GetOptions{}) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } + err = utils.SetConfigMapData(configMap, HTTPConfigMapKey, &managedRouteMap) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } } httpRoute, err := httpRouteClient.Get(ctx, gatewayAPIConfig.HTTPRoute, metav1.GetOptions{}) if err != nil { @@ -177,28 +269,78 @@ func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.Mang } } httpRouteRuleList := HTTPRouteRuleList(httpRoute.Spec.Rules) - httpHeaderManagedRouteMap := httpHeaderRoute.managedRouteMap for _, managedRoute := range managedRouteNameList { managedRouteName := managedRoute.Name - httpRouteRuleListIndex, isOk := httpHeaderManagedRouteMap[managedRouteName] + httpRouteRuleListIndex, isOk := managedRouteMap[managedRouteName] if !isOk { r.LogCtx.Logger.Info(fmt.Sprintf("%s is not in httpHeaderManagedRouteMap", managedRouteName)) - return pluginTypes.RpcError{} - } - updatedHTTPRouteRuleList := httpRouteRuleList[:httpRouteRuleListIndex] - if httpRouteRuleListIndex+1 < len(httpRouteRuleList) { - updatedHTTPRouteRuleList = append(updatedHTTPRouteRuleList, httpRouteRuleList[httpRouteRuleListIndex+1:]...) - } - httpRoute.Spec.Rules = updatedHTTPRouteRuleList - updatedHTTPRoute, err := httpRouteClient.Update(ctx, httpRoute, metav1.UpdateOptions{}) - if r.IsTest { - r.UpdatedHTTPRouteMock = updatedHTTPRoute - } - if err != nil { - msg := fmt.Sprintf(GatewayAPIUpdateError, httpRoute.GetName(), err) - r.LogCtx.Error(msg) - } else { - delete(httpHeaderManagedRouteMap, managedRouteName) + continue + } + httpRouteRuleList = utils.RemoveIndex(httpRouteRuleList, httpRouteRuleListIndex) + delete(managedRouteMap, managedRouteName) + } + oldHTTPRuleList := httpRoute.Spec.Rules + httpRoute.Spec.Rules = httpRouteRuleList + oldConfigMapData := make(map[string]int) + err = utils.SetConfigMapData(configMap, HTTPConfigMapKey, &oldConfigMapData) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } + taskList := []utils.Task{ + { + Action: func() error { + updatedHTTPRoute, err := httpRouteClient.Update(ctx, httpRoute, metav1.UpdateOptions{}) + if r.IsTest { + r.UpdatedHTTPRouteMock = updatedHTTPRoute + } + if err != nil { + return err + } + return nil + }, + ReverseAction: func() error { + httpRoute.Spec.Rules = oldHTTPRuleList + updatedHTTPRoute, err := httpRouteClient.Update(ctx, httpRoute, metav1.UpdateOptions{}) + if r.IsTest { + r.UpdatedHTTPRouteMock = updatedHTTPRoute + } + if err != nil { + return err + } + return nil + }, + }, + { + Action: func() error { + err = utils.UpdateConfigMapData(configMap, managedRouteMap, utils.UpdateConfigMapOptions{ + Clientset: clientset, + ConfigMapKey: HTTPConfigMapKey, + Ctx: ctx, + }) + if err != nil { + return err + } + return nil + }, + ReverseAction: func() error { + err = utils.UpdateConfigMapData(configMap, oldConfigMapData, utils.UpdateConfigMapOptions{ + Clientset: clientset, + ConfigMapKey: HTTPConfigMapKey, + Ctx: ctx, + }) + if err != nil { + return err + } + return nil + }, + }, + } + err = utils.DoTransaction(r.LogCtx, taskList...) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), } } return pluginTypes.RpcError{} diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index ddfe164..5138da3 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -6,6 +6,7 @@ import ( "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/utils" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" pluginTypes "github.com/argoproj/argo-rollouts/utils/plugin/types" + "k8s.io/client-go/kubernetes" gatewayApiClientset "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" ) @@ -24,13 +25,20 @@ func (r *RpcPlugin) InitPlugin() pluginTypes.RpcError { ErrorString: err.Error(), } } - clientset, err := gatewayApiClientset.NewForConfig(kubeConfig) + gatewayAPIClientset, err := gatewayApiClientset.NewForConfig(kubeConfig) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), } } - r.Client = clientset + clientset, err := kubernetes.NewForConfig(kubeConfig) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } + r.GatewayAPIClientset = gatewayAPIClientset + r.Clientset = clientset return pluginTypes.RpcError{} } @@ -65,6 +73,7 @@ func (r *RpcPlugin) SetWeight(rollout *v1alpha1.Rollout, desiredWeight int32, ad return pluginTypes.RpcError{} } +// TODO: Take path from rule where canary service and stable service func (r *RpcPlugin) SetHeaderRoute(rollout *v1alpha1.Rollout, headerRouting *v1alpha1.SetHeaderRoute) pluginTypes.RpcError { gatewayAPIConfig := GatewayAPITrafficRouting{} err := json.Unmarshal(rollout.Spec.Strategy.Canary.TrafficRouting.Plugins[PluginName], &gatewayAPIConfig) diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index 701c514..7d6fe7b 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -12,6 +12,7 @@ import ( rolloutsPlugin "github.com/argoproj/argo-rollouts/rollout/trafficrouting/plugin/rpc" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" "sigs.k8s.io/gateway-api/apis/v1beta1" log "github.com/sirupsen/logrus" @@ -37,8 +38,9 @@ func TestRunSuccessfully(t *testing.T) { rpcPluginImp := &RpcPlugin{ LogCtx: logCtx, IsTest: true, - HTTPRouteClient: gwFake.NewSimpleClientset(&(mocks.HTTPRouteObj)).GatewayV1beta1().HTTPRoutes(mocks.Namespace), - TCPRouteClient: gwFake.NewSimpleClientset(&(mocks.TCPPRouteObj)).GatewayV1alpha2().TCPRoutes(mocks.Namespace), + HTTPRouteClient: gwFake.NewSimpleClientset(&mocks.HTTPRouteObj).GatewayV1beta1().HTTPRoutes(mocks.Namespace), + TCPRouteClient: gwFake.NewSimpleClientset(&mocks.TCPPRouteObj).GatewayV1alpha2().TCPRoutes(mocks.Namespace), + TestClientset: fake.NewSimpleClientset(&mocks.ConfigMapObj).CoreV1().ConfigMaps(mocks.Namespace), } // pluginMap is the map of plugins we can dispense. @@ -128,7 +130,7 @@ func TestRunSuccessfully(t *testing.T) { Prefix: headerValue, } headerRouting := v1alpha1.SetHeaderRoute{ - Name: mocks.ManagedRouteName, + Name: mocks.HTTPManagedRouteName, Match: []v1alpha1.HeaderRoutingMatch{ { HeaderName: headerName, @@ -158,6 +160,7 @@ func TestRunSuccessfully(t *testing.T) { func newRollout(stableSvc, canarySvc, routeType, routeName string) *v1alpha1.Rollout { gatewayAPIConfig := GatewayAPITrafficRouting{ Namespace: mocks.Namespace, + ConfigMap: mocks.ConfigMapName, } switch routeType { case mocks.HTTPRoute: @@ -182,7 +185,7 @@ func newRollout(stableSvc, canarySvc, routeType, routeName string) *v1alpha1.Rol TrafficRouting: &v1alpha1.RolloutTrafficRouting{ ManagedRoutes: []v1alpha1.MangedRoutes{ { - Name: mocks.ManagedRouteName, + Name: mocks.HTTPManagedRouteName, }, }, Plugins: map[string]json.RawMessage{ diff --git a/pkg/plugin/tcproute.go b/pkg/plugin/tcproute.go index e80e512..d67b2af 100644 --- a/pkg/plugin/tcproute.go +++ b/pkg/plugin/tcproute.go @@ -14,7 +14,7 @@ func (r *RpcPlugin) setTCPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight i ctx := context.TODO() tcpRouteClient := r.TCPRouteClient if !r.IsTest { - gatewayV1alpha2 := r.Client.GatewayV1alpha2() + gatewayV1alpha2 := r.GatewayAPIClientset.GatewayV1alpha2() tcpRouteClient = gatewayV1alpha2.TCPRoutes(gatewayAPIConfig.Namespace) } tcpRoute, err := tcpRouteClient.Get(ctx, gatewayAPIConfig.TCPRoute, metav1.GetOptions{}) diff --git a/pkg/plugin/types.go b/pkg/plugin/types.go index 6167003..7e44a15 100644 --- a/pkg/plugin/types.go +++ b/pkg/plugin/types.go @@ -4,9 +4,11 @@ import ( "sync" "github.com/sirupsen/logrus" + "k8s.io/client-go/kubernetes" + v1 "k8s.io/client-go/kubernetes/typed/core/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/apis/v1beta1" - gatewayApiClientset "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" + gatewayAPIClientset "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" gatewayApiv1alpha2 "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/typed/apis/v1alpha2" gatewayApiv1beta1 "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/typed/apis/v1beta1" ) @@ -14,7 +16,9 @@ import ( type RpcPlugin struct { IsTest bool LogCtx *logrus.Entry - Client *gatewayApiClientset.Clientset + GatewayAPIClientset *gatewayAPIClientset.Clientset + Clientset *kubernetes.Clientset + TestClientset v1.ConfigMapInterface UpdatedHTTPRouteMock *v1beta1.HTTPRoute UpdatedTCPRouteMock *v1alpha2.TCPRoute HTTPRouteClient gatewayApiv1beta1.HTTPRouteInterface @@ -30,6 +34,8 @@ type GatewayAPITrafficRouting struct { TCPRoute string `json:"tcpRoute,omitempty"` // Namespace refers to the namespace of the specified resource Namespace string `json:"namespace"` + // ConfigMap name refers to the config map where plugin stores data about managed routes + ConfigMap string `json:"configMap,omitempty"` } type HTTPHeaderRoute struct { diff --git a/utils/common.go b/utils/common.go index 80e986b..39fdcd0 100644 --- a/utils/common.go +++ b/utils/common.go @@ -1,10 +1,14 @@ package utils import ( + "encoding/json" "strings" pluginTypes "github.com/argoproj/argo-rollouts/utils/plugin/types" log "github.com/sirupsen/logrus" + v1 "k8s.io/api/core/v1" + kubeErrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" ) @@ -48,3 +52,69 @@ func CreateFormatter(logFormat string) log.Formatter { return formatType } + +func CreateConfigMap(name string, options CreateConfigMapOptions) (*v1.ConfigMap, error) { + clientset := options.Clientset + ctx := options.Ctx + configMap, err := clientset.Get(ctx, name, metav1.GetOptions{}) + if err != nil && !kubeErrors.IsNotFound(err) { + return nil, err + } + if err == nil { + return configMap, err + } + configMap.Name = name + configMap, err = clientset.Create(ctx, configMap, metav1.CreateOptions{}) + if err != nil { + return nil, err + } + return configMap, err +} + +func SetConfigMapData(configMap *v1.ConfigMap, configMapKey string, destination any) error { + if configMap.Data != nil && configMap.Data[configMapKey] != "" { + err := json.Unmarshal([]byte(configMap.Data[configMapKey]), &destination) + if err != nil { + return err + } + } + return nil +} + +func UpdateConfigMapData(configMap *v1.ConfigMap, configMapData any, options UpdateConfigMapOptions) error { + clientset := options.Clientset + rawConfigMapData, err := json.Marshal(configMapData) + if err != nil { + return err + } + if configMap.Data == nil { + configMap.Data = make(map[string]string) + } + configMap.Data[options.ConfigMapKey] = string(rawConfigMapData) + _, err = clientset.Update(options.Ctx, configMap, metav1.UpdateOptions{}) + return err +} + +func RemoveIndex[T any](original []T, index int) []T { + result := original[:index] + return append(result, original[index+1:]...) +} + +func DoTransaction(logCtx *log.Entry, taskList ...Task) error { + var err, reverseErr error + for index, task := range taskList { + err = task.Action() + if err == nil { + continue + } + logCtx.Error(err.Error()) + for i := index - 1; i > -1; i-- { + reverseErr = taskList[i].ReverseAction() + if err != nil { + return reverseErr + } + } + return err + } + return nil +} diff --git a/utils/types.go b/utils/types.go new file mode 100644 index 0000000..ec82fde --- /dev/null +++ b/utils/types.go @@ -0,0 +1,23 @@ +package utils + +import ( + "context" + + v1 "k8s.io/client-go/kubernetes/typed/core/v1" +) + +type CreateConfigMapOptions struct { + Clientset v1.ConfigMapInterface + Ctx context.Context +} + +type UpdateConfigMapOptions struct { + Clientset v1.ConfigMapInterface + ConfigMapKey string + Ctx context.Context +} + +type Task struct { + Action func() error + ReverseAction func() error +} From dad06d9df1f73cea81f385d11c6fd34a24d3bd3f Mon Sep 17 00:00:00 2001 From: Philipp Plotnikov Date: Fri, 2 Feb 2024 17:36:46 +0500 Subject: [PATCH 08/11] fix: fix getBackendRef function and add getRouteRule function Signed-off-by: Philipp Plotnikov --- pkg/plugin/errors.go | 15 ++++---- pkg/plugin/httproute.go | 81 ++++++++++++++++++++++------------------- pkg/plugin/plugin.go | 58 +++++++++++++++++------------ pkg/plugin/tcproute.go | 40 +++++++++----------- pkg/plugin/types.go | 36 ++++++++---------- utils/common.go | 2 +- 6 files changed, 118 insertions(+), 114 deletions(-) diff --git a/pkg/plugin/errors.go b/pkg/plugin/errors.go index 583e7dc..c0c6f8e 100644 --- a/pkg/plugin/errors.go +++ b/pkg/plugin/errors.go @@ -1,12 +1,11 @@ package plugin const ( - GatewayAPIUpdateError = "Error updating Gateway API %q: %s" - GatewayAPIManifestError = "httpRoute and tcpRoute fields are empty. tcpRoute or httpRoute should be set" - HTTPRouteFieldIsEmptyError = "httpRoute field is empty. It has to be set to remove managed routes" - InvalidHeaderMatchTypeError = "Invalid header match type" - BackendRefWasNotFoundInHTTPRouteError = "backendRef was not found in httpRoute" - BackendRefListWasNotFoundInHTTPRouteError = "backendRef list was not found in httpRoute" - BackendRefWasNotFoundInTCPRouteError = "backendRef was not found in tcpRoute" - BackendRefListWasNotFoundInTCPRouteError = "backendRef list was not found in tcpRoute" + GatewayAPIUpdateError = "Error updating Gateway API %q: %s" + GatewayAPIManifestError = "httpRoute and tcpRoute fields are empty. tcpRoute or httpRoute should be set" + HTTPRouteFieldIsEmptyError = "httpRoute field is empty. It has to be set to remove managed routes" + InvalidHeaderMatchTypeError = "Invalid header match type" + BackendRefWasNotFoundInHTTPRouteError = "backendRef was not found in httpRoute" + BackendRefWasNotFoundInTCPRouteError = "backendRef was not found in tcpRoute" + BackendRefListWasNotFoundInTCPRouteError = "backendRef list was not found in tcpRoute" ) diff --git a/pkg/plugin/httproute.go b/pkg/plugin/httproute.go index 4a01f31..e1b1fb2 100644 --- a/pkg/plugin/httproute.go +++ b/pkg/plugin/httproute.go @@ -21,10 +21,6 @@ var ( httpHeaderRoute = HTTPHeaderRoute{ mutex: sync.Mutex{}, managedRouteMap: make(map[string]int), - rule: v1beta1.HTTPRouteRule{ - Matches: []v1beta1.HTTPRouteMatch{}, - BackendRefs: []v1beta1.HTTPBackendRef{}, - }, } ) @@ -44,14 +40,14 @@ func (r *RpcPlugin) setHTTPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight canaryServiceName := rollout.Spec.Strategy.Canary.CanaryService stableServiceName := rollout.Spec.Strategy.Canary.StableService routeRuleList := HTTPRouteRuleList(httpRoute.Spec.Rules) - canaryBackendRef, err := getBackendRef[*HTTPBackendRef, HTTPBackendRefList](canaryServiceName, routeRuleList) + canaryBackendRef, err := getBackendRef[*HTTPBackendRef, *HTTPRouteRule](canaryServiceName, routeRuleList) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), } } canaryBackendRef.Weight = &desiredWeight - stableBackendRef, err := getBackendRef[*HTTPBackendRef, HTTPBackendRefList](stableServiceName, routeRuleList) + stableBackendRef, err := getBackendRef[*HTTPBackendRef, *HTTPRouteRule](stableServiceName, routeRuleList) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -97,7 +93,7 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting ErrorString: err.Error(), } } - err = utils.SetConfigMapData(configMap, HTTPConfigMapKey, &managedRouteMap) + err = utils.GetConfigMapData(configMap, HTTPConfigMapKey, &managedRouteMap) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -110,6 +106,7 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting } } canaryServiceName := v1beta1.ObjectName(rollout.Spec.Strategy.Canary.CanaryService) + stableServiceName := rollout.Spec.Strategy.Canary.StableService canaryServiceKind := v1beta1.Kind("Service") canaryServiceGroup := v1beta1.Group("") httpHeaderRouteRuleList, rpcError := getHTTPHeaderRouteRuleList(headerRouting) @@ -117,18 +114,30 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting return rpcError } httpRouteRuleList := HTTPRouteRuleList(httpRoute.Spec.Rules) - canaryBackendRef, err := getBackendRef[*HTTPBackendRef, HTTPBackendRefList](string(canaryServiceName), httpRouteRuleList) + backendRefNameList := []string{string(canaryServiceName), stableServiceName} + httpRouteRule, err := getRouteRule[*HTTPBackendRef, *HTTPRouteRule](httpRouteRuleList, backendRefNameList...) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), } } - httpHeaderRouteRule := &httpHeaderRoute.rule - httpHeaderRouteRule.Matches = []v1beta1.HTTPRouteMatch{ - { - Path: httpRouteRuleList[0].Matches[0].Path, - Headers: httpHeaderRouteRuleList, - }, + var canaryBackendRef *HTTPBackendRef + for i := 0; i < len(httpRouteRule.BackendRefs); i++ { + backendRef := httpRouteRule.BackendRefs[i] + if canaryServiceName == backendRef.Name { + canaryBackendRef = (*HTTPBackendRef)(&backendRef) + } + } + httpHeaderRouteRule := v1beta1.HTTPRouteRule{ + Matches: []v1beta1.HTTPRouteMatch{}, + BackendRefs: []v1beta1.HTTPBackendRef{}, + } + for i := 0; i < len(httpRouteRule.Matches); i++ { + httpHeaderRouteRule.Matches = append(httpHeaderRouteRule.Matches, v1beta1.HTTPRouteMatch{ + Path: httpRouteRule.Matches[i].Path, + Headers: httpHeaderRouteRuleList, + QueryParams: httpRouteRule.Matches[i].QueryParams, + }) } httpHeaderRouteRule.BackendRefs = []v1beta1.HTTPBackendRef{ { @@ -142,11 +151,11 @@ func (r *RpcPlugin) setHTTPHeaderRoute(rollout *v1alpha1.Rollout, headerRouting }, }, } - httpRouteRuleList = append(httpRouteRuleList, *httpHeaderRouteRule) + httpRouteRuleList = append(httpRouteRuleList, httpHeaderRouteRule) oldHTTPRuleList := httpRoute.Spec.Rules httpRoute.Spec.Rules = httpRouteRuleList oldConfigMapData := make(map[string]int) - err = utils.SetConfigMapData(configMap, HTTPConfigMapKey, &oldConfigMapData) + err = utils.GetConfigMapData(configMap, HTTPConfigMapKey, &oldConfigMapData) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -256,7 +265,7 @@ func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.Mang ErrorString: err.Error(), } } - err = utils.SetConfigMapData(configMap, HTTPConfigMapKey, &managedRouteMap) + err = utils.GetConfigMapData(configMap, HTTPConfigMapKey, &managedRouteMap) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -282,7 +291,7 @@ func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.Mang oldHTTPRuleList := httpRoute.Spec.Rules httpRoute.Spec.Rules = httpRouteRuleList oldConfigMapData := make(map[string]int) - err = utils.SetConfigMapData(configMap, HTTPConfigMapKey, &oldConfigMapData) + err = utils.GetConfigMapData(configMap, HTTPConfigMapKey, &oldConfigMapData) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -346,39 +355,35 @@ func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.Mang return pluginTypes.RpcError{} } -func (r HTTPRouteRuleList) Iterator() (GatewayAPIRouteRuleIterator[*HTTPBackendRef, HTTPBackendRefList], bool) { - ruleList := r +func (r *HTTPRouteRule) Iterator() (GatewayAPIRouteRuleIterator[*HTTPBackendRef], bool) { + backendRefList := r.BackendRefs index := 0 - next := func() (HTTPBackendRefList, bool) { - if len(ruleList) == index { + next := func() (*HTTPBackendRef, bool) { + if len(backendRefList) == index { return nil, false } - backendRefList := HTTPBackendRefList(ruleList[index].BackendRefs) + backendRef := (*HTTPBackendRef)(&backendRefList[index]) index = index + 1 - return backendRefList, len(ruleList) > index + return backendRef, len(backendRefList) > index } - return next, len(ruleList) != index -} - -func (r HTTPRouteRuleList) Error() error { - return errors.New(BackendRefListWasNotFoundInHTTPRouteError) + return next, len(backendRefList) > index } -func (r HTTPBackendRefList) Iterator() (GatewayAPIBackendRefIterator[*HTTPBackendRef], bool) { - backendRefList := r +func (r HTTPRouteRuleList) Iterator() (GatewayAPIRouteRuleListIterator[*HTTPBackendRef, *HTTPRouteRule], bool) { + routeRuleList := r index := 0 - next := func() (*HTTPBackendRef, bool) { - if len(backendRefList) == index { + next := func() (*HTTPRouteRule, bool) { + if len(routeRuleList) == index { return nil, false } - backendRef := (*HTTPBackendRef)(&backendRefList[index]) - index = index + 1 - return backendRef, len(backendRefList) > index + routeRule := (*HTTPRouteRule)(&routeRuleList[index]) + index++ + return routeRule, len(routeRuleList) > index } - return next, len(backendRefList) > index + return next, len(routeRuleList) != index } -func (r HTTPBackendRefList) Error() error { +func (r HTTPRouteRuleList) Error() error { return errors.New(BackendRefWasNotFoundInHTTPRouteError) } diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 5138da3..b3b5ca3 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -73,7 +73,6 @@ func (r *RpcPlugin) SetWeight(rollout *v1alpha1.Rollout, desiredWeight int32, ad return pluginTypes.RpcError{} } -// TODO: Take path from rule where canary service and stable service func (r *RpcPlugin) SetHeaderRoute(rollout *v1alpha1.Rollout, headerRouting *v1alpha1.SetHeaderRoute) pluginTypes.RpcError { gatewayAPIConfig := GatewayAPITrafficRouting{} err := json.Unmarshal(rollout.Spec.Strategy.Canary.TrafficRouting.Plugins[PluginName], &gatewayAPIConfig) @@ -136,34 +135,45 @@ func (r *RpcPlugin) Type() string { return Type } -func getBackendRefList[T1 GatewayAPIBackendRef, T2 GatewayAPIBackendRefList[T1]](ruleList GatewayAPIRouteRuleCollection[T1, T2]) (T2, error) { - var backendRefList T2 - for next, hasNext := ruleList.Iterator(); hasNext; { - backendRefList, hasNext = next() - if backendRefList == nil { +func getRouteRule[T1 GatewayAPIBackendRef, T2 GatewayAPIRouteRule[T1], T3 GatewayAPIRouteRuleList[T1, T2]](routeRuleList T3, backendRefNameList ...string) (T2, error) { + var backendRef T1 + var routeRule T2 + isFound := false + for next, hasNext := routeRuleList.Iterator(); hasNext; { + routeRule, hasNext = next() + _, hasNext := routeRule.Iterator() + if !hasNext { continue } - return backendRefList, nil + for _, backendRefName := range backendRefNameList { + isFound = false + for next, hasNext := routeRule.Iterator(); hasNext; { + backendRef, hasNext = next() + if backendRefName == backendRef.GetName() { + isFound = true + continue + } + } + if !isFound { + break + } + } + return routeRule, nil } - return nil, ruleList.Error() + return nil, routeRuleList.Error() } -func getBackendRef[T1 GatewayAPIBackendRef, T2 GatewayAPIBackendRefList[T1]](backendRefName string, ruleList GatewayAPIRouteRuleCollection[T1, T2]) (T1, error) { - var selectedService, backendRef T1 - backendRefList, err := getBackendRefList[T1, T2](ruleList) - if err != nil { - return nil, err - } - for next, hasNext := backendRefList.Iterator(); hasNext; { - backendRef, hasNext = next() - nameOfCurrentService := backendRef.GetName() - if nameOfCurrentService == backendRefName { - selectedService = backendRef - break +func getBackendRef[T1 GatewayAPIBackendRef, T2 GatewayAPIRouteRule[T1], T3 GatewayAPIRouteRuleList[T1, T2]](backendRefName string, routeRuleList T3) (T1, error) { + var backendRef T1 + var routeRule T2 + for next, hasNext := routeRuleList.Iterator(); hasNext; { + routeRule, hasNext = next() + for next, hasNext := routeRule.Iterator(); hasNext; { + backendRef, hasNext = next() + if backendRefName == backendRef.GetName() { + return backendRef, nil + } } } - if selectedService == nil { - return nil, backendRefList.Error() - } - return selectedService, nil + return nil, routeRuleList.Error() } diff --git a/pkg/plugin/tcproute.go b/pkg/plugin/tcproute.go index d67b2af..7a9aa3a 100644 --- a/pkg/plugin/tcproute.go +++ b/pkg/plugin/tcproute.go @@ -26,14 +26,14 @@ func (r *RpcPlugin) setTCPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight i canaryServiceName := rollout.Spec.Strategy.Canary.CanaryService stableServiceName := rollout.Spec.Strategy.Canary.StableService routeRuleList := TCPRouteRuleList(tcpRoute.Spec.Rules) - canaryBackendRef, err := getBackendRef[*TCPBackendRef, TCPBackendRefList](canaryServiceName, routeRuleList) + canaryBackendRef, err := getBackendRef[*TCPBackendRef, *TCPRouteRule](canaryServiceName, routeRuleList) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), } } canaryBackendRef.Weight = &desiredWeight - stableBackendRef, err := getBackendRef[*TCPBackendRef, TCPBackendRefList](stableServiceName, routeRuleList) + stableBackendRef, err := getBackendRef[*TCPBackendRef, *TCPRouteRule](stableServiceName, routeRuleList) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -52,40 +52,36 @@ func (r *RpcPlugin) setTCPRouteWeight(rollout *v1alpha1.Rollout, desiredWeight i return pluginTypes.RpcError{} } -func (r TCPRouteRuleList) Iterator() (GatewayAPIRouteRuleIterator[*TCPBackendRef, TCPBackendRefList], bool) { - ruleList := r +func (r *TCPRouteRule) Iterator() (GatewayAPIRouteRuleIterator[*TCPBackendRef], bool) { + backendRefList := r.BackendRefs index := 0 - next := func() (TCPBackendRefList, bool) { - if len(ruleList) == index { + next := func() (*TCPBackendRef, bool) { + if len(backendRefList) == index { return nil, false } - backendRefList := TCPBackendRefList(ruleList[index].BackendRefs) + backendRef := (*TCPBackendRef)(&backendRefList[index]) index = index + 1 - return backendRefList, len(ruleList) > index + return backendRef, len(backendRefList) > index } - return next, len(ruleList) > index -} - -func (r TCPRouteRuleList) Error() error { - return errors.New(BackendRefListWasNotFoundInTCPRouteError) + return next, len(backendRefList) > index } -func (r TCPBackendRefList) Iterator() (GatewayAPIBackendRefIterator[*TCPBackendRef], bool) { - backendRefList := r +func (r TCPRouteRuleList) Iterator() (GatewayAPIRouteRuleListIterator[*TCPBackendRef, *TCPRouteRule], bool) { + routeRuleList := r index := 0 - next := func() (*TCPBackendRef, bool) { - if len(backendRefList) == index { + next := func() (*TCPRouteRule, bool) { + if len(routeRuleList) == index { return nil, false } - backendRef := (*TCPBackendRef)(&backendRefList[index]) + routeRule := (*TCPRouteRule)(&routeRuleList[index]) index = index + 1 - return backendRef, len(backendRefList) > index + return routeRule, len(routeRuleList) > index } - return next, len(backendRefList) > index + return next, len(routeRuleList) > index } -func (r TCPBackendRefList) Error() error { - return errors.New(BackendRefWasNotFoundInTCPRouteError) +func (r TCPRouteRuleList) Error() error { + return errors.New(BackendRefListWasNotFoundInTCPRouteError) } func (r *TCPBackendRef) GetName() string { diff --git a/pkg/plugin/types.go b/pkg/plugin/types.go index 7e44a15..af5d0f9 100644 --- a/pkg/plugin/types.go +++ b/pkg/plugin/types.go @@ -41,42 +41,36 @@ type GatewayAPITrafficRouting struct { type HTTPHeaderRoute struct { mutex sync.Mutex managedRouteMap map[string]int - rule v1beta1.HTTPRouteRule } -type HTTPBackendRef v1beta1.HTTPBackendRef +type HTTPRouteRule v1beta1.HTTPRouteRule -type TCPBackendRef v1beta1.BackendRef +type TCPRouteRule v1alpha2.TCPRouteRule type HTTPRouteRuleList []v1beta1.HTTPRouteRule type TCPRouteRuleList []v1alpha2.TCPRouteRule -type HTTPBackendRefList []v1beta1.HTTPBackendRef - -type TCPBackendRefList []v1beta1.BackendRef +type HTTPBackendRef v1beta1.HTTPBackendRef -type GatewayAPIBackendRef interface { - *HTTPBackendRef | *TCPBackendRef - GetName() string -} +type TCPBackendRef v1beta1.BackendRef -type GatewayAPIBackendRefList[T GatewayAPIBackendRef] interface { - HTTPBackendRefList | TCPBackendRefList - Iterator() (GatewayAPIBackendRefIterator[T], bool) - Error() error +type GatewayAPIRouteRule[T1 GatewayAPIBackendRef] interface { + *HTTPRouteRule | *TCPRouteRule + Iterator() (GatewayAPIRouteRuleIterator[T1], bool) } -type GatewayAPIRouteRuleCollection[T1 GatewayAPIBackendRef, T2 GatewayAPIBackendRefList[T1]] interface { - Iterator() (GatewayAPIRouteRuleIterator[T1, T2], bool) +type GatewayAPIRouteRuleList[T1 GatewayAPIBackendRef, T2 GatewayAPIRouteRule[T1]] interface { + HTTPRouteRuleList | TCPRouteRuleList + Iterator() (GatewayAPIRouteRuleListIterator[T1, T2], bool) Error() error } -type GatewayAPIBackendRefCollection[T GatewayAPIBackendRef] interface { - Iterator() (GatewayAPIBackendRefIterator[T], bool) - Error() error +type GatewayAPIBackendRef interface { + *HTTPBackendRef | *TCPBackendRef + GetName() string } -type GatewayAPIRouteRuleIterator[T1 GatewayAPIBackendRef, T2 GatewayAPIBackendRefList[T1]] func() (T2, bool) +type GatewayAPIRouteRuleListIterator[T1 GatewayAPIBackendRef, T2 GatewayAPIRouteRule[T1]] func() (T2, bool) -type GatewayAPIBackendRefIterator[T GatewayAPIBackendRef] func() (T, bool) +type GatewayAPIRouteRuleIterator[T1 GatewayAPIBackendRef] func() (T1, bool) diff --git a/utils/common.go b/utils/common.go index 39fdcd0..caae18a 100644 --- a/utils/common.go +++ b/utils/common.go @@ -71,7 +71,7 @@ func CreateConfigMap(name string, options CreateConfigMapOptions) (*v1.ConfigMap return configMap, err } -func SetConfigMapData(configMap *v1.ConfigMap, configMapKey string, destination any) error { +func GetConfigMapData(configMap *v1.ConfigMap, configMapKey string, destination any) error { if configMap.Data != nil && configMap.Data[configMapKey] != "" { err := json.Unmarshal([]byte(configMap.Data[configMapKey]), &destination) if err != nil { From c32a95352fcfaa9a8de2fcdae84ca17036a6aace Mon Sep 17 00:00:00 2001 From: Philipp Plotnikov Date: Fri, 2 Feb 2024 20:29:09 +0500 Subject: [PATCH 09/11] refactor: add getGatewayAPITracfficRoutingConfig function Signed-off-by: Philipp Plotnikov --- internal/defaults/defaults.go | 3 +++ {utils => internal/utils}/common.go | 0 {utils => internal/utils}/types.go | 0 main.go | 2 +- pkg/plugin/httproute.go | 2 +- pkg/plugin/plugin.go | 20 +++++++++++++------- pkg/plugin/plugin_test.go | 2 +- 7 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 internal/defaults/defaults.go rename {utils => internal/utils}/common.go (100%) rename {utils => internal/utils}/types.go (100%) diff --git a/internal/defaults/defaults.go b/internal/defaults/defaults.go new file mode 100644 index 0000000..e15c347 --- /dev/null +++ b/internal/defaults/defaults.go @@ -0,0 +1,3 @@ +package defaults + +const ConfigMap = "argo-gatewayapi-configmap" diff --git a/utils/common.go b/internal/utils/common.go similarity index 100% rename from utils/common.go rename to internal/utils/common.go diff --git a/utils/types.go b/internal/utils/types.go similarity index 100% rename from utils/types.go rename to internal/utils/types.go diff --git a/main.go b/main.go index f2b3e0b..f43027e 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,8 @@ package main import ( + "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/internal/utils" "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/pkg/plugin" - "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/utils" rolloutsPlugin "github.com/argoproj/argo-rollouts/rollout/trafficrouting/plugin/rpc" goPlugin "github.com/hashicorp/go-plugin" diff --git a/pkg/plugin/httproute.go b/pkg/plugin/httproute.go index e1b1fb2..1516bad 100644 --- a/pkg/plugin/httproute.go +++ b/pkg/plugin/httproute.go @@ -6,7 +6,7 @@ import ( "fmt" "sync" - "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/utils" + "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/internal/utils" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" pluginTypes "github.com/argoproj/argo-rollouts/utils/plugin/types" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index b3b5ca3..99eb59c 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -3,7 +3,8 @@ package plugin import ( "encoding/json" - "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/utils" + "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/internal/defaults" + "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/internal/utils" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" pluginTypes "github.com/argoproj/argo-rollouts/utils/plugin/types" "k8s.io/client-go/kubernetes" @@ -47,8 +48,7 @@ func (r *RpcPlugin) UpdateHash(rollout *v1alpha1.Rollout, canaryHash, stableHash } func (r *RpcPlugin) SetWeight(rollout *v1alpha1.Rollout, desiredWeight int32, additionalDestinations []v1alpha1.WeightDestination) pluginTypes.RpcError { - gatewayAPIConfig := GatewayAPITrafficRouting{} - err := json.Unmarshal(rollout.Spec.Strategy.Canary.TrafficRouting.Plugins[PluginName], &gatewayAPIConfig) + gatewayAPIConfig, err := getGatewayAPITracfficRoutingConfig(rollout) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -74,8 +74,7 @@ func (r *RpcPlugin) SetWeight(rollout *v1alpha1.Rollout, desiredWeight int32, ad } func (r *RpcPlugin) SetHeaderRoute(rollout *v1alpha1.Rollout, headerRouting *v1alpha1.SetHeaderRoute) pluginTypes.RpcError { - gatewayAPIConfig := GatewayAPITrafficRouting{} - err := json.Unmarshal(rollout.Spec.Strategy.Canary.TrafficRouting.Plugins[PluginName], &gatewayAPIConfig) + gatewayAPIConfig, err := getGatewayAPITracfficRoutingConfig(rollout) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -107,8 +106,7 @@ func (r *RpcPlugin) VerifyWeight(rollout *v1alpha1.Rollout, desiredWeight int32, } func (r *RpcPlugin) RemoveManagedRoutes(rollout *v1alpha1.Rollout) pluginTypes.RpcError { - gatewayAPIConfig := GatewayAPITrafficRouting{} - err := json.Unmarshal(rollout.Spec.Strategy.Canary.TrafficRouting.Plugins[PluginName], &gatewayAPIConfig) + gatewayAPIConfig, err := getGatewayAPITracfficRoutingConfig(rollout) if err != nil { return pluginTypes.RpcError{ ErrorString: err.Error(), @@ -135,6 +133,14 @@ func (r *RpcPlugin) Type() string { return Type } +func getGatewayAPITracfficRoutingConfig(rollout *v1alpha1.Rollout) (GatewayAPITrafficRouting, error) { + gatewayAPIConfig := GatewayAPITrafficRouting{ + ConfigMap: defaults.ConfigMap, + } + err := json.Unmarshal(rollout.Spec.Strategy.Canary.TrafficRouting.Plugins[PluginName], &gatewayAPIConfig) + return gatewayAPIConfig, err +} + func getRouteRule[T1 GatewayAPIBackendRef, T2 GatewayAPIRouteRule[T1], T3 GatewayAPIRouteRuleList[T1, T2]](routeRuleList T3, backendRefNameList ...string) (T2, error) { var backendRef T1 var routeRule T2 diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index 7d6fe7b..3651774 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" + "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/internal/utils" "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/pkg/mocks" - "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/utils" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" rolloutsPlugin "github.com/argoproj/argo-rollouts/rollout/trafficrouting/plugin/rpc" "github.com/stretchr/testify/assert" From 874a83bc9e39303fac6ec66c45ca1d10770579d6 Mon Sep 17 00:00:00 2001 From: Philipp Plotnikov Date: Mon, 5 Feb 2024 13:59:13 +0500 Subject: [PATCH 10/11] fix: fix multiple managed route removement from map Signed-off-by: Philipp Plotnikov --- internal/utils/common.go | 1 - pkg/plugin/errors.go | 5 +++-- pkg/plugin/httproute.go | 10 +++++++--- pkg/plugin/plugin.go | 16 ++++++++++++++++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/internal/utils/common.go b/internal/utils/common.go index caae18a..3c1a56d 100644 --- a/internal/utils/common.go +++ b/internal/utils/common.go @@ -49,7 +49,6 @@ func CreateFormatter(logFormat string) log.Formatter { FullTimestamp: true, } } - return formatType } diff --git a/pkg/plugin/errors.go b/pkg/plugin/errors.go index c0c6f8e..5a60521 100644 --- a/pkg/plugin/errors.go +++ b/pkg/plugin/errors.go @@ -1,11 +1,12 @@ package plugin const ( - GatewayAPIUpdateError = "Error updating Gateway API %q: %s" + GatewayAPIUpdateError = "error updating Gateway API %q: %s" GatewayAPIManifestError = "httpRoute and tcpRoute fields are empty. tcpRoute or httpRoute should be set" HTTPRouteFieldIsEmptyError = "httpRoute field is empty. It has to be set to remove managed routes" - InvalidHeaderMatchTypeError = "Invalid header match type" + InvalidHeaderMatchTypeError = "invalid header match type" BackendRefWasNotFoundInHTTPRouteError = "backendRef was not found in httpRoute" BackendRefWasNotFoundInTCPRouteError = "backendRef was not found in tcpRoute" BackendRefListWasNotFoundInTCPRouteError = "backendRef list was not found in tcpRoute" + ManagedRouteMapEntryDeleteError = "can't delete key %q from managedRouteMap. The key %q is not in the managedRouteMap" ) diff --git a/pkg/plugin/httproute.go b/pkg/plugin/httproute.go index 1516bad..0145de4 100644 --- a/pkg/plugin/httproute.go +++ b/pkg/plugin/httproute.go @@ -280,13 +280,17 @@ func (r *RpcPlugin) removeHTTPManagedRoutes(managedRouteNameList []v1alpha1.Mang httpRouteRuleList := HTTPRouteRuleList(httpRoute.Spec.Rules) for _, managedRoute := range managedRouteNameList { managedRouteName := managedRoute.Name - httpRouteRuleListIndex, isOk := managedRouteMap[managedRouteName] + _, isOk := managedRouteMap[managedRouteName] if !isOk { r.LogCtx.Logger.Info(fmt.Sprintf("%s is not in httpHeaderManagedRouteMap", managedRouteName)) continue } - httpRouteRuleList = utils.RemoveIndex(httpRouteRuleList, httpRouteRuleListIndex) - delete(managedRouteMap, managedRouteName) + httpRouteRuleList, err = removeManagedRouteEntry(managedRouteMap, httpRouteRuleList, managedRouteName) + if err != nil { + return pluginTypes.RpcError{ + ErrorString: err.Error(), + } + } } oldHTTPRuleList := httpRoute.Spec.Rules httpRoute.Spec.Rules = httpRouteRuleList diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 99eb59c..773ba4e 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -2,6 +2,7 @@ package plugin import ( "encoding/json" + "fmt" "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/internal/defaults" "github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/internal/utils" @@ -183,3 +184,18 @@ func getBackendRef[T1 GatewayAPIBackendRef, T2 GatewayAPIRouteRule[T1], T3 Gatew } return nil, routeRuleList.Error() } + +func removeManagedRouteEntry(managedRouteMap map[string]int, routeRuleList HTTPRouteRuleList, managedRouteName string) (HTTPRouteRuleList, error) { + managedRouteIndex, isOk := managedRouteMap[managedRouteName] + if !isOk { + return nil, fmt.Errorf(ManagedRouteMapEntryDeleteError, managedRouteName, managedRouteName) + } + delete(managedRouteMap, managedRouteName) + for key, value := range managedRouteMap { + if value > managedRouteIndex { + managedRouteMap[key]-- + } + } + routeRuleList = utils.RemoveIndex(routeRuleList, managedRouteIndex) + return routeRuleList, nil +} From 524baacdb8c9fcac08bce8edaa4e3a909b1fb52d Mon Sep 17 00:00:00 2001 From: Philipp Plotnikov Date: Mon, 5 Feb 2024 14:40:39 +0500 Subject: [PATCH 11/11] chore: up go version from 1.19 to 1.20 in github workflows Signed-off-by: Philipp Plotnikov --- .github/workflows/ci.yaml | 2 +- .github/workflows/release.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 308931a..2742151 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -7,7 +7,7 @@ on: branches: - "main" env: - GOLANG_VERSION: '1.19' + GOLANG_VERSION: '1.20' jobs: unit-tests: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index bc6be66..42d86fb 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -6,7 +6,7 @@ on: - "release-v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+" env: - GOLANG_VERSION: "1.19" + GOLANG_VERSION: "1.20" jobs: release-creation: