diff --git a/go.mod b/go.mod index 2c8e4e8c25e..a41d619e04b 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/jpillora/backoff v1.0.0 github.com/kong/go-database-reconciler v1.15.0 github.com/kong/go-kong v0.59.1 - github.com/kong/kubernetes-configuration v0.0.44 + github.com/kong/kubernetes-configuration v0.0.46 github.com/kong/kubernetes-telemetry v0.1.7 github.com/kong/kubernetes-testing-framework v0.47.2 github.com/lithammer/dedent v1.1.0 @@ -209,7 +209,7 @@ require ( golang.org/x/term v0.26.0 // indirect golang.org/x/text v0.20.0 // indirect golang.org/x/time v0.8.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/tools v0.26.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect diff --git a/go.sum b/go.sum index f1041897f27..666aa7ae89d 100644 --- a/go.sum +++ b/go.sum @@ -239,8 +239,8 @@ github.com/kong/go-database-reconciler v1.15.0 h1:5F5Zzp2H14aiDmqWUCaU4+LGR/lGnv github.com/kong/go-database-reconciler v1.15.0/go.mod h1:T5BkBw13PZWub3y2jKAoM7fYD+UmXp2iNqj1YqD0L90= github.com/kong/go-kong v0.59.1 h1:AJZtyCD+Zyqe/mF/m+x3/qN/GPVxAH7jq9zGJTHRfjc= github.com/kong/go-kong v0.59.1/go.mod h1:8Vt6HmtgLNgL/7bSwAlz3DIWqBtzG7qEt9+OnMiQOa0= -github.com/kong/kubernetes-configuration v0.0.44 h1:85q8PugPeWQ7AKvEeGXxDoa+XCzTdNMj1qFcHyxHHds= -github.com/kong/kubernetes-configuration v0.0.44/go.mod h1:ym++Oygj/wZjaCanK8a+mjZ1jttPF7gPP1OBV0aqI00= +github.com/kong/kubernetes-configuration v0.0.46 h1:dIxVu9dOtGi9aY2prTlQ1CkiSu8Fk/0oal9m9iiUdSk= +github.com/kong/kubernetes-configuration v0.0.46/go.mod h1:Bk0H032d+aPgVYakc7C9Zo5nLwiXXm9thKUWF8vvisA= github.com/kong/kubernetes-telemetry v0.1.7 h1:R4NUpvbF5uZ+5kgSQsIcf/oulRBGQCHsffFRDE4wxV4= github.com/kong/kubernetes-telemetry v0.1.7/go.mod h1:USy5pcD1+Mm9NtKuz3Pb/rSx71VN76gHCFhdbAB4/lg= github.com/kong/kubernetes-testing-framework v0.47.2 h1:+2Z9anTpbV/hwNeN+NFQz53BMU+g3QJydkweBp3tULo= @@ -539,8 +539,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/hack/generators/controllers/networking/main.go b/hack/generators/controllers/networking/main.go index 120e7cd5665..07c46f473be 100644 --- a/hack/generators/controllers/networking/main.go +++ b/hack/generators/controllers/networking/main.go @@ -162,6 +162,7 @@ var inputControllersNeeded = &typesNeeded{ ProgrammedCondition: ProgrammedConditionConfiguration{ UpdatesEnabled: true, }, + HasControlPlaneReference: true, }, typeNeeded{ Group: "configuration.konghq.com", @@ -181,6 +182,7 @@ var inputControllersNeeded = &typesNeeded{ AcceptsIngressClassNameSpec: false, NeedsUpdateReferences: true, RBACVerbs: []string{"get", "list", "watch"}, + HasControlPlaneReference: true, }, typeNeeded{ Group: "configuration.konghq.com", @@ -263,6 +265,7 @@ var inputControllersNeeded = &typesNeeded{ }, AcceptsIngressClassNameAnnotation: true, RBACVerbs: []string{"get", "list", "watch"}, + HasControlPlaneReference: true, }, typeNeeded{ Group: "configuration.konghq.com", @@ -414,6 +417,11 @@ type typeNeeded struct { // NeedUpdateReferences is true if we need to update the reference relationships // between reconciled object and other objects. NeedsUpdateReferences bool + + // HasControlPlaneReference is true if the object's spec has a control plane reference. + // If true, the controller will only reconcile the object if the control plane reference is set to 'kic' or + // is left empty. + HasControlPlaneReference bool } type ProgrammedConditionConfiguration struct { @@ -560,16 +568,29 @@ func (r *{{.PackageAlias}}{{.Kind}}Reconciler) SetupWithManager(mgr ctrl.Manager } {{- end}} {{- if .AcceptsIngressClassNameAnnotation}} + {{- if .HasControlPlaneReference }} + cpRefPredicate := ctrlutils.GenerateCPReferenceMatchesPredicate[*{{.PackageImportAlias}}.{{.Kind}}]() + {{- end}} if !r.DisableIngressClassLookups { blder.Watches(&netv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listClassless), - builder.WithPredicates(predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass)), + builder.WithPredicates( + predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass), + {{- if .HasControlPlaneReference }} + cpRefPredicate, + {{- end}} + ), ) } preds := ctrlutils.GeneratePredicateFuncsForIngressClassFilter(r.IngressClassName) return blder.Watches(&{{.PackageImportAlias}}.{{.Kind}}{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(preds), + builder.WithPredicates( + preds, + {{- if .HasControlPlaneReference }} + cpRefPredicate, + {{- end}} + ), ). Complete(r) {{- else}} diff --git a/internal/controllers/configuration/zz_generated_controllers.go b/internal/controllers/configuration/zz_generated_controllers.go index 118f1a49bc8..b76a49fa61c 100644 --- a/internal/controllers/configuration/zz_generated_controllers.go +++ b/internal/controllers/configuration/zz_generated_controllers.go @@ -286,13 +286,17 @@ func (r *NetV1IngressReconciler) SetupWithManager(mgr ctrl.Manager) error { if !r.DisableIngressClassLookups { blder.Watches(&netv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listClassless), - builder.WithPredicates(predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass)), + builder.WithPredicates( + predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass), + ), ) } preds := ctrlutils.GeneratePredicateFuncsForIngressClassFilter(r.IngressClassName) return blder.Watches(&netv1.Ingress{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(preds), + builder.WithPredicates( + preds, + ), ). Complete(r) } @@ -734,13 +738,17 @@ func (r *KongV1KongClusterPluginReconciler) SetupWithManager(mgr ctrl.Manager) e if !r.DisableIngressClassLookups { blder.Watches(&netv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listClassless), - builder.WithPredicates(predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass)), + builder.WithPredicates( + predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass), + ), ) } preds := ctrlutils.GeneratePredicateFuncsForIngressClassFilter(r.IngressClassName) return blder.Watches(&kongv1.KongClusterPlugin{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(preds), + builder.WithPredicates( + preds, + ), ). Complete(r) } @@ -902,16 +910,23 @@ func (r *KongV1KongConsumerReconciler) SetupWithManager(mgr ctrl.Manager) error ), ) } + cpRefPredicate := ctrlutils.GenerateCPReferenceMatchesPredicate[*kongv1.KongConsumer]() if !r.DisableIngressClassLookups { blder.Watches(&netv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listClassless), - builder.WithPredicates(predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass)), + builder.WithPredicates( + predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass), + cpRefPredicate, + ), ) } preds := ctrlutils.GeneratePredicateFuncsForIngressClassFilter(r.IngressClassName) return blder.Watches(&kongv1.KongConsumer{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(preds), + builder.WithPredicates( + preds, + cpRefPredicate, + ), ). Complete(r) } @@ -1088,16 +1103,23 @@ func (r *KongV1Beta1KongConsumerGroupReconciler) SetupWithManager(mgr ctrl.Manag ), ) } + cpRefPredicate := ctrlutils.GenerateCPReferenceMatchesPredicate[*kongv1beta1.KongConsumerGroup]() if !r.DisableIngressClassLookups { blder.Watches(&netv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listClassless), - builder.WithPredicates(predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass)), + builder.WithPredicates( + predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass), + cpRefPredicate, + ), ) } preds := ctrlutils.GeneratePredicateFuncsForIngressClassFilter(r.IngressClassName) return blder.Watches(&kongv1beta1.KongConsumerGroup{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(preds), + builder.WithPredicates( + preds, + cpRefPredicate, + ), ). Complete(r) } @@ -1279,13 +1301,17 @@ func (r *KongV1Beta1TCPIngressReconciler) SetupWithManager(mgr ctrl.Manager) err if !r.DisableIngressClassLookups { blder.Watches(&netv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listClassless), - builder.WithPredicates(predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass)), + builder.WithPredicates( + predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass), + ), ) } preds := ctrlutils.GeneratePredicateFuncsForIngressClassFilter(r.IngressClassName) return blder.Watches(&kongv1beta1.TCPIngress{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(preds), + builder.WithPredicates( + preds, + ), ). Complete(r) } @@ -1476,13 +1502,17 @@ func (r *KongV1Beta1UDPIngressReconciler) SetupWithManager(mgr ctrl.Manager) err if !r.DisableIngressClassLookups { blder.Watches(&netv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listClassless), - builder.WithPredicates(predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass)), + builder.WithPredicates( + predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass), + ), ) } preds := ctrlutils.GeneratePredicateFuncsForIngressClassFilter(r.IngressClassName) return blder.Watches(&kongv1beta1.UDPIngress{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(preds), + builder.WithPredicates( + preds, + ), ). Complete(r) } @@ -1732,13 +1762,17 @@ func (r *IncubatorV1Alpha1KongServiceFacadeReconciler) SetupWithManager(mgr ctrl if !r.DisableIngressClassLookups { blder.Watches(&netv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listClassless), - builder.WithPredicates(predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass)), + builder.WithPredicates( + predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass), + ), ) } preds := ctrlutils.GeneratePredicateFuncsForIngressClassFilter(r.IngressClassName) return blder.Watches(&incubatorv1alpha1.KongServiceFacade{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(preds), + builder.WithPredicates( + preds, + ), ). Complete(r) } @@ -1895,16 +1929,23 @@ func (r *KongV1Alpha1KongVaultReconciler) SetupWithManager(mgr ctrl.Manager) err ), ) } + cpRefPredicate := ctrlutils.GenerateCPReferenceMatchesPredicate[*kongv1alpha1.KongVault]() if !r.DisableIngressClassLookups { blder.Watches(&netv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listClassless), - builder.WithPredicates(predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass)), + builder.WithPredicates( + predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass), + cpRefPredicate, + ), ) } preds := ctrlutils.GeneratePredicateFuncsForIngressClassFilter(r.IngressClassName) return blder.Watches(&kongv1alpha1.KongVault{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(preds), + builder.WithPredicates( + preds, + cpRefPredicate, + ), ). Complete(r) } @@ -2063,13 +2104,17 @@ func (r *KongV1Alpha1KongCustomEntityReconciler) SetupWithManager(mgr ctrl.Manag if !r.DisableIngressClassLookups { blder.Watches(&netv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listClassless), - builder.WithPredicates(predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass)), + builder.WithPredicates( + predicate.NewPredicateFuncs(ctrlutils.IsDefaultIngressClass), + ), ) } preds := ctrlutils.GeneratePredicateFuncsForIngressClassFilter(r.IngressClassName) return blder.Watches(&kongv1alpha1.KongCustomEntity{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(preds), + builder.WithPredicates( + preds, + ), ). Complete(r) } diff --git a/internal/controllers/utils/control_plane_reference.go b/internal/controllers/utils/control_plane_reference.go new file mode 100644 index 00000000000..43bb47b1d11 --- /dev/null +++ b/internal/controllers/utils/control_plane_reference.go @@ -0,0 +1,30 @@ +package utils + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + kongv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" +) + +// ObjectWithControlPlaneRef is an interface that represents an object that has a control plane reference. +type ObjectWithControlPlaneRef interface { + GetControlPlaneRef() *kongv1alpha1.ControlPlaneRef +} + +// GenerateCPReferenceMatchesPredicate generates a predicate function that filters out objects that have a control plane +// reference set to a value other than 'kic'. +func GenerateCPReferenceMatchesPredicate[T ObjectWithControlPlaneRef]() predicate.Predicate { + return predicate.NewPredicateFuncs(func(o client.Object) bool { + c, ok := o.(T) + if !ok { + return false + } + if cpRef := c.GetControlPlaneRef(); cpRef != nil { + // If the cpRef is set, reconcile the object only if it is set explicitly to 'kic'. + return cpRef.Type == kongv1alpha1.ControlPlaneRefKIC + } + // If there's no cpRef set, we should reconcile it as by default it's 'kic'. + return true + }) +} diff --git a/internal/controllers/utils/control_plane_reference_test.go b/internal/controllers/utils/control_plane_reference_test.go new file mode 100644 index 00000000000..d261b35f9a0 --- /dev/null +++ b/internal/controllers/utils/control_plane_reference_test.go @@ -0,0 +1,80 @@ +package utils_test + +import ( + "testing" + + "github.com/samber/lo" + "github.com/stretchr/testify/require" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + + kongv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + + ctrlutils "github.com/kong/kubernetes-ingress-controller/v3/internal/controllers/utils" +) + +type objectWithCPRefType struct { + client.Object + cpRef *kongv1alpha1.ControlPlaneRef +} + +func (o *objectWithCPRefType) GetControlPlaneRef() *kongv1alpha1.ControlPlaneRef { + return o.cpRef +} + +func TestGenerateCPReferenceMatchesPredicate(t *testing.T) { + testCases := []struct { + name string + obj objectWithCPRefType + expected bool + }{ + { + name: "control plane reference is nil", + obj: objectWithCPRefType{ + cpRef: nil, + }, + expected: true, + }, + { + name: "control plane reference is set to kic", + obj: objectWithCPRefType{ + cpRef: &kongv1alpha1.ControlPlaneRef{ + Type: kongv1alpha1.ControlPlaneRefKIC, + }, + }, + expected: true, + }, + { + name: "control plane reference is set to konnect", + obj: objectWithCPRefType{ + cpRef: &kongv1alpha1.ControlPlaneRef{ + Type: kongv1alpha1.ControlPlaneRefKonnectID, + KonnectID: lo.ToPtr("konnect-id"), + }, + }, + expected: false, + }, + { + name: "control plane reference is set to konnect namespaced reference", + obj: objectWithCPRefType{ + cpRef: &kongv1alpha1.ControlPlaneRef{ + Type: kongv1alpha1.ControlPlaneRefKonnectNamespacedRef, + KonnectNamespacedRef: &kongv1alpha1.KonnectNamespacedRef{ + Name: "konnect-name", + }, + }, + }, + expected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + pred := ctrlutils.GenerateCPReferenceMatchesPredicate[*objectWithCPRefType]() + actual := pred.Generic(event.GenericEvent{ + Object: &tc.obj, + }) + require.Equal(t, tc.expected, actual) + }) + } +} diff --git a/test/envtest/control_plane_reference_test.go b/test/envtest/control_plane_reference_test.go new file mode 100644 index 00000000000..1a2a0c93d2f --- /dev/null +++ b/test/envtest/control_plane_reference_test.go @@ -0,0 +1,196 @@ +//go:build envtest + +package envtest + +import ( + "context" + "testing" + + "github.com/go-logr/zapr" + "github.com/samber/lo" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + kongv1 "github.com/kong/kubernetes-configuration/api/configuration/v1" + kongv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + kongv1beta1 "github.com/kong/kubernetes-configuration/api/configuration/v1beta1" + + "github.com/kong/kubernetes-ingress-controller/v3/internal/annotations" + "github.com/kong/kubernetes-ingress-controller/v3/test/helpers/conditions" +) + +// TestControlPlaneReferenceHandling tests ControlPlaneReference handling in controllers supporting it. +// It expects that if an object has a ControlPlaneReference set, it should only be programmed if the reference +// is set to 'kic'. +func TestControlPlaneReferenceHandling(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + const ingressClassName = "kongenvtest" + scheme := Scheme(t, WithKong) + envcfg := Setup(t, scheme) + ctrlClient := NewControllerClient(t, scheme, envcfg) + deployIngressClass(ctx, t, ingressClassName, ctrlClient) + logger := zapr.NewLogger(zap.NewNop()) + ctrl.SetLogger(logger) + ns := CreateNamespace(ctx, t, ctrlClient) + RunManager(ctx, t, envcfg, + AdminAPIOptFns(), + WithUpdateStatus(), + WithIngressClass(ingressClassName), + WithPublishService(ns.Name), + WithProxySyncSeconds(0.10), + ) + + var ( + kicCPRef = &kongv1alpha1.ControlPlaneRef{ + Type: kongv1alpha1.ControlPlaneRefKIC, + } + konnectCPRef = &kongv1alpha1.ControlPlaneRef{ + Type: kongv1alpha1.ControlPlaneRefKonnectID, + KonnectID: lo.ToPtr("konnect-id"), + } + + validConsumer = func() *kongv1.KongConsumer { + return &kongv1.KongConsumer{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "consumer-", + Namespace: ns.Name, + Annotations: map[string]string{ + annotations.IngressClassKey: ingressClassName, + }, + }, + Username: "consumer", + } + } + validConsumerGroup = func() *kongv1beta1.KongConsumerGroup { + return &kongv1beta1.KongConsumerGroup{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "consumer-group-", + Namespace: ns.Name, + Annotations: map[string]string{ + annotations.IngressClassKey: ingressClassName, + }, + }, + Spec: kongv1beta1.KongConsumerGroupSpec{ + Name: "consumer-group", + }, + } + } + validVault = func() *kongv1alpha1.KongVault { + return &kongv1alpha1.KongVault{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "vault-", + Namespace: ns.Name, + Annotations: map[string]string{ + annotations.IngressClassKey: ingressClassName, + }, + }, + Spec: kongv1alpha1.KongVaultSpec{ + Backend: "env", + Prefix: "env", + }, + } + } + ) + + testCases := []struct { + name string + object interface { + client.Object + GetConditions() []metav1.Condition + SetControlPlaneRef(*kongv1alpha1.ControlPlaneRef) + } + controlPlaneRef *kongv1alpha1.ControlPlaneRef + expectToBeProgrammed bool + }{ + { + name: "KongConsumer - without ControlPlaneRef", + object: validConsumer(), + expectToBeProgrammed: true, + }, + { + name: "KongConsumer - with ControlPlaneRef == kic", + object: validConsumer(), + controlPlaneRef: kicCPRef, + expectToBeProgrammed: true, + }, + { + name: "KongConsumer - with ControlPlaneRef != kic", + object: validConsumer(), + controlPlaneRef: konnectCPRef, + expectToBeProgrammed: false, + }, + { + name: "KongConsumerGroup - without ControlPlaneRef", + object: validConsumerGroup(), + expectToBeProgrammed: true, + }, + { + name: "KongConsumerGroup - with ControlPlaneRef == kic", + object: validConsumerGroup(), + controlPlaneRef: kicCPRef, + expectToBeProgrammed: true, + }, + { + name: "KongConsumerGroup - with ControlPlaneRef != kic", + object: validConsumerGroup(), + controlPlaneRef: konnectCPRef, + expectToBeProgrammed: false, + }, + { + name: "KongVault - without ControlPlaneRef", + object: validVault(), + expectToBeProgrammed: true, + }, + { + name: "KongVault - with ControlPlaneRef == kic", + object: validVault(), + controlPlaneRef: kicCPRef, + expectToBeProgrammed: true, + }, + { + name: "KongVault - with ControlPlaneRef != kic", + object: validVault(), + controlPlaneRef: konnectCPRef, + expectToBeProgrammed: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if tc.controlPlaneRef != nil { + tc.object.SetControlPlaneRef(tc.controlPlaneRef) + } + require.NoError(t, ctrlClient.Create(ctx, tc.object)) + + if tc.expectToBeProgrammed { + require.EventuallyWithT(t, func(t *assert.CollectT) { + if !assert.NoError(t, ctrlClient.Get(ctx, client.ObjectKeyFromObject(tc.object), tc.object)) { + return + } + assert.Equal(t, tc.expectToBeProgrammed, conditions.Contain( + tc.object.GetConditions(), + conditions.WithType(string(kongv1.ConditionProgrammed)), + conditions.WithStatus(metav1.ConditionTrue), + )) + }, waitTime, tickDuration, "expected object to be programmed") + } else { + require.Never(t, func() bool { + err := ctrlClient.Get(ctx, client.ObjectKeyFromObject(tc.object), tc.object) + return err == nil && conditions.Contain( + tc.object.GetConditions(), + conditions.WithType(string(kongv1.ConditionProgrammed)), + conditions.WithStatus(metav1.ConditionTrue), + ) + }, waitTime, tickDuration, "expected object not to be programmed") + } + }) + } +}