From 848ac5a71986e5093342d808c800173a9e7cd24a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Ma=C5=82ek?= Date: Mon, 27 Jan 2025 16:50:32 +0100 Subject: [PATCH] fix(konnect): do not set owner relationship between ControlPlane and its config entities --- CHANGELOG.md | 4 + .../konnect/reconciler_controlplaneref.go | 25 ---- .../reconciler_controlplaneref_test.go | 5 +- controller/konnect/reconciler_generic.go | 38 +++++- docs/api-reference.md | 2 +- go.mod | 4 +- go.sum | 4 +- pkg/utils/kubernetes/status.go | 9 +- test/envtest/condition_asserts.go | 53 ++++++++ .../envtest/kongpluginbinding_managed_test.go | 22 ++- .../kongpluginbinding_unmanaged_test.go | 32 ++++- .../kongplugincleanupfinalizer_test.go | 13 +- ...konnect_entities_kongcacertificate_test.go | 59 +++++++++ .../konnect_entities_kongcertificate_test.go | 59 +++++++++ .../konnect_entities_kongconsumer_test.go | 86 +++++++++++- ...konnect_entities_kongconsumergroup_test.go | 68 +++++++++- ...ies_kongdataplaneclientcertificate_test.go | 58 +++++++- test/envtest/konnect_entities_kongkey_test.go | 103 ++++++++++++--- .../konnect_entities_kongkeyset_test.go | 69 +++++++++- .../konnect_entities_kongroute_test.go | 4 +- .../konnect_entities_kongservice_test.go | 80 ++++++++++- .../konnect_entities_kongtarget_test.go | 4 +- .../konnect_entities_kongupstream_test.go | 66 ++++++++- test/helpers/deploy/deploy_resources.go | 125 +++++++----------- test/integration/test_konnect_entities.go | 12 +- 25 files changed, 827 insertions(+), 177 deletions(-) create mode 100644 test/envtest/condition_asserts.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 526871168..084f286be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -111,6 +111,10 @@ have any effect. They will be removed in next major release. [#1100](https://github.com/Kong/gateway-operator/pull/1100) +- Konnect entities that are attached to a Konnect CP through a `ControlPlaneRef` + do not get an owner relationship set to the `ControlPlane` anymore hence + they are not deleted when the `ControlPlane` is deleted. + [#1099](https://github.com/Kong/gateway-operator/pull/1099) [kubebuilder_3907]: https://github.com/kubernetes-sigs/kubebuilder/discussions/3907 diff --git a/controller/konnect/reconciler_controlplaneref.go b/controller/konnect/reconciler_controlplaneref.go index e8297c9d1..bfab4c4ac 100644 --- a/controller/konnect/reconciler_controlplaneref.go +++ b/controller/konnect/reconciler_controlplaneref.go @@ -11,7 +11,6 @@ import ( "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" "github.com/kong/gateway-operator/controller/konnect/constraints" @@ -152,30 +151,6 @@ func handleControlPlaneRef[T constraints.SupportedKonnectEntityType, TEnt constr return ctrl.Result{Requeue: true}, nil } - var ( - old = ent.DeepCopyObject().(TEnt) - - // A cluster scoped object cannot set a namespaced object as its owner, and also we cannot set cross namespaced owner reference. - // So we skip setting owner reference for cluster scoped resources (KongVault). - // TODO: handle cross namespace refs - isNamespaceScoped = ent.GetNamespace() != "" - - // If an entity has another owner, we should not set the owner reference as that would prevent the entity from being deleted. - hasNoOwners = len(ent.GetOwnerReferences()) == 0 - ) - if isNamespaceScoped && hasNoOwners { - if err := controllerutil.SetOwnerReference(cp, ent, cl.Scheme(), controllerutil.WithBlockOwnerDeletion(true)); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to set owner reference: %w", err) - } - } - - if err := cl.Patch(ctx, ent, client.MergeFrom(old)); err != nil { - if k8serrors.IsConflict(err) { - return ctrl.Result{Requeue: true}, nil - } - return ctrl.Result{}, fmt.Errorf("failed to update status: %w", err) - } - if resource, ok := any(ent).(EntityWithControlPlaneRef); ok { old := ent.DeepCopyObject().(TEnt) resource.SetControlPlaneID(cp.Status.ID) diff --git a/controller/konnect/reconciler_controlplaneref_test.go b/controller/konnect/reconciler_controlplaneref_test.go index 05ce41e92..36cd13122 100644 --- a/controller/konnect/reconciler_controlplaneref_test.go +++ b/controller/konnect/reconciler_controlplaneref_test.go @@ -181,9 +181,8 @@ func TestHandleControlPlaneRef(t *testing.T) { }), "service should get control plane ID" }, func(svc *configurationv1alpha1.KongService) (bool, string) { - return lo.ContainsBy(svc.OwnerReferences, func(o metav1.OwnerReference) bool { - return o.Kind == "KonnectGatewayControlPlane" && o.Name == "cp-ok" - }), "service should have owner reference set to CP" + return len(svc.OwnerReferences) == 0, + "service should have 0 owner references" }, func(svc *configurationv1alpha1.KongService) (bool, string) { return lo.ContainsBy(svc.Status.Conditions, func(c metav1.Condition) bool { diff --git a/controller/konnect/reconciler_generic.go b/controller/konnect/reconciler_generic.go index 2d06839f1..15aae5d99 100644 --- a/controller/konnect/reconciler_generic.go +++ b/controller/konnect/reconciler_generic.go @@ -160,11 +160,10 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( // If a type has a ControlPlane ref, handle it. res, err := handleControlPlaneRef(ctx, r.Client, ent) if err != nil || !res.IsZero() { - // If the referenced ControlPlane is not found and the object is deleted, - // remove the finalizer and update the status. + // If the referenced ControlPlane is not found, remove the finalizer and update the status. // There's no need to remove the entity on Konnect because the ControlPlane // does not exist anymore. - if !ent.GetDeletionTimestamp().IsZero() && errors.As(err, &ReferencedControlPlaneDoesNotExistError{}) { + if errors.As(err, &ReferencedControlPlaneDoesNotExistError{}) { if controllerutil.RemoveFinalizer(ent, KonnectCleanupFinalizer) { if err := r.Client.Update(ctx, ent); err != nil { if k8serrors.IsConflict(err) { @@ -174,6 +173,13 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( } } } + + if res, err := setProgrammedStatusConditionBasedOnOtherConditions(ctx, r.Client, ent); err != nil { + return res, err + } else if !res.IsZero() { + return res, nil + } + // Status update will requeue the entity. return ctrl.Result{}, nil } @@ -1009,3 +1015,29 @@ func handleKongConsumerRef[T constraints.SupportedKonnectEntityType, TEnt constr return ctrl.Result{}, nil } + +func setProgrammedStatusConditionBasedOnOtherConditions[ + T interface { + client.Object + k8sutils.ConditionsAware + }, +]( + ctx context.Context, + cl client.Client, + ent T, +) (ctrl.Result, error) { + if k8sutils.AreAllConditionsHaveTrueStatus(ent) { + return ctrl.Result{}, nil + } + + if res, errStatus := patch.StatusWithCondition( + ctx, cl, ent, + konnectv1alpha1.KonnectEntityProgrammedConditionType, + metav1.ConditionFalse, + konnectv1alpha1.KonnectEntityProgrammedReasonExistsConditionWithStatusFalse, + "Some conditions have status set to False", + ); errStatus != nil || !res.IsZero() { + return res, errStatus + } + return ctrl.Result{}, nil +} diff --git a/docs/api-reference.md b/docs/api-reference.md index 0d48683e5..e4ebc3738 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -1361,7 +1361,7 @@ _Appears in:_ #### KongServiceSpec -KongServiceSpec defines specification of a Kong Route. +KongServiceSpec defines specification of a Kong Service. diff --git a/go.mod b/go.mod index f6f653d7a..a178256cd 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/google/go-containerregistry v0.20.3 github.com/google/uuid v1.6.0 github.com/gruntwork-io/terratest v0.48.2 - github.com/kong/kubernetes-configuration v1.1.0 + github.com/kong/kubernetes-configuration v1.1.1-0.20250127162809-96d322b98f26 github.com/kong/kubernetes-telemetry v0.1.8 github.com/kong/kubernetes-testing-framework v0.47.2 github.com/kong/semver/v4 v4.0.1 @@ -223,7 +223,7 @@ require ( k8s.io/apiserver v0.32.1 // indirect k8s.io/component-helpers v0.0.0 // indirect k8s.io/controller-manager v0.0.0 // indirect - k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect ) diff --git a/go.sum b/go.sum index 092d5a63e..002250f38 100644 --- a/go.sum +++ b/go.sum @@ -307,8 +307,8 @@ github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IX github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/kong/go-kong v0.63.0 h1:8ECLgkgDqON61qCMq/M0gTwZKYxg55Oy692dRDOOBiU= github.com/kong/go-kong v0.63.0/go.mod h1:ma9GWnhkxtrXZlLFfED955HjVzmUojYEHet3lm+PDik= -github.com/kong/kubernetes-configuration v1.1.0 h1:zCj7QlOiZgwQ2wSNlVNR0K6Z+plXTalfihtDzLXQWzs= -github.com/kong/kubernetes-configuration v1.1.0/go.mod h1:Z9Yo8DyBe/4zw/cgoSYafqHzuviKINVSq1b8D60F0u8= +github.com/kong/kubernetes-configuration v1.1.1-0.20250127162809-96d322b98f26 h1:PpedMiMqEEOigPJnRkhkxf/dwqESsYNCbQbrCiGjPzY= +github.com/kong/kubernetes-configuration v1.1.1-0.20250127162809-96d322b98f26/go.mod h1:B2OJE9gfwSTzr52YNZVsMuI7Ie8wxEVukJCWgc7R1Ao= github.com/kong/kubernetes-telemetry v0.1.8 h1:nbtUmXW9xkzRO7dgvrgVrJZiRksATk4XHrqX+78g/5k= github.com/kong/kubernetes-telemetry v0.1.8/go.mod h1:ZEQY/4DddKoe5XA7UTOIbdI/4d6ZRcrzh2ezRxnuyl0= github.com/kong/kubernetes-testing-framework v0.47.2 h1:+2Z9anTpbV/hwNeN+NFQz53BMU+g3QJydkweBp3tULo= diff --git a/pkg/utils/kubernetes/status.go b/pkg/utils/kubernetes/status.go index 760f9f5b3..f6c619161 100644 --- a/pkg/utils/kubernetes/status.go +++ b/pkg/utils/kubernetes/status.go @@ -114,7 +114,7 @@ func SetReadyWithGeneration(resource ConditionsAndGenerationAware, generation in ObservedGeneration: generation, } - if areAllConditionsHaveTrueStatus(resource) { + if AreAllConditionsHaveTrueStatus(resource) { ready.Status = metav1.ConditionTrue ready.Reason = string(consts.ResourceReadyReason) } else { @@ -138,7 +138,7 @@ func SetProgrammed(resource ConditionsAndGenerationAware) { ObservedGeneration: resource.GetGeneration(), } - if areAllConditionsHaveTrueStatus(resource) { + if AreAllConditionsHaveTrueStatus(resource) { programmed.Status = metav1.ConditionTrue programmed.Reason = string(gatewayv1.GatewayReasonProgrammed) } else { @@ -195,7 +195,10 @@ func SetAcceptedConditionOnGateway(resource ConditionsAndListenerConditionsAndGe } } -func areAllConditionsHaveTrueStatus(resource ConditionsAware) bool { +// AreAllConditionsHaveTrueStatus checks if all the conditions on the resource are in the True state. +// It skips the Programmed condition as that particular condition will be set based on +// the return value of this function. +func AreAllConditionsHaveTrueStatus(resource ConditionsAware) bool { for _, condition := range resource.GetConditions() { if condition.Type == string(gatewayv1.GatewayConditionProgrammed) { continue diff --git a/test/envtest/condition_asserts.go b/test/envtest/condition_asserts.go new file mode 100644 index 000000000..c7df714ff --- /dev/null +++ b/test/envtest/condition_asserts.go @@ -0,0 +1,53 @@ +package envtest + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" + + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" +) + +func conditionsAreSetWhenReferencedControlPlaneIsMissing[ + T interface { + client.Object + k8sutils.ConditionsAware + GetControlPlaneRef() *configurationv1alpha1.ControlPlaneRef + }, +](objToMatch T) func(obj T) bool { + return func(obj T) bool { + if obj.GetName() != objToMatch.GetName() { + return false + } + if obj.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + condCpRef, okCPRef := k8sutils.GetCondition(konnectv1alpha1.ControlPlaneRefValidConditionType, obj) + condProgrammed, okProgrammed := k8sutils.GetCondition(konnectv1alpha1.KonnectEntityProgrammedConditionType, obj) + return okCPRef && okProgrammed && + condCpRef.Status == "False" && + condProgrammed.Status == "False" && + condCpRef.Reason == konnectv1alpha1.ControlPlaneRefReasonInvalid && + condProgrammed.Reason == konnectv1alpha1.KonnectEntityProgrammedReasonExistsConditionWithStatusFalse + } +} + +func conditionProgrammedIsSetToTrue[ + T interface { + client.Object + k8sutils.ConditionsAware + GetControlPlaneRef() *configurationv1alpha1.ControlPlaneRef + GetKonnectID() string + }, +](objToMatch T, id string) func(T) bool { + return func(obj T) bool { + if obj.GetName() != objToMatch.GetName() { + return false + } + if obj.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + return obj.GetKonnectID() == id && k8sutils.IsProgrammed(obj) + } +} diff --git a/test/envtest/kongpluginbinding_managed_test.go b/test/envtest/kongpluginbinding_managed_test.go index d02c6c70b..67ef69f4b 100644 --- a/test/envtest/kongpluginbinding_managed_test.go +++ b/test/envtest/kongpluginbinding_managed_test.go @@ -109,7 +109,8 @@ func TestKongPluginBindingManaged(t *testing.T) { wKongPluginBinding := setupWatch[configurationv1alpha1.KongPluginBindingList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) wKongPlugin := setupWatch[configurationv1.KongPluginList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp, + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithAnnotation(metadata.AnnotationKeyPlugins, rateLimitingkongPlugin.Name), ) t.Cleanup(func() { @@ -211,7 +212,9 @@ func TestKongPluginBindingManaged(t *testing.T) { wKongPluginBinding := setupWatch[configurationv1alpha1.KongPluginBindingList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) wKongPlugin := setupWatch[configurationv1.KongPluginList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp) + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) t.Cleanup(func() { require.NoError(t, clientNamespaced.Delete(ctx, kongService)) }) @@ -317,7 +320,8 @@ func TestKongPluginBindingManaged(t *testing.T) { wKongPluginBinding := setupWatch[configurationv1alpha1.KongPluginBindingList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) wKongPlugin := setupWatch[configurationv1.KongPluginList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp, + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithAnnotation(metadata.AnnotationKeyPlugins, rateLimitingkongPlugin.Name), ) t.Cleanup(func() { @@ -445,7 +449,8 @@ func TestKongPluginBindingManaged(t *testing.T) { wKongPluginBinding := setupWatch[configurationv1alpha1.KongPluginBindingList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) wKongPlugin := setupWatch[configurationv1.KongPluginList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp, + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithAnnotation(metadata.AnnotationKeyPlugins, rateLimitingkongPlugin.Name), ) t.Cleanup(func() { @@ -459,7 +464,8 @@ func TestKongPluginBindingManaged(t *testing.T) { require.NoError(t, clientNamespaced.Delete(ctx, kongRoute)) }) updateKongRouteStatusWithProgrammed(t, ctx, clientNamespaced, kongRoute, routeID, cp.GetKonnectStatus().GetKonnectID(), serviceID) - kongConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, "username-1", cp, + kongConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, "username-1", + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithAnnotation(metadata.AnnotationKeyPlugins, rateLimitingkongPlugin.Name), ) t.Cleanup(func() { @@ -644,7 +650,8 @@ func TestKongPluginBindingManaged(t *testing.T) { wKongPluginBinding := setupWatch[configurationv1alpha1.KongPluginBindingList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) wKongPlugin := setupWatch[configurationv1.KongPluginList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp, + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithAnnotation(metadata.AnnotationKeyPlugins, rateLimitingkongPlugin.Name), ) t.Cleanup(func() { @@ -658,7 +665,8 @@ func TestKongPluginBindingManaged(t *testing.T) { require.NoError(t, clientNamespaced.Delete(ctx, kongRoute)) }) updateKongRouteStatusWithProgrammed(t, ctx, clientNamespaced, kongRoute, routeID, cp.GetKonnectStatus().GetKonnectID(), serviceID) - kongConsumerGroup := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, cp, + kongConsumerGroup := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithAnnotation(metadata.AnnotationKeyPlugins, rateLimitingkongPlugin.Name), ) t.Cleanup(func() { diff --git a/test/envtest/kongpluginbinding_unmanaged_test.go b/test/envtest/kongpluginbinding_unmanaged_test.go index 1689e8d99..c381656f1 100644 --- a/test/envtest/kongpluginbinding_unmanaged_test.go +++ b/test/envtest/kongpluginbinding_unmanaged_test.go @@ -68,7 +68,9 @@ func TestKongPluginBindingUnmanaged(t *testing.T) { ) defer createCall.Unset() - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp) + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) updateKongServiceStatusWithProgrammed(t, ctx, clientNamespaced, kongService, serviceID, cp.GetKonnectStatus().GetKonnectID()) kpb := deploy.KongPluginBinding(t, ctx, clientNamespaced, @@ -141,7 +143,10 @@ func TestKongPluginBindingUnmanaged(t *testing.T) { ) defer createCall.Unset() - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp) + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) + updateKongServiceStatusWithProgrammed(t, ctx, clientNamespaced, kongService, serviceID, cp.GetKonnectStatus().GetKonnectID()) kongRoute := deploy.KongRouteAttachedToService(t, ctx, clientNamespaced, kongService) updateKongRouteStatusWithProgrammed(t, ctx, clientNamespaced, kongRoute, routeID, cp.GetKonnectStatus().GetKonnectID(), serviceID) @@ -205,7 +210,10 @@ func TestKongPluginBindingUnmanaged(t *testing.T) { routeID := uuid.NewString() pluginID := uuid.NewString() - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp) + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) + updateKongServiceStatusWithProgrammed(t, ctx, clientNamespaced, kongService, serviceID, cp.GetKonnectStatus().GetKonnectID()) kongRoute := deploy.KongRouteAttachedToService(t, ctx, clientNamespaced, kongService) updateKongRouteStatusWithProgrammed(t, ctx, clientNamespaced, kongRoute, routeID, cp.GetKonnectStatus().GetKonnectID(), serviceID) @@ -287,12 +295,17 @@ func TestKongPluginBindingUnmanaged(t *testing.T) { pluginID := uuid.NewString() username := "test-user" + uuid.NewString() - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp) + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) + t.Cleanup(func() { require.NoError(t, client.IgnoreNotFound(clientNamespaced.Delete(ctx, kongService))) }) updateKongServiceStatusWithProgrammed(t, ctx, clientNamespaced, kongService, serviceID, cp.GetKonnectStatus().GetKonnectID()) - kongConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, username, cp) + kongConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, username, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) t.Cleanup(func() { require.NoError(t, client.IgnoreNotFound(clientNamespaced.Delete(ctx, kongConsumer))) }) @@ -374,9 +387,14 @@ func TestKongPluginBindingUnmanaged(t *testing.T) { consumerGroupID := uuid.NewString() pluginID := uuid.NewString() - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp) + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) + updateKongServiceStatusWithProgrammed(t, ctx, clientNamespaced, kongService, serviceID, cp.GetKonnectStatus().GetKonnectID()) - kongConsumerGroup := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, cp) + kongConsumerGroup := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) updateKongConsumerGroupStatusWithKonnectID(t, ctx, clientNamespaced, kongConsumerGroup, consumerGroupID, cp.GetKonnectStatus().GetKonnectID()) sdk.PluginSDK.EXPECT(). diff --git a/test/envtest/kongplugincleanupfinalizer_test.go b/test/envtest/kongplugincleanupfinalizer_test.go index 6c5ec4f50..d4e10d94b 100644 --- a/test/envtest/kongplugincleanupfinalizer_test.go +++ b/test/envtest/kongplugincleanupfinalizer_test.go @@ -59,7 +59,8 @@ func TestKongPluginFinalizer(t *testing.T) { }) wKongService := setupWatch[configurationv1alpha1.KongServiceList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp, + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithAnnotation(metadata.AnnotationKeyPlugins, rateLimitingkongPlugin.Name), ) @@ -89,7 +90,9 @@ func TestKongPluginFinalizer(t *testing.T) { require.NoError(t, clientNamespaced.Delete(ctx, rateLimitingkongPlugin)) }) - kongService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp) + kongService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) wKongRoute := setupWatch[configurationv1alpha1.KongRouteList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) kongRoute := deploy.KongRouteAttachedToService(t, ctx, clientNamespaced, kongService, deploy.WithAnnotation(metadata.AnnotationKeyPlugins, rateLimitingkongPlugin.Name), @@ -122,7 +125,8 @@ func TestKongPluginFinalizer(t *testing.T) { }) wKongConsumer := setupWatch[configurationv1.KongConsumerList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) - kongConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, "username-1", cp, + kongConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, "username-1", + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithAnnotation(metadata.AnnotationKeyPlugins, rateLimitingkongPlugin.Name), ) @@ -153,7 +157,8 @@ func TestKongPluginFinalizer(t *testing.T) { }) wKongConsumerGroup := setupWatch[configurationv1beta1.KongConsumerGroupList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) - kongConsumerGroup := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, cp, + kongConsumerGroup := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithAnnotation(metadata.AnnotationKeyPlugins, rateLimitingkongPlugin.Name), ) diff --git a/test/envtest/konnect_entities_kongcacertificate_test.go b/test/envtest/konnect_entities_kongcacertificate_test.go index fd9b3850e..915dd7489 100644 --- a/test/envtest/konnect_entities_kongcacertificate_test.go +++ b/test/envtest/konnect_entities_kongcacertificate_test.go @@ -2,6 +2,7 @@ package envtest import ( "context" + "fmt" "slices" "testing" @@ -223,4 +224,62 @@ func TestKongCACertificate(t *testing.T) { assert.True(c, factory.SDK.CACertificatesSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("removing referenced CP sets the status conditions properly", func(t *testing.T) { + const ( + id = "abc-12345" + ) + + var tags []string = []string{"tag1"} + + t.Log("Creating KonnectAPIAuthConfiguration and KonnectGatewayControlPlane") + apiAuth := deploy.KonnectAPIAuthConfigurationWithProgrammed(t, ctx, clientNamespaced) + cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth) + + t.Log("Setting up a watch for KongCACertifcate events") + w := setupWatch[configurationv1alpha1.KongCACertificateList](t, ctx, cl, client.InNamespace(ns.Name)) + + t.Log("Setting up SDK expectations on KongCACertifcate creation") + sdk.CACertificatesSDK.EXPECT(). + CreateCaCertificate( + mock.Anything, + cp.GetKonnectID(), + mock.MatchedBy(func(req sdkkonnectcomp.CACertificateInput) bool { + return slices.Contains(req.Tags, "tag1") + }), + ). + Return( + &sdkkonnectops.CreateCaCertificateResponse{ + CACertificate: &sdkkonnectcomp.CACertificate{ + ID: lo.ToPtr(id), + }, + }, + nil, + ) + + created := deploy.KongCACertificateAttachedToCP(t, ctx, clientNamespaced, cp, + deploy.WithKonnectIDControlPlaneRef(cp), + func(obj client.Object) { + cert := obj.(*configurationv1alpha1.KongCACertificate) + cert.Spec.Tags = tags + }, + ) + t.Log("Checking SDK CACertificate operations") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.CACertificatesSDK.AssertExpectations(t)) + }, waitTime, tickTime) + + t.Log("Waiting for object to be programmed and get Konnect ID") + watchFor(t, ctx, w, watch.Modified, conditionProgrammedIsSetToTrue(created, id), + fmt.Sprintf("CACertificate didn't get Programmed status condition or didn't get the correct %s Konnect ID assigned", id)) + + t.Log("Deleting KonnectGatewayControlPlane") + require.NoError(t, clientNamespaced.Delete(ctx, cp)) + + t.Log("Waiting for CACert to be get Programmed and ControlPlaneRefValid conditions with status=False") + watchFor(t, ctx, w, watch.Modified, + conditionsAreSetWhenReferencedControlPlaneIsMissing(created), + "KongCACertificate didn't get Programmed and/or ControlPlaneRefValid status condition set to False", + ) + }) } diff --git a/test/envtest/konnect_entities_kongcertificate_test.go b/test/envtest/konnect_entities_kongcertificate_test.go index 847fc39cb..882454776 100644 --- a/test/envtest/konnect_entities_kongcertificate_test.go +++ b/test/envtest/konnect_entities_kongcertificate_test.go @@ -2,6 +2,7 @@ package envtest import ( "context" + "fmt" "slices" "testing" @@ -228,4 +229,62 @@ func TestKongCertificate(t *testing.T) { assert.True(c, factory.SDK.CertificatesSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("removing referenced CP sets the status conditions properly", func(t *testing.T) { + const ( + id = "abc-12345" + ) + + var tags []string = []string{"tag1"} + + t.Log("Creating KonnectAPIAuthConfiguration and KonnectGatewayControlPlane") + apiAuth := deploy.KonnectAPIAuthConfigurationWithProgrammed(t, ctx, clientNamespaced) + cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth) + + t.Log("Setting up a watch for KongCertifcate events") + w := setupWatch[configurationv1alpha1.KongCertificateList](t, ctx, cl, client.InNamespace(ns.Name)) + + t.Log("Setting up SDK expectations on KongCertifcate creation") + sdk.CertificatesSDK.EXPECT(). + CreateCertificate( + mock.Anything, + cp.GetKonnectID(), + mock.MatchedBy(func(req sdkkonnectcomp.CertificateInput) bool { + return slices.Contains(req.Tags, "tag1") + }), + ). + Return( + &sdkkonnectops.CreateCertificateResponse{ + Certificate: &sdkkonnectcomp.Certificate{ + ID: lo.ToPtr(id), + }, + }, + nil, + ) + + created := deploy.KongCertificateAttachedToCP(t, ctx, clientNamespaced, cp, + deploy.WithKonnectIDControlPlaneRef(cp), + func(obj client.Object) { + cert := obj.(*configurationv1alpha1.KongCertificate) + cert.Spec.Tags = tags + }, + ) + t.Log("Checking SDK Certificate operations") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.CACertificatesSDK.AssertExpectations(t)) + }, waitTime, tickTime) + + t.Log("Waiting for object to be programmed and get Konnect ID") + watchFor(t, ctx, w, watch.Modified, conditionProgrammedIsSetToTrue(created, id), + fmt.Sprintf("Certificate didn't get Programmed status condition or didn't get the correct %s Konnect ID assigned", id)) + + t.Log("Deleting KonnectGatewayControlPlane") + require.NoError(t, clientNamespaced.Delete(ctx, cp)) + + t.Log("Waiting for CACert to be get Programmed and ControlPlaneRefValid conditions with status=False") + watchFor(t, ctx, w, watch.Modified, + conditionsAreSetWhenReferencedControlPlaneIsMissing(created), + "KongCACertificate didn't get Programmed and/or ControlPlaneRefValid status condition set to False", + ) + }) } diff --git a/test/envtest/konnect_entities_kongconsumer_test.go b/test/envtest/konnect_entities_kongconsumer_test.go index fac7f2491..fd82e6964 100644 --- a/test/envtest/konnect_entities_kongconsumer_test.go +++ b/test/envtest/konnect_entities_kongconsumer_test.go @@ -2,6 +2,7 @@ package envtest import ( "context" + "fmt" "strings" "testing" @@ -97,7 +98,9 @@ func TestKongConsumer(t *testing.T) { }).Return(&sdkkonnectops.ListConsumerGroupsForConsumerResponse{}, nil) t.Log("Creating KongConsumer") - createdConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, username, cp) + createdConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, username, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) t.Log("Waiting for KongConsumer to be programmed") watchFor(t, ctx, cWatch, watch.Modified, func(c *configurationv1.KongConsumer) bool { @@ -218,7 +221,8 @@ func TestKongConsumer(t *testing.T) { }).Return(&sdkkonnectops.AddConsumerToGroupResponse{}, nil) t.Log("Creating KongConsumerGroup") - createdConsumerGroup := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, cp, + createdConsumerGroup := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), func(obj client.Object) { cg := obj.(*configurationv1beta1.KongConsumerGroup) cg.Spec.Name = consumerGroupName @@ -226,7 +230,9 @@ func TestKongConsumer(t *testing.T) { ) t.Log("Creating KongConsumer and patching it with ConsumerGroup") - createdConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, username, cp) + createdConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, username, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) consumer := createdConsumer.DeepCopy() consumer.ConsumerGroups = []string{createdConsumerGroup.GetName()} require.NoError(t, clientNamespaced.Patch(ctx, consumer, client.MergeFrom(createdConsumer))) @@ -338,7 +344,9 @@ func TestKongConsumer(t *testing.T) { }, nil) t.Log("Creating a KongConsumer") - deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, username, cp) + deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, username, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) t.Log("Watching for KongConsumers to verify the created KongConsumer programmed") watchFor(t, ctx, cWatch, watch.Modified, func(c *configurationv1.KongConsumer) bool { @@ -384,7 +392,10 @@ func TestKongConsumer(t *testing.T) { }).Return(&sdkkonnectops.ListConsumerGroupsForConsumerResponse{}, nil) t.Log("Creating KongConsumer with ControlPlaneRef type=konnectID") - createdConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, username, cp, deploy.WithKonnectIDControlPlaneRef(cp)) + createdConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, username, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + deploy.WithKonnectIDControlPlaneRef(cp), + ) t.Log("Waiting for KongConsumer to be programmed") watchFor(t, ctx, cWatch, watch.Modified, func(c *configurationv1.KongConsumer) bool { @@ -405,4 +416,69 @@ func TestKongConsumer(t *testing.T) { assert.True(c, factory.SDK.ConsumersSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("removing referenced CP sets the status conditions properly", func(t *testing.T) { + const ( + id = "abc-12345" + name = "name-1" + ) + + t.Log("Creating KonnectAPIAuthConfiguration and KonnectGatewayControlPlane") + apiAuth := deploy.KonnectAPIAuthConfigurationWithProgrammed(t, ctx, clientNamespaced) + cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth) + + t.Log("Setting up a watch for KongConsumer events") + w := setupWatch[configurationv1.KongConsumerList](t, ctx, cl, client.InNamespace(ns.Name)) + + t.Log("Setting up SDK expectations on KongConsumer creation") + sdk.ConsumersSDK.EXPECT(). + CreateConsumer( + mock.Anything, + cp.GetKonnectID(), + mock.MatchedBy(func(req sdkkonnectcomp.ConsumerInput) bool { + return req.Username != nil && *req.Username == name + }), + ). + Return( + &sdkkonnectops.CreateConsumerResponse{ + Consumer: &sdkkonnectcomp.Consumer{ + ID: lo.ToPtr(id), + Username: lo.ToPtr(name), + }, + }, + nil, + ) + + t.Log("Setting up SDK expectation on KongConsumerGroups listing") + sdk.ConsumerGroupSDK.EXPECT(). + ListConsumerGroupsForConsumer(mock.Anything, sdkkonnectops.ListConsumerGroupsForConsumerRequest{ + ConsumerID: id, + ControlPlaneID: cp.GetKonnectStatus().GetKonnectID(), + }).Return(&sdkkonnectops.ListConsumerGroupsForConsumerResponse{}, nil) + + created := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, name, + deploy.WithKonnectIDControlPlaneRef(cp), + func(obj client.Object) { + cert := obj.(*configurationv1.KongConsumer) + cert.Username = name + }, + ) + t.Log("Checking SDK Consumer operations") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.ConsumersSDK.AssertExpectations(t)) + }, waitTime, tickTime) + + t.Log("Waiting for object to be programmed and get Konnect ID") + watchFor(t, ctx, w, watch.Modified, conditionProgrammedIsSetToTrue(created, id), + fmt.Sprintf("Consumer didn't get Programmed status condition or didn't get the correct %s Konnect ID assigned", id)) + + t.Log("Deleting KonnectGatewayControlPlane") + require.NoError(t, clientNamespaced.Delete(ctx, cp)) + + t.Log("Waiting for KongConsumer to be get Programmed and ControlPlaneRefValid conditions with status=False") + watchFor(t, ctx, w, watch.Modified, + conditionsAreSetWhenReferencedControlPlaneIsMissing(created), + "KongConsumer didn't get Programmed and/or ControlPlaneRefValid status condition set to False", + ) + }) } diff --git a/test/envtest/konnect_entities_kongconsumergroup_test.go b/test/envtest/konnect_entities_kongconsumergroup_test.go index 80ebade14..3fccb8cf6 100644 --- a/test/envtest/konnect_entities_kongconsumergroup_test.go +++ b/test/envtest/konnect_entities_kongconsumergroup_test.go @@ -2,6 +2,7 @@ package envtest import ( "context" + "fmt" "strings" "testing" @@ -80,7 +81,8 @@ func TestKongConsumerGroup(t *testing.T) { ) t.Log("Creating KongConsumerGroup") - cg := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, cp, + cg := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), func(obj client.Object) { cg := obj.(*configurationv1beta1.KongConsumerGroup) cg.Spec.Name = cgName @@ -182,7 +184,8 @@ func TestKongConsumerGroup(t *testing.T) { ) t.Log("Creating KongConsumerGroup") - deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, cp, + deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), func(obj client.Object) { cg := obj.(*configurationv1beta1.KongConsumerGroup) cg.Spec.Name = cgName @@ -219,7 +222,8 @@ func TestKongConsumerGroup(t *testing.T) { ) t.Log("Creating KongConsumerGroup with ControlPlaneRef type=konnectID") - cg := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, cp, + cg := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), func(obj client.Object) { cg := obj.(*configurationv1beta1.KongConsumerGroup) cg.Spec.Name = cgName @@ -246,4 +250,62 @@ func TestKongConsumerGroup(t *testing.T) { assert.True(c, factory.SDK.ConsumerGroupSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("removing referenced CP sets the status conditions properly", func(t *testing.T) { + const ( + id = "abc-12345" + name = "name-1" + ) + + t.Log("Creating KonnectAPIAuthConfiguration and KonnectGatewayControlPlane") + apiAuth := deploy.KonnectAPIAuthConfigurationWithProgrammed(t, ctx, clientNamespaced) + cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth) + + t.Log("Setting up a watch for KongConsumerGroup events") + w := setupWatch[configurationv1beta1.KongConsumerGroupList](t, ctx, cl, client.InNamespace(ns.Name)) + + t.Log("Setting up SDK expectations on KongConsumerGroup creation") + sdk.ConsumerGroupSDK.EXPECT(). + CreateConsumerGroup( + mock.Anything, + cp.GetKonnectID(), + mock.MatchedBy(func(req sdkkonnectcomp.ConsumerGroupInput) bool { + return req.Name == name + }), + ). + Return( + &sdkkonnectops.CreateConsumerGroupResponse{ + ConsumerGroup: &sdkkonnectcomp.ConsumerGroup{ + ID: lo.ToPtr(id), + Name: name, + }, + }, + nil, + ) + + created := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, + deploy.WithKonnectIDControlPlaneRef(cp), + func(obj client.Object) { + cg := obj.(*configurationv1beta1.KongConsumerGroup) + cg.Spec.Name = name + }, + ) + t.Log("Checking SDK ConsumerGroup operations") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.ConsumerGroupSDK.AssertExpectations(t)) + }, waitTime, tickTime) + + t.Log("Waiting for object to be programmed and get Konnect ID") + watchFor(t, ctx, w, watch.Modified, conditionProgrammedIsSetToTrue(created, id), + fmt.Sprintf("ConsumerGroup didn't get Programmed status condition or didn't get the correct %s Konnect ID assigned", id)) + + t.Log("Deleting KonnectGatewayControlPlane") + require.NoError(t, clientNamespaced.Delete(ctx, cp)) + + t.Log("Waiting for KongConsumerGroup to be get Programmed and ControlPlaneRefValid conditions with status=False") + watchFor(t, ctx, w, watch.Modified, + conditionsAreSetWhenReferencedControlPlaneIsMissing(created), + "KongConsumerGroup didn't get Programmed and/or ControlPlaneRefValid status condition set to False", + ) + }) } diff --git a/test/envtest/konnect_entities_kongdataplaneclientcertificate_test.go b/test/envtest/konnect_entities_kongdataplaneclientcertificate_test.go index 228b9f062..839c32c94 100644 --- a/test/envtest/konnect_entities_kongdataplaneclientcertificate_test.go +++ b/test/envtest/konnect_entities_kongdataplaneclientcertificate_test.go @@ -2,6 +2,7 @@ package envtest import ( "context" + "fmt" "testing" sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" @@ -69,7 +70,9 @@ func TestKongDataPlaneClientCertificate(t *testing.T) { w := setupWatch[configurationv1alpha1.KongDataPlaneClientCertificateList](t, ctx, cl, client.InNamespace(ns.Name)) t.Log("Creating KongDataPlaneClientCertificate") - createdCert := deploy.KongDataPlaneClientCertificateAttachedToCP(t, ctx, clientNamespaced, cp) + createdCert := deploy.KongDataPlaneClientCertificateAttachedToCP(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) t.Log("Waiting for KongDataPlaneClientCertificate to be programmed") watchFor(t, ctx, w, watch.Modified, func(c *configurationv1alpha1.KongDataPlaneClientCertificate) bool { @@ -116,7 +119,7 @@ func TestKongDataPlaneClientCertificate(t *testing.T) { }, nil) t.Log("Creating KongDataPlaneClientCertificate with ControlPlaneRef type=konnectID") - createdCert := deploy.KongDataPlaneClientCertificateAttachedToCP(t, ctx, clientNamespaced, cp, + createdCert := deploy.KongDataPlaneClientCertificateAttachedToCP(t, ctx, clientNamespaced, deploy.WithKonnectIDControlPlaneRef(cp), ) @@ -138,6 +141,57 @@ func TestKongDataPlaneClientCertificate(t *testing.T) { require.EventuallyWithT(t, func(c *assert.CollectT) { assert.True(c, factory.SDK.CACertificatesSDK.AssertExpectations(t)) }, waitTime, tickTime) + }) + + t.Run("removing referenced CP sets the status conditions properly", func(t *testing.T) { + const ( + id = "abc-12345" + ) + + t.Log("Creating KonnectAPIAuthConfiguration and KonnectGatewayControlPlane") + apiAuth := deploy.KonnectAPIAuthConfigurationWithProgrammed(t, ctx, clientNamespaced) + cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth) + + t.Log("Setting up a watch for KongDataPlaneClientCertificate events") + w := setupWatch[configurationv1alpha1.KongDataPlaneClientCertificateList](t, ctx, cl, client.InNamespace(ns.Name)) + + t.Log("Setting up SDK expectations on KongDataPlaneClientCertificate creation") + sdk.DataPlaneCertificatesSDK.EXPECT(). + CreateDataplaneCertificate( + mock.Anything, + cp.GetKonnectID(), + mock.Anything, + ). + Return( + &sdkkonnectops.CreateDataplaneCertificateResponse{ + DataPlaneClientCertificate: &sdkkonnectcomp.DataPlaneClientCertificate{ + Item: &sdkkonnectcomp.DataPlaneClientCertificateItem{ + ID: lo.ToPtr(id), + }, + }, + }, + nil, + ) + created := deploy.KongDataPlaneClientCertificateAttachedToCP(t, ctx, clientNamespaced, + deploy.WithKonnectIDControlPlaneRef(cp), + ) + t.Log("Checking SDK DataPlaneClientCertificate operations") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.DataPlaneCertificatesSDK.AssertExpectations(t)) + }, waitTime, tickTime) + + t.Log("Waiting for object to be programmed and get Konnect ID") + watchFor(t, ctx, w, watch.Modified, conditionProgrammedIsSetToTrue(created, id), + fmt.Sprintf("DataPlaneClientCertificate didn't get Programmed status condition or didn't get the correct %s Konnect ID assigned", id)) + + t.Log("Deleting KonnectGatewayControlPlane") + require.NoError(t, clientNamespaced.Delete(ctx, cp)) + + t.Log("Waiting for DataPlaneClientCertificate to be get Programmed and ControlPlaneRefValid conditions with status=False") + watchFor(t, ctx, w, watch.Modified, + conditionsAreSetWhenReferencedControlPlaneIsMissing(created), + "KongDataPlaneClientCertificate didn't get Programmed and/or ControlPlaneRefValid status condition set to False", + ) }) } diff --git a/test/envtest/konnect_entities_kongkey_test.go b/test/envtest/konnect_entities_kongkey_test.go index 839d1455f..e9e44c3dc 100644 --- a/test/envtest/konnect_entities_kongkey_test.go +++ b/test/envtest/konnect_entities_kongkey_test.go @@ -2,6 +2,8 @@ package envtest import ( "context" + "fmt" + "slices" "strings" "testing" @@ -77,7 +79,9 @@ func TestKongKey(t *testing.T) { t.Run("without KongKeySet", func(t *testing.T) { t.Log("Creating KongKey") - createdKey := deploy.KongKeyAttachedToCP(t, ctx, clientNamespaced, keyKid, keyName, cp) + createdKey := deploy.KongKey(t, ctx, clientNamespaced, keyKid, keyName, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) t.Log("Waiting for KongKey to be programmed") watchFor(t, ctx, w, watch.Modified, func(c *configurationv1alpha1.KongKey) bool { @@ -159,7 +163,9 @@ func TestKongKey(t *testing.T) { }, nil) t.Log("Creating KongKey") - createdKey := deploy.KongKeyAttachedToCP(t, ctx, clientNamespaced, keyKid, keyName, cp) + createdKey := deploy.KongKey(t, ctx, clientNamespaced, keyKid, keyName, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) t.Log("Waiting for KongKey to be programmed") watchFor(t, ctx, w, watch.Modified, func(k *configurationv1alpha1.KongKey) bool { @@ -180,15 +186,18 @@ func TestKongKey(t *testing.T) { t.Run("with KongKeySet", func(t *testing.T) { t.Log("Creating KongKey") - withKeySetRef := func(key *configurationv1alpha1.KongKey) { - key.Spec.KeySetRef = &configurationv1alpha1.KeySetRef{ - Type: configurationv1alpha1.KeySetRefNamespacedRef, - NamespacedRef: lo.ToPtr(configurationv1alpha1.KeySetNamespacedRef{ - Name: keySetName, - }), - } - } - createdKey := deploy.KongKeyAttachedToCP(t, ctx, clientNamespaced, keyKid, keyName, cp, withKeySetRef) + createdKey := deploy.KongKey(t, ctx, clientNamespaced, keyKid, keyName, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + func(obj client.Object) { + key := obj.(*configurationv1alpha1.KongKey) + key.Spec.KeySetRef = &configurationv1alpha1.KeySetRef{ + Type: configurationv1alpha1.KeySetRefNamespacedRef, + NamespacedRef: lo.ToPtr(configurationv1alpha1.KeySetNamespacedRef{ + Name: keySetName, + }), + } + }, + ) t.Log("Waiting for KeySetRefValid condition to be false") watchFor(t, ctx, w, watch.Modified, func(c *configurationv1alpha1.KongKey) bool { @@ -215,7 +224,9 @@ func TestKongKey(t *testing.T) { }, nil) t.Log("Creating KongKeySet") - keySet := deploy.KongKeySetAttachedToCP(t, ctx, clientNamespaced, keySetName, cp) + keySet := deploy.KongKeySet(t, ctx, clientNamespaced, keySetName, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) updateKongKeySetStatusWithProgrammed(t, ctx, clientNamespaced, keySet, keySetID, cp.GetKonnectStatus().GetKonnectID()) t.Log("Waiting for KongKey to be programmed and associated with KongKeySet") @@ -233,9 +244,12 @@ func TestKongKey(t *testing.T) { }) keySetIDPopulated := c.Status.Konnect != nil && c.Status.Konnect.KeySetID != "" exactlyOneOwnerReference := len(c.GetOwnerReferences()) == 1 - hasOwnerRefToKeySet := c.GetOwnerReferences()[0].Name == keySet.GetName() + if !exactlyOneOwnerReference { + return false + } - return programmed && associated && keySetIDPopulated && exactlyOneOwnerReference && hasOwnerRefToKeySet + hasOwnerRefToKeySet := c.GetOwnerReferences()[0].Name == keySet.GetName() + return programmed && associated && keySetIDPopulated && hasOwnerRefToKeySet }, "KongKey's Programmed and KeySetRefValid conditions should be true eventually") t.Log("Waiting for KongKey to be created in the SDK") @@ -285,8 +299,8 @@ func TestKongKey(t *testing.T) { }, nil) t.Log("Creating KongKey with ControlPlaneRef type=konnectID") - createdKey := deploy.KongKeyAttachedToCP(t, ctx, clientNamespaced, keyKid, keyName, cp, - func(k *configurationv1alpha1.KongKey) { deploy.WithKonnectIDControlPlaneRef(cp)(k) }, + createdKey := deploy.KongKey(t, ctx, clientNamespaced, keyKid, keyName, + deploy.WithKonnectIDControlPlaneRef(cp), ) t.Log("Waiting for KongKey to be programmed") @@ -308,4 +322,61 @@ func TestKongKey(t *testing.T) { assert.True(c, factory.SDK.KeysSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("removing referenced CP sets the status conditions properly", func(t *testing.T) { + const ( + id = "abc-12345" + ) + + t.Log("Creating KonnectAPIAuthConfiguration and KonnectGatewayControlPlane") + apiAuth := deploy.KonnectAPIAuthConfigurationWithProgrammed(t, ctx, clientNamespaced) + cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth) + + t.Log("Setting up a watch for KongKey events") + w := setupWatch[configurationv1alpha1.KongKeyList](t, ctx, cl, client.InNamespace(ns.Name)) + + t.Log("Setting up SDK expectations on KongKey creation") + sdk.KeysSDK.EXPECT(). + CreateKey( + mock.Anything, + cp.GetKonnectID(), + mock.MatchedBy(func(req sdkkonnectcomp.KeyInput) bool { + return slices.Contains(req.Tags, "test-1") + }), + ). + Return( + &sdkkonnectops.CreateKeyResponse{ + Key: &sdkkonnectcomp.Key{ + ID: lo.ToPtr(id), + Tags: []string{"test-1"}, + }, + }, + nil, + ) + + created := deploy.KongKey(t, ctx, clientNamespaced, "key-kid", "key-name", + deploy.WithKonnectIDControlPlaneRef(cp), + func(obj client.Object) { + cg := obj.(*configurationv1alpha1.KongKey) + cg.Spec.Tags = append(cg.Spec.Tags, "test-1") + }, + ) + t.Log("Checking SDK Key operations") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.KeysSDK.AssertExpectations(t)) + }, waitTime, tickTime) + + t.Log("Waiting for object to be programmed and get Konnect ID") + watchFor(t, ctx, w, watch.Modified, conditionProgrammedIsSetToTrue(created, id), + fmt.Sprintf("Key didn't get Programmed status condition or didn't get the correct %s Konnect ID assigned", id)) + + t.Log("Deleting KonnectGatewayControlPlane") + require.NoError(t, clientNamespaced.Delete(ctx, cp)) + + t.Log("Waiting for KongKey to be get Programmed and ControlPlaneRefValid conditions with status=False") + watchFor(t, ctx, w, watch.Modified, + conditionsAreSetWhenReferencedControlPlaneIsMissing(created), + "KongKey didn't get Programmed and/or ControlPlaneRefValid status condition set to False", + ) + }) } diff --git a/test/envtest/konnect_entities_kongkeyset_test.go b/test/envtest/konnect_entities_kongkeyset_test.go index a959906a3..f4635515c 100644 --- a/test/envtest/konnect_entities_kongkeyset_test.go +++ b/test/envtest/konnect_entities_kongkeyset_test.go @@ -2,6 +2,8 @@ package envtest import ( "context" + "fmt" + "slices" "strings" "testing" @@ -73,7 +75,9 @@ func TestKongKeySet(t *testing.T) { w := setupWatch[configurationv1alpha1.KongKeySetList](t, ctx, cl, client.InNamespace(ns.Name)) t.Log("Creating KongKeySet") - createdKeySet := deploy.KongKeySetAttachedToCP(t, ctx, clientNamespaced, keySetName, cp) + createdKeySet := deploy.KongKeySet(t, ctx, clientNamespaced, keySetName, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) t.Log("Waiting for KongKeySet to be programmed") watchFor(t, ctx, w, watch.Modified, func(c *configurationv1alpha1.KongKeySet) bool { @@ -151,7 +155,9 @@ func TestKongKeySet(t *testing.T) { }, nil) t.Log("Creating a KeySet") - deploy.KongKeySetAttachedToCP(t, ctx, clientNamespaced, keySetName, cp) + deploy.KongKeySet(t, ctx, clientNamespaced, keySetName, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) t.Log("Watching for KeySet to verify the created KeySet programmed") watchFor(t, ctx, w, watch.Modified, func(c *configurationv1alpha1.KongKeySet) bool { return c.GetKonnectID() == keySetID && k8sutils.IsProgrammed(c) @@ -179,7 +185,7 @@ func TestKongKeySet(t *testing.T) { w := setupWatch[configurationv1alpha1.KongKeySetList](t, ctx, cl, client.InNamespace(ns.Name)) t.Log("Creating KongKeySet with ControlPlaneRef type=konnectID") - createdKeySet := deploy.KongKeySetAttachedToCP(t, ctx, clientNamespaced, keySetName, cp, + createdKeySet := deploy.KongKeySet(t, ctx, clientNamespaced, keySetName, deploy.WithKonnectIDControlPlaneRef(cp), ) @@ -202,4 +208,61 @@ func TestKongKeySet(t *testing.T) { assert.True(c, factory.SDK.KeySetsSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("removing referenced CP sets the status conditions properly", func(t *testing.T) { + const ( + id = "abc-12345" + ) + + t.Log("Creating KonnectAPIAuthConfiguration and KonnectGatewayControlPlane") + apiAuth := deploy.KonnectAPIAuthConfigurationWithProgrammed(t, ctx, clientNamespaced) + cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth) + + t.Log("Setting up a watch for KongKeySet events") + w := setupWatch[configurationv1alpha1.KongKeySetList](t, ctx, cl, client.InNamespace(ns.Name)) + + t.Log("Setting up SDK expectations on KongKeySet creation") + sdk.KeySetsSDK.EXPECT(). + CreateKeySet( + mock.Anything, + cp.GetKonnectID(), + mock.MatchedBy(func(req sdkkonnectcomp.KeySetInput) bool { + return slices.Contains(req.Tags, "test-1") + }), + ). + Return( + &sdkkonnectops.CreateKeySetResponse{ + KeySet: &sdkkonnectcomp.KeySet{ + ID: lo.ToPtr(id), + Tags: []string{"test-1"}, + }, + }, + nil, + ) + + created := deploy.KongKeySet(t, ctx, clientNamespaced, "keyset-1", + deploy.WithKonnectIDControlPlaneRef(cp), + func(obj client.Object) { + cg := obj.(*configurationv1alpha1.KongKeySet) + cg.Spec.Tags = append(cg.Spec.Tags, "test-1") + }, + ) + t.Log("Checking SDK Key operations") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.KeysSDK.AssertExpectations(t)) + }, waitTime, tickTime) + + t.Log("Waiting for object to be programmed and get Konnect ID") + watchFor(t, ctx, w, watch.Modified, conditionProgrammedIsSetToTrue(created, id), + fmt.Sprintf("Key didn't get Programmed status condition or didn't get the correct %s Konnect ID assigned", id)) + + t.Log("Deleting KonnectGatewayControlPlane") + require.NoError(t, clientNamespaced.Delete(ctx, cp)) + + t.Log("Waiting for KongKeySet to be get Programmed and ControlPlaneRefValid conditions with status=False") + watchFor(t, ctx, w, watch.Modified, + conditionsAreSetWhenReferencedControlPlaneIsMissing(created), + "KongKeySet didn't get Programmed and/or ControlPlaneRefValid status condition set to False", + ) + }) } diff --git a/test/envtest/konnect_entities_kongroute_test.go b/test/envtest/konnect_entities_kongroute_test.go index 8bba735e2..d472c5a4b 100644 --- a/test/envtest/konnect_entities_kongroute_test.go +++ b/test/envtest/konnect_entities_kongroute_test.go @@ -50,7 +50,9 @@ func TestKongRoute(t *testing.T) { t.Log("Creating KonnectAPIAuthConfiguration and KonnectGatewayControlPlane") apiAuth := deploy.KonnectAPIAuthConfigurationWithProgrammed(t, ctx, clientNamespaced) cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth) - svc := deploy.KongServiceAttachedToCPWithID(t, ctx, clientNamespaced, cp) + svc := deploy.KongServiceWithID(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) t.Run("adding, patching and deleting KongRoute", func(t *testing.T) { const routeID = "route-12345" diff --git a/test/envtest/konnect_entities_kongservice_test.go b/test/envtest/konnect_entities_kongservice_test.go index bb95eb072..c8cc4bae4 100644 --- a/test/envtest/konnect_entities_kongservice_test.go +++ b/test/envtest/konnect_entities_kongservice_test.go @@ -2,6 +2,7 @@ package envtest import ( "context" + "fmt" "testing" sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" @@ -61,7 +62,9 @@ func TestKongService(t *testing.T) { ) t.Log("Creating a KongUpstream and setting it to programmed") - upstream := deploy.KongUpstreamAttachedToCP(t, ctx, clientNamespaced, cp) + upstream := deploy.KongUpstream(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) updateKongUpstreamStatusWithProgrammed(t, ctx, clientNamespaced, upstream, upstreamID, cp.GetKonnectID()) t.Log("Setting up a watch for KongService events") @@ -86,7 +89,8 @@ func TestKongService(t *testing.T) { ) t.Log("Creating a KongService") - createdService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp, + createdService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), func(obj client.Object) { s := obj.(*configurationv1alpha1.KongService) s.Spec.KongServiceAPISpec.Host = host @@ -155,7 +159,9 @@ func TestKongService(t *testing.T) { ) t.Log("Creating a KongUpstream and setting it to programmed") - upstream := deploy.KongUpstreamAttachedToCP(t, ctx, clientNamespaced, cp) + upstream := deploy.KongUpstream(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) updateKongUpstreamStatusWithProgrammed(t, ctx, clientNamespaced, upstream, upstreamID, cp.GetKonnectID()) t.Log("Setting up a watch for KongService events") @@ -180,7 +186,8 @@ func TestKongService(t *testing.T) { ) t.Log("Creating a KongService with ControlPlaneRef type=konnectID") - createdService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp, + createdService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), func(obj client.Object) { s := obj.(*configurationv1alpha1.KongService) s.Spec.KongServiceAPISpec.Host = host @@ -216,7 +223,9 @@ func TestKongService(t *testing.T) { deploy.KonnectGatewayControlPlaneType(sdkkonnectcomp.CreateControlPlaneRequestClusterTypeClusterTypeK8SIngressController), ) t.Log("Creating a KongUpstream and setting it to programmed") - upstream := deploy.KongUpstreamAttachedToCP(t, ctx, clientNamespaced, cp) + upstream := deploy.KongUpstream(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) updateKongUpstreamStatusWithProgrammed(t, ctx, clientNamespaced, upstream, upstreamID, cp.GetKonnectID()) t.Log("Setting up a watch for KongService events") @@ -249,7 +258,8 @@ func TestKongService(t *testing.T) { ) t.Log("Creating a KongService with ControlPlaneRef type=konnectNamespacedRef") - createdService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp, + createdService := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), func(obj client.Object) { s := obj.(*configurationv1alpha1.KongService) s.Spec.KongServiceAPISpec.Host = host @@ -278,4 +288,62 @@ func TestKongService(t *testing.T) { return c.Status == metav1.ConditionFalse && c.Reason == "FailedToCreate" }, "KongService should get the Programmed condition set to status=False due to using invalid (KIC) ControlPlaneRef") }) + + t.Run("removing referenced CP sets the status conditions properly", func(t *testing.T) { + const ( + id = "service-12345" + host = "example.com" + port = int64(8081) + ) + + t.Log("Creating KonnectAPIAuthConfiguration and KonnectGatewayControlPlane") + apiAuth := deploy.KonnectAPIAuthConfigurationWithProgrammed(t, ctx, clientNamespaced) + cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth) + + t.Log("Setting up a watch for KongService events") + w := setupWatch[configurationv1alpha1.KongServiceList](t, ctx, cl, client.InNamespace(ns.Name)) + + t.Log("Setting up SDK expectations on Service creation") + sdk.ServicesSDK.EXPECT(). + CreateService( + mock.Anything, + cp.GetKonnectID(), + mock.MatchedBy(func(req sdkkonnectcomp.ServiceInput) bool { + return req.Host == host + }), + ). + Return( + &sdkkonnectops.CreateServiceResponse{ + Service: &sdkkonnectcomp.Service{ + ID: lo.ToPtr(id), + }, + }, + nil, + ) + + t.Log("Creating a KongService with ControlPlaneRef type=konnectID") + created := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectIDControlPlaneRef(cp), + func(obj client.Object) { + s := obj.(*configurationv1alpha1.KongService) + s.Spec.KongServiceAPISpec.Host = host + }, + ) + t.Log("Checking SDK KongService operations") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.ServicesSDK.AssertExpectations(t)) + }, waitTime, tickTime) + + t.Log("Waiting for object to be programmed and get Konnect ID") + watchFor(t, ctx, w, watch.Modified, conditionProgrammedIsSetToTrue(created, id), + fmt.Sprintf("KongService didn't get Programmed status condition or didn't get the correct %s Konnect ID assigned", id)) + + t.Log("Deleting KonnectGatewayControlPlane") + require.NoError(t, clientNamespaced.Delete(ctx, cp)) + + t.Log("Waiting for Service to be get Programmed and ControlPlaneRefValid conditions with status=False") + watchFor(t, ctx, w, watch.Modified, + conditionsAreSetWhenReferencedControlPlaneIsMissing(created), + "KongService didn't get Programmed and/or ControlPlaneRefValid status condition set to False") + }) } diff --git a/test/envtest/konnect_entities_kongtarget_test.go b/test/envtest/konnect_entities_kongtarget_test.go index 019e26202..45914a34c 100644 --- a/test/envtest/konnect_entities_kongtarget_test.go +++ b/test/envtest/konnect_entities_kongtarget_test.go @@ -59,7 +59,9 @@ func TestKongTarget(t *testing.T) { ) t.Log("Creating a KongUpstream and setting it to programmed") - upstream := deploy.KongUpstreamAttachedToCP(t, ctx, clientNamespaced, cp) + upstream := deploy.KongUpstream(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), + ) updateKongUpstreamStatusWithProgrammed(t, ctx, clientNamespaced, upstream, upstreamID, cp.GetKonnectID()) t.Log("Setting up a watch for KongTarget events") diff --git a/test/envtest/konnect_entities_kongupstream_test.go b/test/envtest/konnect_entities_kongupstream_test.go index 8d20f92e1..c8dcc3367 100644 --- a/test/envtest/konnect_entities_kongupstream_test.go +++ b/test/envtest/konnect_entities_kongupstream_test.go @@ -2,6 +2,7 @@ package envtest import ( "context" + "fmt" "testing" sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" @@ -12,6 +13,7 @@ import ( "github.com/stretchr/testify/require" k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/watch" + "k8s.io/utils/strings/slices" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kong/gateway-operator/controller/konnect" @@ -75,7 +77,8 @@ func TestKongUpstream(t *testing.T) { ) t.Log("Creating a KongUpstream") - createdUpstream := deploy.KongUpstreamAttachedToCP(t, ctx, clientNamespaced, cp, + createdUpstream := deploy.KongUpstream(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), func(obj client.Object) { s := obj.(*configurationv1alpha1.KongUpstream) s.Spec.KongUpstreamAPISpec.Algorithm = sdkkonnectcomp.UpstreamAlgorithmRoundRobin.ToPointer() @@ -140,7 +143,6 @@ func TestKongUpstream(t *testing.T) { }) t.Run("should handle konnectID control plane reference", func(t *testing.T) { - const upstreamID = "upstream-12345" t.Log("Setting up SDK expectations on Upstream creation") @@ -162,12 +164,12 @@ func TestKongUpstream(t *testing.T) { ) t.Log("Creating a KongUpstream with ControlPlaneRef type=konnectID") - createdUpstream := deploy.KongUpstreamAttachedToCP(t, ctx, clientNamespaced, cp, + createdUpstream := deploy.KongUpstream(t, ctx, clientNamespaced, + deploy.WithKonnectIDControlPlaneRef(cp), func(obj client.Object) { s := obj.(*configurationv1alpha1.KongUpstream) s.Spec.KongUpstreamAPISpec.Algorithm = sdkkonnectcomp.UpstreamAlgorithmRoundRobin.ToPointer() }, - deploy.WithKonnectIDControlPlaneRef(cp), ) t.Log("Waiting for Upstream to be programmed and get Konnect ID") @@ -186,4 +188,60 @@ func TestKongUpstream(t *testing.T) { assert.True(c, factory.SDK.UpstreamsSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("removing referenced CP sets the status conditions properly", func(t *testing.T) { + const ( + id = "abc-12345" + ) + + t.Log("Creating KonnectAPIAuthConfiguration and KonnectGatewayControlPlane") + apiAuth := deploy.KonnectAPIAuthConfigurationWithProgrammed(t, ctx, clientNamespaced) + cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth) + + t.Log("Setting up a watch for KongUpstream events") + w := setupWatch[configurationv1alpha1.KongUpstreamList](t, ctx, cl, client.InNamespace(ns.Name)) + + t.Log("Setting up SDK expectations on Upstream creation") + sdk.UpstreamsSDK.EXPECT(). + CreateUpstream( + mock.Anything, + cp.GetKonnectID(), + mock.MatchedBy(func(req sdkkonnectcomp.UpstreamInput) bool { + return slices.Contains(req.Tags, "test-1") + }), + ). + Return( + &sdkkonnectops.CreateUpstreamResponse{ + Upstream: &sdkkonnectcomp.Upstream{ + ID: lo.ToPtr(id), + }, + }, + nil, + ) + + t.Log("Creating a KongUpstream with ControlPlaneRef type=konnectID") + created := deploy.KongUpstream(t, ctx, clientNamespaced, + deploy.WithKonnectIDControlPlaneRef(cp), + func(obj client.Object) { + s := obj.(*configurationv1alpha1.KongUpstream) + s.Spec.Tags = append(s.Spec.Tags, "test-1") + }, + ) + t.Log("Checking SDK KongUpstream operations") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.UpstreamsSDK.AssertExpectations(t)) + }, waitTime, tickTime) + + t.Log("Waiting for object to be programmed and get Konnect ID") + watchFor(t, ctx, w, watch.Modified, conditionProgrammedIsSetToTrue(created, id), + fmt.Sprintf("KongUpstream didn't get Programmed status condition or didn't get the correct %s Konnect ID assigned", id)) + + t.Log("Deleting KonnectGatewayControlPlane") + require.NoError(t, clientNamespaced.Delete(ctx, cp)) + + t.Log("Waiting for Service to be get Programmed and ControlPlaneRefValid conditions with status=False") + watchFor(t, ctx, w, watch.Modified, + conditionsAreSetWhenReferencedControlPlaneIsMissing(created), + "KongUpstream didn't get Programmed and/or ControlPlaneRefValid status condition set to False") + }) } diff --git a/test/helpers/deploy/deploy_resources.go b/test/helpers/deploy/deploy_resources.go index 4f61e807a..20778202c 100644 --- a/test/helpers/deploy/deploy_resources.go +++ b/test/helpers/deploy/deploy_resources.go @@ -52,22 +52,52 @@ func WithTestIDLabel(testID string) func(obj client.Object) { } } +// WithKonnectNamespacedRefControlPlaneRef returns an ObjOption that sets +// the ControlPlaneRef on the object to a namespaced ref. +// +// NOTE: This only works with namespaced resources. Using it with cluster-scoped +// resources requires additional handling ( to only set the namespace when the resource +// is cluster-scoped). +func WithKonnectNamespacedRefControlPlaneRef(cp *konnectv1alpha1.KonnectGatewayControlPlane) ObjOption { + return func(obj client.Object) { + o, ok := obj.(interface { + GetControlPlaneRef() *configurationv1alpha1.ControlPlaneRef + SetControlPlaneRef(*configurationv1alpha1.ControlPlaneRef) + }) + if !ok { + // As it's only used in tests, we can panic here - it will mean test code is incorrect. + panic(fmt.Errorf("%T does not implement GetControlPlaneRef/SetControlPlaneRef method", obj)) + } + + cpRef := &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, + KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ + Name: cp.GetName(), + }, + } + + o.SetControlPlaneRef(cpRef) + } +} + // WithKonnectIDControlPlaneRef returns an ObjOption that sets the ControlPlaneRef on the object to a KonnectID. func WithKonnectIDControlPlaneRef(cp *konnectv1alpha1.KonnectGatewayControlPlane) ObjOption { return func(obj client.Object) { o, ok := obj.(interface { GetControlPlaneRef() *configurationv1alpha1.ControlPlaneRef + SetControlPlaneRef(*configurationv1alpha1.ControlPlaneRef) }) if !ok { // As it's only used in tests, we can panic here - it will mean test code is incorrect. - panic(fmt.Errorf("%T does not implement GetControlPlaneRef method", obj)) + panic(fmt.Errorf("%T does not implement GetControlPlaneRef/SetControlPlaneRef method", obj)) } - objCPRef := o.GetControlPlaneRef() - *objCPRef = configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectID, - KonnectID: lo.ToPtr(cp.GetKonnectStatus().GetKonnectID()), - } + o.SetControlPlaneRef( + &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectID, + KonnectID: lo.ToPtr(cp.GetKonnectStatus().GetKonnectID()), + }, + ) } } @@ -200,20 +230,19 @@ func KonnectGatewayControlPlaneWithID( return cp } -// KongServiceAttachedToCPWithID deploys a KongService resource and returns the resource. +// KongServiceWithID deploys a KongService resource and returns the resource. // The Status ID and Programmed condition are set on the Service using status Update() call. // It can be useful where the reconciler for KonnectGatewayControlPlane is not started // and hence the status has to be filled manually. -func KongServiceAttachedToCPWithID( +func KongServiceWithID( t *testing.T, ctx context.Context, cl client.Client, - cp *konnectv1alpha1.KonnectGatewayControlPlane, opts ...ObjOption, ) *configurationv1alpha1.KongService { t.Helper() - svc := KongServiceAttachedToCP(t, ctx, cl, cp, opts...) + svc := KongService(t, ctx, cl, opts...) svc.Status.Conditions = []metav1.Condition{ { Type: konnectv1alpha1.KonnectEntityProgrammedConditionType, @@ -228,12 +257,11 @@ func KongServiceAttachedToCPWithID( return svc } -// KongServiceAttachedToCP deploys a KongService resource and returns the resource. -func KongServiceAttachedToCP( +// KongService deploys a KongService resource and returns the resource. +func KongService( t *testing.T, ctx context.Context, cl client.Client, - cp *konnectv1alpha1.KonnectGatewayControlPlane, opts ...ObjOption, ) *configurationv1alpha1.KongService { t.Helper() @@ -249,12 +277,6 @@ func KongServiceAttachedToCP( URL: lo.ToPtr("http://example.com"), Host: "example.com", }, - ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, - KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ - Name: cp.Name, - }, - }, }, } @@ -570,12 +592,11 @@ func KongCertificateAttachedToCP( return cert } -// KongUpstreamAttachedToCP deploys a KongUpstream resource attached to a Control Plane and returns the resource. -func KongUpstreamAttachedToCP( +// KongUpstream deploys a KongUpstream resource and returns it. +func KongUpstream( t *testing.T, ctx context.Context, cl client.Client, - cp *konnectv1alpha1.KonnectGatewayControlPlane, opts ...ObjOption, ) *configurationv1alpha1.KongUpstream { t.Helper() @@ -584,14 +605,7 @@ func KongUpstreamAttachedToCP( ObjectMeta: metav1.ObjectMeta{ GenerateName: "upstream-", }, - Spec: configurationv1alpha1.KongUpstreamSpec{ - ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, - KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ - Name: cp.Name, - }, - }, - }, + Spec: configurationv1alpha1.KongUpstreamSpec{}, } for _, opt := range opts { opt(u) @@ -639,7 +653,6 @@ func KongConsumerAttachedToCP( ctx context.Context, cl client.Client, username string, - cp *konnectv1alpha1.KonnectGatewayControlPlane, opts ...ObjOption, ) *configurationv1.KongConsumer { t.Helper() @@ -648,14 +661,6 @@ func KongConsumerAttachedToCP( ObjectMeta: metav1.ObjectMeta{ GenerateName: "consumer-", }, - Spec: configurationv1.KongConsumerSpec{ - ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, - KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ - Name: cp.Name, - }, - }, - }, Username: username, } for _, opt := range opts { @@ -673,7 +678,6 @@ func KongConsumerGroupAttachedToCP( t *testing.T, ctx context.Context, cl client.Client, - cp *konnectv1alpha1.KonnectGatewayControlPlane, opts ...ObjOption, ) *configurationv1beta1.KongConsumerGroup { t.Helper() @@ -684,12 +688,6 @@ func KongConsumerGroupAttachedToCP( Name: name, }, Spec: configurationv1beta1.KongConsumerGroupSpec{ - ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, - KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ - Name: cp.Name, - }, - }, Name: name, }, } @@ -746,16 +744,13 @@ func KongVaultAttachedToCP( return vault } -type kongKeyOption func(*configurationv1alpha1.KongKey) - -// KongKeyAttachedToCP deploys a KongKey resource attached to a CP and returns the resource. -func KongKeyAttachedToCP( +// KongKey deploys a KongKey resource and returns the resource. +func KongKey( t *testing.T, ctx context.Context, cl client.Client, kid, name string, - cp *konnectv1alpha1.KonnectGatewayControlPlane, - opts ...kongKeyOption, + opts ...ObjOption, ) *configurationv1alpha1.KongKey { t.Helper() @@ -764,12 +759,6 @@ func KongKeyAttachedToCP( GenerateName: "key-", }, Spec: configurationv1alpha1.KongKeySpec{ - ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, - KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ - Name: cp.GetName(), - }, - }, KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ KID: kid, Name: lo.ToPtr(name), @@ -831,13 +820,12 @@ func RateLimitingPlugin( return plugin } -// KongKeySetAttachedToCP deploys a KongKeySet resource attached to a CP and returns the resource. -func KongKeySetAttachedToCP( +// KongKeySet deploys a KongKeySet resource and returns the resource. +func KongKeySet( t *testing.T, ctx context.Context, cl client.Client, name string, - cp *konnectv1alpha1.KonnectGatewayControlPlane, opts ...ObjOption, ) *configurationv1alpha1.KongKeySet { t.Helper() @@ -847,12 +835,6 @@ func KongKeySetAttachedToCP( Name: name, }, Spec: configurationv1alpha1.KongKeySetSpec{ - ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, - KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ - Name: cp.GetName(), - }, - }, KongKeySetAPISpec: configurationv1alpha1.KongKeySetAPISpec{ Name: name, }, @@ -903,12 +885,11 @@ func KongSNIAttachedToCertificate( return sni } -// KongDataPlaneClientCertificateAttachedToCP deploys a KongDataPlaneClientCertificate resource attached to a CP and returns the resource. +// KongDataPlaneClientCertificateAttachedToCP deploys a KongDataPlaneClientCertificate resource and returns the resource. func KongDataPlaneClientCertificateAttachedToCP( t *testing.T, ctx context.Context, cl client.Client, - cp *konnectv1alpha1.KonnectGatewayControlPlane, opts ...ObjOption, ) *configurationv1alpha1.KongDataPlaneClientCertificate { t.Helper() @@ -918,12 +899,6 @@ func KongDataPlaneClientCertificateAttachedToCP( GenerateName: "dp-cert-", }, Spec: configurationv1alpha1.KongDataPlaneClientCertificateSpec{ - ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, - KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ - Name: cp.GetName(), - }, - }, KongDataPlaneClientCertificateAPISpec: configurationv1alpha1.KongDataPlaneClientCertificateAPISpec{ Cert: TestValidCACertPEM, }, diff --git a/test/integration/test_konnect_entities.go b/test/integration/test_konnect_entities.go index 52e440fa7..b25d59243 100644 --- a/test/integration/test_konnect_entities.go +++ b/test/integration/test_konnect_entities.go @@ -65,7 +65,8 @@ func TestKonnectEntities(t *testing.T) { assertKonnectEntityProgrammed(t, cp) }, testutils.ObjectUpdateTimeout, testutils.ObjectUpdateTick) - ks := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp, + ks := deploy.KongService(t, ctx, clientNamespaced, + deploy.WithKonnectIDControlPlaneRef(cp), deploy.WithTestIDLabel(testID), ) @@ -96,7 +97,8 @@ func TestKonnectEntities(t *testing.T) { assertKonnectEntityProgrammed(t, kr) }, testutils.ObjectUpdateTimeout, testutils.ObjectUpdateTick) - kcg := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, cp, + kcg := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithTestIDLabel(testID), ) @@ -108,7 +110,8 @@ func TestKonnectEntities(t *testing.T) { assertKonnectEntityProgrammed(t, kcg) }, testutils.ObjectUpdateTimeout, testutils.ObjectUpdateTick) - kc := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, "kc-"+testID, cp, + kc := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, "kc-"+testID, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithTestIDLabel(testID), func(obj client.Object) { kc := obj.(*configurationv1.KongConsumer) @@ -162,7 +165,8 @@ func TestKonnectEntities(t *testing.T) { assertKonnectEntityProgrammed(t, globalKPB) }, testutils.ObjectUpdateTimeout, testutils.ObjectUpdateTick) - kup := deploy.KongUpstreamAttachedToCP(t, ctx, clientNamespaced, cp, + kup := deploy.KongUpstream(t, ctx, clientNamespaced, + deploy.WithKonnectNamespacedRefControlPlaneRef(cp), deploy.WithTestIDLabel(testID), func(obj client.Object) { kup := obj.(*configurationv1alpha1.KongUpstream)