Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions bundle/manifests/kuadrant-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: {}
Expand Down Expand Up @@ -502,6 +502,12 @@ spec:
- patch
- update
- watch
- apiGroups:
- extensions.istio.io
resources:
- wasmplugins
verbs:
- delete
- apiGroups:
- extensions.kuadrant.io
resources:
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -914,12 +920,12 @@ 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
- image: quay.io/kuadrant/console-plugin:v0.4.0
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
2 changes: 1 addition & 1 deletion bundle/metadata/dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions charts/kuadrant-operator/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions charts/kuadrant-operator/templates/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14111,6 +14111,12 @@ rules:
- patch
- update
- watch
- apiGroups:
- extensions.istio.io
resources:
- wasmplugins
verbs:
- delete
- apiGroups:
- extensions.kuadrant.io
resources:
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion config/dependencies/authorino/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion config/deploy/olm/catalogsource.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion config/manager/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {}
Expand Down Expand Up @@ -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
6 changes: 6 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ rules:
- patch
- update
- watch
- apiGroups:
- extensions.istio.io
resources:
- wasmplugins
verbs:
- delete
- apiGroups:
- extensions.kuadrant.io
resources:
Expand Down
8 changes: 8 additions & 0 deletions internal/controller/data_plane_policies_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
8 changes: 8 additions & 0 deletions internal/controller/istio_extension_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
82 changes: 82 additions & 0 deletions internal/controller/istio_extension_reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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])
})
}
16 changes: 14 additions & 2 deletions internal/controller/ratelimit_workflow_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"encoding/hex"
"errors"
"fmt"
"slices"
"strings"
"sync"
"unicode"

Expand Down Expand Up @@ -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
},
Expand Down Expand Up @@ -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
},
Expand Down
Loading
Loading