diff --git a/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml b/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml index 926f36aa3..740dda6b9 100644 --- a/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml +++ b/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml @@ -224,14 +224,14 @@ metadata: capabilities: Basic Install categories: Integration & Delivery console.openshift.io/plugins: '["kuadrant-console-plugin"]' - containerImage: quay.io/kuadrant/kuadrant-operator:v1.5.0-rc2 - createdAt: "2026-06-02T08:04:09Z" + containerImage: quay.io/kuadrant/kuadrant-operator:v1.5.0-rc3 + createdAt: "2026-06-26T08:32:19Z" description: A Kubernetes Operator to manage the lifecycle of the Kuadrant system operators.operatorframework.io/builder: operator-sdk-v1.33.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 repository: https://github.com/Kuadrant/kuadrant-operator support: kuadrant - name: kuadrant-operator.v1.5.0-rc2 + name: kuadrant-operator.v1.5.0-rc3 namespace: placeholder spec: apiservicedefinitions: {} @@ -502,6 +502,12 @@ spec: - patch - update - watch + - apiGroups: + - extensions.istio.io + resources: + - wasmplugins + verbs: + - delete - apiGroups: - extensions.kuadrant.io resources: @@ -752,7 +758,7 @@ spec: - name: EXTENSIONS_DESCRIPTOR_SERVICE_PORT value: "50051" - name: RELATED_IMAGE_WASMSHIM - value: quay.io/kuadrant/wasm-shim:v0.13.0 + value: quay.io/kuadrant/wasm-shim:v0.14.1 - name: RELATED_IMAGE_DEVELOPERPORTAL value: quay.io/kuadrant/developer-portal-controller:v0.2.0 - name: RELATED_IMAGE_CONSOLE_PLUGIN_LATEST @@ -763,7 +769,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: quay.io/kuadrant/kuadrant-operator:v1.5.0-rc2 + image: quay.io/kuadrant/kuadrant-operator:v1.5.0-rc3 livenessProbe: httpGet: path: /healthz @@ -914,7 +920,7 @@ spec: name: Red Hat url: https://github.com/Kuadrant/kuadrant-operator relatedImages: - - image: quay.io/kuadrant/wasm-shim:v0.13.0 + - image: quay.io/kuadrant/wasm-shim:v0.14.1 name: wasmshim - image: quay.io/kuadrant/developer-portal-controller:v0.2.0 name: developerportal @@ -922,4 +928,4 @@ spec: name: console-plugin-latest - image: quay.io/kuadrant/console-plugin:v0.1.5-2 name: console-plugin-pf5 - version: 1.5.0-rc2 + version: 1.5.0-rc3 diff --git a/bundle/metadata/dependencies.yaml b/bundle/metadata/dependencies.yaml index d706353cb..5f54101b4 100644 --- a/bundle/metadata/dependencies.yaml +++ b/bundle/metadata/dependencies.yaml @@ -2,7 +2,7 @@ dependencies: - type: olm.package value: packageName: authorino-operator - version: "0.25.0" + version: "0.25.1" - type: olm.package value: packageName: limitador-operator diff --git a/charts/kuadrant-operator/Chart.yaml b/charts/kuadrant-operator/Chart.yaml index df8c4ab00..900fab017 100644 --- a/charts/kuadrant-operator/Chart.yaml +++ b/charts/kuadrant-operator/Chart.yaml @@ -20,11 +20,11 @@ sources: kubeVersion: ">=1.19.0-0" type: application # The chart version and dependencies will be properly set when the chart is released matching the operator version -version: "1.5.0-rc2" -appVersion: "1.5.0-rc2" +version: "1.5.0-rc3" +appVersion: "1.5.0-rc3" dependencies: - name: authorino-operator - version: 0.25.0 + version: 0.25.1 repository: https://kuadrant.io/helm-charts/ - name: limitador-operator version: 0.18.2 diff --git a/charts/kuadrant-operator/templates/manifests.yaml b/charts/kuadrant-operator/templates/manifests.yaml index 152ef860d..4b652dd27 100644 --- a/charts/kuadrant-operator/templates/manifests.yaml +++ b/charts/kuadrant-operator/templates/manifests.yaml @@ -14111,6 +14111,12 @@ rules: - patch - update - watch +- apiGroups: + - extensions.istio.io + resources: + - wasmplugins + verbs: + - delete - apiGroups: - extensions.kuadrant.io resources: @@ -14487,7 +14493,7 @@ spec: - name: EXTENSIONS_DESCRIPTOR_SERVICE_PORT value: "50051" - name: RELATED_IMAGE_WASMSHIM - value: quay.io/kuadrant/wasm-shim:v0.13.0 + value: quay.io/kuadrant/wasm-shim:v0.14.1 - name: RELATED_IMAGE_DEVELOPERPORTAL value: quay.io/kuadrant/developer-portal-controller:v0.2.0 - name: RELATED_IMAGE_CONSOLE_PLUGIN_LATEST @@ -14498,7 +14504,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: quay.io/kuadrant/kuadrant-operator:v1.5.0-rc2 + image: quay.io/kuadrant/kuadrant-operator:v1.5.0-rc3 livenessProbe: httpGet: path: /healthz diff --git a/config/dependencies/authorino/kustomization.yaml b/config/dependencies/authorino/kustomization.yaml index 6c2a6953f..9cbb029fe 100644 --- a/config/dependencies/authorino/kustomization.yaml +++ b/config/dependencies/authorino/kustomization.yaml @@ -1,2 +1,2 @@ resources: -- github.com/Kuadrant/authorino-operator/config/deploy?ref=v0.25.0 +- github.com/Kuadrant/authorino-operator/config/deploy?ref=v0.25.1 diff --git a/config/deploy/olm/catalogsource.yaml b/config/deploy/olm/catalogsource.yaml index e9c9dfd01..10e49d148 100644 --- a/config/deploy/olm/catalogsource.yaml +++ b/config/deploy/olm/catalogsource.yaml @@ -4,7 +4,7 @@ metadata: name: kuadrant-operator-catalog spec: sourceType: grpc - image: quay.io/kuadrant/kuadrant-operator-catalog:v1.5.0-rc2 + image: quay.io/kuadrant/kuadrant-operator-catalog:v1.5.0-rc3 displayName: Kuadrant Operators grpcPodConfig: securityContextConfig: restricted diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 192f0c556..82feab20c 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -12,4 +12,4 @@ kind: Kustomization images: - name: controller newName: quay.io/kuadrant/kuadrant-operator - newTag: v1.5.0-rc2 + newTag: v1.5.0-rc3 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index c26225574..0afe74889 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -36,7 +36,7 @@ spec: - name: EXTENSIONS_DESCRIPTOR_SERVICE_PORT value: "50051" - name: RELATED_IMAGE_WASMSHIM - value: "quay.io/kuadrant/wasm-shim:v0.13.0" + value: "quay.io/kuadrant/wasm-shim:v0.14.1" - name: RELATED_IMAGE_DEVELOPERPORTAL value: "quay.io/kuadrant/developer-portal-controller:v0.2.0" - name: RELATED_IMAGE_CONSOLE_PLUGIN_LATEST diff --git a/config/manifests/bases/kuadrant-operator.clusterserviceversion.yaml b/config/manifests/bases/kuadrant-operator.clusterserviceversion.yaml index 5b036b524..9d19627d5 100644 --- a/config/manifests/bases/kuadrant-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/kuadrant-operator.clusterserviceversion.yaml @@ -6,13 +6,13 @@ metadata: capabilities: Basic Install categories: Integration & Delivery console.openshift.io/plugins: '["kuadrant-console-plugin"]' - containerImage: quay.io/kuadrant/kuadrant-operator:v1.5.0-rc2 + containerImage: quay.io/kuadrant/kuadrant-operator:v1.5.0-rc3 description: A Kubernetes Operator to manage the lifecycle of the Kuadrant system operators.operatorframework.io/builder: operator-sdk-v1.9.0 operators.operatorframework.io/project_layout: unknown repository: https://github.com/Kuadrant/kuadrant-operator support: kuadrant - name: kuadrant-operator.v1.5.0-rc2 + name: kuadrant-operator.v1.5.0-rc3 namespace: placeholder spec: apiservicedefinitions: {} @@ -88,4 +88,4 @@ spec: provider: name: Red Hat url: https://github.com/Kuadrant/kuadrant-operator - version: 1.5.0-rc2 + version: 1.5.0-rc3 diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 993979629..385e376d9 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -112,6 +112,12 @@ rules: - patch - update - watch +- apiGroups: + - extensions.istio.io + resources: + - wasmplugins + verbs: + - delete - apiGroups: - extensions.kuadrant.io resources: diff --git a/internal/controller/data_plane_policies_workflow.go b/internal/controller/data_plane_policies_workflow.go index 202084979..346bbd878 100644 --- a/internal/controller/data_plane_policies_workflow.go +++ b/internal/controller/data_plane_policies_workflow.go @@ -219,6 +219,14 @@ func mergeAndVerify(ctx context.Context, actions []wasm.Action) ([]wasm.Action, // Merge source policy locators - deduplicate them lastAction.SourcePolicyLocators = lo.Uniq(append(lastAction.SourcePolicyLocators, currentAction.SourcePolicyLocators...)) slices.Sort(lastAction.SourcePolicyLocators) + + // Sort by the first predicate (if any), then by the concatenation of all predicates + slices.SortFunc(lastAction.ConditionalData, func(a, b wasm.ConditionalData) int { + // Compare by predicates (join them into a single string for comparison) + aKey := strings.Join(a.Predicates, "|") + bKey := strings.Join(b.Predicates, "|") + return strings.Compare(aKey, bKey) + }) } else { result = append(result, currentAction) } diff --git a/internal/controller/istio_extension_reconciler.go b/internal/controller/istio_extension_reconciler.go index bcd72b478..4dd2efd31 100644 --- a/internal/controller/istio_extension_reconciler.go +++ b/internal/controller/istio_extension_reconciler.go @@ -16,6 +16,7 @@ import ( istioapinetworkingv1alpha3 "istio.io/api/networking/v1alpha3" istiov1beta1 "istio.io/api/type/v1beta1" istioclientgonetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" k8stypes "k8s.io/apimachinery/pkg/types" @@ -35,6 +36,7 @@ import ( "github.com/kuadrant/kuadrant-operator/internal/wasm" ) +//+kubebuilder:rbac:groups=extensions.istio.io,resources=wasmplugins,verbs=delete //+kubebuilder:rbac:groups=networking.istio.io,resources=envoyfilters,verbs=get;list;watch;create;update;patch;delete // IstioExtensionReconciler reconciles Istio EnvoyFilter custom resources for wasm plugin injection @@ -131,6 +133,12 @@ func (r *IstioExtensionReconciler) Reconcile(ctx context.Context, _ []controller continue } + // Clean up old WasmPlugin for this specific gateway - temporary to be removed + wasmPluginName := wasm.ExtensionName(gateway.GetName()) + if err := r.client.Resource(kuadrantistio.WasmPluginsResource).Namespace(gateway.GetNamespace()).Delete(ctx, wasmPluginName, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) { + logger.Error(err, "failed to delete old wasmplugin", "gateway", gatewayKey.String(), "wasmplugin", wasmPluginName) + } + existingEnvoyFilter := existingEnvoyFilterObj.(*controller.RuntimeObject).Object.(*istioclientgonetworkingv1alpha3.EnvoyFilter) // delete diff --git a/internal/controller/istio_extension_reconciler_test.go b/internal/controller/istio_extension_reconciler_test.go index 005970d13..d4be344fc 100644 --- a/internal/controller/istio_extension_reconciler_test.go +++ b/internal/controller/istio_extension_reconciler_test.go @@ -636,4 +636,86 @@ func TestMergeAndVerifyEdgeCases(t *testing.T) { _, err := mergeAndVerify(context.TODO(), actions) assert.ErrorContains(t, err, "duplicate key '' with different values") }) + + t.Run("deterministic conditional data ordering after merge", func(t *testing.T) { + // Create two sets of actions in different orders + actionsOrder1 := []wasm.Action{ + { + ServiceName: wasm.RateLimitCheckServiceName, + Scope: "route-0", + ConditionalData: []wasm.ConditionalData{ + { + Predicates: []string{"auth.identity.bob"}, + Data: []wasm.DataType{ + {Value: &wasm.Static{Static: wasm.StaticSpec{Key: "limit.bob", Value: "1"}}}, + }, + }, + }, + }, + { + ServiceName: wasm.RateLimitCheckServiceName, + Scope: "route-0", + ConditionalData: []wasm.ConditionalData{ + { + Predicates: []string{"auth.identity.alice"}, + Data: []wasm.DataType{ + {Value: &wasm.Static{Static: wasm.StaticSpec{Key: "limit.alice", Value: "1"}}}, + }, + }, + }, + }, + } + + actionsOrder2 := []wasm.Action{ + { + ServiceName: wasm.RateLimitCheckServiceName, + Scope: "route-0", + ConditionalData: []wasm.ConditionalData{ + { + Predicates: []string{"auth.identity.alice"}, + Data: []wasm.DataType{ + {Value: &wasm.Static{Static: wasm.StaticSpec{Key: "limit.alice", Value: "1"}}}, + }, + }, + }, + }, + { + ServiceName: wasm.RateLimitCheckServiceName, + Scope: "route-0", + ConditionalData: []wasm.ConditionalData{ + { + Predicates: []string{"auth.identity.bob"}, + Data: []wasm.DataType{ + {Value: &wasm.Static{Static: wasm.StaticSpec{Key: "limit.bob", Value: "1"}}}, + }, + }, + }, + }, + } + + result1, err := mergeAndVerify(context.TODO(), actionsOrder1) + assert.NilError(t, err) + assert.Equal(t, 1, len(result1)) + + result2, err := mergeAndVerify(context.TODO(), actionsOrder2) + assert.NilError(t, err) + assert.Equal(t, 1, len(result2)) + + // Both results should have exactly the same ConditionalData in the same order + assert.Equal(t, len(result1[0].ConditionalData), len(result2[0].ConditionalData)) + for i := range result1[0].ConditionalData { + cd1 := result1[0].ConditionalData[i] + cd2 := result2[0].ConditionalData[i] + + // Predicates should be in the same order + assert.DeepEqual(t, cd1.Predicates, cd2.Predicates) + + // Data should be in the same order + assert.Equal(t, len(cd1.Data), len(cd2.Data)) + } + + // The ConditionalData should be sorted by predicates (alice before bob) + assert.Equal(t, "auth.identity.alice", result1[0].ConditionalData[0].Predicates[0]) + assert.Equal(t, "auth.identity.bob", result1[0].ConditionalData[1].Predicates[0]) + }) } diff --git a/internal/controller/ratelimit_workflow_helpers.go b/internal/controller/ratelimit_workflow_helpers.go index 07bf101fa..d28921f21 100644 --- a/internal/controller/ratelimit_workflow_helpers.go +++ b/internal/controller/ratelimit_workflow_helpers.go @@ -5,6 +5,8 @@ import ( "encoding/hex" "errors" "fmt" + "slices" + "strings" "sync" "unicode" @@ -331,7 +333,12 @@ func buildWasmActionsForTokenRateLimit(effectivePolicy EffectiveTokenRateLimitPo } limitsNamespace := LimitsNamespaceFromRoute(parsed.GetRoute()) - topLevelRules, limitRules := lo.FilterReject(lo.Entries(rules), + rulesEntries := lo.Entries(rules) + slices.SortFunc(rulesEntries, func(a, b lo.Entry[string, kuadrantv1.MergeableRule]) int { + return strings.Compare(a.Key, b.Key) + }) + + topLevelRules, limitRules := lo.FilterReject(rulesEntries, func(r lo.Entry[string, kuadrantv1.MergeableRule], _ int) bool { return r.Key == kuadrantv1.RulesKeyTopLevelPredicates }, @@ -386,7 +393,12 @@ func buildWasmActionsForAnyRateLimit( } limitsNamespace := LimitsNamespaceFromRoute(parsed.GetRoute()) - topLevelRules, limitRules := lo.FilterReject(lo.Entries(rules), + rulesEntries := lo.Entries(rules) + slices.SortFunc(rulesEntries, func(a, b lo.Entry[string, kuadrantv1.MergeableRule]) int { + return strings.Compare(a.Key, b.Key) + }) + + topLevelRules, limitRules := lo.FilterReject(rulesEntries, func(r lo.Entry[string, kuadrantv1.MergeableRule], _ int) bool { return r.Key == topLevelPredicatesKey }, diff --git a/internal/istio/utils.go b/internal/istio/utils.go index 0b6fe41fe..53e04044b 100644 --- a/internal/istio/utils.go +++ b/internal/istio/utils.go @@ -7,6 +7,7 @@ import ( "github.com/kuadrant/policy-machinery/controller" "github.com/kuadrant/policy-machinery/machinery" "github.com/samber/lo" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" istioapimetav1alpha1 "istio.io/api/meta/v1alpha1" istioapinetworkingv1alpha3 "istio.io/api/networking/v1alpha3" @@ -131,6 +132,7 @@ func buildWasmFilterConfig(wasmURL, imagePullSecret, imageSHA, clusterName strin }, "allow_precompiled": true, }, + "failure_policy": "FAIL_RELOAD", "allow_on_headers_stop_iteration": true, } @@ -194,12 +196,9 @@ func EqualEnvoyFilters(a, b *istioclientgonetworkingv1alpha3.EnvoyFilter) bool { if (aListener == nil) != (bListener == nil) { return false } - // For HTTP_FILTER patches, we compare the listener match structure if present - // Since the structure is complex, we'll compare the JSON representation + // For HTTP_FILTER patches, compare the match structure using protobuf equality if aListener != nil && bListener != nil { - aMatchJSON, aErr := json.Marshal(aConfigPatch.Match) - bMatchJSON, _ := json.Marshal(bConfigPatch.Match) - if string(aMatchJSON) != string(bMatchJSON) || aErr != nil { + if !proto.Equal(aConfigPatch.Match, bConfigPatch.Match) { return false } } @@ -214,10 +213,8 @@ func EqualEnvoyFilters(a, b *istioclientgonetworkingv1alpha3.EnvoyFilter) bool { return false } default: - // For other patch types, compare the match structure via JSON - aMatchJSON, aErr := json.Marshal(aConfigPatch.Match) - bMatchJSON, _ := json.Marshal(bConfigPatch.Match) - if string(aMatchJSON) != string(bMatchJSON) || aErr != nil { + // For other patch types, compare the match structure using protobuf equality + if !proto.Equal(aConfigPatch.Match, bConfigPatch.Match) { return false } } @@ -229,9 +226,9 @@ func EqualEnvoyFilters(a, b *istioclientgonetworkingv1alpha3.EnvoyFilter) bool { if aPatch.Operation != bPatch.Operation || aPatch.FilterClass != bPatch.FilterClass { return false } - aPatchJSON, _ := aPatch.Value.MarshalJSON() - bPatchJSON, _ := bPatch.Value.MarshalJSON() - return string(aPatchJSON) == string(bPatchJSON) + + // Use protobuf equality for patch values to handle non-deterministic map ordering. + return proto.Equal(aPatch.Value, bPatch.Value) }) }) } diff --git a/internal/istio/utils_test.go b/internal/istio/utils_test.go index 387afca8d..4f5e8e77a 100644 --- a/internal/istio/utils_test.go +++ b/internal/istio/utils_test.go @@ -209,6 +209,100 @@ func TestEqualEnvoyFilters(t *testing.T) { assert.Assert(subT, !EqualEnvoyFilters(a, b)) }) + + t.Run("equal patch values with maps in different order", func(subT *testing.T) { + a := testBasicEnvoyFilter(subT) + b := testBasicEnvoyFilter(subT) + + // Create identical data but from separate map instances (which could marshal differently) + mapData := map[string]any{ + "actionSets": []any{ + map[string]any{ + "name": "route-0", + "routeRuleConditions": map[string]any{ + "hostnames": []any{"*.example.com"}, + }, + "actions": []any{ + map[string]any{ + "service": "ratelimit", + "scope": "route-0-rlp", + "conditionalData": []any{ + map[string]any{ + "predicates": []string{"auth.identity.bob"}, + "data": []any{ + map[string]any{ + "static": map[string]any{ + "key": "limit.route-0__1234", + "value": "1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + // Marshal to JSON and unmarshal into structpb.Struct for both a and b + aJSON, _ := json.Marshal(mapData) + aPatchValue := &_struct.Struct{} + assert.NilError(subT, aPatchValue.UnmarshalJSON(aJSON)) + a.Spec.ConfigPatches[0].Patch.Value = aPatchValue + + bJSON, _ := json.Marshal(mapData) + bPatchValue := &_struct.Struct{} + assert.NilError(subT, bPatchValue.UnmarshalJSON(bJSON)) + b.Spec.ConfigPatches[0].Patch.Value = bPatchValue + + // With proto.Equal, these should be equal even if JSON string representations differ + assert.Assert(subT, EqualEnvoyFilters(a, b), + "EnvoyFilters with semantically identical patch values should be equal") + }) + + t.Run("equal HTTP_FILTER match with complex listener structure", func(subT *testing.T) { + // This test validates proto.Equal is used for Match comparison (another fix for #2033) + a := testBasicEnvoyFilter(subT) + a.Spec.ConfigPatches[0].ApplyTo = istioapinetworkingv1alpha3.EnvoyFilter_HTTP_FILTER + a.Spec.ConfigPatches[0].Match = &istioapinetworkingv1alpha3.EnvoyFilter_EnvoyConfigObjectMatch{ + Context: istioapinetworkingv1alpha3.EnvoyFilter_GATEWAY, + ObjectTypes: &istioapinetworkingv1alpha3.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ + Listener: &istioapinetworkingv1alpha3.EnvoyFilter_ListenerMatch{ + FilterChain: &istioapinetworkingv1alpha3.EnvoyFilter_ListenerMatch_FilterChainMatch{ + Filter: &istioapinetworkingv1alpha3.EnvoyFilter_ListenerMatch_FilterMatch{ + Name: "envoy.filters.network.http_connection_manager", + SubFilter: &istioapinetworkingv1alpha3.EnvoyFilter_ListenerMatch_SubFilterMatch{ + Name: "envoy.filters.http.router", + }, + }, + }, + }, + }, + } + + b := testBasicEnvoyFilter(subT) + b.Spec.ConfigPatches[0].ApplyTo = istioapinetworkingv1alpha3.EnvoyFilter_HTTP_FILTER + b.Spec.ConfigPatches[0].Match = &istioapinetworkingv1alpha3.EnvoyFilter_EnvoyConfigObjectMatch{ + Context: istioapinetworkingv1alpha3.EnvoyFilter_GATEWAY, + ObjectTypes: &istioapinetworkingv1alpha3.EnvoyFilter_EnvoyConfigObjectMatch_Listener{ + Listener: &istioapinetworkingv1alpha3.EnvoyFilter_ListenerMatch{ + FilterChain: &istioapinetworkingv1alpha3.EnvoyFilter_ListenerMatch_FilterChainMatch{ + Filter: &istioapinetworkingv1alpha3.EnvoyFilter_ListenerMatch_FilterMatch{ + Name: "envoy.filters.network.http_connection_manager", + SubFilter: &istioapinetworkingv1alpha3.EnvoyFilter_ListenerMatch_SubFilterMatch{ + Name: "envoy.filters.http.router", + }, + }, + }, + }, + }, + } + + // With proto.Equal, these identical Match structures should be equal + assert.Assert(subT, EqualEnvoyFilters(a, b), + "EnvoyFilters with identical HTTP_FILTER Match structures should be equal") + }) } func TestEqualTargetRefs(t *testing.T) { diff --git a/release.yaml b/release.yaml index 997cd5370..ad4a246b7 100644 --- a/release.yaml +++ b/release.yaml @@ -1,13 +1,13 @@ kuadrant-operator: - version: "1.5.0-rc2" + version: "1.5.0-rc3" olm: channels: - "stable" default-channel: "stable" dependencies: - authorino-operator: "0.25.0" + authorino-operator: "0.25.1" console-plugin: "0.4.0" developer-portal-controller: "0.2.0" dns-operator: "0.17.0" limitador-operator: "0.18.2" - wasm-shim: "0.13.0" + wasm-shim: "0.14.1"