From c5ebdb33dd28aedf78dc015b02daff14191cd8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Burzy=C5=84ski?= Date: Thu, 9 Jan 2025 15:13:47 +0100 Subject: [PATCH 1/2] feat: add support for konnectID CP references --- CHANGELOG.md | 3 + .../konnect_controlplaneref_by_id.yaml | 46 +++++ controller/konnect/errors.go | 18 +- controller/konnect/index.go | 37 ++-- controller/konnect/index_kongcacertificate.go | 13 +- controller/konnect/index_kongcertificate.go | 13 +- controller/konnect/index_kongconsumer.go | 13 +- controller/konnect/index_kongconsumergroup.go | 13 +- .../konnect/index_kongdataplanecertificate.go | 13 +- controller/konnect/index_kongkey.go | 13 +- controller/konnect/index_kongkeyset.go | 13 +- controller/konnect/index_kongservice.go | 13 +- controller/konnect/index_kongupstream.go | 13 +- controller/konnect/index_kongvault.go | 13 +- .../index_konnectgatewaycontrolplane.go | 20 ++ controller/konnect/index_test.go | 30 ++- .../konnect/reconciler_certificateref.go | 7 +- .../konnect/reconciler_certificateref_test.go | 5 +- .../konnect/reconciler_controlplaneref.go | 193 +++++++++--------- .../reconciler_controlplaneref_test.go | 2 +- controller/konnect/reconciler_generic.go | 68 +++++- controller/konnect/reconciler_kongplugin.go | 10 +- .../reconciler_kongplugin_combinations.go | 45 ++-- ...reconciler_kongplugin_combinations_test.go | 13 +- controller/konnect/reconciler_serviceref.go | 2 +- controller/konnect/reconciler_upstreamref.go | 7 +- .../konnect/reconciler_upstreamref_test.go | 3 +- controller/konnect/watch.go | 16 +- controller/konnect/watch_credential.go | 2 +- controller/konnect/watch_credentialacl.go | 13 +- controller/konnect/watch_credentialapikey.go | 13 +- .../konnect/watch_credentialbasicauth.go | 13 +- controller/konnect/watch_credentialhmac.go | 13 +- controller/konnect/watch_credentialjwt.go | 13 +- controller/konnect/watch_kongconsumer.go | 61 ++---- controller/konnect/watch_kongpluginbinding.go | 2 +- controller/konnect/watch_kongroute.go | 6 +- controller/konnect/watch_kongservice.go | 61 ++---- controller/konnect/watch_kongsni.go | 4 +- controller/konnect/watch_kongtarget.go | 4 +- controller/konnect/watch_test.go | 27 ++- modules/manager/controller_setup.go | 21 +- test/envtest/controller.go | 29 ++- ...konnect_entities_kongcacertificate_test.go | 44 +++- .../konnect_entities_kongcertificate_test.go | 45 +++- .../konnect_entities_kongconsumer_test.go | 58 +++++- ...konnect_entities_kongconsumergroup_test.go | 50 ++++- ...ies_kongdataplaneclientcertificate_test.go | 44 +++- test/envtest/konnect_entities_kongkey_test.go | 40 +++- .../konnect_entities_kongkeyset_test.go | 42 +++- .../konnect_entities_kongroute_test.go | 7 +- .../konnect_entities_kongservice_test.go | 61 +++++- .../konnect_entities_kongupstream_test.go | 60 +++++- .../konnect_entities_kongvault_test.go | 45 +++- test/helpers/deploy/deploy_resources.go | 77 ++++--- 55 files changed, 1017 insertions(+), 493 deletions(-) create mode 100644 config/samples/konnect_controlplaneref_by_id.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index cf03a71bb..e2540f434 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,9 @@ owning service. Currently specifying ingress service of `DataPlane` is supported. [#966](https://github.com/Kong/gateway-operator/pull/966) +- Added support for `ControlPlaneRef`s with `type` equal to `konnectID` for + all Konnect entities that refer to a `ControlPlane`. + [#985](https://github.com/Kong/gateway-operator/pull/985) ### Changed diff --git a/config/samples/konnect_controlplaneref_by_id.yaml b/config/samples/konnect_controlplaneref_by_id.yaml new file mode 100644 index 000000000..445960973 --- /dev/null +++ b/config/samples/konnect_controlplaneref_by_id.yaml @@ -0,0 +1,46 @@ +kind: KonnectAPIAuthConfiguration +apiVersion: konnect.konghq.com/v1alpha1 +metadata: + name: konnect-api-auth-dev + namespace: default +spec: + type: token + token: kpat_XXXXXX + serverURL: us.api.konghq.tech +--- +kind: KonnectGatewayControlPlane +apiVersion: konnect.konghq.com/v1alpha1 +metadata: + name: cp-to-be-referred-by-id + namespace: default +spec: + name: cp-to-be-referred-by-id + konnect: + authRef: + name: konnect-api-auth-dev +--- +kind: KongService +apiVersion: configuration.konghq.com/v1alpha1 +metadata: + name: service-1 + namespace: default +spec: + name: service-1 + host: example.com + controlPlaneRef: + type: konnectID + # Once `cp-to-be-referred-by-id` is created and has a Konnect ID assigned, replace the value below with it. + konnectID: "e43fcae3-1851-44e9-9b55-50e51b97a741" +--- +kind: KongConsumer +apiVersion: configuration.konghq.com/v1 +metadata: + name: consumer-1 + namespace: default +username: consumer-1 +custom_id: 08433C12-2B81-4738-B61D-3AA2136F0212 +spec: + controlPlaneRef: + type: konnectID + # Once `cp-to-be-referred-by-id` is created and has a Konnect ID assigned, replace the value below with it. + konnectID: "e43fcae3-1851-44e9-9b55-50e51b97a741" diff --git a/controller/konnect/errors.go b/controller/konnect/errors.go index b5c5d15d4..42d572447 100644 --- a/controller/konnect/errors.go +++ b/controller/konnect/errors.go @@ -5,19 +5,21 @@ import ( "time" "k8s.io/apimachinery/pkg/types" + + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" ) // ReferencedControlPlaneDoesNotExistError is an error type that is returned when // a Konnect entity references a KonnectGatewayControlPlane that does not exist. type ReferencedControlPlaneDoesNotExistError struct { - Reference types.NamespacedName + Reference configurationv1alpha1.ControlPlaneRef Err error } // Error implements the error interface. func (e ReferencedControlPlaneDoesNotExistError) Error() string { - return fmt.Sprintf("referenced Control Plane %s does not exist: %v", - e.Reference, e.Err, + return fmt.Sprintf("referenced Control Plane %q does not exist: %v", + controlPlaneRefToString(e.Reference), e.Err, ) } @@ -136,3 +138,13 @@ func (e ReferencedKongKeySetIsBeingDeleted) Error() string { return fmt.Sprintf("referenced KongKeySet %s is being deleted (deletion timestamp: %s)", e.Reference, e.DeletionTimestamp) } + +// ReferencedKongGatewayControlPlaneIsUnsupported is an error type that is returned when a given CP reference type is not +// supported. +type ReferencedKongGatewayControlPlaneIsUnsupported struct { + Reference configurationv1alpha1.ControlPlaneRef +} + +func (e ReferencedKongGatewayControlPlaneIsUnsupported) Error() string { + return fmt.Sprintf("referenced ControlPlaneRef %s is unsupported", controlPlaneRefToString(e.Reference)) +} diff --git a/controller/konnect/index.go b/controller/konnect/index.go index 51ba200ca..fa61d4681 100644 --- a/controller/konnect/index.go +++ b/controller/konnect/index.go @@ -1,6 +1,8 @@ package konnect import ( + "context" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kong/gateway-operator/controller/konnect/constraints" @@ -13,24 +15,33 @@ type ReconciliationIndexOption struct { ExtractValue client.IndexerFunc } -// controlPlaneKonnectNamespacedRefAsSlice returns a slice of strings representing -// the KonnectNamespacedRef of the object. -func controlPlaneKonnectNamespacedRefAsSlice[ +// indexKonnectGatewayControlPlaneRef returns a function that extracts the KonnectGatewayControlPlane reference from the +// object and returns it as a slice of strings for indexing. +func indexKonnectGatewayControlPlaneRef[ + T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T], +](cl client.Client) client.IndexerFunc { + return func(obj client.Object) []string { + o, ok := obj.(TEnt) + if !ok { + return nil + } + return controlPlaneRefAsSlice(o, cl) + } +} + +// controlPlaneRefAsSlice returns a slice of strings representing the KonnectNamespacedRef of the object. +func controlPlaneRefAsSlice[ T constraints.SupportedKonnectEntityType, TEnt constraints.EntityType[T], -](ent TEnt) []string { - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(ent) +](ent TEnt, cl client.Client) []string { + cpRef, ok := getControlPlaneRef(ent).Get() if !ok { return nil } - - konnectRef := cpRef.KonnectNamespacedRef - // NOTE: cross namespace refs are not supported yet. - // TODO: https://github.com/Kong/kubernetes-configuration/issues/36 - // Specifying the same namespace is optional and is supported. - if konnectRef.Namespace != "" && konnectRef.Namespace != ent.GetNamespace() { + cp, err := getCPForRef(context.Background(), cl, cpRef, ent.GetNamespace()) + if err != nil { return nil } - - return []string{ent.GetNamespace() + "/" + konnectRef.Name} + return []string{client.ObjectKeyFromObject(cp).String()} } diff --git a/controller/konnect/index_kongcacertificate.go b/controller/konnect/index_kongcacertificate.go index de2a449e3..aee8791a0 100644 --- a/controller/konnect/index_kongcacertificate.go +++ b/controller/konnect/index_kongcacertificate.go @@ -12,21 +12,12 @@ const ( ) // IndexOptionsForKongCACertificate returns required Index options for KongCACertificate reconclier. -func IndexOptionsForKongCACertificate() []ReconciliationIndexOption { +func IndexOptionsForKongCACertificate(cl client.Client) []ReconciliationIndexOption { return []ReconciliationIndexOption{ { IndexObject: &configurationv1alpha1.KongCACertificate{}, IndexField: IndexFieldKongCACertificateOnKonnectGatewayControlPlane, - ExtractValue: konnectGatewayControlPlaneRefFromKongCACertificate, + ExtractValue: indexKonnectGatewayControlPlaneRef[configurationv1alpha1.KongCACertificate](cl), }, } } - -// konnectGatewayControlPlaneRefFromKongCACertificate returns namespace/name of referenced KonnectGatewayControlPlane in KongCACertificate spec. -func konnectGatewayControlPlaneRefFromKongCACertificate(obj client.Object) []string { - cert, ok := obj.(*configurationv1alpha1.KongCACertificate) - if !ok { - return nil - } - return controlPlaneKonnectNamespacedRefAsSlice(cert) -} diff --git a/controller/konnect/index_kongcertificate.go b/controller/konnect/index_kongcertificate.go index 6fe3ff649..f00a32c10 100644 --- a/controller/konnect/index_kongcertificate.go +++ b/controller/konnect/index_kongcertificate.go @@ -12,21 +12,12 @@ const ( ) // IndexOptionsForKongCertificate returns required Index options for KongCertificate reconclier. -func IndexOptionsForKongCertificate() []ReconciliationIndexOption { +func IndexOptionsForKongCertificate(cl client.Client) []ReconciliationIndexOption { return []ReconciliationIndexOption{ { IndexObject: &configurationv1alpha1.KongCertificate{}, IndexField: IndexFieldKongCertificateOnKonnectGatewayControlPlane, - ExtractValue: konnectGatewayControlPlaneRefFromKongCertificate, + ExtractValue: indexKonnectGatewayControlPlaneRef[configurationv1alpha1.KongCertificate](cl), }, } } - -// konnectGatewayControlPlaneRefFromKongCertificate returns namespace/name of referenced KonnectGatewayControlPlane in KongCertificate spec. -func konnectGatewayControlPlaneRefFromKongCertificate(obj client.Object) []string { - cert, ok := obj.(*configurationv1alpha1.KongCertificate) - if !ok { - return nil - } - return controlPlaneKonnectNamespacedRefAsSlice(cert) -} diff --git a/controller/konnect/index_kongconsumer.go b/controller/konnect/index_kongconsumer.go index 1b2225884..a07ce778e 100644 --- a/controller/konnect/index_kongconsumer.go +++ b/controller/konnect/index_kongconsumer.go @@ -17,7 +17,7 @@ const ( ) // IndexOptionsForKongConsumer returns required Index options for KongConsumer reconciler. -func IndexOptionsForKongConsumer() []ReconciliationIndexOption { +func IndexOptionsForKongConsumer(cl client.Client) []ReconciliationIndexOption { return []ReconciliationIndexOption{ { IndexObject: &configurationv1.KongConsumer{}, @@ -32,7 +32,7 @@ func IndexOptionsForKongConsumer() []ReconciliationIndexOption { { IndexObject: &configurationv1.KongConsumer{}, IndexField: IndexFieldKongConsumerOnKonnectGatewayControlPlane, - ExtractValue: kongConsumerReferencesKonnectGatewayControlPlane, + ExtractValue: indexKonnectGatewayControlPlaneRef[configurationv1.KongConsumer](cl), }, } } @@ -52,12 +52,3 @@ func kongConsumerReferencesKongPluginsViaAnnotation(object client.Object) []stri } return metadata.ExtractPluginsWithNamespaces(consumer) } - -func kongConsumerReferencesKonnectGatewayControlPlane(object client.Object) []string { - consumer, ok := object.(*configurationv1.KongConsumer) - if !ok { - return nil - } - - return controlPlaneKonnectNamespacedRefAsSlice(consumer) -} diff --git a/controller/konnect/index_kongconsumergroup.go b/controller/konnect/index_kongconsumergroup.go index cdcc0ec83..0a80674f5 100644 --- a/controller/konnect/index_kongconsumergroup.go +++ b/controller/konnect/index_kongconsumergroup.go @@ -15,7 +15,7 @@ const ( ) // IndexOptionsForKongConsumerGroup returns required Index options for KongConsumerGroup reconciler. -func IndexOptionsForKongConsumerGroup() []ReconciliationIndexOption { +func IndexOptionsForKongConsumerGroup(cl client.Client) []ReconciliationIndexOption { return []ReconciliationIndexOption{ { IndexObject: &configurationv1beta1.KongConsumerGroup{}, @@ -25,7 +25,7 @@ func IndexOptionsForKongConsumerGroup() []ReconciliationIndexOption { { IndexObject: &configurationv1beta1.KongConsumerGroup{}, IndexField: IndexFieldKongConsumerGroupOnKonnectGatewayControlPlane, - ExtractValue: kongConsumerGroupReferencesKonnectGatewayControlPlane, + ExtractValue: indexKonnectGatewayControlPlaneRef[configurationv1beta1.KongConsumerGroup](cl), }, } } @@ -37,12 +37,3 @@ func kongConsumerGroupReferencesKongPluginsViaAnnotation(object client.Object) [ } return metadata.ExtractPluginsWithNamespaces(consumerGroup) } - -func kongConsumerGroupReferencesKonnectGatewayControlPlane(object client.Object) []string { - group, ok := object.(*configurationv1beta1.KongConsumerGroup) - if !ok { - return nil - } - - return controlPlaneKonnectNamespacedRefAsSlice(group) -} diff --git a/controller/konnect/index_kongdataplanecertificate.go b/controller/konnect/index_kongdataplanecertificate.go index fee351ce0..3896c29db 100644 --- a/controller/konnect/index_kongdataplanecertificate.go +++ b/controller/konnect/index_kongdataplanecertificate.go @@ -12,21 +12,12 @@ const ( ) // IndexOptionsForKongDataPlaneCertificate returns required Index options for KongConsumer reconciler. -func IndexOptionsForKongDataPlaneCertificate() []ReconciliationIndexOption { +func IndexOptionsForKongDataPlaneCertificate(cl client.Client) []ReconciliationIndexOption { return []ReconciliationIndexOption{ { IndexObject: &configurationv1alpha1.KongDataPlaneClientCertificate{}, IndexField: IndexFieldKongDataPlaneClientCertificateOnKonnectGatewayControlPlane, - ExtractValue: kongDataPlaneCertificateReferencesKonnectGatewayControlPlane, + ExtractValue: indexKonnectGatewayControlPlaneRef[configurationv1alpha1.KongDataPlaneClientCertificate](cl), }, } } - -func kongDataPlaneCertificateReferencesKonnectGatewayControlPlane(object client.Object) []string { - dpCert, ok := object.(*configurationv1alpha1.KongDataPlaneClientCertificate) - if !ok { - return nil - } - - return controlPlaneKonnectNamespacedRefAsSlice(dpCert) -} diff --git a/controller/konnect/index_kongkey.go b/controller/konnect/index_kongkey.go index ef1f0e92d..c750a6daf 100644 --- a/controller/konnect/index_kongkey.go +++ b/controller/konnect/index_kongkey.go @@ -15,7 +15,7 @@ const ( ) // IndexOptionsForKongKey returns required Index options for KongKey reconclier. -func IndexOptionsForKongKey() []ReconciliationIndexOption { +func IndexOptionsForKongKey(cl client.Client) []ReconciliationIndexOption { return []ReconciliationIndexOption{ { IndexObject: &configurationv1alpha1.KongKey{}, @@ -25,7 +25,7 @@ func IndexOptionsForKongKey() []ReconciliationIndexOption { { IndexObject: &configurationv1alpha1.KongKey{}, IndexField: IndexFieldKongKeyOnKonnectGatewayControlPlane, - ExtractValue: konnectGatewayControlPlaneRefFromKongKey, + ExtractValue: indexKonnectGatewayControlPlaneRef[configurationv1alpha1.KongKey](cl), }, } } @@ -45,12 +45,3 @@ func kongKeySetRefFromKongKey(obj client.Object) []string { return []string{key.GetNamespace() + "/" + key.Spec.KeySetRef.NamespacedRef.Name} } - -// konnectGatewayControlPlaneRefFromKongKey returns namespace/name of referenced KonnectGatewayControlPlane in KongKey spec. -func konnectGatewayControlPlaneRefFromKongKey(obj client.Object) []string { - key, ok := obj.(*configurationv1alpha1.KongKey) - if !ok { - return nil - } - return controlPlaneKonnectNamespacedRefAsSlice(key) -} diff --git a/controller/konnect/index_kongkeyset.go b/controller/konnect/index_kongkeyset.go index a59b0c784..37ecfe85a 100644 --- a/controller/konnect/index_kongkeyset.go +++ b/controller/konnect/index_kongkeyset.go @@ -12,21 +12,12 @@ const ( ) // IndexOptionsForKongKeySet returns required Index options for KongKeySet reconclier. -func IndexOptionsForKongKeySet() []ReconciliationIndexOption { +func IndexOptionsForKongKeySet(cl client.Client) []ReconciliationIndexOption { return []ReconciliationIndexOption{ { IndexObject: &configurationv1alpha1.KongKeySet{}, IndexField: IndexFieldKongKeySetOnKonnectGatewayControlPlane, - ExtractValue: konnectGatewayControlPlaneRefFromKongKeySet, + ExtractValue: indexKonnectGatewayControlPlaneRef[configurationv1alpha1.KongKeySet](cl), }, } } - -// konnectGatewayControlPlaneRefFromKongKeySet returns namespace/name of referenced KonnectGatewayControlPlane in KongKeySet spec. -func konnectGatewayControlPlaneRefFromKongKeySet(obj client.Object) []string { - keySet, ok := obj.(*configurationv1alpha1.KongKeySet) - if !ok { - return nil - } - return controlPlaneKonnectNamespacedRefAsSlice(keySet) -} diff --git a/controller/konnect/index_kongservice.go b/controller/konnect/index_kongservice.go index 17e755573..76692129f 100644 --- a/controller/konnect/index_kongservice.go +++ b/controller/konnect/index_kongservice.go @@ -15,7 +15,7 @@ const ( ) // IndexOptionsForKongService returns required Index options for KongService reconciler. -func IndexOptionsForKongService() []ReconciliationIndexOption { +func IndexOptionsForKongService(cl client.Client) []ReconciliationIndexOption { return []ReconciliationIndexOption{ { IndexObject: &configurationv1alpha1.KongService{}, @@ -25,7 +25,7 @@ func IndexOptionsForKongService() []ReconciliationIndexOption { { IndexObject: &configurationv1alpha1.KongService{}, IndexField: IndexFieldKongServiceOnKonnectGatewayControlPlane, - ExtractValue: kongServiceReferencesKonnectGatewayControlPlane, + ExtractValue: indexKonnectGatewayControlPlaneRef[configurationv1alpha1.KongService](cl), }, } } @@ -38,12 +38,3 @@ func kongServiceUsesPlugins(object client.Object) []string { return metadata.ExtractPluginsWithNamespaces(svc) } - -func kongServiceReferencesKonnectGatewayControlPlane(object client.Object) []string { - svc, ok := object.(*configurationv1alpha1.KongService) - if !ok { - return nil - } - - return controlPlaneKonnectNamespacedRefAsSlice(svc) -} diff --git a/controller/konnect/index_kongupstream.go b/controller/konnect/index_kongupstream.go index b0341d90f..a2788a0b4 100644 --- a/controller/konnect/index_kongupstream.go +++ b/controller/konnect/index_kongupstream.go @@ -12,21 +12,12 @@ const ( ) // IndexOptionsForKongUpstream returns required Index options for KongUpstream reconciler. -func IndexOptionsForKongUpstream() []ReconciliationIndexOption { +func IndexOptionsForKongUpstream(cl client.Client) []ReconciliationIndexOption { return []ReconciliationIndexOption{ { IndexObject: &configurationv1alpha1.KongUpstream{}, IndexField: IndexFieldKongUpstreamOnKonnectGatewayControlPlane, - ExtractValue: kongUpstreamReferencesKonnectGatewayControlPlane, + ExtractValue: indexKonnectGatewayControlPlaneRef[configurationv1alpha1.KongUpstream](cl), }, } } - -func kongUpstreamReferencesKonnectGatewayControlPlane(object client.Object) []string { - upstream, ok := object.(*configurationv1alpha1.KongUpstream) - if !ok { - return nil - } - - return controlPlaneKonnectNamespacedRefAsSlice(upstream) -} diff --git a/controller/konnect/index_kongvault.go b/controller/konnect/index_kongvault.go index 9a3832bbb..cbd34e051 100644 --- a/controller/konnect/index_kongvault.go +++ b/controller/konnect/index_kongvault.go @@ -12,21 +12,12 @@ const ( ) // IndexOptionsForKongVault returns required Index options for KongVault reconciler. -func IndexOptionsForKongVault() []ReconciliationIndexOption { +func IndexOptionsForKongVault(cl client.Client) []ReconciliationIndexOption { return []ReconciliationIndexOption{ { IndexObject: &configurationv1alpha1.KongVault{}, IndexField: IndexFieldKongVaultOnKonnectGatewayControlPlane, - ExtractValue: kongVaultReferencesKonnectGatewayControlPlane, + ExtractValue: indexKonnectGatewayControlPlaneRef[configurationv1alpha1.KongVault](cl), }, } } - -func kongVaultReferencesKonnectGatewayControlPlane(object client.Object) []string { - vault, ok := object.(*configurationv1alpha1.KongVault) - if !ok { - return nil - } - - return controlPlaneKonnectNamespacedRefAsSlice(vault) -} diff --git a/controller/konnect/index_konnectgatewaycontrolplane.go b/controller/konnect/index_konnectgatewaycontrolplane.go index 7620d14c3..fc9f9a90b 100644 --- a/controller/konnect/index_konnectgatewaycontrolplane.go +++ b/controller/konnect/index_konnectgatewaycontrolplane.go @@ -13,6 +13,9 @@ const ( // IndexFieldKonnectGatewayControlPlaneOnAPIAuthConfiguration is the index field for KonnectGatewayControlPlane -> APIAuthConfiguration. IndexFieldKonnectGatewayControlPlaneOnAPIAuthConfiguration = "konnectGatewayControlPlaneAPIAuthConfigurationRef" + + // IndexFieldKonnectGatewayControlPlaneOnKonnectID is the index field for KonnectGatewayControlPlane -> KonnectID. + IndexFieldKonnectGatewayControlPlaneOnKonnectID = "konnectGatewayControlPlaneKonnectID" ) // IndexOptionsForKonnectGatewayControlPlane returns required Index options for KonnectGatewayControlPlane reconciler. @@ -28,6 +31,11 @@ func IndexOptionsForKonnectGatewayControlPlane() []ReconciliationIndexOption { IndexField: IndexFieldKonnectGatewayControlPlaneOnAPIAuthConfiguration, ExtractValue: konnectGatewayControlPlaneAPIAuthConfigurationRef, }, + { + IndexObject: &konnectv1alpha1.KonnectGatewayControlPlane{}, + IndexField: IndexFieldKonnectGatewayControlPlaneOnKonnectID, + ExtractValue: konnectGatewayControlPlaneKonnectID, + }, } } @@ -61,3 +69,15 @@ func konnectGatewayControlPlaneAPIAuthConfigurationRef(object client.Object) []s return []string{cp.Spec.KonnectConfiguration.APIAuthConfigurationRef.Name} } + +func konnectGatewayControlPlaneKonnectID(object client.Object) []string { + cp, ok := object.(*konnectv1alpha1.KonnectGatewayControlPlane) + if !ok { + return nil + } + + if konnectID := cp.GetKonnectStatus().GetKonnectID(); konnectID != "" { + return []string{konnectID} + } + return nil +} diff --git a/controller/konnect/index_test.go b/controller/konnect/index_test.go index 6e1bc585d..40f5fd05a 100644 --- a/controller/konnect/index_test.go +++ b/controller/konnect/index_test.go @@ -5,13 +5,32 @@ import ( "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/kong/gateway-operator/controller/konnect/constraints" + "github.com/kong/gateway-operator/modules/manager/scheme" configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" ) -func TestControlPlaneKonnectNamespacedRefAsSlice(t *testing.T) { +func TestIndexKonnectGatewayControlPlaneRef(t *testing.T) { + cp := &konnectv1alpha1.KonnectGatewayControlPlane{ + TypeMeta: metav1.TypeMeta{ + APIVersion: konnectv1alpha1.GroupVersion.String(), + Kind: "KonnectGatewayControlPlane", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "cp-1", + }, + } + cl := fakeclient.NewClientBuilder(). + WithScheme(scheme.Get()). + WithObjects(cp). + Build() + t.Run("KongService", func(t *testing.T) { tests := []struct { name string @@ -76,7 +95,7 @@ func TestControlPlaneKonnectNamespacedRefAsSlice(t *testing.T) { }, } - testControlPlaneKonnectNamespacedRefAsSlice(t, tests) + testIndexKonnectGatewayControlPlaneRef(t, cl, tests) }) t.Run("KongRoute", func(t *testing.T) { @@ -143,15 +162,16 @@ func TestControlPlaneKonnectNamespacedRefAsSlice(t *testing.T) { }, } - testControlPlaneKonnectNamespacedRefAsSlice(t, tests) + testIndexKonnectGatewayControlPlaneRef(t, cl, tests) }) } -func testControlPlaneKonnectNamespacedRefAsSlice[ +func testIndexKonnectGatewayControlPlaneRef[ T constraints.SupportedKonnectEntityType, TEnt constraints.EntityType[T], ]( t *testing.T, + cl client.Client, tests []struct { name string ent TEnt @@ -162,7 +182,7 @@ func testControlPlaneKonnectNamespacedRefAsSlice[ for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := controlPlaneKonnectNamespacedRefAsSlice(tt.ent) + result := indexKonnectGatewayControlPlaneRef[T, TEnt](cl)(tt.ent) assert.Equal(t, tt.expected, result) }) } diff --git a/controller/konnect/reconciler_certificateref.go b/controller/konnect/reconciler_certificateref.go index e290ed7a8..2428dfba5 100644 --- a/controller/konnect/reconciler_certificateref.go +++ b/controller/konnect/reconciler_certificateref.go @@ -146,11 +146,8 @@ func handleKongCertificateRef[T constraints.SupportedKonnectEntityType, TEnt con } if k8serrors.IsNotFound(err) { return ctrl.Result{}, ReferencedControlPlaneDoesNotExistError{ - Reference: types.NamespacedName{ - Namespace: ent.GetNamespace(), - Name: cpRef.KonnectNamespacedRef.Name, - }, - Err: err, + Reference: cpRef, + Err: err, } } return ctrl.Result{}, err diff --git a/controller/konnect/reconciler_certificateref_test.go b/controller/konnect/reconciler_certificateref_test.go index c5b9b4ee4..d05c5941e 100644 --- a/controller/konnect/reconciler_certificateref_test.go +++ b/controller/konnect/reconciler_certificateref_test.go @@ -321,8 +321,7 @@ func TestHandleCertificateRef(t *testing.T) { testKongCertificateControlPlaneRefNotFound, }, expectError: true, - expectErrorContains: fmt.Sprintf("referenced Control Plane %s/%s does not exist", - testKongCertificateControlPlaneRefNotFound.Namespace, + expectErrorContains: fmt.Sprintf("referenced Control Plane %q does not exist", testKongCertificateControlPlaneRefNotFound.Spec.ControlPlaneRef.KonnectNamespacedRef.Name, ), }, @@ -389,7 +388,7 @@ func testHandleCertificateRef[T constraints.SupportedKonnectEntityType, TEnt con if tc.expectError { require.Error(t, err) - require.Contains(t, err.Error(), tc.expectErrorContains) + require.ErrorContains(t, err, tc.expectErrorContains) return } diff --git a/controller/konnect/reconciler_controlplaneref.go b/controller/konnect/reconciler_controlplaneref.go index b5de69fd5..a2407332c 100644 --- a/controller/konnect/reconciler_controlplaneref.go +++ b/controller/konnect/reconciler_controlplaneref.go @@ -5,6 +5,7 @@ import ( "fmt" sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + "github.com/samber/lo" "github.com/samber/mo" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -107,120 +108,100 @@ func handleControlPlaneRef[T constraints.SupportedKonnectEntityType, TEnt constr return ctrl.Result{}, nil } - switch cpRef.Type { - case configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef: - cp := konnectv1alpha1.KonnectGatewayControlPlane{} - // TODO(pmalek): handle cross namespace refs - nn := types.NamespacedName{ - Name: cpRef.KonnectNamespacedRef.Name, - Namespace: ent.GetNamespace(), - } - // Set namespace of control plane when it is non-empty. Only applyies for cluster scoped resources (KongVault). - if ent.GetNamespace() == "" && cpRef.KonnectNamespacedRef.Namespace != "" { - nn.Namespace = cpRef.KonnectNamespacedRef.Namespace - } - if err := cl.Get(ctx, nn, &cp); err != nil { - if res, errStatus := patch.StatusWithCondition( - ctx, cl, ent, - konnectv1alpha1.ControlPlaneRefValidConditionType, - metav1.ConditionFalse, - konnectv1alpha1.ControlPlaneRefReasonInvalid, - err.Error(), - ); errStatus != nil || !res.IsZero() { - return res, errStatus - } - if k8serrors.IsNotFound(err) { - return ctrl.Result{}, ReferencedControlPlaneDoesNotExistError{ - Reference: nn, - Err: err, - } - } - return ctrl.Result{}, err + cp, err := getCPForRef(ctx, cl, cpRef, ent.GetNamespace()) + if err != nil { + if res, errStatus := patch.StatusWithCondition( + ctx, cl, ent, + konnectv1alpha1.ControlPlaneRefValidConditionType, + metav1.ConditionFalse, + konnectv1alpha1.ControlPlaneRefReasonInvalid, + err.Error(), + ); errStatus != nil || !res.IsZero() { + return res, errStatus } - // Do not continue reconciling of the control plane has incompatible cluster type to prevent repeated failure of creation. - // Only CLUSTER_TYPE_CONTROL_PLANE is supported. - // The configuration in control plane group type are read only so they are unsupported to attach entities to them: - // https://docs.konghq.com/konnect/gateway-manager/control-plane-groups/#limitations - if cp.Spec.ClusterType != nil && - *cp.Spec.ClusterType != sdkkonnectcomp.CreateControlPlaneRequestClusterTypeClusterTypeControlPlane { - if res, errStatus := patch.StatusWithCondition( - ctx, cl, ent, - konnectv1alpha1.ControlPlaneRefValidConditionType, - metav1.ConditionFalse, - konnectv1alpha1.ControlPlaneRefReasonInvalid, - fmt.Sprintf("Attaching to ControlPlane %s with cluster type %s is not supported", nn, *cp.Spec.ClusterType), - ); errStatus != nil || !res.IsZero() { - return res, errStatus - } - return ctrl.Result{}, nil - } - - cond, ok := k8sutils.GetCondition(konnectv1alpha1.KonnectEntityProgrammedConditionType, &cp) - if !ok || cond.Status != metav1.ConditionTrue || cond.ObservedGeneration != cp.GetGeneration() { - if res, errStatus := patch.StatusWithCondition( - ctx, cl, ent, - konnectv1alpha1.ControlPlaneRefValidConditionType, - metav1.ConditionFalse, - konnectv1alpha1.ControlPlaneRefReasonInvalid, - fmt.Sprintf("Referenced ControlPlane %s is not programmed yet", nn), - ); errStatus != nil || !res.IsZero() { - return res, errStatus - } + return ctrl.Result{}, err + } - return ctrl.Result{Requeue: true}, nil + // Do not continue reconciling of the control plane has incompatible cluster type to prevent repeated failure of creation. + // Only CLUSTER_TYPE_CONTROL_PLANE is supported. + // The configuration in control plane group type are read only so they are unsupported to attach entities to them: + // https://docs.konghq.com/konnect/gateway-manager/control-plane-groups/#limitations + if cp.Spec.ClusterType != nil && + *cp.Spec.ClusterType != sdkkonnectcomp.CreateControlPlaneRequestClusterTypeClusterTypeControlPlane { + if res, errStatus := patch.StatusWithCondition( + ctx, cl, ent, + konnectv1alpha1.ControlPlaneRefValidConditionType, + metav1.ConditionFalse, + konnectv1alpha1.ControlPlaneRefReasonInvalid, + fmt.Sprintf("Attaching to ControlPlane %s with cluster type %s is not supported", controlPlaneRefToString(cpRef), *cp.Spec.ClusterType), + ); errStatus != nil || !res.IsZero() { + return res, errStatus } + return ctrl.Result{}, nil + } - var ( - old = ent.DeepCopyObject().(TEnt) + cond, ok := k8sutils.GetCondition(konnectv1alpha1.KonnectEntityProgrammedConditionType, cp) + if !ok || cond.Status != metav1.ConditionTrue || cond.ObservedGeneration != cp.GetGeneration() { + if res, errStatus := patch.StatusWithCondition( + ctx, cl, ent, + konnectv1alpha1.ControlPlaneRefValidConditionType, + metav1.ConditionFalse, + konnectv1alpha1.ControlPlaneRefReasonInvalid, + fmt.Sprintf("Referenced ControlPlane %s is not programmed yet", controlPlaneRefToString(cpRef)), + ); errStatus != nil || !res.IsZero() { + return res, errStatus + } - // 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() != "" + return ctrl.Result{Requeue: true}, nil + } - // 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) - } + 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 err := cl.Patch(ctx, ent, client.MergeFrom(old)); err != nil { + if resource, ok := any(ent).(EntityWithControlPlaneRef); ok { + old := ent.DeepCopyObject().(TEnt) + resource.SetControlPlaneID(cp.Status.ID) + _, err := patch.ApplyStatusPatchIfNotEmpty(ctx, cl, ctrllog.FromContext(ctx), ent, old) + if 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) - _, err := patch.ApplyStatusPatchIfNotEmpty(ctx, cl, ctrllog.FromContext(ctx), ent, old) - if err != nil { - if k8serrors.IsConflict(err) { - return ctrl.Result{Requeue: true}, nil - } - return ctrl.Result{}, err - } - } - - if res, errStatus := patch.StatusWithCondition( - ctx, cl, ent, - konnectv1alpha1.ControlPlaneRefValidConditionType, - metav1.ConditionTrue, - konnectv1alpha1.ControlPlaneRefReasonValid, - fmt.Sprintf("Referenced ControlPlane %s is programmed", nn), - ); errStatus != nil || !res.IsZero() { - return res, errStatus + return ctrl.Result{}, err } - return ctrl.Result{}, nil + } - default: - return ctrl.Result{}, fmt.Errorf("unimplemented ControlPlane ref type %q", cpRef.Type) + if res, errStatus := patch.StatusWithCondition( + ctx, cl, ent, + konnectv1alpha1.ControlPlaneRefValidConditionType, + metav1.ConditionTrue, + konnectv1alpha1.ControlPlaneRefReasonValid, + fmt.Sprintf("Referenced ControlPlane %s is programmed", controlPlaneRefToString(cpRef)), + ); errStatus != nil || !res.IsZero() { + return res, errStatus } + return ctrl.Result{}, nil } func conditionMessageReferenceKonnectAPIAuthConfigurationInvalid(apiAuthRef types.NamespacedName) string { @@ -230,3 +211,17 @@ func conditionMessageReferenceKonnectAPIAuthConfigurationInvalid(apiAuthRef type func conditionMessageReferenceKonnectAPIAuthConfigurationValid(apiAuthRef types.NamespacedName) string { return fmt.Sprintf("referenced KonnectAPIAuthConfiguration %s is valid", apiAuthRef) } + +func controlPlaneRefToString(cpRef configurationv1alpha1.ControlPlaneRef) string { + switch cpRef.Type { + case configurationv1alpha1.ControlPlaneRefKonnectID: + return lo.FromPtrOr(cpRef.KonnectID, "nil") + case configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef: + if cpRef.KonnectNamespacedRef.Namespace == "" { + return cpRef.KonnectNamespacedRef.Name + } + return cpRef.KonnectNamespacedRef.Namespace + "/" + cpRef.KonnectNamespacedRef.Name + default: + return fmt.Sprintf("unknown type %q", cpRef.Type) + } +} diff --git a/controller/konnect/reconciler_controlplaneref_test.go b/controller/konnect/reconciler_controlplaneref_test.go index 995ab5fcb..8a9d1b9cc 100644 --- a/controller/konnect/reconciler_controlplaneref_test.go +++ b/controller/konnect/reconciler_controlplaneref_test.go @@ -197,7 +197,7 @@ func TestHandleControlPlaneRef(t *testing.T) { ent: svcCPRefNotFound, expectResult: ctrl.Result{}, expectError: true, - expectErrorContains: `referenced Control Plane default/cp-not-found does not exist`, + expectErrorContains: `referenced Control Plane "cp-not-found" does not exist`, updatedEntAssertions: []func(svc *configurationv1alpha1.KongService) (ok bool, message string){ 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 59269dfde..4989f4bbd 100644 --- a/controller/konnect/reconciler_generic.go +++ b/controller/konnect/reconciler_generic.go @@ -623,17 +623,68 @@ func getCPForRef( cpRef configurationv1alpha1.ControlPlaneRef, namespace string, ) (*konnectv1alpha1.KonnectGatewayControlPlane, error) { - if cpRef.Type != configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef { - return nil, fmt.Errorf("unsupported ControlPlane ref type %q", cpRef.Type) + switch cpRef.Type { + case configurationv1alpha1.ControlPlaneRefKonnectID: + return getCPForKonnectID(ctx, cl, cpRef) + case configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef: + return getCPForNamespacedRef(ctx, cl, cpRef, namespace) + default: + return nil, ReferencedKongGatewayControlPlaneIsUnsupported{Reference: cpRef} + } +} + +func getCPForKonnectID( + ctx context.Context, + cl client.Client, + cpRef configurationv1alpha1.ControlPlaneRef, +) (*konnectv1alpha1.KonnectGatewayControlPlane, error) { + var l konnectv1alpha1.KonnectGatewayControlPlaneList + if err := cl.List(ctx, &l, + client.MatchingFields{ + IndexFieldKonnectGatewayControlPlaneOnKonnectID: *cpRef.KonnectID, + }, + ); err != nil { + return nil, fmt.Errorf("failed to list ControlPlanes: %w", err) + } + + if len(l.Items) == 0 { + return nil, ReferencedControlPlaneDoesNotExistError{ + Reference: cpRef, + Err: errors.New("no KonnectControlPlane with given status.konnectID found"), + } } + return &l.Items[0], nil +} + +func getCPForNamespacedRef( + ctx context.Context, + cl client.Client, + ref configurationv1alpha1.ControlPlaneRef, + namespace string, +) (*konnectv1alpha1.KonnectGatewayControlPlane, error) { // TODO(pmalek): handle cross namespace refs + if namespace != "" && ref.KonnectNamespacedRef.Namespace != "" && ref.KonnectNamespacedRef.Namespace != namespace { + return nil, fmt.Errorf("%s ControlPlaneRef from different namespace than %s", ref.KonnectNamespacedRef.Namespace, namespace) + } + nn := types.NamespacedName{ - Name: cpRef.KonnectNamespacedRef.Name, + Name: ref.KonnectNamespacedRef.Name, Namespace: namespace, } + // Set namespace of control plane when it is non-empty. Only applies for cluster-scoped resources (KongVault). + if namespace == "" && ref.KonnectNamespacedRef.Namespace != "" { + nn.Namespace = ref.KonnectNamespacedRef.Namespace + } + var cp konnectv1alpha1.KonnectGatewayControlPlane if err := cl.Get(ctx, nn, &cp); err != nil { + if k8serrors.IsNotFound(err) { + return nil, ReferencedControlPlaneDoesNotExistError{ + Reference: ref, + Err: err, + } + } return nil, fmt.Errorf("failed to get ControlPlane %s: %w", nn, err) } return &cp, nil @@ -666,9 +717,14 @@ func getAPIAuthRefNN[T constraints.SupportedKonnectEntityType, TEnt constraints. // ref from the referenced ControlPlane. cpRef, ok := getControlPlaneRef(ent).Get() if ok { + cp, err := getCPForRef(ctx, cl, cpRef, ent.GetNamespace()) + if err != nil { + return types.NamespacedName{}, fmt.Errorf("failed to get ControlPlane for %s: %w", client.ObjectKeyFromObject(ent), err) + } + cpNamespace := ent.GetNamespace() - if ent.GetNamespace() == "" && cpRef.KonnectNamespacedRef.Namespace != "" { - cpNamespace = cpRef.KonnectNamespacedRef.Namespace + if ent.GetNamespace() == "" && cp.GetNamespace() != "" { + cpNamespace = cp.GetNamespace() } return getCPAuthRefForRef(ctx, cl, cpRef, cpNamespace) } @@ -907,7 +963,7 @@ func handleKongConsumerRef[T constraints.SupportedKonnectEntityType, TEnt constr } if k8serrors.IsNotFound(err) { return ctrl.Result{}, ReferencedControlPlaneDoesNotExistError{ - Reference: nn, + Reference: cpRef, Err: err, } } diff --git a/controller/konnect/reconciler_kongplugin.go b/controller/konnect/reconciler_kongplugin.go index d3d7986e0..b2ace35f9 100644 --- a/controller/konnect/reconciler_kongplugin.go +++ b/controller/konnect/reconciler_kongplugin.go @@ -375,16 +375,20 @@ func deleteUnusedKongPluginBindings( continue } - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(&pb) + cpRef, ok := getControlPlaneRef(&pb).Get() if !ok { continue } + cp, err := getCPForRef(ctx, clientWithNamespace, cpRef, pb.Namespace) + if err != nil { + return fmt.Errorf("failed to get ControlPlane for KongPluginBinding: %w", err) + } - // If a ControlPlane this KongPluginBinding references, is not found, delete the it. + // If a ControlPlane this KongPluginBinding references, is not found, delete it. combinations, ok := groupedCombinations[types.NamespacedName{ // TODO: implement cross namespace references Namespace: pb.Namespace, - Name: cpRef.KonnectNamespacedRef.Name, + Name: cp.Name, }] if !ok { pluginBindingsToDelete[client.ObjectKeyFromObject(&pb)] = pb diff --git a/controller/konnect/reconciler_kongplugin_combinations.go b/controller/konnect/reconciler_kongplugin_combinations.go index 588ceda59..b2d1ccdc1 100644 --- a/controller/konnect/reconciler_kongplugin_combinations.go +++ b/controller/konnect/reconciler_kongplugin_combinations.go @@ -2,6 +2,7 @@ package konnect import ( "context" + "fmt" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -32,22 +33,25 @@ func (f ForeignRelationsGroupedByControlPlane) GetCombinations() map[types.Names return ret } -// GetCombinations groups all the entities by ControlPlane. -// NOTE: currently only supports Konnect ControlPlane which is referenced by a konnectNamespacedRef. +// GroupByControlPlane groups all the entities by ControlPlane. func (relations *ForeignRelations) GroupByControlPlane( ctx context.Context, cl client.Client, ) (ForeignRelationsGroupedByControlPlane, error) { ret := make(map[types.NamespacedName]ForeignRelations) for _, service := range relations.Service { - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(&service) + cpRef, ok := getControlPlaneRef(&service).Get() if !ok { continue } + cp, err := getCPForRef(ctx, cl, cpRef, service.Namespace) + if err != nil { + return nil, fmt.Errorf("failed to get ControlPlane for KongService %s: %w", service.Name, err) + } nn := types.NamespacedName{ // TODO: implement cross namespace references Namespace: service.Namespace, - Name: cpRef.KonnectNamespacedRef.Name, + Name: cp.Name, } fr := ret[nn] fr.Service = append(fr.Service, service) @@ -56,15 +60,18 @@ func (relations *ForeignRelations) GroupByControlPlane( for _, route := range relations.Route { svcRef := route.Spec.ServiceRef if svcRef == nil || svcRef.NamespacedRef == nil { - - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(&route) + cpRef, ok := getControlPlaneRef(&route).Get() if !ok { continue } + cp, err := getCPForRef(ctx, cl, cpRef, route.Namespace) + if err != nil { + return nil, fmt.Errorf("failed to get ControlPlane for KongRoute %s: %w", route.Name, err) + } nn := types.NamespacedName{ Namespace: route.Namespace, - Name: cpRef.KonnectNamespacedRef.Name, + Name: cp.Name, } fr := ret[nn] fr.Route = append(fr.Route, route) @@ -84,43 +91,55 @@ func (relations *ForeignRelations) GroupByControlPlane( return nil, err } - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(&svc) + cpRef, ok := getControlPlaneRef(&svc).Get() if !ok { continue } + cp, err := getCPForRef(ctx, cl, cpRef, svc.Namespace) + if err != nil { + return nil, fmt.Errorf("failed to get ControlPlane for KongService %s: %w", svc.Name, err) + } nn := types.NamespacedName{ // TODO: implement cross namespace references Namespace: route.Namespace, - Name: cpRef.KonnectNamespacedRef.Name, + Name: cp.Name, } fr := ret[nn] fr.Route = append(fr.Route, route) ret[nn] = fr } for _, consumer := range relations.Consumer { - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(&consumer) + cpRef, ok := getControlPlaneRef(&consumer).Get() if !ok { continue } + cp, err := getCPForRef(ctx, cl, cpRef, consumer.Namespace) + if err != nil { + return nil, fmt.Errorf("failed to get ControlPlane for KongConsumer %s: %w", consumer.Name, err) + } nn := types.NamespacedName{ // TODO: implement cross namespace references Namespace: consumer.Namespace, - Name: cpRef.KonnectNamespacedRef.Name, + Name: cp.Name, } fr := ret[nn] fr.Consumer = append(fr.Consumer, consumer) ret[nn] = fr } for _, group := range relations.ConsumerGroup { - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(&group) + cpRef, ok := getControlPlaneRef(&group).Get() if !ok { continue } + cp, err := getCPForRef(ctx, cl, cpRef, group.Namespace) + if err != nil { + return nil, fmt.Errorf("failed to get ControlPlane for KongConsumerGroup %s: %w", group.Name, err) + } nn := types.NamespacedName{ // TODO: implement cross namespace references Namespace: group.Namespace, - Name: cpRef.KonnectNamespacedRef.Name, + Name: cp.Name, } fr := ret[nn] fr.ConsumerGroup = append(fr.ConsumerGroup, group) diff --git a/controller/konnect/reconciler_kongplugin_combinations_test.go b/controller/konnect/reconciler_kongplugin_combinations_test.go index 409d186bd..2c7f65ce9 100644 --- a/controller/konnect/reconciler_kongplugin_combinations_test.go +++ b/controller/konnect/reconciler_kongplugin_combinations_test.go @@ -15,6 +15,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" ) func TestGetCombinations(t *testing.T) { @@ -473,6 +474,14 @@ func TestGetCombinations(t *testing.T) { } func TestGroupByControlPlane(t *testing.T) { + cpWithName := func(name string) *konnectv1alpha1.KonnectGatewayControlPlane { + return &konnectv1alpha1.KonnectGatewayControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: name, + }, + } + } type args struct { relations ForeignRelations } @@ -800,9 +809,11 @@ func TestGroupByControlPlane(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + objects := tt.objects + objects = append(objects, cpWithName("cp1"), cpWithName("cp2")) cl := fake.NewClientBuilder(). WithScheme(scheme.Get()). - WithObjects(tt.objects...). + WithObjects(objects...). Build() got, err := tt.args.relations.GroupByControlPlane(context.Background(), cl) require.NoError(t, err) diff --git a/controller/konnect/reconciler_serviceref.go b/controller/konnect/reconciler_serviceref.go index 580433db3..0d5dd1ec2 100644 --- a/controller/konnect/reconciler_serviceref.go +++ b/controller/konnect/reconciler_serviceref.go @@ -155,7 +155,7 @@ func handleKongServiceRef[T constraints.SupportedKonnectEntityType, TEnt constra } if k8serrors.IsNotFound(err) { return ctrl.Result{}, ReferencedControlPlaneDoesNotExistError{ - Reference: nn, + Reference: cpRef, Err: err, } } diff --git a/controller/konnect/reconciler_upstreamref.go b/controller/konnect/reconciler_upstreamref.go index ec1a4d459..0c90b91ae 100644 --- a/controller/konnect/reconciler_upstreamref.go +++ b/controller/konnect/reconciler_upstreamref.go @@ -147,11 +147,8 @@ func handleKongUpstreamRef[T constraints.SupportedKonnectEntityType, TEnt constr } if k8serrors.IsNotFound(err) { return ctrl.Result{}, ReferencedControlPlaneDoesNotExistError{ - Reference: types.NamespacedName{ - Namespace: ent.GetNamespace(), - Name: cpRef.KonnectNamespacedRef.Name, - }, - Err: err, + Reference: cpRef, + Err: err, } } return ctrl.Result{}, err diff --git a/controller/konnect/reconciler_upstreamref_test.go b/controller/konnect/reconciler_upstreamref_test.go index e794a7e39..da8db5754 100644 --- a/controller/konnect/reconciler_upstreamref_test.go +++ b/controller/konnect/reconciler_upstreamref_test.go @@ -349,8 +349,7 @@ func TestHandleUpstreamRef(t *testing.T) { }, objects: []client.Object{testKongUpstreamControlPlaneRefNotFound}, expectError: true, - expectErrorContains: fmt.Sprintf("referenced Control Plane %s/%s does not exist", - testKongUpstreamControlPlaneRefNotFound.Namespace, + expectErrorContains: fmt.Sprintf(`referenced Control Plane %q does not exist`, testKongUpstreamControlPlaneRefNotFound.Spec.ControlPlaneRef.KonnectNamespacedRef.Name, ), }, diff --git a/controller/konnect/watch.go b/controller/konnect/watch.go index 6c30f52e7..92d5be773 100644 --- a/controller/konnect/watch.go +++ b/controller/konnect/watch.go @@ -91,15 +91,23 @@ func objRefersToKonnectGatewayControlPlane[ return false } - return objHasControlPlaneRefKonnectNamespacedRef(ent) + return objHasControlPlaneRef(ent) } -func objHasControlPlaneRefKonnectNamespacedRef[ +func objHasControlPlaneRef[ T constraints.SupportedKonnectEntityType, TEnt constraints.EntityType[T], ](ent TEnt) bool { - _, ok := controlPlaneRefIsKonnectNamespacedRef(ent) - return ok + cpRef, ok := getControlPlaneRef(ent).Get() + if !ok { + return false + } + switch cpRef.Type { + case configurationv1alpha1.ControlPlaneRefKonnectID, configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef: + return true + default: + return false + } } // controlPlaneRefIsKonnectNamespacedRef returns: diff --git a/controller/konnect/watch_credential.go b/controller/konnect/watch_credential.go index e11a3a2a4..62739641e 100644 --- a/controller/konnect/watch_credential.go +++ b/controller/konnect/watch_credential.go @@ -51,6 +51,6 @@ func kongCredentialRefersToKonnectGatewayControlPlane[ return false } - return objHasControlPlaneRefKonnectNamespacedRef(&consumer) + return objHasControlPlaneRef(&consumer) } } diff --git a/controller/konnect/watch_credentialacl.go b/controller/konnect/watch_credentialacl.go index 117a3f6a8..45c75926e 100644 --- a/controller/konnect/watch_credentialacl.go +++ b/controller/konnect/watch_credentialacl.go @@ -89,21 +89,16 @@ func kongCredentialACLForKonnectAPIAuthConfiguration( var ret []reconcile.Request for _, consumer := range l.Items { - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(&consumer) + cpRef, ok := getControlPlaneRef(&consumer).Get() if !ok { continue } - - cpNN := types.NamespacedName{ - Name: cpRef.KonnectNamespacedRef.Name, - Namespace: consumer.Namespace, - } - var cp konnectv1alpha1.KonnectGatewayControlPlane - if err := cl.Get(ctx, cpNN, &cp); err != nil { + cp, err := getCPForRef(ctx, cl, cpRef, consumer.Namespace) + if err != nil { ctrllog.FromContext(ctx).Error( err, "failed to get KonnectGatewayControlPlane", - "KonnectGatewayControlPlane", cpNN, + "KonnectGatewayControlPlane", cpRef, ) continue } diff --git a/controller/konnect/watch_credentialapikey.go b/controller/konnect/watch_credentialapikey.go index aa0fb7314..2addb5782 100644 --- a/controller/konnect/watch_credentialapikey.go +++ b/controller/konnect/watch_credentialapikey.go @@ -89,21 +89,16 @@ func kongCredentialAPIKeyForKonnectAPIAuthConfiguration( var ret []reconcile.Request for _, consumer := range l.Items { - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(&consumer) + cpRef, ok := getControlPlaneRef(&consumer).Get() if !ok { continue } - - cpNN := types.NamespacedName{ - Name: cpRef.KonnectNamespacedRef.Name, - Namespace: consumer.Namespace, - } - var cp konnectv1alpha1.KonnectGatewayControlPlane - if err := cl.Get(ctx, cpNN, &cp); err != nil { + cp, err := getCPForRef(ctx, cl, cpRef, consumer.Namespace) + if err != nil { ctrllog.FromContext(ctx).Error( err, "failed to get KonnectGatewayControlPlane", - "KonnectGatewayControlPlane", cpNN, + "KonnectGatewayControlPlane", cpRef, ) continue } diff --git a/controller/konnect/watch_credentialbasicauth.go b/controller/konnect/watch_credentialbasicauth.go index 2d2d16e31..f14db47e8 100644 --- a/controller/konnect/watch_credentialbasicauth.go +++ b/controller/konnect/watch_credentialbasicauth.go @@ -89,21 +89,16 @@ func kongCredentialBasicAuthForKonnectAPIAuthConfiguration( var ret []reconcile.Request for _, consumer := range l.Items { - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(&consumer) + cpRef, ok := getControlPlaneRef(&consumer).Get() if !ok { continue } - - cpNN := types.NamespacedName{ - Name: cpRef.KonnectNamespacedRef.Name, - Namespace: consumer.Namespace, - } - var cp konnectv1alpha1.KonnectGatewayControlPlane - if err := cl.Get(ctx, cpNN, &cp); err != nil { + cp, err := getCPForRef(ctx, cl, cpRef, consumer.Namespace) + if err != nil { ctrllog.FromContext(ctx).Error( err, "failed to get KonnectGatewayControlPlane", - "KonnectGatewayControlPlane", cpNN, + "KonnectGatewayControlPlane", cpRef, ) continue } diff --git a/controller/konnect/watch_credentialhmac.go b/controller/konnect/watch_credentialhmac.go index d7b08ced9..a5ac189d0 100644 --- a/controller/konnect/watch_credentialhmac.go +++ b/controller/konnect/watch_credentialhmac.go @@ -89,21 +89,16 @@ func kongCredentialHMACForKonnectAPIAuthConfiguration( var ret []reconcile.Request for _, consumer := range l.Items { - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(&consumer) + cpRef, ok := getControlPlaneRef(&consumer).Get() if !ok { continue } - - cpNN := types.NamespacedName{ - Name: cpRef.KonnectNamespacedRef.Name, - Namespace: consumer.Namespace, - } - var cp konnectv1alpha1.KonnectGatewayControlPlane - if err := cl.Get(ctx, cpNN, &cp); err != nil { + cp, err := getCPForRef(ctx, cl, cpRef, consumer.Namespace) + if err != nil { ctrllog.FromContext(ctx).Error( err, "failed to get KonnectGatewayControlPlane", - "KonnectGatewayControlPlane", cpNN, + "KonnectGatewayControlPlane", cpRef, ) continue } diff --git a/controller/konnect/watch_credentialjwt.go b/controller/konnect/watch_credentialjwt.go index 097f1e0af..df2fa9b12 100644 --- a/controller/konnect/watch_credentialjwt.go +++ b/controller/konnect/watch_credentialjwt.go @@ -89,21 +89,16 @@ func kongCredentialJWTForKonnectAPIAuthConfiguration( var ret []reconcile.Request for _, consumer := range l.Items { - cpRef, ok := controlPlaneRefIsKonnectNamespacedRef(&consumer) + cpRef, ok := getControlPlaneRef(&consumer).Get() if !ok { continue } - - cpNN := types.NamespacedName{ - Name: cpRef.KonnectNamespacedRef.Name, - Namespace: consumer.Namespace, - } - var cp konnectv1alpha1.KonnectGatewayControlPlane - if err := cl.Get(ctx, cpNN, &cp); err != nil { + cp, err := getCPForRef(ctx, cl, cpRef, consumer.Namespace) + if err != nil { ctrllog.FromContext(ctx).Error( err, "failed to get KonnectGatewayControlPlane", - "KonnectGatewayControlPlane", cpNN, + "KonnectGatewayControlPlane", cpRef, ) continue } diff --git a/controller/konnect/watch_kongconsumer.go b/controller/konnect/watch_kongconsumer.go index e87176996..4a06d598e 100644 --- a/controller/konnect/watch_kongconsumer.go +++ b/controller/konnect/watch_kongconsumer.go @@ -2,7 +2,6 @@ package konnect import ( "context" - "fmt" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -13,10 +12,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "github.com/kong/gateway-operator/modules/manager/logging" - 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" ) @@ -97,54 +93,29 @@ func enqueueKongConsumerForKonnectAPIAuthConfiguration( continue } - switch cpRef.Type { - case configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef: - nn := types.NamespacedName{ - Name: cpRef.KonnectNamespacedRef.Name, - Namespace: consumer.Namespace, - } - // TODO: change this when cross namespace refs are allowed. - if nn.Namespace != auth.Namespace { - continue - } - var cp konnectv1alpha1.KonnectGatewayControlPlane - if err := cl.Get(ctx, nn, &cp); err != nil { - ctrllog.FromContext(ctx).Error( - err, - "failed to get KonnectGatewayControlPlane", - "KonnectGatewayControlPlane", nn, - ) - continue - } - - // TODO: change this when cross namespace refs are allowed. - if cp.GetKonnectAPIAuthConfigurationRef().Name != auth.Name { - continue - } - - ret = append(ret, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: consumer.Namespace, - Name: consumer.Name, - }, - }) - - case configurationv1alpha1.ControlPlaneRefKonnectID: + cp, err := getCPForRef(ctx, cl, cpRef, consumer.GetNamespace()) + if err != nil { ctrllog.FromContext(ctx).Error( - fmt.Errorf("unimplemented ControlPlaneRef type %q", cpRef.Type), - "unimplemented ControlPlaneRef for KongConsumer", - "KongConsumer", consumer, "refType", cpRef.Type, + err, + "failed to get KonnectGatewayControlPlane", + "KonnectGatewayControlPlane", cpRef, ) continue + } - default: - ctrllog.FromContext(ctx).V(logging.DebugLevel.Value()).Info( - "unsupported ControlPlaneRef for KongConsumer", - "KongConsumer", consumer, "refType", cpRef.Type, - ) + // TODO: change this when cross namespace refs are allowed. + if cp.GetKonnectAPIAuthConfigurationRef().Name != auth.Name { continue } + + ret = append(ret, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: consumer.Namespace, + Name: consumer.Name, + }, + }) } + return ret } } diff --git a/controller/konnect/watch_kongpluginbinding.go b/controller/konnect/watch_kongpluginbinding.go index 3c80ccf40..a084d14b0 100644 --- a/controller/konnect/watch_kongpluginbinding.go +++ b/controller/konnect/watch_kongpluginbinding.go @@ -279,5 +279,5 @@ func enqueueKongPluginBindingFor[ } func kongPluginBindingIsBoundToKonnectGatewayControlPlane(kpb *configurationv1alpha1.KongPluginBinding) bool { - return objHasControlPlaneRefKonnectNamespacedRef(kpb) + return objHasControlPlaneRef(kpb) } diff --git a/controller/konnect/watch_kongroute.go b/controller/konnect/watch_kongroute.go index 6d0216b0c..e1f26d234 100644 --- a/controller/konnect/watch_kongroute.go +++ b/controller/konnect/watch_kongroute.go @@ -69,7 +69,7 @@ func kongRouteRefersToKonnectGatewayControlPlane(cl client.Client) func(obj clie // If the KongRoute refers to a KonnectGatewayControlPlane directly (it's a serviceless route), // enqueue it. - if objHasControlPlaneRefKonnectNamespacedRef(kongRoute) { + if objHasControlPlaneRef(kongRoute) { return true } @@ -85,7 +85,7 @@ func kongRouteRefersToKonnectGatewayControlPlane(cl client.Client) func(obj clie if err := cl.Get(context.Background(), nn, &kongSvc); client.IgnoreNotFound(err) != nil { return true } - return objHasControlPlaneRefKonnectNamespacedRef(&kongSvc) + return objHasControlPlaneRef(&kongSvc) } } @@ -100,7 +100,7 @@ func enqueueKongRouteForKongService( // If the KongService does not refer to a KonnectGatewayControlPlane, // we do not need to enqueue any KongRoutes bound to this KongService. - if !objHasControlPlaneRefKonnectNamespacedRef(kongSvc) { + if !objHasControlPlaneRef(kongSvc) { return nil } diff --git a/controller/konnect/watch_kongservice.go b/controller/konnect/watch_kongservice.go index f8089ae47..9dfba3daf 100644 --- a/controller/konnect/watch_kongservice.go +++ b/controller/konnect/watch_kongservice.go @@ -2,7 +2,6 @@ package konnect import ( "context" - "fmt" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -13,8 +12,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "github.com/kong/gateway-operator/modules/manager/logging" - configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" ) @@ -83,54 +80,28 @@ func enqueueKongServiceForKonnectAPIAuthConfiguration( if !ok { continue } - - switch cpRef.Type { - case configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef: - nn := types.NamespacedName{ - Name: cpRef.KonnectNamespacedRef.Name, - Namespace: svc.Namespace, - } - // TODO: change this when cross namespace refs are allowed. - if nn.Namespace != auth.Namespace { - continue - } - var cp konnectv1alpha1.KonnectGatewayControlPlane - if err := cl.Get(ctx, nn, &cp); err != nil { - ctrllog.FromContext(ctx).Error( - err, - "failed to get KonnectGatewayControlPlane", - "KonnectGatewayControlPlane", nn, - ) - continue - } - - // TODO: change this when cross namespace refs are allowed. - if cp.GetKonnectAPIAuthConfigurationRef().Name != auth.Name { - continue - } - - ret = append(ret, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: svc.Namespace, - Name: svc.Name, - }, - }) - - case configurationv1alpha1.ControlPlaneRefKonnectID: + cp, err := getCPForRef(ctx, cl, cpRef, svc.GetNamespace()) + if err != nil { ctrllog.FromContext(ctx).Error( - fmt.Errorf("unimplemented ControlPlaneRef type %q", cpRef.Type), - "unimplemented ControlPlaneRef for KongService", - "KongService", svc, "refType", cpRef.Type, + err, + "failed to get ControlPlane for KongService", + "KongService", client.ObjectKeyFromObject(&svc).String(), ) continue + } - default: - ctrllog.FromContext(ctx).V(logging.DebugLevel.Value()).Info( - "unsupported ControlPlaneRef for KongService", - "KongService", svc, "refType", cpRef.Type, - ) + // TODO: change this when cross namespace refs are allowed. + if cp.GetKonnectAPIAuthConfigurationRef().Name != auth.Name { continue } + + ret = append(ret, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: svc.Namespace, + Name: svc.Name, + }, + }) + } return ret } diff --git a/controller/konnect/watch_kongsni.go b/controller/konnect/watch_kongsni.go index 02fabdcde..2f6a9762a 100644 --- a/controller/konnect/watch_kongsni.go +++ b/controller/konnect/watch_kongsni.go @@ -58,7 +58,7 @@ func kongSNIRefersToKonnectGatewayControlPlane( return true } - return objHasControlPlaneRefKonnectNamespacedRef(&cert) + return objHasControlPlaneRef(&cert) } } @@ -71,7 +71,7 @@ func enqueueKongSNIForKongCertificate( return nil } - if !objHasControlPlaneRefKonnectNamespacedRef(cert) { + if !objHasControlPlaneRef(cert) { return nil } diff --git a/controller/konnect/watch_kongtarget.go b/controller/konnect/watch_kongtarget.go index 8975e72cf..44b7c370f 100644 --- a/controller/konnect/watch_kongtarget.go +++ b/controller/konnect/watch_kongtarget.go @@ -62,7 +62,7 @@ func kongTargetRefersToKonnectGatewayControlPlane(cl client.Client) func(obj cli if err := cl.Get(context.Background(), nn, &upstream); client.IgnoreNotFound(err) != nil { return true } - return objHasControlPlaneRefKonnectNamespacedRef(&upstream) + return objHasControlPlaneRef(&upstream) } } @@ -73,7 +73,7 @@ func enqueueKongTargetForKongUpstream(cl client.Client, if !ok { return nil } - if !objHasControlPlaneRefKonnectNamespacedRef(kongUpstream) { + if !objHasControlPlaneRef(kongUpstream) { return nil } diff --git a/controller/konnect/watch_test.go b/controller/konnect/watch_test.go index e8f50b9de..07d0bc97a 100644 --- a/controller/konnect/watch_test.go +++ b/controller/konnect/watch_test.go @@ -88,6 +88,10 @@ func TestObjectListToReconcileRequests(t *testing.T) { func TestEnqueueObjectForKonnectGatewayControlPlane(t *testing.T) { cp := &konnectv1alpha1.KonnectGatewayControlPlane{ + TypeMeta: metav1.TypeMeta{ + APIVersion: konnectv1alpha1.GroupVersion.String(), + Kind: "KonnectGatewayControlPlane", + }, ObjectMeta: metav1.ObjectMeta{ Name: "test1", Namespace: "default", @@ -98,13 +102,13 @@ func TestEnqueueObjectForKonnectGatewayControlPlane(t *testing.T) { name string index string list []client.Object - extractFunc client.IndexerFunc + extractFunc func(client.Client) client.IndexerFunc expected []ctrl.Request }{ { name: "no ControlPlane reference", index: IndexFieldKongConsumerOnKonnectGatewayControlPlane, - extractFunc: kongConsumerReferencesKonnectGatewayControlPlane, + extractFunc: indexKonnectGatewayControlPlaneRef[configurationv1.KongConsumer], list: []client.Object{ &configurationv1.KongConsumer{ ObjectMeta: metav1.ObjectMeta{ @@ -123,7 +127,7 @@ func TestEnqueueObjectForKonnectGatewayControlPlane(t *testing.T) { { name: "1 KongConumser refers to KonnectGatewayControlPlane", index: IndexFieldKongConsumerOnKonnectGatewayControlPlane, - extractFunc: kongConsumerReferencesKonnectGatewayControlPlane, + extractFunc: indexKonnectGatewayControlPlaneRef[configurationv1.KongConsumer], list: []client.Object{ &configurationv1.KongConsumer{ ObjectMeta: metav1.ObjectMeta{ @@ -158,7 +162,7 @@ func TestEnqueueObjectForKonnectGatewayControlPlane(t *testing.T) { { name: "1 KongConumser refers to a different KonnectGatewayControlPlane", index: IndexFieldKongConsumerOnKonnectGatewayControlPlane, - extractFunc: kongConsumerReferencesKonnectGatewayControlPlane, + extractFunc: indexKonnectGatewayControlPlaneRef[configurationv1.KongConsumer], list: []client.Object{ &configurationv1.KongConsumer{ ObjectMeta: metav1.ObjectMeta{ @@ -185,11 +189,18 @@ func TestEnqueueObjectForKonnectGatewayControlPlane(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cl := fakectrlruntimeclient.NewClientBuilder(). + builder := fakectrlruntimeclient.NewClientBuilder(). WithScheme(scheme.Get()). - WithObjects(tt.list...). - WithIndex(&configurationv1.KongConsumer{}, tt.index, tt.extractFunc). - Build() + WithObjects(append(tt.list, cp)...) + + // Build a separate client for indices as we have kind of a chicken-egg problem here. We need a client + // in the extract function passed to the builder's WithIndex function, but it's the builder that creates + // the client. So we build the client for indices first (without the index) and then build the client + // with the index. + clForIndices := builder.Build() + require.NotNil(t, clForIndices) + + cl := builder.WithIndex(&configurationv1.KongConsumer{}, tt.index, tt.extractFunc(clForIndices)).Build() require.NotNil(t, cl) f := enqueueObjectForKonnectGatewayControlPlane[configurationv1.KongConsumerList](cl, tt.index) diff --git a/modules/manager/controller_setup.go b/modules/manager/controller_setup.go index f1810b79c..aec968cc1 100644 --- a/modules/manager/controller_setup.go +++ b/modules/manager/controller_setup.go @@ -588,6 +588,7 @@ func SetupControllers(mgr manager.Manager, c *Config) (map[string]ControllerDef, // This is done only once because 1 manager's cache can only have one index with // a predefined key and so that different controllers can share the same indices. func SetupCacheIndicesForKonnectTypes(ctx context.Context, mgr manager.Manager, developmentMode bool) error { + cl := mgr.GetClient() types := []struct { Object interface { client.Object @@ -621,15 +622,15 @@ func SetupCacheIndicesForKonnectTypes(ctx context.Context, mgr manager.Manager, }, { Object: &configurationv1.KongConsumer{}, - IndexOptions: konnect.IndexOptionsForKongConsumer(), + IndexOptions: konnect.IndexOptionsForKongConsumer(cl), }, { Object: &configurationv1beta1.KongConsumerGroup{}, - IndexOptions: konnect.IndexOptionsForKongConsumerGroup(), + IndexOptions: konnect.IndexOptionsForKongConsumerGroup(cl), }, { Object: &configurationv1alpha1.KongService{}, - IndexOptions: konnect.IndexOptionsForKongService(), + IndexOptions: konnect.IndexOptionsForKongService(cl), }, { Object: &configurationv1alpha1.KongRoute{}, @@ -637,7 +638,7 @@ func SetupCacheIndicesForKonnectTypes(ctx context.Context, mgr manager.Manager, }, { Object: &configurationv1alpha1.KongUpstream{}, - IndexOptions: konnect.IndexOptionsForKongUpstream(), + IndexOptions: konnect.IndexOptionsForKongUpstream(cl), }, { Object: &configurationv1alpha1.KongTarget{}, @@ -649,27 +650,27 @@ func SetupCacheIndicesForKonnectTypes(ctx context.Context, mgr manager.Manager, }, { Object: &configurationv1alpha1.KongKey{}, - IndexOptions: konnect.IndexOptionsForKongKey(), + IndexOptions: konnect.IndexOptionsForKongKey(cl), }, { Object: &configurationv1alpha1.KongKeySet{}, - IndexOptions: konnect.IndexOptionsForKongKeySet(), + IndexOptions: konnect.IndexOptionsForKongKeySet(cl), }, { Object: &configurationv1alpha1.KongDataPlaneClientCertificate{}, - IndexOptions: konnect.IndexOptionsForKongDataPlaneCertificate(), + IndexOptions: konnect.IndexOptionsForKongDataPlaneCertificate(cl), }, { Object: &configurationv1alpha1.KongVault{}, - IndexOptions: konnect.IndexOptionsForKongVault(), + IndexOptions: konnect.IndexOptionsForKongVault(cl), }, { Object: &configurationv1alpha1.KongCertificate{}, - IndexOptions: konnect.IndexOptionsForKongCertificate(), + IndexOptions: konnect.IndexOptionsForKongCertificate(cl), }, { Object: &configurationv1alpha1.KongCACertificate{}, - IndexOptions: konnect.IndexOptionsForKongCACertificate(), + IndexOptions: konnect.IndexOptionsForKongCACertificate(cl), }, { Object: &konnectv1alpha1.KonnectGatewayControlPlane{}, diff --git a/test/envtest/controller.go b/test/envtest/controller.go index 9254cc156..4852700e9 100644 --- a/test/envtest/controller.go +++ b/test/envtest/controller.go @@ -11,10 +11,11 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" - ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/config" "sigs.k8s.io/controller-runtime/pkg/manager" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + + kgomanager "github.com/kong/gateway-operator/modules/manager" ) // Reconciler represents a reconciler. @@ -22,11 +23,21 @@ type Reconciler interface { SetupWithManager(context.Context, ctrl.Manager) error } +// ManagerOption is a function that can be used to configure the manager. +type ManagerOption func(manager.Manager) error + +// WithKonnectCacheIndices is a manager option that sets up cache indices for Konnect types. +func WithKonnectCacheIndices(ctx context.Context) ManagerOption { + return func(mgr manager.Manager) error { + return kgomanager.SetupCacheIndicesForKonnectTypes(ctx, mgr, true) + } +} + // NewManager returns a manager and a logs observer. // The logs observer can be used to dump logs if the test fails. // The returned manager can be used with StartReconcilers() to start a list of // provided reconcilers with the manager. -func NewManager(t *testing.T, ctx context.Context, cfg *rest.Config, s *runtime.Scheme) (manager.Manager, LogsObserver) { +func NewManager(t *testing.T, ctx context.Context, cfg *rest.Config, s *runtime.Scheme, opts ...ManagerOption) (manager.Manager, LogsObserver) { _, logger, logs := CreateTestLogger(ctx) o := manager.Options{ @@ -50,6 +61,11 @@ func NewManager(t *testing.T, ctx context.Context, cfg *rest.Config, s *runtime. mgr, err := ctrl.NewManager(cfg, o) require.NoError(t, err) + + for _, opt := range opts { + require.NoError(t, opt(mgr)) + } + return mgr, logs } @@ -84,12 +100,3 @@ func StartReconcilers( DumpLogsIfTestFailed(t, logs) }) } - -// NewControllerClient returns a new controller-runtime Client for provided runtime.Scheme and rest.Config. -func NewControllerClient(t *testing.T, scheme *runtime.Scheme, cfg *rest.Config) ctrlclient.Client { - client, err := ctrlclient.New(cfg, ctrlclient.Options{ - Scheme: scheme, - }) - require.NoError(t, err) - return client -} diff --git a/test/envtest/konnect_entities_kongcacertificate_test.go b/test/envtest/konnect_entities_kongcacertificate_test.go index 72e1fb118..fd9b3850e 100644 --- a/test/envtest/konnect_entities_kongcacertificate_test.go +++ b/test/envtest/konnect_entities_kongcacertificate_test.go @@ -37,7 +37,7 @@ func TestKongCACertificate(t *testing.T) { ) t.Log("Setting up the manager with reconcilers") - mgr, logs := NewManager(t, ctx, cfg, scheme.Get()) + mgr, logs := NewManager(t, ctx, cfg, scheme.Get(), WithKonnectCacheIndices(ctx)) factory := sdkmocks.NewMockSDKFactory(t) sdk := factory.SDK StartReconcilers(ctx, t, mgr, logs, @@ -181,4 +181,46 @@ func TestKongCACertificate(t *testing.T) { assert.True(c, sdk.CACertificatesSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("should handle konnectID control plane reference", func(t *testing.T) { + t.Log("Setting up SDK expectations on KongCACertificate creation") + sdk.CACertificatesSDK.EXPECT().CreateCaCertificate(mock.Anything, cp.GetKonnectStatus().GetKonnectID(), + mock.MatchedBy(func(input sdkkonnectcomp.CACertificateInput) bool { + return input.Cert == deploy.TestValidCACertPEM && + slices.Contains(input.Tags, tagName) + }), + ).Return(&sdkkonnectops.CreateCaCertificateResponse{ + CACertificate: &sdkkonnectcomp.CACertificate{ + ID: lo.ToPtr("12345"), + }, + }, nil) + + t.Log("Creating KongCACertificate with ControlPlaneRef type=konnectID") + createdCert := deploy.KongCACertificateAttachedToCP(t, ctx, clientNamespaced, cp, + func(obj client.Object) { + cert := obj.(*configurationv1alpha1.KongCACertificate) + cert.Spec.Tags = []string{tagName} + }, + deploy.WithKonnectIDControlPlaneRef(cp), + ) + + t.Log("Waiting for KongCACertificate to be programmed") + watchFor(t, ctx, w, watch.Modified, func(c *configurationv1alpha1.KongCACertificate) bool { + if c.GetName() != createdCert.GetName() { + return false + } + if c.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + return lo.ContainsBy(c.Status.Conditions, func(condition metav1.Condition) bool { + return condition.Type == konnectv1alpha1.KonnectEntityProgrammedConditionType && + condition.Status == metav1.ConditionTrue + }) + }, "KongCACertificate's Programmed condition should be true eventually") + + t.Log("Waiting for KongCACertificate to be created in the SDK") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.CACertificatesSDK.AssertExpectations(t)) + }, waitTime, tickTime) + }) } diff --git a/test/envtest/konnect_entities_kongcertificate_test.go b/test/envtest/konnect_entities_kongcertificate_test.go index 481b900f5..847fc39cb 100644 --- a/test/envtest/konnect_entities_kongcertificate_test.go +++ b/test/envtest/konnect_entities_kongcertificate_test.go @@ -33,7 +33,7 @@ func TestKongCertificate(t *testing.T) { cfg, ns := Setup(t, ctx, scheme.Get()) t.Log("Setting up the manager with reconcilers") - mgr, logs := NewManager(t, ctx, cfg, scheme.Get()) + mgr, logs := NewManager(t, ctx, cfg, scheme.Get(), WithKonnectCacheIndices(ctx)) factory := sdkmocks.NewMockSDKFactory(t) sdk := factory.SDK StartReconcilers(ctx, t, mgr, logs, @@ -185,4 +185,47 @@ func TestKongCertificate(t *testing.T) { assert.True(c, sdk.CertificatesSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("should handle konnectID control plane reference", func(t *testing.T) { + t.Log("Setting up SDK expectations on KongCertificate creation") + sdk.CertificatesSDK.EXPECT().CreateCertificate(mock.Anything, cp.GetKonnectStatus().GetKonnectID(), + mock.MatchedBy(func(input sdkkonnectcomp.CertificateInput) bool { + return input.Cert == deploy.TestValidCertPEM && + input.Key == deploy.TestValidCertKeyPEM && + slices.Contains(input.Tags, "tag1") + }), + ).Return(&sdkkonnectops.CreateCertificateResponse{ + Certificate: &sdkkonnectcomp.Certificate{ + ID: lo.ToPtr("cert-12345"), + }, + }, nil) + + t.Log("Creating KongCertificate with ControlPlaneRef type=konnectID") + createdCert := deploy.KongCertificateAttachedToCP(t, ctx, clientNamespaced, cp, + func(obj client.Object) { + cert := obj.(*configurationv1alpha1.KongCertificate) + cert.Spec.Tags = []string{"tag1"} + }, + deploy.WithKonnectIDControlPlaneRef(cp), + ) + + t.Log("Waiting for KongCertificate to be programmed") + watchFor(t, ctx, w, watch.Modified, func(c *configurationv1alpha1.KongCertificate) bool { + if c.GetName() != createdCert.GetName() { + return false + } + if c.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + return lo.ContainsBy(c.Status.Conditions, func(condition metav1.Condition) bool { + return condition.Type == konnectv1alpha1.KonnectEntityProgrammedConditionType && + condition.Status == metav1.ConditionTrue + }) + }, "KongCertificate's Programmed condition should be true eventually") + + t.Log("Waiting for KongCertificate to be created in the SDK") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.CertificatesSDK.AssertExpectations(t)) + }, waitTime, tickTime) + }) } diff --git a/test/envtest/konnect_entities_kongconsumer_test.go b/test/envtest/konnect_entities_kongconsumer_test.go index 67e1e1ffd..fac7f2491 100644 --- a/test/envtest/konnect_entities_kongconsumer_test.go +++ b/test/envtest/konnect_entities_kongconsumer_test.go @@ -24,6 +24,7 @@ import ( "github.com/kong/gateway-operator/test/helpers/deploy" 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" ) @@ -35,7 +36,7 @@ func TestKongConsumer(t *testing.T) { cfg, ns := Setup(t, ctx, scheme.Get()) t.Log("Setting up the manager with reconcilers") - mgr, logs := NewManager(t, ctx, cfg, scheme.Get()) + mgr, logs := NewManager(t, ctx, cfg, scheme.Get(), WithKonnectCacheIndices(ctx)) factory := sdkmocks.NewMockSDKFactory(t) sdk := factory.SDK reconcilers := []Reconciler{ @@ -349,4 +350,59 @@ func TestKongConsumer(t *testing.T) { assert.True(c, factory.SDK.ConsumersSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("should handle konnectID control plane reference", func(t *testing.T) { + const ( + consumerID = "consumer-with-cp-konnect-id" + username = "user-with-cp-konnect-id" + ) + t.Log("Setting up SDK expectations on KongConsumer creation") + sdk.ConsumersSDK.EXPECT(). + CreateConsumer(mock.Anything, cp.GetKonnectStatus().GetKonnectID(), + mock.MatchedBy(func(input sdkkonnectcomp.ConsumerInput) bool { + return input.Username != nil && *input.Username == username + }), + ).Return(&sdkkonnectops.CreateConsumerResponse{ + Consumer: &sdkkonnectcomp.Consumer{ + ID: lo.ToPtr(consumerID), + }, + }, nil) + + t.Log("Setting up SDK expectation on possibly updating KongConsumer ( due to asynchronous nature of updates between KongConsumer and KongConsumerGroup)") + sdk.ConsumersSDK.EXPECT(). + UpsertConsumer(mock.Anything, mock.MatchedBy(func(r sdkkonnectops.UpsertConsumerRequest) bool { + return r.ConsumerID == consumerID + })). + Return(&sdkkonnectops.UpsertConsumerResponse{}, nil). + Maybe() + + t.Log("Setting up SDK expectation on KongConsumerGroups listing") + sdk.ConsumerGroupSDK.EXPECT(). + ListConsumerGroupsForConsumer(mock.Anything, sdkkonnectops.ListConsumerGroupsForConsumerRequest{ + ConsumerID: consumerID, + ControlPlaneID: cp.GetKonnectStatus().GetKonnectID(), + }).Return(&sdkkonnectops.ListConsumerGroupsForConsumerResponse{}, nil) + + t.Log("Creating KongConsumer with ControlPlaneRef type=konnectID") + createdConsumer := deploy.KongConsumerAttachedToCP(t, ctx, clientNamespaced, username, cp, deploy.WithKonnectIDControlPlaneRef(cp)) + + t.Log("Waiting for KongConsumer to be programmed") + watchFor(t, ctx, cWatch, watch.Modified, func(c *configurationv1.KongConsumer) bool { + if c.GetName() != createdConsumer.GetName() { + return false + } + if c.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + return lo.ContainsBy(c.Status.Conditions, func(condition metav1.Condition) bool { + return condition.Type == konnectv1alpha1.KonnectEntityProgrammedConditionType && + condition.Status == metav1.ConditionTrue + }) + }, "KongConsumer's Programmed condition should be true eventually") + + t.Log("Waiting for KongConsumer to be created in the SDK") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.ConsumersSDK.AssertExpectations(t)) + }, waitTime, tickTime) + }) } diff --git a/test/envtest/konnect_entities_kongconsumergroup_test.go b/test/envtest/konnect_entities_kongconsumergroup_test.go index 288aee8c9..80ebade14 100644 --- a/test/envtest/konnect_entities_kongconsumergroup_test.go +++ b/test/envtest/konnect_entities_kongconsumergroup_test.go @@ -24,6 +24,7 @@ import ( k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" "github.com/kong/gateway-operator/test/helpers/deploy" + 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" ) @@ -35,7 +36,7 @@ func TestKongConsumerGroup(t *testing.T) { cfg, ns := Setup(t, ctx, scheme.Get()) t.Log("Setting up the manager with reconcilers") - mgr, logs := NewManager(t, ctx, cfg, scheme.Get()) + mgr, logs := NewManager(t, ctx, cfg, scheme.Get(), WithKonnectCacheIndices(ctx)) factory := sdkmocks.NewMockSDKFactory(t) sdk := factory.SDK reconcilers := []Reconciler{ @@ -198,4 +199,51 @@ func TestKongConsumerGroup(t *testing.T) { assert.True(c, factory.SDK.ConsumerGroupSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("should handle konnectID control plane reference", func(t *testing.T) { + const ( + cgID = "cg-with-konnectid-cp-ref-id" + cgName = "cg-with-konnectid-cp-ref" + ) + t.Log("Setting up SDK expectations on KongConsumerGroup creation") + sdk.ConsumerGroupSDK.EXPECT(). + CreateConsumerGroup(mock.Anything, cp.GetKonnectStatus().GetKonnectID(), + mock.MatchedBy(func(cg sdkkonnectcomp.ConsumerGroupInput) bool { + return cg.Name == cgName + }), + ).Return(&sdkkonnectops.CreateConsumerGroupResponse{ + ConsumerGroup: &sdkkonnectcomp.ConsumerGroup{ + ID: lo.ToPtr(cgID), + }, + }, nil, + ) + + t.Log("Creating KongConsumerGroup with ControlPlaneRef type=konnectID") + cg := deploy.KongConsumerGroupAttachedToCP(t, ctx, clientNamespaced, cp, + func(obj client.Object) { + cg := obj.(*configurationv1beta1.KongConsumerGroup) + cg.Spec.Name = cgName + }, + deploy.WithKonnectIDControlPlaneRef(cp), + ) + + t.Log("Waiting for KongConsumerGroup to be programmed") + watchFor(t, ctx, cWatch, watch.Modified, func(c *configurationv1beta1.KongConsumerGroup) bool { + if c.GetName() != cg.GetName() { + return false + } + if c.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + return lo.ContainsBy(c.Status.Conditions, func(condition metav1.Condition) bool { + return condition.Type == konnectv1alpha1.KonnectEntityProgrammedConditionType && + condition.Status == metav1.ConditionTrue + }) + }, "KongConsumerGroup's Programmed condition should be true eventually") + + t.Log("Waiting for KongConsumerGroup to be created in the SDK") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.ConsumerGroupSDK.AssertExpectations(t)) + }, waitTime, tickTime) + }) } diff --git a/test/envtest/konnect_entities_kongdataplaneclientcertificate_test.go b/test/envtest/konnect_entities_kongdataplaneclientcertificate_test.go index f19a2df0b..228b9f062 100644 --- a/test/envtest/konnect_entities_kongdataplaneclientcertificate_test.go +++ b/test/envtest/konnect_entities_kongdataplaneclientcertificate_test.go @@ -30,7 +30,7 @@ func TestKongDataPlaneClientCertificate(t *testing.T) { cfg, ns := Setup(t, ctx, scheme.Get()) t.Log("Setting up the manager with reconcilers") - mgr, logs := NewManager(t, ctx, cfg, scheme.Get()) + mgr, logs := NewManager(t, ctx, cfg, scheme.Get(), WithKonnectCacheIndices(ctx)) factory := sdkmocks.NewMockSDKFactory(t) sdk := factory.SDK StartReconcilers(ctx, t, mgr, logs, @@ -98,4 +98,46 @@ func TestKongDataPlaneClientCertificate(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { assert.True(c, factory.SDK.CACertificatesSDK.AssertExpectations(t)) }, waitTime, tickTime) + + t.Run("should handle konnectID control plane reference", func(t *testing.T) { + t.Log("Setting up SDK expectations on KongDataPlaneClientCertificate creation") + const dpCertID = "dp-cert-id-with-konnectid-cp-ref" + sdk.DataPlaneCertificatesSDK.EXPECT().CreateDataplaneCertificate(mock.Anything, cp.GetKonnectStatus().GetKonnectID(), + mock.MatchedBy(func(input *sdkkonnectcomp.DataPlaneClientCertificateRequest) bool { + return input.Cert == deploy.TestValidCACertPEM + }), + ).Return(&sdkkonnectops.CreateDataplaneCertificateResponse{ + DataPlaneClientCertificate: &sdkkonnectcomp.DataPlaneClientCertificate{ + Item: &sdkkonnectcomp.DataPlaneClientCertificateItem{ + ID: lo.ToPtr(dpCertID), + Cert: lo.ToPtr(deploy.TestValidCACertPEM), + }, + }, + }, nil) + + t.Log("Creating KongDataPlaneClientCertificate with ControlPlaneRef type=konnectID") + createdCert := deploy.KongDataPlaneClientCertificateAttachedToCP(t, ctx, clientNamespaced, cp, + deploy.WithKonnectIDControlPlaneRef(cp), + ) + + t.Log("Waiting for KongDataPlaneClientCertificate to be programmed") + watchFor(t, ctx, w, watch.Modified, func(c *configurationv1alpha1.KongDataPlaneClientCertificate) bool { + if c.GetName() != createdCert.GetName() { + return false + } + if c.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + return lo.ContainsBy(c.Status.Conditions, func(condition metav1.Condition) bool { + return condition.Type == konnectv1alpha1.KonnectEntityProgrammedConditionType && + condition.Status == metav1.ConditionTrue + }) + }, "KongDataPlaneClientCertificate's Programmed condition should be true eventually") + + t.Log("Waiting for KongDataPlaneClientCertificate to be created in the SDK") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.CACertificatesSDK.AssertExpectations(t)) + }, waitTime, tickTime) + + }) } diff --git a/test/envtest/konnect_entities_kongkey_test.go b/test/envtest/konnect_entities_kongkey_test.go index 6b3e3df82..839d1455f 100644 --- a/test/envtest/konnect_entities_kongkey_test.go +++ b/test/envtest/konnect_entities_kongkey_test.go @@ -40,7 +40,7 @@ func TestKongKey(t *testing.T) { cfg, ns := Setup(t, ctx, scheme.Get()) t.Log("Setting up the manager with reconcilers") - mgr, logs := NewManager(t, ctx, cfg, scheme.Get()) + mgr, logs := NewManager(t, ctx, cfg, scheme.Get(), WithKonnectCacheIndices(ctx)) factory := sdkmocks.NewMockSDKFactory(t) sdk := factory.SDK StartReconcilers(ctx, t, mgr, logs, @@ -270,4 +270,42 @@ func TestKongKey(t *testing.T) { assert.True(c, factory.SDK.KeysSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("should handle konnectID control plane reference", func(t *testing.T) { + t.Log("Setting up SDK expectations on KongKey creation") + sdk.KeysSDK.EXPECT().CreateKey(mock.Anything, cp.GetKonnectStatus().GetKonnectID(), + mock.MatchedBy(func(input sdkkonnectcomp.KeyInput) bool { + return input.Kid == keyKid && + input.Name != nil && *input.Name == keyName + }), + ).Return(&sdkkonnectops.CreateKeyResponse{ + Key: &sdkkonnectcomp.Key{ + ID: lo.ToPtr(keyID), + }, + }, 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) }, + ) + + t.Log("Waiting for KongKey to be programmed") + watchFor(t, ctx, w, watch.Modified, func(c *configurationv1alpha1.KongKey) bool { + if c.GetName() != createdKey.GetName() { + return false + } + if c.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + return lo.ContainsBy(c.Status.Conditions, func(condition metav1.Condition) bool { + return condition.Type == konnectv1alpha1.KonnectEntityProgrammedConditionType && + condition.Status == metav1.ConditionTrue + }) + }, "KongKey's Programmed condition should be true eventually") + + t.Log("Checking SDK KongKey operations") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.KeysSDK.AssertExpectations(t)) + }, waitTime, tickTime) + }) } diff --git a/test/envtest/konnect_entities_kongkeyset_test.go b/test/envtest/konnect_entities_kongkeyset_test.go index 51fe6ccb5..a959906a3 100644 --- a/test/envtest/konnect_entities_kongkeyset_test.go +++ b/test/envtest/konnect_entities_kongkeyset_test.go @@ -38,7 +38,7 @@ func TestKongKeySet(t *testing.T) { cfg, ns := Setup(t, ctx, scheme.Get()) t.Log("Setting up the manager with reconcilers") - mgr, logs := NewManager(t, ctx, cfg, scheme.Get()) + mgr, logs := NewManager(t, ctx, cfg, scheme.Get(), WithKonnectCacheIndices(ctx)) factory := sdkmocks.NewMockSDKFactory(t) sdk := factory.SDK StartReconcilers(ctx, t, mgr, logs, @@ -162,4 +162,44 @@ func TestKongKeySet(t *testing.T) { assert.True(c, factory.SDK.ConsumersSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("should handle konnectID control plane reference", func(t *testing.T) { + t.Log("Setting up SDK expectations on KongKeySet creation") + sdk.KeySetsSDK.EXPECT().CreateKeySet(mock.Anything, cp.GetKonnectStatus().GetKonnectID(), + mock.MatchedBy(func(input sdkkonnectcomp.KeySetInput) bool { + return input.Name != nil && *input.Name == keySetName + }), + ).Return(&sdkkonnectops.CreateKeySetResponse{ + KeySet: &sdkkonnectcomp.KeySet{ + ID: lo.ToPtr(keySetID), + }, + }, nil) + + t.Log("Setting up a watch for KongKeySet events") + 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, + deploy.WithKonnectIDControlPlaneRef(cp), + ) + + t.Log("Waiting for KongKeySet to be programmed") + watchFor(t, ctx, w, watch.Modified, func(c *configurationv1alpha1.KongKeySet) bool { + if c.GetName() != createdKeySet.GetName() { + return false + } + if c.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + return lo.ContainsBy(c.Status.Conditions, func(condition metav1.Condition) bool { + return condition.Type == konnectv1alpha1.KonnectEntityProgrammedConditionType && + condition.Status == metav1.ConditionTrue + }) + }, "KongKeySet's Programmed condition should be true eventually") + + t.Log("Waiting for KongKeySet to be created in the SDK") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.KeySetsSDK.AssertExpectations(t)) + }, waitTime, tickTime) + }) } diff --git a/test/envtest/konnect_entities_kongroute_test.go b/test/envtest/konnect_entities_kongroute_test.go index 3b3b1f3a1..8bba735e2 100644 --- a/test/envtest/konnect_entities_kongroute_test.go +++ b/test/envtest/konnect_entities_kongroute_test.go @@ -53,9 +53,7 @@ func TestKongRoute(t *testing.T) { svc := deploy.KongServiceAttachedToCPWithID(t, ctx, clientNamespaced, cp) t.Run("adding, patching and deleting KongRoute", func(t *testing.T) { - const ( - routeID = "route-12345" - ) + const routeID = "route-12345" t.Log("Setting up a watch for KongRoute events") w := setupWatch[configurationv1alpha1.KongRouteList](t, ctx, cl, client.InNamespace(ns.Name)) @@ -88,6 +86,9 @@ func TestKongRoute(t *testing.T) { t.Log("Waiting for Route to be programmed and get Konnect ID") watchFor(t, ctx, w, watch.Modified, func(r *configurationv1alpha1.KongRoute) bool { + if r.GetName() != createdRoute.GetName() { + return false + } return r.GetKonnectID() == routeID && k8sutils.IsProgrammed(r) }, "KongRoute didn't get Programmed status condition or didn't get the correct (route-12345) Konnect ID assigned") diff --git a/test/envtest/konnect_entities_kongservice_test.go b/test/envtest/konnect_entities_kongservice_test.go index 16b8f4155..9aa7e5e13 100644 --- a/test/envtest/konnect_entities_kongservice_test.go +++ b/test/envtest/konnect_entities_kongservice_test.go @@ -30,7 +30,7 @@ func TestKongService(t *testing.T) { cfg, ns := Setup(t, ctx, scheme.Get()) t.Log("Setting up the manager with reconcilers") - mgr, logs := NewManager(t, ctx, cfg, scheme.Get()) + mgr, logs := NewManager(t, ctx, cfg, scheme.Get(), WithKonnectCacheIndices(ctx)) factory := sdkmocks.NewMockSDKFactory(t) sdk := factory.SDK StartReconcilers(ctx, t, mgr, logs, @@ -143,4 +143,63 @@ func TestKongService(t *testing.T) { assert.True(c, factory.SDK.ServicesSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("should handle konnectID control plane references", func(t *testing.T) { + const ( + upstreamID = "kup-12345" + serviceID = "service-12345" + host = "example.com" + port = int64(8081) + ) + + t.Log("Creating a KongUpstream and setting it to programmed") + upstream := deploy.KongUpstreamAttachedToCP(t, ctx, clientNamespaced, cp) + updateKongUpstreamStatusWithProgrammed(t, ctx, clientNamespaced, upstream, upstreamID, cp.GetKonnectID()) + + 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(serviceID), + }, + }, + nil, + ) + + t.Log("Creating a KongService with ControlPlaneRef type=konnectID") + createdService := deploy.KongServiceAttachedToCP(t, ctx, clientNamespaced, cp, + func(obj client.Object) { + s := obj.(*configurationv1alpha1.KongService) + s.Spec.KongServiceAPISpec.Host = host + }, + deploy.WithKonnectIDControlPlaneRef(cp), + ) + 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 Service to be programmed and get Konnect ID") + watchFor(t, ctx, w, watch.Modified, func(kt *configurationv1alpha1.KongService) bool { + if kt.GetName() != createdService.GetName() { + return false + } + if kt.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + return kt.GetKonnectID() == serviceID && k8sutils.IsProgrammed(kt) + }, "KongService didn't get Programmed status condition or didn't get the correct (service-12345) Konnect ID assigned") + + }) } diff --git a/test/envtest/konnect_entities_kongupstream_test.go b/test/envtest/konnect_entities_kongupstream_test.go index d127b666d..8d20f92e1 100644 --- a/test/envtest/konnect_entities_kongupstream_test.go +++ b/test/envtest/konnect_entities_kongupstream_test.go @@ -30,7 +30,7 @@ func TestKongUpstream(t *testing.T) { cfg, ns := Setup(t, ctx, scheme.Get()) t.Log("Setting up the manager with reconcilers") - mgr, logs := NewManager(t, ctx, cfg, scheme.Get()) + mgr, logs := NewManager(t, ctx, cfg, scheme.Get(), WithKonnectCacheIndices(ctx)) factory := sdkmocks.NewMockSDKFactory(t) sdk := factory.SDK StartReconcilers(ctx, t, mgr, logs, @@ -50,13 +50,11 @@ func TestKongUpstream(t *testing.T) { apiAuth := deploy.KonnectAPIAuthConfigurationWithProgrammed(t, ctx, clientNamespaced) cp := deploy.KonnectGatewayControlPlaneWithID(t, ctx, clientNamespaced, apiAuth) - t.Run("adding, patching and deleting KongUpstream", func(t *testing.T) { - const ( - upstreamID = "upstream-12345" - ) + t.Log("Setting up a watch for KongUpstream events") + w := setupWatch[configurationv1alpha1.KongUpstreamList](t, ctx, cl, client.InNamespace(ns.Name)) - t.Log("Setting up a watch for KongUpstream events") - w := setupWatch[configurationv1alpha1.KongUpstreamList](t, ctx, cl, client.InNamespace(ns.Name)) + t.Run("adding, patching and deleting KongUpstream", func(t *testing.T) { + const upstreamID = "upstream-12345" t.Log("Setting up SDK expectations on Upstream creation") sdk.UpstreamsSDK.EXPECT(). @@ -140,4 +138,52 @@ func TestKongUpstream(t *testing.T) { assert.True(c, factory.SDK.UpstreamsSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + 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") + sdk.UpstreamsSDK.EXPECT(). + CreateUpstream( + mock.Anything, + cp.GetKonnectID(), + mock.MatchedBy(func(req sdkkonnectcomp.UpstreamInput) bool { + return req.Algorithm != nil && *req.Algorithm == "round-robin" + }), + ). + Return( + &sdkkonnectops.CreateUpstreamResponse{ + Upstream: &sdkkonnectcomp.Upstream{ + ID: lo.ToPtr(upstreamID), + }, + }, + nil, + ) + + t.Log("Creating a KongUpstream with ControlPlaneRef type=konnectID") + createdUpstream := deploy.KongUpstreamAttachedToCP(t, ctx, clientNamespaced, 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") + watchFor(t, ctx, w, watch.Modified, func(r *configurationv1alpha1.KongUpstream) bool { + if r.GetName() != createdUpstream.GetName() { + return false + } + if r.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + return r.GetKonnectID() == upstreamID && k8sutils.IsProgrammed(r) + }, "KongUpstream didn't get Programmed status condition or didn't get the correct (upstream-12345) Konnect ID assigned") + + t.Log("Checking SDK KongUpstream operations") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.UpstreamsSDK.AssertExpectations(t)) + }, waitTime, tickTime) + }) } diff --git a/test/envtest/konnect_entities_kongvault_test.go b/test/envtest/konnect_entities_kongvault_test.go index d1c340740..da65a79f0 100644 --- a/test/envtest/konnect_entities_kongvault_test.go +++ b/test/envtest/konnect_entities_kongvault_test.go @@ -33,7 +33,7 @@ func TestKongVault(t *testing.T) { cfg, ns := Setup(t, ctx, scheme.Get()) t.Log("Setting up the manager with reconcilers") - mgr, logs := NewManager(t, ctx, cfg, scheme.Get()) + mgr, logs := NewManager(t, ctx, cfg, scheme.Get(), WithKonnectCacheIndices(ctx)) factory := sdkmocks.NewMockSDKFactory(t) sdk := factory.SDK reconcilers := []Reconciler{ @@ -57,7 +57,7 @@ func TestKongVault(t *testing.T) { t.Log("Setting up a watch for KongVault events") vaultWatch := setupWatch[configurationv1alpha1.KongVaultList](t, ctx, cl) - t.Run("Should create, update and delete vault successfully", func(t *testing.T) { + t.Run("should create, update and delete vault successfully", func(t *testing.T) { const ( vaultBackend = "env" vaultPrefix = "env-vault" @@ -118,7 +118,7 @@ func TestKongVault(t *testing.T) { }, waitTime, tickTime) }) - t.Run("Should correctly handle conflict on create", func(t *testing.T) { + t.Run("should correctly handle conflict on create", func(t *testing.T) { const ( vaultBackend = "env-conflict" vaultPrefix = "env-vault-conflict" @@ -182,4 +182,43 @@ func TestKongVault(t *testing.T) { assert.True(c, factory.SDK.VaultSDK.AssertExpectations(t)) }, waitTime, tickTime) }) + + t.Run("should handle konnectID control plane reference", func(t *testing.T) { + const ( + vaultBackend = "env" + vaultPrefix = "env-vault" + vaultRawConfig = `{"prefix":"env_vault"}` + vaultID = "vault-12345" + ) + + t.Log("Setting up mock SDK for vault creation") + sdk.VaultSDK.EXPECT().CreateVault(mock.Anything, cp.GetKonnectStatus().GetKonnectID(), mock.MatchedBy(func(input sdkkonnectcomp.VaultInput) bool { + return input.Name == vaultBackend && input.Prefix == vaultPrefix + })).Return(&sdkkonnectops.CreateVaultResponse{ + Vault: &sdkkonnectcomp.Vault{ + ID: lo.ToPtr(vaultID), + }, + }, nil) + + t.Log("Creating a KongVault with ControlPlaneRef type=konnectID") + vault := deploy.KongVaultAttachedToCP(t, ctx, cl, vaultBackend, vaultPrefix, []byte(vaultRawConfig), cp, + deploy.WithKonnectIDControlPlaneRef(cp), + ) + + t.Log("Waiting for KongVault to be programmed") + watchFor(t, ctx, vaultWatch, watch.Modified, func(v *configurationv1alpha1.KongVault) bool { + if vault.GetName() != v.GetName() { + return false + } + if vault.GetControlPlaneRef().Type != configurationv1alpha1.ControlPlaneRefKonnectID { + return false + } + return v.GetKonnectID() == vaultID && k8sutils.IsProgrammed(v) + }, "KongVault didn't get Programmed status condition or didn't get the correct (vault-12345) Konnect ID assigned") + + t.Log("Waiting for KongVault to be created in the SDK") + require.EventuallyWithT(t, func(c *assert.CollectT) { + assert.True(c, factory.SDK.VaultSDK.AssertExpectations(t)) + }, waitTime, tickTime) + }) } diff --git a/test/helpers/deploy/deploy_resources.go b/test/helpers/deploy/deploy_resources.go index a8e4f1612..6e1c03c19 100644 --- a/test/helpers/deploy/deploy_resources.go +++ b/test/helpers/deploy/deploy_resources.go @@ -2,6 +2,7 @@ package deploy import ( "context" + "fmt" "testing" sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" @@ -24,10 +25,11 @@ const ( TestIDLabel = "konghq.com/test-id" ) -type objOption func(obj client.Object) +// ObjOption is a function that modifies a client.Object. +type ObjOption func(obj client.Object) -// WithAnnotation returns an objOption that sets the given key-value pair as an annotation on the object. -func WithAnnotation(key, value string) objOption { +// WithAnnotation returns an ObjOption that sets the given key-value pair as an annotation on the object. +func WithAnnotation(key, value string) ObjOption { return func(obj client.Object) { annotations := obj.GetAnnotations() if annotations == nil { @@ -38,7 +40,7 @@ func WithAnnotation(key, value string) objOption { } } -// WithTestIDLabel returns an objOption that sets the test ID label on the object. +// WithTestIDLabel returns an ObjOption that sets the test ID label on the object. func WithTestIDLabel(testID string) func(obj client.Object) { return func(obj client.Object) { labels := obj.GetLabels() @@ -50,13 +52,21 @@ func WithTestIDLabel(testID string) func(obj client.Object) { } } -// WithLabels returns an objOption that sets the given key-value pairs as labels on the object. -func WithLabels[ - T client.Object, -](labels map[string]string) func(obj T) { - return func(obj T) { - for k, v := range labels { - obj.GetLabels()[k] = v +// 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 + }) + 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)) + } + + objCPRef := o.GetControlPlaneRef() + *objCPRef = configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectID, + KonnectID: lo.ToPtr(cp.GetKonnectStatus().GetKonnectID()), } } } @@ -67,7 +77,7 @@ func KonnectAPIAuthConfiguration( t *testing.T, ctx context.Context, cl client.Client, - opts ...objOption, + opts ...ObjOption, ) *konnectv1alpha1.KonnectAPIAuthConfiguration { t.Helper() @@ -122,7 +132,7 @@ func KonnectGatewayControlPlane( ctx context.Context, cl client.Client, apiAuth *konnectv1alpha1.KonnectAPIAuthConfiguration, - opts ...objOption, + opts ...ObjOption, ) *konnectv1alpha1.KonnectGatewayControlPlane { t.Helper() @@ -160,7 +170,7 @@ func KonnectGatewayControlPlaneWithID( ctx context.Context, cl client.Client, apiAuth *konnectv1alpha1.KonnectAPIAuthConfiguration, - opts ...objOption, + opts ...ObjOption, ) *konnectv1alpha1.KonnectGatewayControlPlane { t.Helper() @@ -188,7 +198,7 @@ func KongServiceAttachedToCPWithID( ctx context.Context, cl client.Client, cp *konnectv1alpha1.KonnectGatewayControlPlane, - opts ...objOption, + opts ...ObjOption, ) *configurationv1alpha1.KongService { t.Helper() @@ -213,7 +223,7 @@ func KongServiceAttachedToCP( ctx context.Context, cl client.Client, cp *konnectv1alpha1.KonnectGatewayControlPlane, - opts ...objOption, + opts ...ObjOption, ) *configurationv1alpha1.KongService { t.Helper() @@ -252,7 +262,7 @@ func KongRouteAttachedToService( ctx context.Context, cl client.Client, kongService *configurationv1alpha1.KongService, - opts ...objOption, + opts ...ObjOption, ) *configurationv1alpha1.KongRoute { t.Helper() @@ -315,7 +325,7 @@ func KongPluginBinding( ctx context.Context, cl client.Client, kpb *configurationv1alpha1.KongPluginBinding, - opts ...objOption, + opts ...ObjOption, ) *configurationv1alpha1.KongPluginBinding { t.Helper() @@ -484,7 +494,7 @@ func KongCACertificateAttachedToCP( ctx context.Context, cl client.Client, cp *konnectv1alpha1.KonnectGatewayControlPlane, - opts ...objOption, + opts ...ObjOption, ) *configurationv1alpha1.KongCACertificate { t.Helper() @@ -519,7 +529,7 @@ func KongCertificateAttachedToCP( ctx context.Context, cl client.Client, cp *konnectv1alpha1.KonnectGatewayControlPlane, - opts ...objOption, + opts ...ObjOption, ) *configurationv1alpha1.KongCertificate { t.Helper() @@ -555,7 +565,7 @@ func KongUpstreamAttachedToCP( ctx context.Context, cl client.Client, cp *konnectv1alpha1.KonnectGatewayControlPlane, - opts ...objOption, + opts ...ObjOption, ) *configurationv1alpha1.KongUpstream { t.Helper() @@ -588,7 +598,7 @@ func KongTargetAttachedToUpstream( ctx context.Context, cl client.Client, upstream *configurationv1alpha1.KongUpstream, - opts ...objOption, + opts ...ObjOption, ) *configurationv1alpha1.KongTarget { t.Helper() @@ -619,7 +629,7 @@ func KongConsumerAttachedToCP( cl client.Client, username string, cp *konnectv1alpha1.KonnectGatewayControlPlane, - opts ...objOption, + opts ...ObjOption, ) *configurationv1.KongConsumer { t.Helper() @@ -653,7 +663,7 @@ func KongConsumerGroupAttachedToCP( ctx context.Context, cl client.Client, cp *konnectv1alpha1.KonnectGatewayControlPlane, - opts ...objOption, + opts ...ObjOption, ) *configurationv1beta1.KongConsumerGroup { t.Helper() @@ -691,6 +701,7 @@ func KongVaultAttachedToCP( prefix string, rawConfig []byte, cp *konnectv1alpha1.KonnectGatewayControlPlane, + opts ...ObjOption, ) *configurationv1alpha1.KongVault { t.Helper() @@ -714,6 +725,10 @@ func KongVaultAttachedToCP( }, } + for _, opt := range opts { + opt(vault) + } + require.NoError(t, cl.Create(ctx, vault)) logObjectCreate(t, vault) @@ -812,6 +827,7 @@ func KongKeySetAttachedToCP( cl client.Client, name string, cp *konnectv1alpha1.KonnectGatewayControlPlane, + opts ...ObjOption, ) *configurationv1alpha1.KongKeySet { t.Helper() @@ -831,6 +847,11 @@ func KongKeySetAttachedToCP( }, }, } + + for _, opt := range opts { + opt(keySet) + } + require.NoError(t, cl.Create(ctx, keySet)) logObjectCreate(t, keySet) @@ -843,7 +864,7 @@ func KongSNIAttachedToCertificate( ctx context.Context, cl client.Client, cert *configurationv1alpha1.KongCertificate, - opts ...objOption, + opts ...ObjOption, ) *configurationv1alpha1.KongSNI { t.Helper() @@ -877,6 +898,7 @@ func KongDataPlaneClientCertificateAttachedToCP( ctx context.Context, cl client.Client, cp *konnectv1alpha1.KonnectGatewayControlPlane, + opts ...ObjOption, ) *configurationv1alpha1.KongDataPlaneClientCertificate { t.Helper() @@ -896,6 +918,11 @@ func KongDataPlaneClientCertificateAttachedToCP( }, }, } + + for _, opt := range opts { + opt(cert) + } + require.NoError(t, cl.Create(ctx, cert)) logObjectCreate(t, cert) From c6f1739971369127043bb69738eeb26936fad73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Burzy=C5=84ski?= Date: Thu, 16 Jan 2025 15:01:26 +0100 Subject: [PATCH 2/2] bump kcfg and use cpref string method --- controller/konnect/errors.go | 4 ++-- .../konnect/reconciler_certificateref_test.go | 2 +- .../konnect/reconciler_controlplaneref.go | 21 +++---------------- .../reconciler_controlplaneref_test.go | 2 +- .../konnect/reconciler_upstreamref_test.go | 2 +- go.mod | 6 +++--- go.sum | 8 +++---- 7 files changed, 15 insertions(+), 30 deletions(-) diff --git a/controller/konnect/errors.go b/controller/konnect/errors.go index 42d572447..367c6fa1f 100644 --- a/controller/konnect/errors.go +++ b/controller/konnect/errors.go @@ -19,7 +19,7 @@ type ReferencedControlPlaneDoesNotExistError struct { // Error implements the error interface. func (e ReferencedControlPlaneDoesNotExistError) Error() string { return fmt.Sprintf("referenced Control Plane %q does not exist: %v", - controlPlaneRefToString(e.Reference), e.Err, + e.Reference.String(), e.Err, ) } @@ -146,5 +146,5 @@ type ReferencedKongGatewayControlPlaneIsUnsupported struct { } func (e ReferencedKongGatewayControlPlaneIsUnsupported) Error() string { - return fmt.Sprintf("referenced ControlPlaneRef %s is unsupported", controlPlaneRefToString(e.Reference)) + return fmt.Sprintf("referenced ControlPlaneRef %s is unsupported", e.Reference.String()) } diff --git a/controller/konnect/reconciler_certificateref_test.go b/controller/konnect/reconciler_certificateref_test.go index d05c5941e..a1f33e70d 100644 --- a/controller/konnect/reconciler_certificateref_test.go +++ b/controller/konnect/reconciler_certificateref_test.go @@ -322,7 +322,7 @@ func TestHandleCertificateRef(t *testing.T) { }, expectError: true, expectErrorContains: fmt.Sprintf("referenced Control Plane %q does not exist", - testKongCertificateControlPlaneRefNotFound.Spec.ControlPlaneRef.KonnectNamespacedRef.Name, + testKongCertificateControlPlaneRefNotFound.Spec.ControlPlaneRef.String(), ), }, { diff --git a/controller/konnect/reconciler_controlplaneref.go b/controller/konnect/reconciler_controlplaneref.go index a2407332c..5c6032294 100644 --- a/controller/konnect/reconciler_controlplaneref.go +++ b/controller/konnect/reconciler_controlplaneref.go @@ -5,7 +5,6 @@ import ( "fmt" sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" - "github.com/samber/lo" "github.com/samber/mo" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -134,7 +133,7 @@ func handleControlPlaneRef[T constraints.SupportedKonnectEntityType, TEnt constr konnectv1alpha1.ControlPlaneRefValidConditionType, metav1.ConditionFalse, konnectv1alpha1.ControlPlaneRefReasonInvalid, - fmt.Sprintf("Attaching to ControlPlane %s with cluster type %s is not supported", controlPlaneRefToString(cpRef), *cp.Spec.ClusterType), + fmt.Sprintf("Attaching to ControlPlane %s with cluster type %s is not supported", cpRef.String(), *cp.Spec.ClusterType), ); errStatus != nil || !res.IsZero() { return res, errStatus } @@ -148,7 +147,7 @@ func handleControlPlaneRef[T constraints.SupportedKonnectEntityType, TEnt constr konnectv1alpha1.ControlPlaneRefValidConditionType, metav1.ConditionFalse, konnectv1alpha1.ControlPlaneRefReasonInvalid, - fmt.Sprintf("Referenced ControlPlane %s is not programmed yet", controlPlaneRefToString(cpRef)), + fmt.Sprintf("Referenced ControlPlane %s is not programmed yet", cpRef.String()), ); errStatus != nil || !res.IsZero() { return res, errStatus } @@ -197,7 +196,7 @@ func handleControlPlaneRef[T constraints.SupportedKonnectEntityType, TEnt constr konnectv1alpha1.ControlPlaneRefValidConditionType, metav1.ConditionTrue, konnectv1alpha1.ControlPlaneRefReasonValid, - fmt.Sprintf("Referenced ControlPlane %s is programmed", controlPlaneRefToString(cpRef)), + fmt.Sprintf("Referenced ControlPlane %s is programmed", cpRef.String()), ); errStatus != nil || !res.IsZero() { return res, errStatus } @@ -211,17 +210,3 @@ func conditionMessageReferenceKonnectAPIAuthConfigurationInvalid(apiAuthRef type func conditionMessageReferenceKonnectAPIAuthConfigurationValid(apiAuthRef types.NamespacedName) string { return fmt.Sprintf("referenced KonnectAPIAuthConfiguration %s is valid", apiAuthRef) } - -func controlPlaneRefToString(cpRef configurationv1alpha1.ControlPlaneRef) string { - switch cpRef.Type { - case configurationv1alpha1.ControlPlaneRefKonnectID: - return lo.FromPtrOr(cpRef.KonnectID, "nil") - case configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef: - if cpRef.KonnectNamespacedRef.Namespace == "" { - return cpRef.KonnectNamespacedRef.Name - } - return cpRef.KonnectNamespacedRef.Namespace + "/" + cpRef.KonnectNamespacedRef.Name - default: - return fmt.Sprintf("unknown type %q", cpRef.Type) - } -} diff --git a/controller/konnect/reconciler_controlplaneref_test.go b/controller/konnect/reconciler_controlplaneref_test.go index 8a9d1b9cc..05ce41e92 100644 --- a/controller/konnect/reconciler_controlplaneref_test.go +++ b/controller/konnect/reconciler_controlplaneref_test.go @@ -197,7 +197,7 @@ func TestHandleControlPlaneRef(t *testing.T) { ent: svcCPRefNotFound, expectResult: ctrl.Result{}, expectError: true, - expectErrorContains: `referenced Control Plane "cp-not-found" does not exist`, + expectErrorContains: `referenced Control Plane "" does not exist`, updatedEntAssertions: []func(svc *configurationv1alpha1.KongService) (ok bool, message string){ func(svc *configurationv1alpha1.KongService) (bool, string) { return lo.ContainsBy(svc.Status.Conditions, func(c metav1.Condition) bool { diff --git a/controller/konnect/reconciler_upstreamref_test.go b/controller/konnect/reconciler_upstreamref_test.go index da8db5754..60c859327 100644 --- a/controller/konnect/reconciler_upstreamref_test.go +++ b/controller/konnect/reconciler_upstreamref_test.go @@ -350,7 +350,7 @@ func TestHandleUpstreamRef(t *testing.T) { objects: []client.Object{testKongUpstreamControlPlaneRefNotFound}, expectError: true, expectErrorContains: fmt.Sprintf(`referenced Control Plane %q does not exist`, - testKongUpstreamControlPlaneRefNotFound.Spec.ControlPlaneRef.KonnectNamespacedRef.Name, + testKongUpstreamControlPlaneRefNotFound.Spec.ControlPlaneRef.String(), ), }, { diff --git a/go.mod b/go.mod index 81136c327..630b15c1b 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.1 - github.com/kong/kubernetes-configuration v1.0.6-0.20250113094037-f1f2a6542010 + github.com/kong/kubernetes-configuration v1.0.6 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 @@ -34,7 +34,7 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/mod v0.22.0 k8s.io/api v0.32.1 - k8s.io/apiextensions-apiserver v0.32.0 + k8s.io/apiextensions-apiserver v0.32.1 k8s.io/apimachinery v0.32.1 k8s.io/client-go v0.32.1 k8s.io/kubernetes v1.32.1 @@ -155,7 +155,7 @@ require ( github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kong/go-kong v0.62.0 // indirect + github.com/kong/go-kong v0.63.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect diff --git a/go.sum b/go.sum index beb3db37d..512d02003 100644 --- a/go.sum +++ b/go.sum @@ -303,10 +303,10 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/kong/go-kong v0.62.0 h1:Mu0ANZ0xgLQrM1hLoOAvfF+8qsbIVLfixzESXtZRFMI= -github.com/kong/go-kong v0.62.0/go.mod h1:ma9GWnhkxtrXZlLFfED955HjVzmUojYEHet3lm+PDik= -github.com/kong/kubernetes-configuration v1.0.6-0.20250113094037-f1f2a6542010 h1:qN9xDc/y2RIu9s1X1SZe5kypvI1gnrxDc01Qj4GrgeA= -github.com/kong/kubernetes-configuration v1.0.6-0.20250113094037-f1f2a6542010/go.mod h1:lN4oTvW3S8zFh4oYTFUMj6rXwOV1gz1fwEXlIjtwppA= +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.0.6 h1:vAYPV/j4utNpFEoXZxjwhQmczcamcodq1DlC3QnXBkg= +github.com/kong/kubernetes-configuration v1.0.6/go.mod h1:LNmmSe8xV0MeGCxYim/0ZuZhOLNMvsWIQsfKRfqZdPc= 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= pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy