From 13e958603f019bd543ac7fd319982c4312b4c566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Ma=C5=82ek?= Date: Thu, 26 Sep 2024 16:25:07 +0200 Subject: [PATCH] feat(konnect): allow binding KongConsumerGroup in KongPluginBinding (#654) --- CHANGELOG.md | 1 + ...luginbinding-kongservice-kongconsumer.yaml | 17 +- ...ngpluginbinding-kongservice-kongroute.yaml | 2 +- config/samples/konnect-kongpluginbinding.yaml | 2 +- ...ervice-and-kongroute-plugin-annotated.yaml | 2 +- .../konnect-kongservice-plugin-annotated.yaml | 2 +- controller/konnect/kongpluginbuilder.go | 30 +++ .../konnect/ops/ops_kongpluginbinding.go | 18 +- test/envtest/deploy_resources.go | 4 +- .../kongpluginbinding_unmanaged_test.go | 212 ++++++++++-------- test/envtest/update_status.go | 17 ++ 11 files changed, 200 insertions(+), 107 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79cea83a2..a2ce5305c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ - `KongService` [#550](https://github.com/Kong/gateway-operator/pull/550) - `KongRoute` [#644](https://github.com/Kong/gateway-operator/pull/644) - `KongConsumer` [#652](https://github.com/Kong/gateway-operator/pull/652) + - `KongConsumerGroup` [#654](https://github.com/Kong/gateway-operator/pull/654) These `KongPluginBinding`s are taken by the `KongPluginBinding` reconciler to create the corresponding plugin objects in Konnect. - `KongConsumer` associated with `ConsumerGroups` is now reconciled in Konnect by removing/adding diff --git a/config/samples/konnect-kongpluginbinding-kongservice-kongconsumer.yaml b/config/samples/konnect-kongpluginbinding-kongservice-kongconsumer.yaml index f8da8bbd7..8e658e7d8 100644 --- a/config/samples/konnect-kongpluginbinding-kongservice-kongconsumer.yaml +++ b/config/samples/konnect-kongpluginbinding-kongservice-kongconsumer.yaml @@ -7,7 +7,7 @@ metadata: spec: type: token token: kpat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - serverURL: eu.api.konghq.tech + serverURL: eu.api.konghq.tech --- kind: KonnectGatewayControlPlane apiVersion: konnect.konghq.com/v1alpha1 @@ -46,14 +46,13 @@ config: policy: local plugin: rate-limiting --- -kind: KongConsumer -apiVersion: configuration.konghq.com/v1 +kind: KongConsumerGroup +apiVersion: configuration.konghq.com/v1beta1 metadata: - name: consumer-for-service-plugin-binding + name: consumer-group-for-service-pluginbinding namespace: default -username: consumer-for-service-plugin-binding -custom_id: 08433C12-2B81-4738-B61D-3AA2136F0102 spec: + name: consumer-group-for-service-pluginbinding controlPlaneRef: type: konnectNamespacedRef konnectNamespacedRef: @@ -62,7 +61,7 @@ spec: apiVersion: configuration.konghq.com/v1alpha1 kind: KongPluginBinding metadata: - name: plugin-binding-kongservice-kongroute + name: plugin-binding-kongservice-kongconsumergroup spec: controlPlaneRef: type: konnectNamespacedRef @@ -75,5 +74,5 @@ spec: name: service-1 kind: KongService group: configuration.konghq.com - consumerRef: - name: consumer-for-service-plugin-binding + consumerGroupRef: + name: consumer-group-for-service-pluginbinding diff --git a/config/samples/konnect-kongpluginbinding-kongservice-kongroute.yaml b/config/samples/konnect-kongpluginbinding-kongservice-kongroute.yaml index 709fafad1..2fa287231 100644 --- a/config/samples/konnect-kongpluginbinding-kongservice-kongroute.yaml +++ b/config/samples/konnect-kongpluginbinding-kongservice-kongroute.yaml @@ -7,7 +7,7 @@ metadata: spec: type: token token: kpat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - serverURL: eu.api.konghq.tech + serverURL: eu.api.konghq.tech --- kind: KonnectGatewayControlPlane apiVersion: konnect.konghq.com/v1alpha1 diff --git a/config/samples/konnect-kongpluginbinding.yaml b/config/samples/konnect-kongpluginbinding.yaml index de7a219d3..9a853fde8 100644 --- a/config/samples/konnect-kongpluginbinding.yaml +++ b/config/samples/konnect-kongpluginbinding.yaml @@ -7,7 +7,7 @@ metadata: spec: type: token token: kpat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - serverURL: eu.api.konghq.tech + serverURL: eu.api.konghq.tech --- kind: KonnectGatewayControlPlane apiVersion: konnect.konghq.com/v1alpha1 diff --git a/config/samples/konnect-kongservice-and-kongroute-plugin-annotated.yaml b/config/samples/konnect-kongservice-and-kongroute-plugin-annotated.yaml index f013eb129..86a226be7 100644 --- a/config/samples/konnect-kongservice-and-kongroute-plugin-annotated.yaml +++ b/config/samples/konnect-kongservice-and-kongroute-plugin-annotated.yaml @@ -6,7 +6,7 @@ metadata: spec: type: token token: kpat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - serverURL: eu.api.konghq.tech + serverURL: eu.api.konghq.tech --- kind: KonnectGatewayControlPlane apiVersion: konnect.konghq.com/v1alpha1 diff --git a/config/samples/konnect-kongservice-plugin-annotated.yaml b/config/samples/konnect-kongservice-plugin-annotated.yaml index 83156030e..d69063fd3 100644 --- a/config/samples/konnect-kongservice-plugin-annotated.yaml +++ b/config/samples/konnect-kongservice-plugin-annotated.yaml @@ -6,7 +6,7 @@ metadata: spec: type: token token: kpat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - serverURL: eu.api.konghq.tech + serverURL: eu.api.konghq.tech --- kind: KonnectGatewayControlPlane apiVersion: konnect.konghq.com/v1alpha1 diff --git a/controller/konnect/kongpluginbuilder.go b/controller/konnect/kongpluginbuilder.go index 090be7246..5fc8d461a 100644 --- a/controller/konnect/kongpluginbuilder.go +++ b/controller/konnect/kongpluginbuilder.go @@ -54,6 +54,36 @@ func (b *KongPluginBindingBuilder) WithControlPlaneRef(ref *configurationv1alpha return b } +// WithControlPlaneRefKonnectNamespaced sets the control plane reference of the KongPluginBinding. +// NOTE: Users have to ensure that the ControlPlaneRef that's set here +// is the same across all the KongPluginBinding targets. +func (b *KongPluginBindingBuilder) WithControlPlaneRefKonnectNamespaced(name string) *KongPluginBindingBuilder { + b.binding.Spec.ControlPlaneRef = &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, + KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ + Name: name, + }, + } + + return b +} + +// WithConsumerTarget sets the consumer target of the KongPluginBinding. +func (b *KongPluginBindingBuilder) WithConsumerTarget(consumer string) *KongPluginBindingBuilder { + b.binding.Spec.Targets.ConsumerReference = &configurationv1alpha1.TargetRef{ + Name: consumer, + } + return b +} + +// WithConsumerGroupTarget sets the consumer group target of the KongPluginBinding. +func (b *KongPluginBindingBuilder) WithConsumerGroupTarget(cg string) *KongPluginBindingBuilder { + b.binding.Spec.Targets.ConsumerGroupReference = &configurationv1alpha1.TargetRef{ + Name: cg, + } + return b +} + // WithServiceTarget sets the service target of the KongPluginBinding. func (b *KongPluginBindingBuilder) WithServiceTarget(serviceName string) *KongPluginBindingBuilder { b.binding.Spec.Targets.ServiceReference = &configurationv1alpha1.TargetRefWithGroupKind{ diff --git a/controller/konnect/ops/ops_kongpluginbinding.go b/controller/konnect/ops/ops_kongpluginbinding.go index df2f5f773..da9e1d409 100644 --- a/controller/konnect/ops/ops_kongpluginbinding.go +++ b/controller/konnect/ops/ops_kongpluginbinding.go @@ -18,6 +18,7 @@ import ( configurationv1 "github.com/kong/kubernetes-configuration/api/configuration/v1" configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + configurationv1beta1 "github.com/kong/kubernetes-configuration/api/configuration/v1beta1" "github.com/kong/kubernetes-configuration/pkg/metadata" ) @@ -199,7 +200,6 @@ func getPluginBindingTargets( targetObjects = append(targetObjects, &kongRoute) } if ref := targets.ConsumerReference; ref != nil { - kongConsumer := configurationv1.KongConsumer{} kongConsumer.SetName(ref.Name) kongConsumer.SetNamespace(pluginBinding.GetNamespace()) @@ -208,8 +208,15 @@ func getPluginBindingTargets( } targetObjects = append(targetObjects, &kongConsumer) } - - // TODO: https://github.com/Kong/gateway-operator/issues/527 add support for KongConsumerGroup + if ref := targets.ConsumerGroupReference; ref != nil { + kongConsumerGroup := configurationv1beta1.KongConsumerGroup{} + kongConsumerGroup.SetName(ref.Name) + kongConsumerGroup.SetNamespace(pluginBinding.GetNamespace()) + if err := cl.Get(ctx, client.ObjectKeyFromObject(&kongConsumerGroup), &kongConsumerGroup); err != nil { + return nil, err + } + targetObjects = append(targetObjects, &kongConsumerGroup) + } return targetObjects, nil } @@ -281,7 +288,10 @@ func kongPluginWithTargetsToKongPluginInput( pluginInput.Consumer = &sdkkonnectcomp.PluginConsumer{ ID: lo.ToPtr(id), } - // TODO: https://github.com/Kong/gateway-operator/issues/527 add support for KongConsumerGroup + case *configurationv1beta1.KongConsumerGroup: + pluginInput.ConsumerGroup = &sdkkonnectcomp.PluginConsumerGroup{ + ID: lo.ToPtr(id), + } default: return nil, fmt.Errorf("unsupported target type %T", t) } diff --git a/test/envtest/deploy_resources.go b/test/envtest/deploy_resources.go index 782ac1639..cc227e151 100644 --- a/test/envtest/deploy_resources.go +++ b/test/envtest/deploy_resources.go @@ -388,7 +388,7 @@ func deployKongConsumerGroupAttachedToCP( cl client.Client, cgName string, cp *konnectv1alpha1.KonnectGatewayControlPlane, -) configurationv1beta1.KongConsumerGroup { +) *configurationv1beta1.KongConsumerGroup { t.Helper() cg := configurationv1beta1.KongConsumerGroup{ @@ -409,7 +409,7 @@ func deployKongConsumerGroupAttachedToCP( require.NoError(t, cl.Create(ctx, &cg)) t.Logf("deployed new KongConsumerGroup %s", client.ObjectKeyFromObject(&cg)) - return cg + return &cg } func deployKongVaultAttachedToCP( diff --git a/test/envtest/kongpluginbinding_unmanaged_test.go b/test/envtest/kongpluginbinding_unmanaged_test.go index 160d0c27a..3e6d5a524 100644 --- a/test/envtest/kongpluginbinding_unmanaged_test.go +++ b/test/envtest/kongpluginbinding_unmanaged_test.go @@ -81,26 +81,11 @@ func TestKongPluginBindingUnmanaged(t *testing.T) { wKongPlugin := setupWatch[configurationv1.KongPluginList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) kpb := deployKongPluginBinding(t, ctx, clientNamespaced, - &configurationv1alpha1.KongPluginBinding{ - Spec: configurationv1alpha1.KongPluginBindingSpec{ - ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, - KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ - Name: cp.Name, - }, - }, - PluginReference: configurationv1alpha1.PluginRef{ - Name: proxyCacheKongPlugin.Name, - }, - Targets: configurationv1alpha1.KongPluginBindingTargets{ - ServiceReference: &configurationv1alpha1.TargetRefWithGroupKind{ - Group: configurationv1alpha1.GroupVersion.Group, - Kind: "KongService", - Name: kongService.Name, - }, - }, - }, - }, + konnect.NewKongPluginBindingBuilder(). + WithControlPlaneRefKonnectNamespaced(cp.Name). + WithPluginRef(proxyCacheKongPlugin.Name). + WithServiceTarget(kongService.Name). + Build(), ) t.Logf( "wait for the controller to pick the new unmanaged KongPluginBinding %s and put a %s finalizer on the referenced plugin %s", @@ -190,26 +175,11 @@ func TestKongPluginBindingUnmanaged(t *testing.T) { wKongPlugin := setupWatch[configurationv1.KongPluginList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) kpb := deployKongPluginBinding(t, ctx, clientNamespaced, - &configurationv1alpha1.KongPluginBinding{ - Spec: configurationv1alpha1.KongPluginBindingSpec{ - ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, - KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ - Name: cp.Name, - }, - }, - PluginReference: configurationv1alpha1.PluginRef{ - Name: proxyCacheKongPlugin.Name, - }, - Targets: configurationv1alpha1.KongPluginBindingTargets{ - RouteReference: &configurationv1alpha1.TargetRefWithGroupKind{ - Group: configurationv1alpha1.GroupVersion.Group, - Kind: "KongRoute", - Name: kongRoute.Name, - }, - }, - }, - }, + konnect.NewKongPluginBindingBuilder(). + WithControlPlaneRefKonnectNamespaced(cp.Name). + WithPluginRef(proxyCacheKongPlugin.Name). + WithRouteTarget(kongRoute.Name). + Build(), ) t.Logf( "wait for the controller to pick the new unmanaged KongPluginBinding %s and put a %s finalizer on the referenced plugin %s", @@ -305,31 +275,12 @@ func TestKongPluginBindingUnmanaged(t *testing.T) { nil, ) kpb := deployKongPluginBinding(t, ctx, clientNamespaced, - &configurationv1alpha1.KongPluginBinding{ - Spec: configurationv1alpha1.KongPluginBindingSpec{ - ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, - KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ - Name: cp.Name, - }, - }, - PluginReference: configurationv1alpha1.PluginRef{ - Name: proxyCacheKongPlugin.Name, - }, - Targets: configurationv1alpha1.KongPluginBindingTargets{ - RouteReference: &configurationv1alpha1.TargetRefWithGroupKind{ - Group: configurationv1alpha1.GroupVersion.Group, - Kind: "KongRoute", - Name: kongRoute.Name, - }, - ServiceReference: &configurationv1alpha1.TargetRefWithGroupKind{ - Group: configurationv1alpha1.GroupVersion.Group, - Kind: "KongService", - Name: kongService.Name, - }, - }, - }, - }, + konnect.NewKongPluginBindingBuilder(). + WithControlPlaneRefKonnectNamespaced(cp.Name). + WithPluginRef(proxyCacheKongPlugin.Name). + WithRouteTarget(kongRoute.Name). + WithServiceTarget(kongService.Name). + Build(), ) t.Logf( "wait for the controller to pick the new unmanaged KongPluginBinding %s and put a %s finalizer on the referenced plugin %s", @@ -432,29 +383,12 @@ func TestKongPluginBindingUnmanaged(t *testing.T) { nil, ) kpb := deployKongPluginBinding(t, ctx, clientNamespaced, - &configurationv1alpha1.KongPluginBinding{ - Spec: configurationv1alpha1.KongPluginBindingSpec{ - ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ - Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, - KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ - Name: cp.Name, - }, - }, - PluginReference: configurationv1alpha1.PluginRef{ - Name: proxyCacheKongPlugin.Name, - }, - Targets: configurationv1alpha1.KongPluginBindingTargets{ - ConsumerReference: &configurationv1alpha1.TargetRef{ - Name: kongConsumer.Name, - }, - ServiceReference: &configurationv1alpha1.TargetRefWithGroupKind{ - Group: configurationv1alpha1.GroupVersion.Group, - Kind: "KongService", - Name: kongService.Name, - }, - }, - }, - }, + konnect.NewKongPluginBindingBuilder(). + WithControlPlaneRefKonnectNamespaced(cp.Name). + WithPluginRef(proxyCacheKongPlugin.Name). + WithConsumerTarget(kongConsumer.Name). + WithServiceTarget(kongService.Name). + Build(), ) t.Logf( "wait for the controller to pick the new unmanaged KongPluginBinding %s and put a %s finalizer on the referenced plugin %s", @@ -519,4 +453,106 @@ func TestKongPluginBindingUnmanaged(t *testing.T) { assert.True(c, sdk.PluginSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("binding to KongService and KongConsumerGroup", func(t *testing.T) { + proxyCacheKongPlugin := deployProxyCachePlugin(t, ctx, clientNamespaced) + + serviceID := uuid.NewString() + consumerGroupID := uuid.NewString() + pluginID := uuid.NewString() + cgName := "test-group-" + uuid.NewString() + + kongService := deployKongServiceAttachedToCP(t, ctx, clientNamespaced, cp) + updateKongServiceStatusWithProgrammed(t, ctx, clientNamespaced, kongService, serviceID, cp.GetKonnectStatus().GetKonnectID()) + kongConsumerGroup := deployKongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, cgName, cp) + updateKongConsumerGroupStatusWithKonnectID(t, ctx, clientNamespaced, kongConsumerGroup, consumerGroupID, cp.GetKonnectStatus().GetKonnectID()) + + wKongPlugin := setupWatch[configurationv1.KongPluginList](t, ctx, clientWithWatch, client.InNamespace(ns.Name)) + sdk.PluginSDK.EXPECT(). + CreatePlugin( + mock.Anything, + cp.GetKonnectStatus().GetKonnectID(), + mock.MatchedBy(func(pi sdkkonnectcomp.PluginInput) bool { + return pi.ConsumerGroup != nil && pi.ConsumerGroup.ID != nil && *pi.ConsumerGroup.ID == consumerGroupID && + pi.Service != nil && pi.Service.ID != nil && *pi.Service.ID == serviceID + })). + Return( + &sdkkonnectops.CreatePluginResponse{ + Plugin: &sdkkonnectcomp.Plugin{ + ID: lo.ToPtr(pluginID), + }, + }, + nil, + ) + kpb := deployKongPluginBinding(t, ctx, clientNamespaced, + konnect.NewKongPluginBindingBuilder(). + WithControlPlaneRefKonnectNamespaced(cp.Name). + WithPluginRef(proxyCacheKongPlugin.Name). + WithServiceTarget(kongService.Name). + WithConsumerGroupTarget(kongConsumerGroup.Name). + Build(), + ) + t.Logf( + "wait for the controller to pick the new unmanaged KongPluginBinding %s and put a %s finalizer on the referenced plugin %s", + client.ObjectKeyFromObject(kpb), + consts.PluginInUseFinalizer, + client.ObjectKeyFromObject(proxyCacheKongPlugin), + ) + _ = watchFor(t, ctx, wKongPlugin, watch.Modified, + func(kp *configurationv1.KongPlugin) bool { + return kp.Name == proxyCacheKongPlugin.Name && + controllerutil.ContainsFinalizer(kp, consts.PluginInUseFinalizer) + }, + "KongPlugin wasn't updated to get the plugin-in-use finalizer", + ) + assert.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, sdk.PluginSDK.AssertExpectations(t)) + }, waitTime, tickTime) + + sdk.PluginSDK.EXPECT(). + DeletePlugin(mock.Anything, cp.GetKonnectStatus().GetKonnectID(), mock.Anything). + Return( + &sdkkonnectops.DeletePluginResponse{ + StatusCode: 200, + }, + nil, + ) + + t.Logf("delete the KongPlugin %s, then check it does not get collected", client.ObjectKeyFromObject(proxyCacheKongPlugin)) + require.NoError(t, clientNamespaced.Delete(ctx, proxyCacheKongPlugin)) + assert.EventuallyWithT(t, func(c *assert.CollectT) { + assert.False(c, k8serrors.IsNotFound( + clientNamespaced.Get(ctx, client.ObjectKeyFromObject(proxyCacheKongPlugin), proxyCacheKongPlugin), + )) + assert.True(c, proxyCacheKongPlugin.DeletionTimestamp != nil) + assert.True(c, controllerutil.ContainsFinalizer(proxyCacheKongPlugin, consts.PluginInUseFinalizer)) + }, waitTime, tickTime) + + t.Logf("delete the unmanaged KongPluginBinding %s, then check the proxy-cache KongPlugin %s gets collected", + client.ObjectKeyFromObject(kpb), + client.ObjectKeyFromObject(proxyCacheKongPlugin), + ) + require.NoError(t, clientNamespaced.Delete(ctx, kpb)) + _ = watchFor(t, ctx, wKongPlugin, watch.Deleted, + func(kp *configurationv1.KongPlugin) bool { + return kp.Name == proxyCacheKongPlugin.Name + }, + "KongPlugin did not got deleted but shouldn't have", + ) + + t.Logf( + "delete the KongConsumerGroup %s and check it gets collected, as the KongPluginBinding finalizer should have been removed", + client.ObjectKeyFromObject(kongConsumerGroup), + ) + require.NoError(t, clientNamespaced.Delete(ctx, kongConsumerGroup)) + assert.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, k8serrors.IsNotFound( + clientNamespaced.Get(ctx, client.ObjectKeyFromObject(kongConsumerGroup), kongConsumerGroup), + )) + }, waitTime, tickTime) + + assert.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, sdk.PluginSDK.AssertExpectations(t)) + }, waitTime, tickTime) + }) } diff --git a/test/envtest/update_status.go b/test/envtest/update_status.go index ef518b2d4..3034b14c6 100644 --- a/test/envtest/update_status.go +++ b/test/envtest/update_status.go @@ -13,6 +13,7 @@ import ( configurationv1 "github.com/kong/kubernetes-configuration/api/configuration/v1" configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + configurationv1beta1 "github.com/kong/kubernetes-configuration/api/configuration/v1beta1" konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" ) @@ -32,6 +33,22 @@ func updateKongConsumerStatusWithKonnectID( require.NoError(t, cl.Status().Update(ctx, obj)) } +func updateKongConsumerGroupStatusWithKonnectID( + t *testing.T, + ctx context.Context, + cl client.Client, + obj *configurationv1beta1.KongConsumerGroup, + id string, + cpID string, +) { + obj.Status.Konnect = &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + ControlPlaneID: cpID, + KonnectEntityStatus: konnectEntityStatus(id), + } + + require.NoError(t, cl.Status().Update(ctx, obj)) +} + func updateKongServiceStatusWithProgrammed( t *testing.T, ctx context.Context,