From 9b10f6c3778a02bba65ae39a3f4403d0d7e33767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Ma=C5=82ek?= Date: Tue, 20 Aug 2024 10:15:31 +0200 Subject: [PATCH 1/6] refactor(konnect): add SDK interface types for each entity type --- controller/konnect/ops.go | 24 ++++++++++++------------ controller/konnect/ops_controlplane.go | 20 ++++++++++++++------ controller/konnect/ops_kongconsumer.go | 20 +++++++++++++------- controller/konnect/ops_kongroute.go | 21 +++++++++++++++------ controller/konnect/ops_kongservice.go | 20 +++++++++++++------- 5 files changed, 67 insertions(+), 38 deletions(-) diff --git a/controller/konnect/ops.go b/controller/konnect/ops.go index c48d15a5f..aadc33b4e 100644 --- a/controller/konnect/ops.go +++ b/controller/konnect/ops.go @@ -47,13 +47,13 @@ func Create[ switch ent := any(e).(type) { case *konnectv1alpha1.KonnectControlPlane: - return e, createControlPlane(ctx, sdk, ent) + return e, createControlPlane(ctx, sdk.ControlPlanes, ent) case *configurationv1alpha1.KongService: - return e, createService(ctx, sdk, ent) + return e, createService(ctx, sdk.Services, ent) case *configurationv1alpha1.KongRoute: - return e, createRoute(ctx, sdk, ent) + return e, createRoute(ctx, sdk.Routes, ent) case *configurationv1.KongConsumer: - return e, createConsumer(ctx, sdk, ent) + return e, createConsumer(ctx, sdk.Consumers, ent) case *configurationv1beta1.KongConsumerGroup: return e, createConsumerGroup(ctx, sdk, ent) @@ -83,13 +83,13 @@ func Delete[ switch ent := any(e).(type) { case *konnectv1alpha1.KonnectControlPlane: - return deleteControlPlane(ctx, sdk, ent) + return deleteControlPlane(ctx, sdk.ControlPlanes, ent) case *configurationv1alpha1.KongService: - return deleteService(ctx, sdk, ent) + return deleteService(ctx, sdk.Services, ent) case *configurationv1alpha1.KongRoute: - return deleteRoute(ctx, sdk, ent) + return deleteRoute(ctx, sdk.Routes, ent) case *configurationv1.KongConsumer: - return deleteConsumer(ctx, sdk, ent) + return deleteConsumer(ctx, sdk.Consumers, ent) case *configurationv1beta1.KongConsumerGroup: return deleteConsumerGroup(ctx, sdk, ent) @@ -144,13 +144,13 @@ func Update[ switch ent := any(e).(type) { case *konnectv1alpha1.KonnectControlPlane: - return ctrl.Result{}, updateControlPlane(ctx, sdk, ent) + return ctrl.Result{}, updateControlPlane(ctx, sdk.ControlPlanes, ent) case *configurationv1alpha1.KongService: - return ctrl.Result{}, updateService(ctx, sdk, cl, ent) + return ctrl.Result{}, updateService(ctx, sdk.Services, cl, ent) case *configurationv1alpha1.KongRoute: - return ctrl.Result{}, updateRoute(ctx, sdk, cl, ent) + return ctrl.Result{}, updateRoute(ctx, sdk.Routes, cl, ent) case *configurationv1.KongConsumer: - return ctrl.Result{}, updateConsumer(ctx, sdk, cl, ent) + return ctrl.Result{}, updateConsumer(ctx, sdk.Consumers, cl, ent) case *configurationv1beta1.KongConsumerGroup: return ctrl.Result{}, updateConsumerGroup(ctx, sdk, cl, ent) diff --git a/controller/konnect/ops_controlplane.go b/controller/konnect/ops_controlplane.go index 40f53d165..5f951017d 100644 --- a/controller/konnect/ops_controlplane.go +++ b/controller/konnect/ops_controlplane.go @@ -6,6 +6,7 @@ import ( sdkkonnectgo "github.com/Kong/sdk-konnect-go" "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" @@ -15,12 +16,19 @@ import ( konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" ) +// ControlPlaneSDK is the interface for the Konnect ControlPlane SDK. +type ControlPlaneSDK interface { + CreateControlPlane(ctx context.Context, req components.CreateControlPlaneRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateControlPlaneResponse, error) + DeleteControlPlane(ctx context.Context, id string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteControlPlaneResponse, error) + UpdateControlPlane(ctx context.Context, id string, req components.UpdateControlPlaneRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpdateControlPlaneResponse, error) +} + func createControlPlane( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ControlPlaneSDK, cp *konnectv1alpha1.KonnectControlPlane, ) error { - resp, err := sdk.ControlPlanes.CreateControlPlane(ctx, cp.Spec.CreateControlPlaneRequest) + resp, err := sdk.CreateControlPlane(ctx, cp.Spec.CreateControlPlaneRequest) // TODO: handle already exists // Can't adopt it as it will cause conflicts between the controller // that created that entity and already manages it, hm @@ -58,11 +66,11 @@ func createControlPlane( // It is assumed that the Konnect ControlPlane has a Konnect ID. func deleteControlPlane( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ControlPlaneSDK, cp *konnectv1alpha1.KonnectControlPlane, ) error { id := cp.GetKonnectStatus().GetKonnectID() - _, err := sdk.ControlPlanes.DeleteControlPlane(ctx, id) + _, err := sdk.DeleteControlPlane(ctx, id) if errWrap := wrapErrIfKonnectOpFailed(err, DeleteOp, cp); errWrap != nil { var sdkNotFoundError *sdkerrors.NotFoundError if errors.As(err, &sdkNotFoundError) { @@ -93,7 +101,7 @@ func deleteControlPlane( // It returns an error if the operation fails. func updateControlPlane( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ControlPlaneSDK, cp *konnectv1alpha1.KonnectControlPlane, ) error { id := cp.GetKonnectStatus().GetKonnectID() @@ -105,7 +113,7 @@ func updateControlPlane( Labels: cp.Spec.Labels, } - resp, err := sdk.ControlPlanes.UpdateControlPlane(ctx, id, req) + resp, err := sdk.UpdateControlPlane(ctx, id, req) var sdkError *sdkerrors.NotFoundError if errors.As(err, &sdkError) { ctrllog.FromContext(ctx). diff --git a/controller/konnect/ops_kongconsumer.go b/controller/konnect/ops_kongconsumer.go index b036b527c..d92429ed9 100644 --- a/controller/konnect/ops_kongconsumer.go +++ b/controller/konnect/ops_kongconsumer.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" - sdkkonnectgo "github.com/Kong/sdk-konnect-go" sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" "github.com/Kong/sdk-konnect-go/models/sdkerrors" @@ -21,16 +20,23 @@ import ( "github.com/kong/kubernetes-configuration/pkg/metadata" ) +// ConsumersSDK is the interface for the Konnect Consumers SDK. +type ConsumersSDK interface { + CreateConsumer(ctx context.Context, controlPlaneID string, consumerInput sdkkonnectgocomp.ConsumerInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateConsumerResponse, error) + UpsertConsumer(ctx context.Context, upsertConsumerRequest sdkkonnectgoops.UpsertConsumerRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertConsumerResponse, error) + DeleteConsumer(ctx context.Context, controlPlaneID string, consumerID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteConsumerResponse, error) +} + func createConsumer( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ConsumersSDK, consumer *configurationv1.KongConsumer, ) error { if consumer.GetControlPlaneID() == "" { return fmt.Errorf("can't create %T %s without a Konnect ControlPlane ID", consumer, client.ObjectKeyFromObject(consumer)) } - resp, err := sdk.Consumers.CreateConsumer(ctx, + resp, err := sdk.CreateConsumer(ctx, consumer.Status.Konnect.ControlPlaneID, kongConsumerToSDKConsumerInput(consumer), ) @@ -72,7 +78,7 @@ func createConsumer( // It returns an error if the KongConsumer does not have a ControlPlaneRef. func updateConsumer( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ConsumersSDK, cl client.Client, consumer *configurationv1.KongConsumer, ) error { @@ -100,7 +106,7 @@ func updateConsumer( ) } - resp, err := sdk.Consumers.UpsertConsumer(ctx, + resp, err := sdk.UpsertConsumer(ctx, sdkkonnectgoops.UpsertConsumerRequest{ ControlPlaneID: cp.Status.ID, ConsumerID: consumer.GetKonnectStatus().GetKonnectID(), @@ -146,11 +152,11 @@ func updateConsumer( // It returns an error if the operation fails. func deleteConsumer( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ConsumersSDK, consumer *configurationv1.KongConsumer, ) error { id := consumer.Status.Konnect.GetKonnectID() - _, err := sdk.Consumers.DeleteConsumer(ctx, consumer.Status.Konnect.ControlPlaneID, id) + _, err := sdk.DeleteConsumer(ctx, consumer.Status.Konnect.ControlPlaneID, id) if errWrapped := wrapErrIfKonnectOpFailed(err, DeleteOp, consumer); errWrapped != nil { // Consumer delete operation returns an SDKError instead of a NotFoundError. var sdkError *sdkerrors.SDKError diff --git a/controller/konnect/ops_kongroute.go b/controller/konnect/ops_kongroute.go index 85d39feb2..a830e36d2 100644 --- a/controller/konnect/ops_kongroute.go +++ b/controller/konnect/ops_kongroute.go @@ -20,16 +20,23 @@ import ( konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" ) +// RoutesSDK is the interface for the Konnect Routes SDK. +type RoutesSDK interface { + CreateRoute(ctx context.Context, controlPlaneID string, route sdkkonnectgocomp.RouteInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateRouteResponse, error) + UpsertRoute(ctx context.Context, req sdkkonnectgoops.UpsertRouteRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertRouteResponse, error) + DeleteRoute(ctx context.Context, controlPlaneID, routeID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteRouteResponse, error) +} + func createRoute( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk RoutesSDK, route *configurationv1alpha1.KongRoute, ) error { if route.GetControlPlaneID() == "" { return fmt.Errorf("can't create %T %s without a Konnect ControlPlane ID", route, client.ObjectKeyFromObject(route)) } - resp, err := sdk.Routes.CreateRoute(ctx, route.Status.Konnect.ControlPlaneID, kongRouteToSDKRouteInput(route)) + resp, err := sdk.CreateRoute(ctx, route.Status.Konnect.ControlPlaneID, kongRouteToSDKRouteInput(route)) // TODO: handle already exists // Can't adopt it as it will cause conflicts between the controller @@ -69,7 +76,8 @@ func createRoute( // if the operation fails. func updateRoute( ctx context.Context, - sdk *sdkkonnectgo.SDK, + // sdk *sdkkonnectgo.SDK, + sdk RoutesSDK, cl client.Client, route *configurationv1alpha1.KongRoute, ) error { @@ -114,7 +122,8 @@ func updateRoute( ) } - resp, err := sdk.Routes.UpsertRoute(ctx, sdkkonnectgoops.UpsertRouteRequest{ + resp, err := sdk.UpsertRoute(ctx, sdkkonnectgoops.UpsertRouteRequest{ + // resp, err := sdk.UpsertRoute(ctx, sdkkonnectgoops.UpsertRouteRequest{ ControlPlaneID: cp.Status.ID, RouteID: route.Status.Konnect.ID, Route: kongRouteToSDKRouteInput(route), @@ -159,11 +168,11 @@ func updateRoute( // It returns an error if the operation fails. func deleteRoute( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk RoutesSDK, route *configurationv1alpha1.KongRoute, ) error { id := route.GetKonnectStatus().GetKonnectID() - _, err := sdk.Routes.DeleteRoute(ctx, route.Status.Konnect.ControlPlaneID, id) + _, err := sdk.DeleteRoute(ctx, route.Status.Konnect.ControlPlaneID, id) if errWrapped := wrapErrIfKonnectOpFailed(err, DeleteOp, route); errWrapped != nil { // Service delete operation returns an SDKError instead of a NotFoundError. var sdkError *sdkerrors.SDKError diff --git a/controller/konnect/ops_kongservice.go b/controller/konnect/ops_kongservice.go index 635b31dcc..f42aa60b2 100644 --- a/controller/konnect/ops_kongservice.go +++ b/controller/konnect/ops_kongservice.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" - sdkkonnectgo "github.com/Kong/sdk-konnect-go" sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" "github.com/Kong/sdk-konnect-go/models/sdkerrors" @@ -20,16 +19,23 @@ import ( konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" ) +// ServicesSDK is the interface for the Konnect Service SDK. +type ServicesSDK interface { + CreateService(ctx context.Context, controlPlaneID string, service sdkkonnectgocomp.ServiceInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateServiceResponse, error) + UpsertService(ctx context.Context, req sdkkonnectgoops.UpsertServiceRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertServiceResponse, error) + DeleteService(ctx context.Context, controlPlaneID, serviceID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteServiceResponse, error) +} + func createService( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ServicesSDK, svc *configurationv1alpha1.KongService, ) error { if svc.GetControlPlaneID() == "" { return fmt.Errorf("can't create %T %s without a Konnect ControlPlane ID", svc, client.ObjectKeyFromObject(svc)) } - resp, err := sdk.Services.CreateService(ctx, + resp, err := sdk.CreateService(ctx, svc.Status.Konnect.ControlPlaneID, kongServiceToSDKServiceInput(svc), ) @@ -72,7 +78,7 @@ func createService( // if the operation fails. func updateService( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ServicesSDK, cl client.Client, svc *configurationv1alpha1.KongService, ) error { @@ -100,7 +106,7 @@ func updateService( ) } - resp, err := sdk.Services.UpsertService(ctx, + resp, err := sdk.UpsertService(ctx, sdkkonnectgoops.UpsertServiceRequest{ ControlPlaneID: cp.Status.ID, ServiceID: svc.GetKonnectStatus().GetKonnectID(), @@ -146,11 +152,11 @@ func updateService( // It returns an error if the operation fails. func deleteService( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ServicesSDK, svc *configurationv1alpha1.KongService, ) error { id := svc.GetKonnectStatus().GetKonnectID() - _, err := sdk.Services.DeleteService(ctx, svc.Status.Konnect.ControlPlaneID, id) + _, err := sdk.DeleteService(ctx, svc.Status.Konnect.ControlPlaneID, id) if errWrapped := wrapErrIfKonnectOpFailed(err, DeleteOp, svc); errWrapped != nil { // Service delete operation returns an SDKError instead of a NotFoundError. var sdkError *sdkerrors.SDKError From 39da364cec505c2544ab3bb4f876f64f059faec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Ma=C5=82ek?= Date: Tue, 20 Aug 2024 14:45:14 +0200 Subject: [PATCH 2/6] tests: add mockery for Konnect SDK --- .gitattributes | 1 + .golangci.yaml | 7 + .mockery.yaml | 14 + .tools_versions.yaml | 2 + Makefile | 14 +- .../konnect/{ => conditions}/conditions.go | 2 +- .../konnect/{ => constraints}/constraints.go | 6 +- .../konnect/constraints/entitytypename.go | 7 + controller/konnect/entitytypename.go | 6 - controller/konnect/errors.go | 19 - controller/konnect/ops/controlplane.go | 15 + .../konnect/ops/controlplanesdk_mock_test.go | 263 ++++++++++ controller/konnect/ops/errors.go | 26 + controller/konnect/ops/kongconsumer.go | 15 + controller/konnect/ops/kongconsumergroup.go | 15 + controller/konnect/ops/kongroute.go | 15 + controller/konnect/ops/kongservice.go | 15 + controller/konnect/{ => ops}/ops.go | 45 +- .../konnect/{ => ops}/ops_controlplane.go | 37 +- .../konnect/ops/ops_controlplane_test.go | 450 ++++++++++++++++++ .../konnect/{ => ops}/ops_kongconsumer.go | 26 +- .../{ => ops}/ops_kongconsumergroup.go | 32 +- controller/konnect/{ => ops}/ops_kongroute.go | 26 +- .../konnect/{ => ops}/ops_kongservice.go | 26 +- controller/konnect/reconciler_generic.go | 126 ++--- controller/konnect/reconciler_generic_test.go | 5 +- .../konnect/reconciler_konnectapiauth.go | 21 +- controller/konnect/watch.go | 6 +- controller/konnect/watch_test.go | 6 +- go.mod | 1 + 30 files changed, 1035 insertions(+), 214 deletions(-) create mode 100644 .mockery.yaml rename controller/konnect/{ => conditions}/conditions.go (99%) rename controller/konnect/{ => constraints}/constraints.go (91%) create mode 100644 controller/konnect/constraints/entitytypename.go delete mode 100644 controller/konnect/entitytypename.go create mode 100644 controller/konnect/ops/controlplane.go create mode 100644 controller/konnect/ops/controlplanesdk_mock_test.go create mode 100644 controller/konnect/ops/errors.go create mode 100644 controller/konnect/ops/kongconsumer.go create mode 100644 controller/konnect/ops/kongconsumergroup.go create mode 100644 controller/konnect/ops/kongroute.go create mode 100644 controller/konnect/ops/kongservice.go rename controller/konnect/{ => ops}/ops.go (83%) rename controller/konnect/{ => ops}/ops_controlplane.go (74%) create mode 100644 controller/konnect/ops/ops_controlplane_test.go rename controller/konnect/{ => ops}/ops_kongconsumer.go (83%) rename controller/konnect/{ => ops}/ops_kongconsumergroup.go (86%) rename controller/konnect/{ => ops}/ops_kongroute.go (87%) rename controller/konnect/{ => ops}/ops_kongservice.go (85%) diff --git a/.gitattributes b/.gitattributes index 8f1c1fee6..5a81cdb4f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ **/zz_generated*.go linguist-generated=true pkg/clientset/** linguist-generated=true +controller/konnect/ops/*_mock_test.go linguist-generated=true diff --git a/.golangci.yaml b/.golangci.yaml index c99fe7bc8..b5d4679ca 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -62,6 +62,13 @@ linters-settings: - pkg: github.com/kong/gateway-operator/internal/types alias: gwtypes + + - pkg: github.com/Kong/sdk-konnect-go/models/components + alias: sdkkonnectgocomp + - pkg: github.com/Kong/sdk-konnect-go/models/operations + alias: sdkkonnectgoops + - pkg: github.com/Kong/sdk-konnect-go/models/sdkerrors + alias: sdkkonnectgoerrs revive: rules: - name: errorf diff --git a/.mockery.yaml b/.mockery.yaml new file mode 100644 index 000000000..8497c5f34 --- /dev/null +++ b/.mockery.yaml @@ -0,0 +1,14 @@ +quiet: False +inpackage: True +disable-version-string: True +with-expecter: True + +filename: "{{.InterfaceName | lower}}_mock_test.go" +dir: "{{.InterfaceDir}}" +mockname: "Mock{{.InterfaceName}}" +outpkg: "{{.PackageName}}" + +packages: + github.com/kong/gateway-operator/controller/konnect/ops: + interfaces: + ControlPlaneSDK: diff --git a/.tools_versions.yaml b/.tools_versions.yaml index 2830b3e66..5b62ad971 100644 --- a/.tools_versions.yaml +++ b/.tools_versions.yaml @@ -14,3 +14,5 @@ dlv: "1.23.0" gotestsum: "1.12.0" # renovate: datasource=github-releases depName=elastic/crd-ref-docs crd-ref-docs: "0.1.0" +# renovate: datasource=github-releases depName=vektra/mockery +mockery: "2.44.2" diff --git a/Makefile b/Makefile index 5d165fd39..89c255a59 100644 --- a/Makefile +++ b/Makefile @@ -126,6 +126,13 @@ skaffold: mise yq ## Download skaffold locally if necessary. @$(MISE) plugin install --yes -q skaffold @$(MISE) install -q skaffold@$(SKAFFOLD_VERSION) +MOCKERY_VERSION = $(shell $(YQ) -r '.mockery' < $(TOOLS_VERSIONS_FILE)) +MOCKERY = $(PROJECT_DIR)/bin/installs/mockery/$(MOCKERY_VERSION)/bin/mockery +.PHONY: mockery +mockery: mise yq ## Download mockery locally if necessary. + @$(MISE) plugin install --yes -q mockery https://github.com/cabify/asdf-mockery.git + @$(MISE) install -q mockery@$(MOCKERY_VERSION) + # ------------------------------------------------------------------------------ # Build # ------------------------------------------------------------------------------ @@ -191,7 +198,7 @@ verify.generators: verify.repo generate verify.diff API_DIR ?= api .PHONY: generate -generate: generate.api generate.clientsets generate.rbacs generate.gateway-api-urls generate.docs generate.k8sio-gomod-replace generate.testcases-registration generate.kic-webhook-config +generate: generate.api generate.clientsets generate.rbacs generate.gateway-api-urls generate.docs generate.k8sio-gomod-replace generate.testcases-registration generate.kic-webhook-config generate.mocks .PHONY: generate.api generate.api: controller-gen @@ -388,6 +395,11 @@ test.conformance: test.samples: kustomize find ./config/samples -not -name "kustomization.*" -type f | sort | xargs -I{} bash -c "kubectl apply -f {}; kubectl delete -f {}" +# https://github.com/vektra/mockery/issues/803#issuecomment-2287198024 +.PHONY: generate.mocks +generate.mocks: mockery + GODEBUG=gotypesalias=0 $(MOCKERY) + # ------------------------------------------------------------------------------ # Gateway API # ------------------------------------------------------------------------------ diff --git a/controller/konnect/conditions.go b/controller/konnect/conditions/conditions.go similarity index 99% rename from controller/konnect/conditions.go rename to controller/konnect/conditions/conditions.go index 0a0e1e226..bce9d594c 100644 --- a/controller/konnect/conditions.go +++ b/controller/konnect/conditions/conditions.go @@ -1,4 +1,4 @@ -package konnect +package conditions // TODO(pmalek): move this to Konnect API directory so that it's part of the API contract. // https://github.com/Kong/kubernetes-configuration/issues/14 diff --git a/controller/konnect/constraints.go b/controller/konnect/constraints/constraints.go similarity index 91% rename from controller/konnect/constraints.go rename to controller/konnect/constraints/constraints.go index 3d8e86be4..7978beff9 100644 --- a/controller/konnect/constraints.go +++ b/controller/konnect/constraints/constraints.go @@ -1,4 +1,4 @@ -package konnect +package constraints import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -24,9 +24,9 @@ type SupportedKonnectEntityType interface { } // EntityType is an interface that all Konnect entity types must implement. -// Separating this from SupportedKonnectEntityType allows us to use EntityType +// Separating this from constraints.SupportedKonnectEntityType allows us to use EntityType // where client.Object is required, since it embeds client.Object and uses pointer -// to refer to the SupportedKonnectEntityType. +// to refer to the constraints.SupportedKonnectEntityType. type EntityType[T SupportedKonnectEntityType] interface { *T // Kubernetes Object methods diff --git a/controller/konnect/constraints/entitytypename.go b/controller/konnect/constraints/entitytypename.go new file mode 100644 index 000000000..4dfc22b33 --- /dev/null +++ b/controller/konnect/constraints/entitytypename.go @@ -0,0 +1,7 @@ +package constraints + +// EntityTypeName returns the name of the entity type. +func EntityTypeName[T SupportedKonnectEntityType]() string { + var e T + return e.GetTypeName() +} diff --git a/controller/konnect/entitytypename.go b/controller/konnect/entitytypename.go deleted file mode 100644 index a75f73ff7..000000000 --- a/controller/konnect/entitytypename.go +++ /dev/null @@ -1,6 +0,0 @@ -package konnect - -func entityTypeName[T SupportedKonnectEntityType]() string { - var e T - return e.GetTypeName() -} diff --git a/controller/konnect/errors.go b/controller/konnect/errors.go index a1d8908a4..d37019152 100644 --- a/controller/konnect/errors.go +++ b/controller/konnect/errors.go @@ -6,25 +6,6 @@ import ( "k8s.io/apimachinery/pkg/types" ) -// FailedKonnectOpError is an error type that is returned when an operation against -// Konnect API fails. -type FailedKonnectOpError[T SupportedKonnectEntityType] struct { - Op Op - Err error -} - -// Error implements the error interface. -func (e FailedKonnectOpError[T]) Error() string { - return fmt.Sprintf("failed to %s %s on Konnect: %v", - e.Op, entityTypeName[T](), e.Err, - ) -} - -// Unwrap returns the underlying error. -func (e FailedKonnectOpError[T]) Unwrap() error { - return e.Err -} - // ReferencedControlPlaneDoesNotExistError is an error type that is returned when // a Konnect entity references a KonnectControlPlane that does not exist. type ReferencedControlPlaneDoesNotExistError struct { diff --git a/controller/konnect/ops/controlplane.go b/controller/konnect/ops/controlplane.go new file mode 100644 index 000000000..2b58dfe50 --- /dev/null +++ b/controller/konnect/ops/controlplane.go @@ -0,0 +1,15 @@ +package ops + +import ( + "context" + + sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" +) + +// ControlPlaneSDK is the interface for the Konnect ControlPlaneSDK SDK. +type ControlPlaneSDK interface { + CreateControlPlane(ctx context.Context, req sdkkonnectgocomp.CreateControlPlaneRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateControlPlaneResponse, error) + DeleteControlPlane(ctx context.Context, id string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteControlPlaneResponse, error) + UpdateControlPlane(ctx context.Context, id string, req sdkkonnectgocomp.UpdateControlPlaneRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpdateControlPlaneResponse, error) +} diff --git a/controller/konnect/ops/controlplanesdk_mock_test.go b/controller/konnect/ops/controlplanesdk_mock_test.go new file mode 100644 index 000000000..006ab5a77 --- /dev/null +++ b/controller/konnect/ops/controlplanesdk_mock_test.go @@ -0,0 +1,263 @@ +// Code generated by mockery. DO NOT EDIT. + +package ops + +import ( + context "context" + + components "github.com/Kong/sdk-konnect-go/models/components" + + mock "github.com/stretchr/testify/mock" + + operations "github.com/Kong/sdk-konnect-go/models/operations" +) + +// MockControlPlaneSDK is an autogenerated mock type for the ControlPlaneSDK type +type MockControlPlaneSDK struct { + mock.Mock +} + +type MockControlPlaneSDK_Expecter struct { + mock *mock.Mock +} + +func (_m *MockControlPlaneSDK) EXPECT() *MockControlPlaneSDK_Expecter { + return &MockControlPlaneSDK_Expecter{mock: &_m.Mock} +} + +// CreateControlPlane provides a mock function with given fields: ctx, req, opts +func (_m *MockControlPlaneSDK) CreateControlPlane(ctx context.Context, req components.CreateControlPlaneRequest, opts ...operations.Option) (*operations.CreateControlPlaneResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, req) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for CreateControlPlane") + } + + var r0 *operations.CreateControlPlaneResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, components.CreateControlPlaneRequest, ...operations.Option) (*operations.CreateControlPlaneResponse, error)); ok { + return rf(ctx, req, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, components.CreateControlPlaneRequest, ...operations.Option) *operations.CreateControlPlaneResponse); ok { + r0 = rf(ctx, req, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*operations.CreateControlPlaneResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, components.CreateControlPlaneRequest, ...operations.Option) error); ok { + r1 = rf(ctx, req, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockControlPlaneSDK_CreateControlPlane_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateControlPlane' +type MockControlPlaneSDK_CreateControlPlane_Call struct { + *mock.Call +} + +// CreateControlPlane is a helper method to define mock.On call +// - ctx context.Context +// - req components.CreateControlPlaneRequest +// - opts ...operations.Option +func (_e *MockControlPlaneSDK_Expecter) CreateControlPlane(ctx interface{}, req interface{}, opts ...interface{}) *MockControlPlaneSDK_CreateControlPlane_Call { + return &MockControlPlaneSDK_CreateControlPlane_Call{Call: _e.mock.On("CreateControlPlane", + append([]interface{}{ctx, req}, opts...)...)} +} + +func (_c *MockControlPlaneSDK_CreateControlPlane_Call) Run(run func(ctx context.Context, req components.CreateControlPlaneRequest, opts ...operations.Option)) *MockControlPlaneSDK_CreateControlPlane_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]operations.Option, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(operations.Option) + } + } + run(args[0].(context.Context), args[1].(components.CreateControlPlaneRequest), variadicArgs...) + }) + return _c +} + +func (_c *MockControlPlaneSDK_CreateControlPlane_Call) Return(_a0 *operations.CreateControlPlaneResponse, _a1 error) *MockControlPlaneSDK_CreateControlPlane_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockControlPlaneSDK_CreateControlPlane_Call) RunAndReturn(run func(context.Context, components.CreateControlPlaneRequest, ...operations.Option) (*operations.CreateControlPlaneResponse, error)) *MockControlPlaneSDK_CreateControlPlane_Call { + _c.Call.Return(run) + return _c +} + +// DeleteControlPlane provides a mock function with given fields: ctx, id, opts +func (_m *MockControlPlaneSDK) DeleteControlPlane(ctx context.Context, id string, opts ...operations.Option) (*operations.DeleteControlPlaneResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, id) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for DeleteControlPlane") + } + + var r0 *operations.DeleteControlPlaneResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, ...operations.Option) (*operations.DeleteControlPlaneResponse, error)); ok { + return rf(ctx, id, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, ...operations.Option) *operations.DeleteControlPlaneResponse); ok { + r0 = rf(ctx, id, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*operations.DeleteControlPlaneResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, ...operations.Option) error); ok { + r1 = rf(ctx, id, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockControlPlaneSDK_DeleteControlPlane_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteControlPlane' +type MockControlPlaneSDK_DeleteControlPlane_Call struct { + *mock.Call +} + +// DeleteControlPlane is a helper method to define mock.On call +// - ctx context.Context +// - id string +// - opts ...operations.Option +func (_e *MockControlPlaneSDK_Expecter) DeleteControlPlane(ctx interface{}, id interface{}, opts ...interface{}) *MockControlPlaneSDK_DeleteControlPlane_Call { + return &MockControlPlaneSDK_DeleteControlPlane_Call{Call: _e.mock.On("DeleteControlPlane", + append([]interface{}{ctx, id}, opts...)...)} +} + +func (_c *MockControlPlaneSDK_DeleteControlPlane_Call) Run(run func(ctx context.Context, id string, opts ...operations.Option)) *MockControlPlaneSDK_DeleteControlPlane_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]operations.Option, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(operations.Option) + } + } + run(args[0].(context.Context), args[1].(string), variadicArgs...) + }) + return _c +} + +func (_c *MockControlPlaneSDK_DeleteControlPlane_Call) Return(_a0 *operations.DeleteControlPlaneResponse, _a1 error) *MockControlPlaneSDK_DeleteControlPlane_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockControlPlaneSDK_DeleteControlPlane_Call) RunAndReturn(run func(context.Context, string, ...operations.Option) (*operations.DeleteControlPlaneResponse, error)) *MockControlPlaneSDK_DeleteControlPlane_Call { + _c.Call.Return(run) + return _c +} + +// UpdateControlPlane provides a mock function with given fields: ctx, id, req, opts +func (_m *MockControlPlaneSDK) UpdateControlPlane(ctx context.Context, id string, req components.UpdateControlPlaneRequest, opts ...operations.Option) (*operations.UpdateControlPlaneResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, id, req) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for UpdateControlPlane") + } + + var r0 *operations.UpdateControlPlaneResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, components.UpdateControlPlaneRequest, ...operations.Option) (*operations.UpdateControlPlaneResponse, error)); ok { + return rf(ctx, id, req, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, components.UpdateControlPlaneRequest, ...operations.Option) *operations.UpdateControlPlaneResponse); ok { + r0 = rf(ctx, id, req, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*operations.UpdateControlPlaneResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, components.UpdateControlPlaneRequest, ...operations.Option) error); ok { + r1 = rf(ctx, id, req, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockControlPlaneSDK_UpdateControlPlane_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateControlPlane' +type MockControlPlaneSDK_UpdateControlPlane_Call struct { + *mock.Call +} + +// UpdateControlPlane is a helper method to define mock.On call +// - ctx context.Context +// - id string +// - req components.UpdateControlPlaneRequest +// - opts ...operations.Option +func (_e *MockControlPlaneSDK_Expecter) UpdateControlPlane(ctx interface{}, id interface{}, req interface{}, opts ...interface{}) *MockControlPlaneSDK_UpdateControlPlane_Call { + return &MockControlPlaneSDK_UpdateControlPlane_Call{Call: _e.mock.On("UpdateControlPlane", + append([]interface{}{ctx, id, req}, opts...)...)} +} + +func (_c *MockControlPlaneSDK_UpdateControlPlane_Call) Run(run func(ctx context.Context, id string, req components.UpdateControlPlaneRequest, opts ...operations.Option)) *MockControlPlaneSDK_UpdateControlPlane_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]operations.Option, len(args)-3) + for i, a := range args[3:] { + if a != nil { + variadicArgs[i] = a.(operations.Option) + } + } + run(args[0].(context.Context), args[1].(string), args[2].(components.UpdateControlPlaneRequest), variadicArgs...) + }) + return _c +} + +func (_c *MockControlPlaneSDK_UpdateControlPlane_Call) Return(_a0 *operations.UpdateControlPlaneResponse, _a1 error) *MockControlPlaneSDK_UpdateControlPlane_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockControlPlaneSDK_UpdateControlPlane_Call) RunAndReturn(run func(context.Context, string, components.UpdateControlPlaneRequest, ...operations.Option) (*operations.UpdateControlPlaneResponse, error)) *MockControlPlaneSDK_UpdateControlPlane_Call { + _c.Call.Return(run) + return _c +} + +// NewMockControlPlaneSDK creates a new instance of MockControlPlaneSDK. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockControlPlaneSDK(t interface { + mock.TestingT + Cleanup(func()) +}) *MockControlPlaneSDK { + mock := &MockControlPlaneSDK{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/controller/konnect/ops/errors.go b/controller/konnect/ops/errors.go new file mode 100644 index 000000000..e4034c64e --- /dev/null +++ b/controller/konnect/ops/errors.go @@ -0,0 +1,26 @@ +package ops + +import ( + "fmt" + + "github.com/kong/gateway-operator/controller/konnect/constraints" +) + +// FailedKonnectOpError is an error type that is returned when an operation against +// Konnect API fails. +type FailedKonnectOpError[T constraints.SupportedKonnectEntityType] struct { + Op Op + Err error +} + +// Error implements the error interface. +func (e FailedKonnectOpError[T]) Error() string { + return fmt.Sprintf("failed to %s %s on Konnect: %v", + e.Op, constraints.EntityTypeName[T](), e.Err, + ) +} + +// Unwrap returns the underlying error. +func (e FailedKonnectOpError[T]) Unwrap() error { + return e.Err +} diff --git a/controller/konnect/ops/kongconsumer.go b/controller/konnect/ops/kongconsumer.go new file mode 100644 index 000000000..ba705bab4 --- /dev/null +++ b/controller/konnect/ops/kongconsumer.go @@ -0,0 +1,15 @@ +package ops + +import ( + "context" + + sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" +) + +// ConsumersSDK is the interface for the Konnect Consumers SDK. +type ConsumersSDK interface { + CreateConsumer(ctx context.Context, controlPlaneID string, consumerInput sdkkonnectgocomp.ConsumerInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateConsumerResponse, error) + UpsertConsumer(ctx context.Context, upsertConsumerRequest sdkkonnectgoops.UpsertConsumerRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertConsumerResponse, error) + DeleteConsumer(ctx context.Context, controlPlaneID string, consumerID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteConsumerResponse, error) +} diff --git a/controller/konnect/ops/kongconsumergroup.go b/controller/konnect/ops/kongconsumergroup.go new file mode 100644 index 000000000..c87cfc1a7 --- /dev/null +++ b/controller/konnect/ops/kongconsumergroup.go @@ -0,0 +1,15 @@ +package ops + +import ( + "context" + + sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" +) + +// ConsumerGroupSDK is the interface for the Konnect ConsumerGroups SDK. +type ConsumerGroupSDK interface { + CreateConsumerGroup(ctx context.Context, controlPlaneID string, consumerInput sdkkonnectgocomp.ConsumerGroupInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateConsumerGroupResponse, error) + UpsertConsumerGroup(ctx context.Context, upsertConsumerRequest sdkkonnectgoops.UpsertConsumerGroupRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertConsumerGroupResponse, error) + DeleteConsumerGroup(ctx context.Context, controlPlaneID string, consumerID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteConsumerGroupResponse, error) +} diff --git a/controller/konnect/ops/kongroute.go b/controller/konnect/ops/kongroute.go new file mode 100644 index 000000000..0473cee1a --- /dev/null +++ b/controller/konnect/ops/kongroute.go @@ -0,0 +1,15 @@ +package ops + +import ( + "context" + + sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" +) + +// RoutesSDK is the interface for the Konnect Routes SDK. +type RoutesSDK interface { + CreateRoute(ctx context.Context, controlPlaneID string, route sdkkonnectgocomp.RouteInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateRouteResponse, error) + UpsertRoute(ctx context.Context, req sdkkonnectgoops.UpsertRouteRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertRouteResponse, error) + DeleteRoute(ctx context.Context, controlPlaneID, routeID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteRouteResponse, error) +} diff --git a/controller/konnect/ops/kongservice.go b/controller/konnect/ops/kongservice.go new file mode 100644 index 000000000..137082b9a --- /dev/null +++ b/controller/konnect/ops/kongservice.go @@ -0,0 +1,15 @@ +package ops + +import ( + "context" + + sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" +) + +// ServicesSDK is the interface for the Konnect Service SDK. +type ServicesSDK interface { + CreateService(ctx context.Context, controlPlaneID string, service sdkkonnectgocomp.ServiceInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateServiceResponse, error) + UpsertService(ctx context.Context, req sdkkonnectgoops.UpsertServiceRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertServiceResponse, error) + DeleteService(ctx context.Context, controlPlaneID, serviceID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteServiceResponse, error) +} diff --git a/controller/konnect/ops.go b/controller/konnect/ops/ops.go similarity index 83% rename from controller/konnect/ops.go rename to controller/konnect/ops/ops.go index aadc33b4e..91bebe1dc 100644 --- a/controller/konnect/ops.go +++ b/controller/konnect/ops/ops.go @@ -1,4 +1,4 @@ -package konnect +package ops import ( "context" @@ -11,6 +11,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/kong/gateway-operator/controller/konnect/conditions" + "github.com/kong/gateway-operator/controller/konnect/constraints" "github.com/kong/gateway-operator/controller/pkg/log" k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" @@ -40,8 +42,8 @@ const ( // Create creates a Konnect entity. func Create[ - T SupportedKonnectEntityType, - TEnt EntityType[T], + T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T], ](ctx context.Context, sdk *sdkkonnectgo.SDK, cl client.Client, e *T) (*T, error) { defer logOpComplete[T, TEnt](ctx, time.Now(), CreateOp, e) @@ -55,7 +57,7 @@ func Create[ case *configurationv1.KongConsumer: return e, createConsumer(ctx, sdk.Consumers, ent) case *configurationv1beta1.KongConsumerGroup: - return e, createConsumerGroup(ctx, sdk, ent) + return e, createConsumerGroup(ctx, sdk.ConsumerGroups, ent) // --------------------------------------------------------------------- // TODO: add other Konnect types @@ -68,8 +70,8 @@ func Create[ // Delete deletes a Konnect entity. // It returns an error if the entity does not have a Konnect ID or if the operation fails. func Delete[ - T SupportedKonnectEntityType, - TEnt EntityType[T], + T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T], ](ctx context.Context, sdk *sdkkonnectgo.SDK, cl client.Client, e *T) error { ent := TEnt(e) if ent.GetKonnectStatus().GetKonnectID() == "" { @@ -91,7 +93,7 @@ func Delete[ case *configurationv1.KongConsumer: return deleteConsumer(ctx, sdk.Consumers, ent) case *configurationv1beta1.KongConsumerGroup: - return deleteConsumerGroup(ctx, sdk, ent) + return deleteConsumerGroup(ctx, sdk.ConsumerGroups, ent) // --------------------------------------------------------------------- // TODO: add other Konnect types @@ -104,12 +106,12 @@ func Delete[ // Update updates a Konnect entity. // It returns an error if the entity does not have a Konnect ID or if the operation fails. func Update[ - T SupportedKonnectEntityType, - TEnt EntityType[T], + T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T], ](ctx context.Context, sdk *sdkkonnectgo.SDK, syncPeriod time.Duration, cl client.Client, e *T) (ctrl.Result, error) { var ( ent = TEnt(e) - condProgrammed, ok = k8sutils.GetCondition(KonnectEntityProgrammedConditionType, ent) + condProgrammed, ok = k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, ent) now = time.Now() timeFromLastUpdate = time.Since(condProgrammed.LastTransitionTime.Time) ) @@ -117,7 +119,7 @@ func Update[ // the configured sync period, requeue after the remaining time. if ok && condProgrammed.Status == metav1.ConditionTrue && - condProgrammed.Reason == KonnectEntityProgrammedReasonProgrammed && + condProgrammed.Reason == conditions.KonnectEntityProgrammedReasonProgrammed && condProgrammed.ObservedGeneration == ent.GetObjectMeta().GetGeneration() && timeFromLastUpdate <= syncPeriod { requeueAfter := syncPeriod - timeFromLastUpdate @@ -152,7 +154,7 @@ func Update[ case *configurationv1.KongConsumer: return ctrl.Result{}, updateConsumer(ctx, sdk.Consumers, cl, ent) case *configurationv1beta1.KongConsumerGroup: - return ctrl.Result{}, updateConsumerGroup(ctx, sdk, cl, ent) + return ctrl.Result{}, updateConsumerGroup(ctx, sdk.ConsumerGroups, cl, ent) // --------------------------------------------------------------------- // TODO: add other Konnect types @@ -163,8 +165,8 @@ func Update[ } func logOpComplete[ - T SupportedKonnectEntityType, - TEnt EntityType[T], + T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T], ](ctx context.Context, start time.Time, op Op, e TEnt) { s := e.GetKonnectStatus() if s == nil { @@ -175,7 +177,7 @@ func logOpComplete[ Info("operation in Konnect API complete", "op", op, "duration", time.Since(start), - "type", entityTypeName[T](), + "type", constraints.EntityTypeName[T](), "konnect_id", s.GetKonnectID(), ) } @@ -183,17 +185,18 @@ func logOpComplete[ // wrapErrIfKonnectOpFailed checks the response from the Konnect API and returns a uniform // error for all Konnect entities if the operation failed. func wrapErrIfKonnectOpFailed[ - T SupportedKonnectEntityType, - TEnt EntityType[T], + T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T], ](err error, op Op, e TEnt) error { if err != nil { + entityTypeName := constraints.EntityTypeName[T]() if e == nil { - return fmt.Errorf("failed to %s for %T: %w", - op, e, err, + return fmt.Errorf("failed to %s %s: %w", + op, entityTypeName, err, ) } - return fmt.Errorf("failed to %s for %T %q: %w", - op, client.ObjectKeyFromObject(e), e, err, + return fmt.Errorf("failed to %s %s %s: %w", + op, entityTypeName, client.ObjectKeyFromObject(e), err, ) } return nil diff --git a/controller/konnect/ops_controlplane.go b/controller/konnect/ops/ops_controlplane.go similarity index 74% rename from controller/konnect/ops_controlplane.go rename to controller/konnect/ops/ops_controlplane.go index 5f951017d..9cc78fac4 100644 --- a/controller/konnect/ops_controlplane.go +++ b/controller/konnect/ops/ops_controlplane.go @@ -1,28 +1,21 @@ -package konnect +package ops import ( "context" "errors" sdkkonnectgo "github.com/Kong/sdk-konnect-go" - "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" - "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/kong/gateway-operator/controller/konnect/conditions" k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" ) -// ControlPlaneSDK is the interface for the Konnect ControlPlane SDK. -type ControlPlaneSDK interface { - CreateControlPlane(ctx context.Context, req components.CreateControlPlaneRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateControlPlaneResponse, error) - DeleteControlPlane(ctx context.Context, id string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteControlPlaneResponse, error) - UpdateControlPlane(ctx context.Context, id string, req components.UpdateControlPlaneRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpdateControlPlaneResponse, error) -} - func createControlPlane( ctx context.Context, sdk ControlPlaneSDK, @@ -36,7 +29,7 @@ func createControlPlane( if errWrap := wrapErrIfKonnectOpFailed(err, CreateOp, cp); errWrap != nil { k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, "FailedToCreate", errWrap.Error(), @@ -50,9 +43,9 @@ func createControlPlane( cp.Status.SetKonnectID(resp.ControlPlane.ID) k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionTrue, - KonnectEntityProgrammedReasonProgrammed, + conditions.KonnectEntityProgrammedReasonProgrammed, "", cp.GetGeneration(), ), @@ -72,7 +65,7 @@ func deleteControlPlane( id := cp.GetKonnectStatus().GetKonnectID() _, err := sdk.DeleteControlPlane(ctx, id) if errWrap := wrapErrIfKonnectOpFailed(err, DeleteOp, cp); errWrap != nil { - var sdkNotFoundError *sdkerrors.NotFoundError + var sdkNotFoundError *sdkkonnectgoerrs.NotFoundError if errors.As(err, &sdkNotFoundError) { ctrllog.FromContext(ctx). Info("entity not found in Konnect, skipping delete", @@ -80,7 +73,7 @@ func deleteControlPlane( ) return nil } - var sdkError *sdkerrors.SDKError + var sdkError *sdkkonnectgoerrs.SDKError if errors.As(errWrap, &sdkError) { return FailedKonnectOpError[konnectv1alpha1.KonnectControlPlane]{ Op: DeleteOp, @@ -105,16 +98,16 @@ func updateControlPlane( cp *konnectv1alpha1.KonnectControlPlane, ) error { id := cp.GetKonnectStatus().GetKonnectID() - req := components.UpdateControlPlaneRequest{ + req := sdkkonnectgocomp.UpdateControlPlaneRequest{ Name: sdkkonnectgo.String(cp.Spec.Name), Description: cp.Spec.Description, - AuthType: (*components.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), + AuthType: (*sdkkonnectgocomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), ProxyUrls: cp.Spec.ProxyUrls, Labels: cp.Spec.Labels, } resp, err := sdk.UpdateControlPlane(ctx, id, req) - var sdkError *sdkerrors.NotFoundError + var sdkError *sdkkonnectgoerrs.NotFoundError if errors.As(err, &sdkError) { ctrllog.FromContext(ctx). Info("entity not found in Konnect, trying to recreate", @@ -134,7 +127,7 @@ func updateControlPlane( if errWrap := wrapErrIfKonnectOpFailed(err, UpdateOp, cp); errWrap != nil { k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, "FailedToUpdate", errWrap.Error(), @@ -151,9 +144,9 @@ func updateControlPlane( cp.Status.SetKonnectID(resp.ControlPlane.ID) k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionTrue, - KonnectEntityProgrammedReasonProgrammed, + conditions.KonnectEntityProgrammedReasonProgrammed, "", cp.GetGeneration(), ), diff --git a/controller/konnect/ops/ops_controlplane_test.go b/controller/konnect/ops/ops_controlplane_test.go new file mode 100644 index 000000000..dc40c3b87 --- /dev/null +++ b/controller/konnect/ops/ops_controlplane_test.go @@ -0,0 +1,450 @@ +package ops + +import ( + "context" + "testing" + + sdkkonnectgo "github.com/Kong/sdk-konnect-go" + sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kong/gateway-operator/controller/konnect/conditions" + k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" + + konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" +) + +func TestCreateControlPlane(t *testing.T) { + ctx := context.Background() + testCases := []struct { + name string + mockCPPair func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) + expectedErr bool + assertions func(*testing.T, *konnectv1alpha1.KonnectControlPlane) + }{ + { + name: "success", + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + sdk := &MockControlPlaneSDK{} + cp := &konnectv1alpha1.KonnectControlPlane{ + Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + Name: "cp-1", + }, + }, + } + + sdk. + EXPECT(). + CreateControlPlane(ctx, cp.Spec.CreateControlPlaneRequest). + Return( + &sdkkonnectgoops.CreateControlPlaneResponse{ + ControlPlane: &sdkkonnectgocomp.ControlPlane{ + ID: "12345", + }, + }, + nil, + ) + + return sdk, cp + }, + assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectControlPlane) { + assert.Equal(t, "12345", cp.Status.GetKonnectID()) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) + require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + assert.Equal(t, metav1.ConditionTrue, cond.Status) + assert.Equal(t, conditions.KonnectEntityProgrammedReasonProgrammed, cond.Reason) + assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) + }, + }, + { + name: "fail", + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + sdk := &MockControlPlaneSDK{} + cp := &konnectv1alpha1.KonnectControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cp-1", + Namespace: "default", + }, + Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + Name: "cp-1", + }, + }, + } + + sdk. + EXPECT(). + CreateControlPlane(ctx, cp.Spec.CreateControlPlaneRequest). + Return( + nil, + &sdkkonnectgoerrs.BadRequestError{ + Status: 400, + Detail: "bad request", + }, + ) + + return sdk, cp + }, + assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectControlPlane) { + assert.Equal(t, "", cp.Status.GetKonnectID()) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) + require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + assert.Equal(t, metav1.ConditionFalse, cond.Status) + assert.Equal(t, "FailedToCreate", cond.Reason) + assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) + assert.Equal(t, "failed to create KonnectControlPlane default/cp-1: {\"status\":400,\"title\":\"\",\"instance\":\"\",\"detail\":\"bad request\",\"invalid_parameters\":null}", cond.Message) + }, + expectedErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sdk, cp := tc.mockCPPair() + + err := createControlPlane(ctx, sdk, cp) + t.Cleanup(func() { + assert.True(t, sdk.AssertExpectations(t)) + }) + + tc.assertions(t, cp) + + if tc.expectedErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + }) + } +} + +func TestDeleteControlPlane(t *testing.T) { + ctx := context.Background() + testCases := []struct { + name string + mockCPPair func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) + expectedErr bool + assertions func(*testing.T, *konnectv1alpha1.KonnectControlPlane) + }{ + { + name: "success", + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + sdk := &MockControlPlaneSDK{} + cp := &konnectv1alpha1.KonnectControlPlane{ + Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + Name: "cp-1", + }, + }, + Status: konnectv1alpha1.KonnectControlPlaneStatus{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "12345", + }, + }, + } + sdk. + EXPECT(). + DeleteControlPlane(ctx, "12345"). + Return( + &sdkkonnectgoops.DeleteControlPlaneResponse{ + StatusCode: 200, + }, + nil, + ) + + return sdk, cp + }, + }, + { + name: "fail", + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + sdk := &MockControlPlaneSDK{} + cp := &konnectv1alpha1.KonnectControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cp-1", + Namespace: "default", + }, + Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + Name: "cp-1", + }, + }, + Status: konnectv1alpha1.KonnectControlPlaneStatus{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "12345", + }, + }, + } + sdk. + EXPECT(). + DeleteControlPlane(ctx, "12345"). + Return( + nil, + &sdkkonnectgoerrs.BadRequestError{ + Status: 400, + Detail: "bad request", + }, + ) + + return sdk, cp + }, + expectedErr: true, + }, + { + name: "not found error is ignore and considered a success when trying to delete", + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + sdk := &MockControlPlaneSDK{} + cp := &konnectv1alpha1.KonnectControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cp-1", + Namespace: "default", + }, + Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + Name: "cp-1", + }, + }, + Status: konnectv1alpha1.KonnectControlPlaneStatus{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "12345", + }, + }, + } + sdk. + EXPECT(). + DeleteControlPlane(ctx, "12345"). + Return( + nil, + &sdkkonnectgoerrs.NotFoundError{ + Status: 404, + Detail: "not found", + }, + ) + + return sdk, cp + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sdk, cp := tc.mockCPPair() + + err := deleteControlPlane(ctx, sdk, cp) + + if tc.assertions != nil { + tc.assertions(t, cp) + } + + if tc.expectedErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.True(t, sdk.AssertExpectations(t)) + }) + } +} + +func TestUpdateControlPlane(t *testing.T) { + ctx := context.Background() + testCases := []struct { + name string + mockCPPair func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) + expectedErr bool + assertions func(*testing.T, *konnectv1alpha1.KonnectControlPlane) + }{ + { + name: "success", + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + sdk := &MockControlPlaneSDK{} + cp := &konnectv1alpha1.KonnectControlPlane{ + Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + Name: "cp-1", + }, + }, + Status: konnectv1alpha1.KonnectControlPlaneStatus{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "12345", + }, + }, + } + sdk. + EXPECT(). + UpdateControlPlane(ctx, "12345", + sdkkonnectgocomp.UpdateControlPlaneRequest{ + Name: sdkkonnectgo.String(cp.Spec.Name), + Description: cp.Spec.Description, + AuthType: (*sdkkonnectgocomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), + ProxyUrls: cp.Spec.ProxyUrls, + Labels: cp.Spec.Labels, + }, + ). + Return( + &sdkkonnectgoops.UpdateControlPlaneResponse{ + ControlPlane: &sdkkonnectgocomp.ControlPlane{ + ID: "12345", + }, + }, + nil, + ) + + return sdk, cp + }, + assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectControlPlane) { + assert.Equal(t, "12345", cp.Status.GetKonnectID()) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) + require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + assert.Equal(t, metav1.ConditionTrue, cond.Status) + assert.Equal(t, conditions.KonnectEntityProgrammedReasonProgrammed, cond.Reason) + assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) + assert.Equal(t, "", cond.Message) + }, + }, + { + name: "fail", + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + sdk := &MockControlPlaneSDK{} + cp := &konnectv1alpha1.KonnectControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cp-1", + Namespace: "default", + }, + Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + Name: "cp-1", + }, + }, + Status: konnectv1alpha1.KonnectControlPlaneStatus{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "12345", + }, + }, + } + + sdk. + EXPECT(). + UpdateControlPlane(ctx, "12345", + sdkkonnectgocomp.UpdateControlPlaneRequest{ + Name: sdkkonnectgo.String(cp.Spec.Name), + Description: cp.Spec.Description, + AuthType: (*sdkkonnectgocomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), + ProxyUrls: cp.Spec.ProxyUrls, + Labels: cp.Spec.Labels, + }, + ). + Return( + nil, + &sdkkonnectgoerrs.BadRequestError{ + Status: 400, + Detail: "bad request", + }, + ) + + return sdk, cp + }, + assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectControlPlane) { + assert.Equal(t, "12345", cp.Status.GetKonnectID()) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) + require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + assert.Equal(t, metav1.ConditionFalse, cond.Status) + assert.Equal(t, "FailedToUpdate", cond.Reason) + assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) + assert.Equal(t, "failed to update KonnectControlPlane default/cp-1: {\"status\":400,\"title\":\"\",\"instance\":\"\",\"detail\":\"bad request\",\"invalid_parameters\":null}", cond.Message) + }, + expectedErr: true, + }, + { + name: "when not found then try to create", + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + sdk := &MockControlPlaneSDK{} + cp := &konnectv1alpha1.KonnectControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cp-1", + Namespace: "default", + }, + Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + Name: "cp-1", + }, + }, + Status: konnectv1alpha1.KonnectControlPlaneStatus{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "12345", + }, + }, + } + + sdk. + EXPECT(). + UpdateControlPlane(ctx, "12345", + sdkkonnectgocomp.UpdateControlPlaneRequest{ + Name: sdkkonnectgo.String(cp.Spec.Name), + Description: cp.Spec.Description, + AuthType: (*sdkkonnectgocomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), + ProxyUrls: cp.Spec.ProxyUrls, + Labels: cp.Spec.Labels, + }, + ). + Return( + nil, + &sdkkonnectgoerrs.NotFoundError{ + Status: 404, + Detail: "not found", + }, + ) + + sdk. + EXPECT(). + CreateControlPlane(ctx, cp.Spec.CreateControlPlaneRequest). + Return( + &sdkkonnectgoops.CreateControlPlaneResponse{ + ControlPlane: &sdkkonnectgocomp.ControlPlane{ + ID: "12345", + }, + }, + nil, + ) + + return sdk, cp + }, + assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectControlPlane) { + assert.Equal(t, "12345", cp.Status.GetKonnectID()) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) + require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + assert.Equal(t, metav1.ConditionTrue, cond.Status) + assert.Equal(t, conditions.KonnectEntityProgrammedReasonProgrammed, cond.Reason) + assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) + assert.Equal(t, "", cond.Message) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sdk, cp := tc.mockCPPair() + + err := updateControlPlane(ctx, sdk, cp) + + if tc.assertions != nil { + tc.assertions(t, cp) + } + + if tc.expectedErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.True(t, sdk.AssertExpectations(t)) + }) + } +} diff --git a/controller/konnect/ops_kongconsumer.go b/controller/konnect/ops/ops_kongconsumer.go similarity index 83% rename from controller/konnect/ops_kongconsumer.go rename to controller/konnect/ops/ops_kongconsumer.go index d92429ed9..8aa3ff931 100644 --- a/controller/konnect/ops_kongconsumer.go +++ b/controller/konnect/ops/ops_kongconsumer.go @@ -1,4 +1,4 @@ -package konnect +package ops import ( "context" @@ -7,12 +7,13 @@ import ( sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" - "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/kong/gateway-operator/controller/konnect/conditions" k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" configurationv1 "github.com/kong/kubernetes-configuration/api/configuration/v1" @@ -20,13 +21,6 @@ import ( "github.com/kong/kubernetes-configuration/pkg/metadata" ) -// ConsumersSDK is the interface for the Konnect Consumers SDK. -type ConsumersSDK interface { - CreateConsumer(ctx context.Context, controlPlaneID string, consumerInput sdkkonnectgocomp.ConsumerInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateConsumerResponse, error) - UpsertConsumer(ctx context.Context, upsertConsumerRequest sdkkonnectgoops.UpsertConsumerRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertConsumerResponse, error) - DeleteConsumer(ctx context.Context, controlPlaneID string, consumerID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteConsumerResponse, error) -} - func createConsumer( ctx context.Context, sdk ConsumersSDK, @@ -47,7 +41,7 @@ func createConsumer( if errWrapped := wrapErrIfKonnectOpFailed(err, CreateOp, consumer); errWrapped != nil { k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, "FailedToCreate", errWrapped.Error(), @@ -61,9 +55,9 @@ func createConsumer( consumer.Status.Konnect.SetKonnectID(*resp.Consumer.ID) k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionTrue, - KonnectEntityProgrammedReasonProgrammed, + conditions.KonnectEntityProgrammedReasonProgrammed, "", consumer.GetGeneration(), ), @@ -120,7 +114,7 @@ func updateConsumer( if errWrapped := wrapErrIfKonnectOpFailed(err, UpdateOp, consumer); errWrapped != nil { k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, "FailedToCreate", errWrapped.Error(), @@ -135,9 +129,9 @@ func updateConsumer( consumer.Status.Konnect.SetControlPlaneID(cp.Status.ID) k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionTrue, - KonnectEntityProgrammedReasonProgrammed, + conditions.KonnectEntityProgrammedReasonProgrammed, "", consumer.GetGeneration(), ), @@ -159,7 +153,7 @@ func deleteConsumer( _, err := sdk.DeleteConsumer(ctx, consumer.Status.Konnect.ControlPlaneID, id) if errWrapped := wrapErrIfKonnectOpFailed(err, DeleteOp, consumer); errWrapped != nil { // Consumer delete operation returns an SDKError instead of a NotFoundError. - var sdkError *sdkerrors.SDKError + var sdkError *sdkkonnectgoerrs.SDKError if errors.As(errWrapped, &sdkError) { if sdkError.StatusCode == 404 { ctrllog.FromContext(ctx). diff --git a/controller/konnect/ops_kongconsumergroup.go b/controller/konnect/ops/ops_kongconsumergroup.go similarity index 86% rename from controller/konnect/ops_kongconsumergroup.go rename to controller/konnect/ops/ops_kongconsumergroup.go index 160d108f1..35fe60992 100644 --- a/controller/konnect/ops_kongconsumergroup.go +++ b/controller/konnect/ops/ops_kongconsumergroup.go @@ -1,19 +1,19 @@ -package konnect +package ops import ( "context" "errors" "fmt" - sdkkonnectgo "github.com/Kong/sdk-konnect-go" sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" - "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/kong/gateway-operator/controller/konnect/conditions" k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" configurationv1beta1 "github.com/kong/kubernetes-configuration/api/configuration/v1beta1" @@ -23,14 +23,14 @@ import ( func createConsumerGroup( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ConsumerGroupSDK, group *configurationv1beta1.KongConsumerGroup, ) error { if group.GetControlPlaneID() == "" { return fmt.Errorf("can't create %T %s without a Konnect ControlPlane ID", group, client.ObjectKeyFromObject(group)) } - resp, err := sdk.ConsumerGroups.CreateConsumerGroup(ctx, + resp, err := sdk.CreateConsumerGroup(ctx, group.Status.Konnect.ControlPlaneID, kongConsumerGroupToSDKConsumerGroupInput(group), ) @@ -41,7 +41,7 @@ func createConsumerGroup( if errWrapped := wrapErrIfKonnectOpFailed(err, CreateOp, group); errWrapped != nil { k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, "FailedToCreate", errWrapped.Error(), @@ -55,9 +55,9 @@ func createConsumerGroup( group.Status.Konnect.SetKonnectID(*resp.ConsumerGroup.ID) k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionTrue, - KonnectEntityProgrammedReasonProgrammed, + conditions.KonnectEntityProgrammedReasonProgrammed, "", group.GetGeneration(), ), @@ -72,7 +72,7 @@ func createConsumerGroup( // It returns an error if the KongConsumerGroup does not have a ControlPlaneRef. func updateConsumerGroup( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ConsumerGroupSDK, cl client.Client, group *configurationv1beta1.KongConsumerGroup, ) error { @@ -100,7 +100,7 @@ func updateConsumerGroup( ) } - resp, err := sdk.ConsumerGroups.UpsertConsumerGroup(ctx, + resp, err := sdk.UpsertConsumerGroup(ctx, sdkkonnectgoops.UpsertConsumerGroupRequest{ ControlPlaneID: cp.Status.ID, ConsumerGroupID: group.GetKonnectStatus().GetKonnectID(), @@ -114,7 +114,7 @@ func updateConsumerGroup( if errWrapped := wrapErrIfKonnectOpFailed(err, UpdateOp, group); errWrapped != nil { k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, "FailedToCreate", errWrapped.Error(), @@ -129,9 +129,9 @@ func updateConsumerGroup( group.Status.Konnect.SetControlPlaneID(cp.Status.ID) k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionTrue, - KonnectEntityProgrammedReasonProgrammed, + conditions.KonnectEntityProgrammedReasonProgrammed, "", group.GetGeneration(), ), @@ -146,14 +146,14 @@ func updateConsumerGroup( // It returns an error if the operation fails. func deleteConsumerGroup( ctx context.Context, - sdk *sdkkonnectgo.SDK, + sdk ConsumerGroupSDK, consumer *configurationv1beta1.KongConsumerGroup, ) error { id := consumer.Status.Konnect.GetKonnectID() - _, err := sdk.ConsumerGroups.DeleteConsumerGroup(ctx, consumer.Status.Konnect.ControlPlaneID, id) + _, err := sdk.DeleteConsumerGroup(ctx, consumer.Status.Konnect.ControlPlaneID, id) if errWrapped := wrapErrIfKonnectOpFailed(err, DeleteOp, consumer); errWrapped != nil { // Consumer delete operation returns an SDKError instead of a NotFoundError. - var sdkError *sdkerrors.SDKError + var sdkError *sdkkonnectgoerrs.SDKError if errors.As(errWrapped, &sdkError) { if sdkError.StatusCode == 404 { ctrllog.FromContext(ctx). diff --git a/controller/konnect/ops_kongroute.go b/controller/konnect/ops/ops_kongroute.go similarity index 87% rename from controller/konnect/ops_kongroute.go rename to controller/konnect/ops/ops_kongroute.go index a830e36d2..6b69a8a88 100644 --- a/controller/konnect/ops_kongroute.go +++ b/controller/konnect/ops/ops_kongroute.go @@ -1,4 +1,4 @@ -package konnect +package ops import ( "context" @@ -8,25 +8,19 @@ import ( sdkkonnectgo "github.com/Kong/sdk-konnect-go" sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" - "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/kong/gateway-operator/controller/konnect/conditions" k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" ) -// RoutesSDK is the interface for the Konnect Routes SDK. -type RoutesSDK interface { - CreateRoute(ctx context.Context, controlPlaneID string, route sdkkonnectgocomp.RouteInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateRouteResponse, error) - UpsertRoute(ctx context.Context, req sdkkonnectgoops.UpsertRouteRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertRouteResponse, error) - DeleteRoute(ctx context.Context, controlPlaneID, routeID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteRouteResponse, error) -} - func createRoute( ctx context.Context, sdk RoutesSDK, @@ -44,7 +38,7 @@ func createRoute( if errWrapped := wrapErrIfKonnectOpFailed(err, CreateOp, route); errWrapped != nil { k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, "FailedToCreate", errWrapped.Error(), @@ -58,9 +52,9 @@ func createRoute( route.Status.Konnect.SetKonnectID(*resp.Route.ID) k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionTrue, - KonnectEntityProgrammedReasonProgrammed, + conditions.KonnectEntityProgrammedReasonProgrammed, "", route.GetGeneration(), ), @@ -136,7 +130,7 @@ func updateRoute( if errWrapped := wrapErrIfKonnectOpFailed(err, UpdateOp, route); errWrapped != nil { k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, "FailedToCreate", errWrapped.Error(), @@ -151,9 +145,9 @@ func updateRoute( route.Status.Konnect.SetControlPlaneID(cp.Status.ID) k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionTrue, - KonnectEntityProgrammedReasonProgrammed, + conditions.KonnectEntityProgrammedReasonProgrammed, "", route.GetGeneration(), ), @@ -175,7 +169,7 @@ func deleteRoute( _, err := sdk.DeleteRoute(ctx, route.Status.Konnect.ControlPlaneID, id) if errWrapped := wrapErrIfKonnectOpFailed(err, DeleteOp, route); errWrapped != nil { // Service delete operation returns an SDKError instead of a NotFoundError. - var sdkError *sdkerrors.SDKError + var sdkError *sdkkonnectgoerrs.SDKError if errors.As(errWrapped, &sdkError) { if sdkError.StatusCode == 404 { ctrllog.FromContext(ctx). diff --git a/controller/konnect/ops_kongservice.go b/controller/konnect/ops/ops_kongservice.go similarity index 85% rename from controller/konnect/ops_kongservice.go rename to controller/konnect/ops/ops_kongservice.go index f42aa60b2..9dce8957b 100644 --- a/controller/konnect/ops_kongservice.go +++ b/controller/konnect/ops/ops_kongservice.go @@ -1,4 +1,4 @@ -package konnect +package ops import ( "context" @@ -7,25 +7,19 @@ import ( sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" - "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/kong/gateway-operator/controller/konnect/conditions" k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" ) -// ServicesSDK is the interface for the Konnect Service SDK. -type ServicesSDK interface { - CreateService(ctx context.Context, controlPlaneID string, service sdkkonnectgocomp.ServiceInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateServiceResponse, error) - UpsertService(ctx context.Context, req sdkkonnectgoops.UpsertServiceRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertServiceResponse, error) - DeleteService(ctx context.Context, controlPlaneID, serviceID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteServiceResponse, error) -} - func createService( ctx context.Context, sdk ServicesSDK, @@ -46,7 +40,7 @@ func createService( if errWrapped := wrapErrIfKonnectOpFailed(err, CreateOp, svc); errWrapped != nil { k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, "FailedToCreate", errWrapped.Error(), @@ -60,9 +54,9 @@ func createService( svc.Status.Konnect.SetKonnectID(*resp.Service.ID) k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionTrue, - KonnectEntityProgrammedReasonProgrammed, + conditions.KonnectEntityProgrammedReasonProgrammed, "", svc.GetGeneration(), ), @@ -120,7 +114,7 @@ func updateService( if errWrapped := wrapErrIfKonnectOpFailed(err, UpdateOp, svc); errWrapped != nil { k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, "FailedToCreate", errWrapped.Error(), @@ -135,9 +129,9 @@ func updateService( svc.Status.Konnect.SetControlPlaneID(cp.Status.ID) k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionTrue, - KonnectEntityProgrammedReasonProgrammed, + conditions.KonnectEntityProgrammedReasonProgrammed, "", svc.GetGeneration(), ), @@ -159,7 +153,7 @@ func deleteService( _, err := sdk.DeleteService(ctx, svc.Status.Konnect.ControlPlaneID, id) if errWrapped := wrapErrIfKonnectOpFailed(err, DeleteOp, svc); errWrapped != nil { // Service delete operation returns an SDKError instead of a NotFoundError. - var sdkError *sdkerrors.SDKError + var sdkError *sdkkonnectgoerrs.SDKError if errors.As(errWrapped, &sdkError) { switch sdkError.StatusCode { case 404: diff --git a/controller/konnect/reconciler_generic.go b/controller/konnect/reconciler_generic.go index 859d32aad..1fb787a51 100644 --- a/controller/konnect/reconciler_generic.go +++ b/controller/konnect/reconciler_generic.go @@ -16,6 +16,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/kong/gateway-operator/controller/konnect/conditions" + "github.com/kong/gateway-operator/controller/konnect/constraints" + "github.com/kong/gateway-operator/controller/konnect/ops" "github.com/kong/gateway-operator/controller/pkg/log" "github.com/kong/gateway-operator/pkg/consts" k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" @@ -35,7 +38,7 @@ const ( // KonnectEntityReconciler reconciles a Konnect entities. // It uses the generic type constraints to constrain the supported types. -type KonnectEntityReconciler[T SupportedKonnectEntityType, TEnt EntityType[T]] struct { +type KonnectEntityReconciler[T constraints.SupportedKonnectEntityType, TEnt constraints.EntityType[T]] struct { sdkFactory SDKFactory DevelopmentMode bool Client client.Client @@ -44,12 +47,12 @@ type KonnectEntityReconciler[T SupportedKonnectEntityType, TEnt EntityType[T]] s // KonnectEntityReconcilerOption is a functional option for the KonnectEntityReconciler. type KonnectEntityReconcilerOption[ - T SupportedKonnectEntityType, - TEnt EntityType[T], + T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T], ] func(*KonnectEntityReconciler[T, TEnt]) // WithKonnectEntitySyncPeriod sets the sync period for the reconciler. -func WithKonnectEntitySyncPeriod[T SupportedKonnectEntityType, TEnt EntityType[T]]( +func WithKonnectEntitySyncPeriod[T constraints.SupportedKonnectEntityType, TEnt constraints.EntityType[T]]( d time.Duration, ) KonnectEntityReconcilerOption[T, TEnt] { return func(r *KonnectEntityReconciler[T, TEnt]) { @@ -60,8 +63,8 @@ func WithKonnectEntitySyncPeriod[T SupportedKonnectEntityType, TEnt EntityType[T // NewKonnectEntityReconciler returns a new KonnectEntityReconciler for the given // Konnect entity type. func NewKonnectEntityReconciler[ - T SupportedKonnectEntityType, - TEnt EntityType[T], + T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T], ]( sdkFactory SDKFactory, developmentMode bool, @@ -92,7 +95,7 @@ func (r *KonnectEntityReconciler[T, TEnt]) SetupWithManager(mgr ctrl.Manager) er e T ent = TEnt(&e) b = ctrl.NewControllerManagedBy(mgr). - Named(entityTypeName[T]()). + Named(constraints.EntityTypeName[T]()). WithOptions(controller.Options{ MaxConcurrentReconciles: MaxConcurrentReconciles, }) @@ -109,7 +112,7 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( ctx context.Context, req ctrl.Request, ) (ctrl.Result, error) { var ( - entityTypeName = entityTypeName[T]() + entityTypeName = constraints.EntityTypeName[T]() logger = log.GetLogger(ctx, entityTypeName, r.DevelopmentMode) ) @@ -178,9 +181,9 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( if k8serrors.IsNotFound(err) { if res, err := updateStatusWithCondition( ctx, r.Client, ent, - KonnectEntityAPIAuthConfigurationResolvedRefConditionType, + conditions.KonnectEntityAPIAuthConfigurationResolvedRefConditionType, metav1.ConditionFalse, - KonnectEntityAPIAuthConfigurationResolvedRefReasonRefNotFound, + conditions.KonnectEntityAPIAuthConfigurationResolvedRefReasonRefNotFound, fmt.Sprintf("Referenced KonnectAPIAuthConfiguration %s not found", apiAuthRef), ); err != nil || res.Requeue { return ctrl.Result{}, err @@ -191,9 +194,9 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( if res, err := updateStatusWithCondition( ctx, r.Client, ent, - KonnectEntityAPIAuthConfigurationResolvedRefConditionType, + conditions.KonnectEntityAPIAuthConfigurationResolvedRefConditionType, metav1.ConditionFalse, - KonnectEntityAPIAuthConfigurationResolvedRefReasonRefInvalid, + conditions.KonnectEntityAPIAuthConfigurationResolvedRefReasonRefInvalid, fmt.Sprintf("KonnectAPIAuthConfiguration reference %s is invalid: %v", apiAuthRef, err), ); err != nil || res.Requeue { return ctrl.Result{}, err @@ -203,15 +206,15 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( } // Update the status if the reference is resolved and it's not as expected. - if cond, present := k8sutils.GetCondition(KonnectEntityAPIAuthConfigurationResolvedRefConditionType, ent); !present || + if cond, present := k8sutils.GetCondition(conditions.KonnectEntityAPIAuthConfigurationResolvedRefConditionType, ent); !present || cond.Status != metav1.ConditionTrue || cond.ObservedGeneration != ent.GetGeneration() || - cond.Reason != KonnectEntityAPIAuthConfigurationResolvedRefReasonResolvedRef { + cond.Reason != conditions.KonnectEntityAPIAuthConfigurationResolvedRefReasonResolvedRef { if res, err := updateStatusWithCondition( ctx, r.Client, ent, - KonnectEntityAPIAuthConfigurationResolvedRefConditionType, + conditions.KonnectEntityAPIAuthConfigurationResolvedRefConditionType, metav1.ConditionTrue, - KonnectEntityAPIAuthConfigurationResolvedRefReasonResolvedRef, + conditions.KonnectEntityAPIAuthConfigurationResolvedRefReasonResolvedRef, fmt.Sprintf("KonnectAPIAuthConfiguration reference %s is resolved", apiAuthRef), ); err != nil || res.Requeue { return res, err @@ -220,17 +223,17 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( } // Check if the referenced APIAuthConfiguration is valid. - if cond, present := k8sutils.GetCondition(KonnectEntityAPIAuthConfigurationValidConditionType, &apiAuth); !present || + if cond, present := k8sutils.GetCondition(conditions.KonnectEntityAPIAuthConfigurationValidConditionType, &apiAuth); !present || cond.Status != metav1.ConditionTrue || - cond.Reason != KonnectEntityAPIAuthConfigurationReasonValid { + cond.Reason != conditions.KonnectEntityAPIAuthConfigurationReasonValid { // If it's invalid then set the "APIAuthValid" status condition on // the entity to False with "Invalid" reason. if res, err := updateStatusWithCondition( ctx, r.Client, ent, - KonnectEntityAPIAuthConfigurationValidConditionType, + conditions.KonnectEntityAPIAuthConfigurationValidConditionType, metav1.ConditionFalse, - KonnectEntityAPIAuthConfigurationReasonInvalid, + conditions.KonnectEntityAPIAuthConfigurationReasonInvalid, conditionMessageReferenceKonnectAPIAuthConfigurationInvalid(apiAuthRef), ); err != nil || res.Requeue { return res, err @@ -242,17 +245,17 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( // If the referenced APIAuthConfiguration is valid, set the "APIAuthValid" // condition to True with "Valid" reason. // Only perform the update if the condition is not as expected. - if cond, present := k8sutils.GetCondition(KonnectEntityAPIAuthConfigurationValidConditionType, ent); !present || + if cond, present := k8sutils.GetCondition(conditions.KonnectEntityAPIAuthConfigurationValidConditionType, ent); !present || cond.Status != metav1.ConditionTrue || - cond.Reason != KonnectEntityAPIAuthConfigurationReasonValid || + cond.Reason != conditions.KonnectEntityAPIAuthConfigurationReasonValid || cond.ObservedGeneration != ent.GetGeneration() || cond.Message != conditionMessageReferenceKonnectAPIAuthConfigurationValid(apiAuthRef) { if res, err := updateStatusWithCondition( ctx, r.Client, ent, - KonnectEntityAPIAuthConfigurationValidConditionType, + conditions.KonnectEntityAPIAuthConfigurationValidConditionType, metav1.ConditionTrue, - KonnectEntityAPIAuthConfigurationReasonValid, + conditions.KonnectEntityAPIAuthConfigurationReasonValid, conditionMessageReferenceKonnectAPIAuthConfigurationValid(apiAuthRef), ); err != nil || res.Requeue { return res, err @@ -264,9 +267,9 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( if err != nil { if res, errStatus := updateStatusWithCondition( ctx, r.Client, &apiAuth, - KonnectEntityAPIAuthConfigurationValidConditionType, + conditions.KonnectEntityAPIAuthConfigurationValidConditionType, metav1.ConditionFalse, - KonnectEntityAPIAuthConfigurationReasonInvalid, + conditions.KonnectEntityAPIAuthConfigurationReasonInvalid, err.Error(), ); errStatus != nil || res.Requeue { return res, errStatus @@ -296,12 +299,12 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( } if controllerutil.RemoveFinalizer(ent, KonnectCleanupFinalizer) { - if err := Delete[T, TEnt](ctx, sdk, r.Client, ent); err != nil { + if err := ops.Delete[T, TEnt](ctx, sdk, r.Client, ent); err != nil { if res, errStatus := updateStatusWithCondition( ctx, r.Client, ent, - KonnectEntityProgrammedConditionType, + conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, - KonnectEntityProgrammedReasonKonnectAPIOpFailed, + conditions.KonnectEntityProgrammedReasonKonnectAPIOpFailed, err.Error(), ); errStatus != nil || res.Requeue { return res, errStatus @@ -326,7 +329,7 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( // We should look at the "expectations" for this: // https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/controller_utils.go if status := ent.GetKonnectStatus(); status == nil || status.GetKonnectID() == "" { - _, err := Create[T, TEnt](ctx, sdk, r.Client, ent) + _, err := ops.Create[T, TEnt](ctx, sdk, r.Client, ent) if err != nil { // TODO(pmalek): this is actually not 100% error prone because when status // update fails we don't store the Konnect ID and hence the reconciler @@ -338,8 +341,9 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( return ctrl.Result{}, fmt.Errorf("failed to update status after creating object: %w", err) } - return ctrl.Result{}, FailedKonnectOpError[T]{ - Op: CreateOp, + return ctrl.Result{}, ops.FailedKonnectOpError[T]{ + Op: ops.CreateOp, + Err: err, } } @@ -366,7 +370,7 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile( return ctrl.Result{}, nil } - if res, err := Update[T, TEnt](ctx, sdk, r.SyncPeriod, r.Client, ent); err != nil { + if res, err := ops.Update[T, TEnt](ctx, sdk, r.SyncPeriod, r.Client, ent); err != nil { return ctrl.Result{}, fmt.Errorf("failed to update object: %w", err) } else if res.Requeue || res.RequeueAfter > 0 { return res, nil @@ -424,7 +428,7 @@ func updateStatusWithCondition[T interface { } return ctrl.Result{}, fmt.Errorf( "failed to update status with %s condition: %w", - KonnectEntityAPIAuthConfigurationResolvedRefConditionType, err, + conditions.KonnectEntityAPIAuthConfigurationResolvedRefConditionType, err, ) } @@ -471,7 +475,7 @@ func getCPAuthRefForRef( }, nil } -func getAPIAuthRefNN[T SupportedKonnectEntityType, TEnt EntityType[T]]( +func getAPIAuthRefNN[T constraints.SupportedKonnectEntityType, TEnt constraints.EntityType[T]]( ctx context.Context, cl client.Client, ent TEnt, @@ -508,7 +512,7 @@ func getAPIAuthRefNN[T SupportedKonnectEntityType, TEnt EntityType[T]]( return getCPAuthRefForRef(ctx, cl, cpRef, ent.GetNamespace()) } - if ref, ok := any(ent).(EntityWithKonnectAPIAuthConfigurationRef); ok { + if ref, ok := any(ent).(constraints.EntityWithKonnectAPIAuthConfigurationRef); ok { return types.NamespacedName{ Name: ref.GetKonnectAPIAuthConfigurationRef().Name, // TODO(pmalek): enable if cross namespace refs are allowed @@ -522,7 +526,7 @@ func getAPIAuthRefNN[T SupportedKonnectEntityType, TEnt EntityType[T]]( ) } -func getServiceRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( +func getServiceRef[T constraints.SupportedKonnectEntityType, TEnt constraints.EntityType[T]]( e TEnt, ) mo.Option[configurationv1alpha1.ServiceRef] { switch e := any(e).(type) { @@ -544,7 +548,7 @@ func getServiceRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( // handleKongServiceRef handles the ServiceRef for the given entity. // It sets the owner reference to the referenced KongService and updates the // status of the entity based on the referenced KongService status. -func handleKongServiceRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( +func handleKongServiceRef[T constraints.SupportedKonnectEntityType, TEnt constraints.EntityType[T]]( ctx context.Context, cl client.Client, ent TEnt, @@ -565,9 +569,9 @@ func handleKongServiceRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( if err := cl.Get(ctx, nn, &svc); err != nil { if res, errStatus := updateStatusWithCondition( ctx, cl, ent, - KongServiceRefValidConditionType, + conditions.KongServiceRefValidConditionType, metav1.ConditionFalse, - KongServiceRefReasonInvalid, + conditions.KongServiceRefReasonInvalid, err.Error(), ); errStatus != nil || res.Requeue { return res, errStatus @@ -584,14 +588,14 @@ func handleKongServiceRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( } } - cond, ok := k8sutils.GetCondition(KonnectEntityProgrammedConditionType, &svc) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, &svc) if !ok || cond.Status != metav1.ConditionTrue { ent.SetKonnectID("") if res, err := updateStatusWithCondition( ctx, cl, ent, - KongServiceRefValidConditionType, + conditions.KongServiceRefValidConditionType, metav1.ConditionFalse, - KongServiceRefReasonInvalid, + conditions.KongServiceRefReasonInvalid, fmt.Sprintf("Referenced KongService %s is not programmed yet", nn), ); err != nil || res.Requeue { return ctrl.Result{}, err @@ -622,9 +626,9 @@ func handleKongServiceRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( if res, errStatus := updateStatusWithCondition( ctx, cl, ent, - KongServiceRefValidConditionType, + conditions.KongServiceRefValidConditionType, metav1.ConditionTrue, - KongServiceRefReasonValid, + conditions.KongServiceRefReasonValid, fmt.Sprintf("Referenced KongService %s programmed", nn), ); errStatus != nil || res.Requeue { return res, errStatus @@ -641,9 +645,9 @@ func handleKongServiceRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( if err != nil { if res, errStatus := updateStatusWithCondition( ctx, cl, ent, - ControlPlaneRefValidConditionType, + conditions.ControlPlaneRefValidConditionType, metav1.ConditionFalse, - ControlPlaneRefReasonInvalid, + conditions.ControlPlaneRefReasonInvalid, err.Error(), ); errStatus != nil || res.Requeue { return res, errStatus @@ -657,13 +661,13 @@ func handleKongServiceRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( return ctrl.Result{}, err } - cond, ok = k8sutils.GetCondition(KonnectEntityProgrammedConditionType, cp) + cond, ok = k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) if !ok || cond.Status != metav1.ConditionTrue || cond.ObservedGeneration != cp.GetGeneration() { if res, errStatus := updateStatusWithCondition( ctx, cl, ent, - ControlPlaneRefValidConditionType, + conditions.ControlPlaneRefValidConditionType, metav1.ConditionFalse, - ControlPlaneRefReasonInvalid, + conditions.ControlPlaneRefReasonInvalid, fmt.Sprintf("Referenced ControlPlane %s is not programmed yet", nn), ); errStatus != nil || res.Requeue { return res, errStatus @@ -681,9 +685,9 @@ func handleKongServiceRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( if res, errStatus := updateStatusWithCondition( ctx, cl, ent, - ControlPlaneRefValidConditionType, + conditions.ControlPlaneRefValidConditionType, metav1.ConditionTrue, - ControlPlaneRefReasonValid, + conditions.ControlPlaneRefReasonValid, fmt.Sprintf("Referenced ControlPlane %s is programmed", nn), ); errStatus != nil || res.Requeue { return res, errStatus @@ -696,7 +700,7 @@ func handleKongServiceRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( return ctrl.Result{}, nil } -func getControlPlaneRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( +func getControlPlaneRef[T constraints.SupportedKonnectEntityType, TEnt constraints.EntityType[T]]( e TEnt, ) mo.Option[configurationv1alpha1.ControlPlaneRef] { switch e := any(e).(type) { @@ -725,7 +729,7 @@ func getControlPlaneRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( // handleControlPlaneRef handles the ControlPlaneRef for the given entity. // It sets the owner reference to the referenced ControlPlane and updates the // status of the entity based on the referenced ControlPlane status. -func handleControlPlaneRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( +func handleControlPlaneRef[T constraints.SupportedKonnectEntityType, TEnt constraints.EntityType[T]]( ctx context.Context, cl client.Client, ent TEnt, @@ -746,9 +750,9 @@ func handleControlPlaneRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( if err := cl.Get(ctx, nn, &cp); err != nil { if res, errStatus := updateStatusWithCondition( ctx, cl, ent, - ControlPlaneRefValidConditionType, + conditions.ControlPlaneRefValidConditionType, metav1.ConditionFalse, - ControlPlaneRefReasonInvalid, + conditions.ControlPlaneRefReasonInvalid, err.Error(), ); errStatus != nil || res.Requeue { return res, errStatus @@ -762,13 +766,13 @@ func handleControlPlaneRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( return ctrl.Result{}, err } - cond, ok := k8sutils.GetCondition(KonnectEntityProgrammedConditionType, &cp) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, &cp) if !ok || cond.Status != metav1.ConditionTrue || cond.ObservedGeneration != cp.GetGeneration() { if res, errStatus := updateStatusWithCondition( ctx, cl, ent, - ControlPlaneRefValidConditionType, + conditions.ControlPlaneRefValidConditionType, metav1.ConditionFalse, - ControlPlaneRefReasonInvalid, + conditions.ControlPlaneRefReasonInvalid, fmt.Sprintf("Referenced ControlPlane %s is not programmed yet", nn), ); errStatus != nil || res.Requeue { return res, errStatus @@ -797,9 +801,9 @@ func handleControlPlaneRef[T SupportedKonnectEntityType, TEnt EntityType[T]]( if res, errStatus := updateStatusWithCondition( ctx, cl, ent, - ControlPlaneRefValidConditionType, + conditions.ControlPlaneRefValidConditionType, metav1.ConditionTrue, - ControlPlaneRefReasonValid, + conditions.ControlPlaneRefReasonValid, fmt.Sprintf("Referenced ControlPlane %s is programmed", nn), ); errStatus != nil || res.Requeue { return res, errStatus diff --git a/controller/konnect/reconciler_generic_test.go b/controller/konnect/reconciler_generic_test.go index d434220b5..44f8f4d07 100644 --- a/controller/konnect/reconciler_generic_test.go +++ b/controller/konnect/reconciler_generic_test.go @@ -8,6 +8,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" fakectrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + "github.com/kong/gateway-operator/controller/konnect/constraints" "github.com/kong/gateway-operator/modules/manager/scheme" configurationv1 "github.com/kong/kubernetes-configuration/api/configuration/v1" @@ -25,8 +26,8 @@ func TestNewKonnectEntityReconciler(t *testing.T) { } func testNewKonnectEntityReconciler[ - T SupportedKonnectEntityType, - TEnt EntityType[T], + T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T], ]( t *testing.T, ent T, diff --git a/controller/konnect/reconciler_konnectapiauth.go b/controller/konnect/reconciler_konnectapiauth.go index 3125e5cce..82c97c9f2 100644 --- a/controller/konnect/reconciler_konnectapiauth.go +++ b/controller/konnect/reconciler_konnectapiauth.go @@ -16,6 +16,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" + "github.com/kong/gateway-operator/controller/konnect/conditions" "github.com/kong/gateway-operator/controller/pkg/log" k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" @@ -119,9 +120,9 @@ func (r *KonnectAPIAuthConfigurationReconciler) Reconcile( if err != nil { if res, errStatus := updateStatusWithCondition( ctx, r.Client, &apiAuth, - KonnectEntityAPIAuthConfigurationValidConditionType, + conditions.KonnectEntityAPIAuthConfigurationValidConditionType, metav1.ConditionFalse, - KonnectEntityAPIAuthConfigurationReasonInvalid, + conditions.KonnectEntityAPIAuthConfigurationReasonInvalid, err.Error(), ); errStatus != nil || res.Requeue { return res, errStatus @@ -146,9 +147,9 @@ func (r *KonnectAPIAuthConfigurationReconciler) Reconcile( respOrg, err := sdk.Me.GetOrganizationsMe(ctx, sdkkonnectgoops.WithServerURL("https://"+apiAuth.Spec.ServerURL)) if err != nil { logger.Error(err, "failed to get organization info from Konnect") - if cond, ok := k8sutils.GetCondition(KonnectEntityAPIAuthConfigurationValidConditionType, &apiAuth); !ok || + if cond, ok := k8sutils.GetCondition(conditions.KonnectEntityAPIAuthConfigurationValidConditionType, &apiAuth); !ok || cond.Status != metav1.ConditionFalse || - cond.Reason != KonnectEntityAPIAuthConfigurationReasonInvalid || + cond.Reason != conditions.KonnectEntityAPIAuthConfigurationReasonInvalid || cond.ObservedGeneration != apiAuth.GetGeneration() || apiAuth.Status.OrganizationID != "" || apiAuth.Status.ServerURL != apiAuth.Spec.ServerURL { @@ -158,9 +159,9 @@ func (r *KonnectAPIAuthConfigurationReconciler) Reconcile( res, errUpdate := updateStatusWithCondition( ctx, r.Client, &apiAuth, - KonnectEntityAPIAuthConfigurationValidConditionType, + conditions.KonnectEntityAPIAuthConfigurationValidConditionType, metav1.ConditionFalse, - KonnectEntityAPIAuthConfigurationReasonInvalid, + conditions.KonnectEntityAPIAuthConfigurationReasonInvalid, err.Error(), ) if errUpdate != nil || res.Requeue { @@ -183,10 +184,10 @@ func (r *KonnectAPIAuthConfigurationReconciler) Reconcile( } condMessage = fmt.Sprintf("Token from Secret %s is valid", nn) } - if cond, ok := k8sutils.GetCondition(KonnectEntityAPIAuthConfigurationValidConditionType, &apiAuth); !ok || + if cond, ok := k8sutils.GetCondition(conditions.KonnectEntityAPIAuthConfigurationValidConditionType, &apiAuth); !ok || cond.Status != metav1.ConditionTrue || cond.Message != condMessage || - cond.Reason != KonnectEntityAPIAuthConfigurationReasonValid || + cond.Reason != conditions.KonnectEntityAPIAuthConfigurationReasonValid || cond.ObservedGeneration != apiAuth.GetGeneration() || apiAuth.Status.OrganizationID != *respOrg.MeOrganization.ID || apiAuth.Status.ServerURL != apiAuth.Spec.ServerURL { @@ -196,9 +197,9 @@ func (r *KonnectAPIAuthConfigurationReconciler) Reconcile( res, err := updateStatusWithCondition( ctx, r.Client, &apiAuth, - KonnectEntityAPIAuthConfigurationValidConditionType, + conditions.KonnectEntityAPIAuthConfigurationValidConditionType, metav1.ConditionTrue, - KonnectEntityAPIAuthConfigurationReasonValid, + conditions.KonnectEntityAPIAuthConfigurationReasonValid, condMessage, ) if err != nil || res.Requeue { diff --git a/controller/konnect/watch.go b/controller/konnect/watch.go index 8277acd11..5973a43b2 100644 --- a/controller/konnect/watch.go +++ b/controller/konnect/watch.go @@ -6,6 +6,8 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/kong/gateway-operator/controller/konnect/constraints" + 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" @@ -15,8 +17,8 @@ import ( // ReconciliationWatchOptionsForEntity returns the watch options for the given // Konnect entity type. func ReconciliationWatchOptionsForEntity[ - T SupportedKonnectEntityType, - TEnt EntityType[T], + T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T], ]( cl client.Client, ent TEnt, diff --git a/controller/konnect/watch_test.go b/controller/konnect/watch_test.go index 0a406a2eb..6c23a442e 100644 --- a/controller/konnect/watch_test.go +++ b/controller/konnect/watch_test.go @@ -6,6 +6,8 @@ import ( "github.com/stretchr/testify/require" fakectrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + "github.com/kong/gateway-operator/controller/konnect/constraints" + configurationv1 "github.com/kong/kubernetes-configuration/api/configuration/v1" configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" @@ -19,8 +21,8 @@ func TestWatchOptions(t *testing.T) { } func testReconciliationWatchOptionsForEntity[ - T SupportedKonnectEntityType, - TEnt EntityType[T], + T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T], ]( t *testing.T, ent TEnt, diff --git a/go.mod b/go.mod index 369837057..2c276a3a2 100644 --- a/go.mod +++ b/go.mod @@ -86,6 +86,7 @@ require ( github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.3.1 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/texttheater/golang-levenshtein v1.0.1 // indirect github.com/tidwall/gjson v1.17.3 // indirect github.com/tidwall/match v1.1.1 // indirect From e97b1b4d702279aa859257de356d6043d7bfd445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Ma=C5=82ek?= Date: Fri, 23 Aug 2024 19:11:26 +0200 Subject: [PATCH 3/6] tests: add mocks for KongService SDK --- .mockery.yaml | 9 +- ...mock_test.go => controlplane_mock_test.go} | 0 .../konnect/ops/kongservice_mock_test.go | 264 +++++++++ controller/konnect/ops/ops.go | 2 +- .../konnect/ops/ops_controlplane_test.go | 8 +- controller/konnect/ops/ops_kongservice.go | 63 ++- .../konnect/ops/ops_kongservice_test.go | 501 ++++++++++++++++++ 7 files changed, 809 insertions(+), 38 deletions(-) rename controller/konnect/ops/{controlplanesdk_mock_test.go => controlplane_mock_test.go} (100%) create mode 100644 controller/konnect/ops/kongservice_mock_test.go create mode 100644 controller/konnect/ops/ops_kongservice_test.go diff --git a/.mockery.yaml b/.mockery.yaml index 8497c5f34..36d799f63 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -3,12 +3,13 @@ inpackage: True disable-version-string: True with-expecter: True -filename: "{{.InterfaceName | lower}}_mock_test.go" -dir: "{{.InterfaceDir}}" -mockname: "Mock{{.InterfaceName}}" -outpkg: "{{.PackageName}}" +filename: "{{ trimSuffix .InterfaceFile \".go\" | base | lower }}_mock_test.go" +dir: "{{ .InterfaceDir }}" +mockname: "Mock{{ .InterfaceName }}" +outpkg: "{{ .PackageName }}" packages: github.com/kong/gateway-operator/controller/konnect/ops: interfaces: ControlPlaneSDK: + ServicesSDK: diff --git a/controller/konnect/ops/controlplanesdk_mock_test.go b/controller/konnect/ops/controlplane_mock_test.go similarity index 100% rename from controller/konnect/ops/controlplanesdk_mock_test.go rename to controller/konnect/ops/controlplane_mock_test.go diff --git a/controller/konnect/ops/kongservice_mock_test.go b/controller/konnect/ops/kongservice_mock_test.go new file mode 100644 index 000000000..9340df0f0 --- /dev/null +++ b/controller/konnect/ops/kongservice_mock_test.go @@ -0,0 +1,264 @@ +// Code generated by mockery. DO NOT EDIT. + +package ops + +import ( + context "context" + + components "github.com/Kong/sdk-konnect-go/models/components" + + mock "github.com/stretchr/testify/mock" + + operations "github.com/Kong/sdk-konnect-go/models/operations" +) + +// MockServicesSDK is an autogenerated mock type for the ServicesSDK type +type MockServicesSDK struct { + mock.Mock +} + +type MockServicesSDK_Expecter struct { + mock *mock.Mock +} + +func (_m *MockServicesSDK) EXPECT() *MockServicesSDK_Expecter { + return &MockServicesSDK_Expecter{mock: &_m.Mock} +} + +// CreateService provides a mock function with given fields: ctx, controlPlaneID, service, opts +func (_m *MockServicesSDK) CreateService(ctx context.Context, controlPlaneID string, service components.ServiceInput, opts ...operations.Option) (*operations.CreateServiceResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, controlPlaneID, service) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for CreateService") + } + + var r0 *operations.CreateServiceResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, components.ServiceInput, ...operations.Option) (*operations.CreateServiceResponse, error)); ok { + return rf(ctx, controlPlaneID, service, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, components.ServiceInput, ...operations.Option) *operations.CreateServiceResponse); ok { + r0 = rf(ctx, controlPlaneID, service, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*operations.CreateServiceResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, components.ServiceInput, ...operations.Option) error); ok { + r1 = rf(ctx, controlPlaneID, service, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockServicesSDK_CreateService_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateService' +type MockServicesSDK_CreateService_Call struct { + *mock.Call +} + +// CreateService is a helper method to define mock.On call +// - ctx context.Context +// - controlPlaneID string +// - service components.ServiceInput +// - opts ...operations.Option +func (_e *MockServicesSDK_Expecter) CreateService(ctx interface{}, controlPlaneID interface{}, service interface{}, opts ...interface{}) *MockServicesSDK_CreateService_Call { + return &MockServicesSDK_CreateService_Call{Call: _e.mock.On("CreateService", + append([]interface{}{ctx, controlPlaneID, service}, opts...)...)} +} + +func (_c *MockServicesSDK_CreateService_Call) Run(run func(ctx context.Context, controlPlaneID string, service components.ServiceInput, opts ...operations.Option)) *MockServicesSDK_CreateService_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]operations.Option, len(args)-3) + for i, a := range args[3:] { + if a != nil { + variadicArgs[i] = a.(operations.Option) + } + } + run(args[0].(context.Context), args[1].(string), args[2].(components.ServiceInput), variadicArgs...) + }) + return _c +} + +func (_c *MockServicesSDK_CreateService_Call) Return(_a0 *operations.CreateServiceResponse, _a1 error) *MockServicesSDK_CreateService_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockServicesSDK_CreateService_Call) RunAndReturn(run func(context.Context, string, components.ServiceInput, ...operations.Option) (*operations.CreateServiceResponse, error)) *MockServicesSDK_CreateService_Call { + _c.Call.Return(run) + return _c +} + +// DeleteService provides a mock function with given fields: ctx, controlPlaneID, serviceID, opts +func (_m *MockServicesSDK) DeleteService(ctx context.Context, controlPlaneID string, serviceID string, opts ...operations.Option) (*operations.DeleteServiceResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, controlPlaneID, serviceID) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for DeleteService") + } + + var r0 *operations.DeleteServiceResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, ...operations.Option) (*operations.DeleteServiceResponse, error)); ok { + return rf(ctx, controlPlaneID, serviceID, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, ...operations.Option) *operations.DeleteServiceResponse); ok { + r0 = rf(ctx, controlPlaneID, serviceID, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*operations.DeleteServiceResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, ...operations.Option) error); ok { + r1 = rf(ctx, controlPlaneID, serviceID, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockServicesSDK_DeleteService_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteService' +type MockServicesSDK_DeleteService_Call struct { + *mock.Call +} + +// DeleteService is a helper method to define mock.On call +// - ctx context.Context +// - controlPlaneID string +// - serviceID string +// - opts ...operations.Option +func (_e *MockServicesSDK_Expecter) DeleteService(ctx interface{}, controlPlaneID interface{}, serviceID interface{}, opts ...interface{}) *MockServicesSDK_DeleteService_Call { + return &MockServicesSDK_DeleteService_Call{Call: _e.mock.On("DeleteService", + append([]interface{}{ctx, controlPlaneID, serviceID}, opts...)...)} +} + +func (_c *MockServicesSDK_DeleteService_Call) Run(run func(ctx context.Context, controlPlaneID string, serviceID string, opts ...operations.Option)) *MockServicesSDK_DeleteService_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]operations.Option, len(args)-3) + for i, a := range args[3:] { + if a != nil { + variadicArgs[i] = a.(operations.Option) + } + } + run(args[0].(context.Context), args[1].(string), args[2].(string), variadicArgs...) + }) + return _c +} + +func (_c *MockServicesSDK_DeleteService_Call) Return(_a0 *operations.DeleteServiceResponse, _a1 error) *MockServicesSDK_DeleteService_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockServicesSDK_DeleteService_Call) RunAndReturn(run func(context.Context, string, string, ...operations.Option) (*operations.DeleteServiceResponse, error)) *MockServicesSDK_DeleteService_Call { + _c.Call.Return(run) + return _c +} + +// UpsertService provides a mock function with given fields: ctx, req, opts +func (_m *MockServicesSDK) UpsertService(ctx context.Context, req operations.UpsertServiceRequest, opts ...operations.Option) (*operations.UpsertServiceResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, req) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for UpsertService") + } + + var r0 *operations.UpsertServiceResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, operations.UpsertServiceRequest, ...operations.Option) (*operations.UpsertServiceResponse, error)); ok { + return rf(ctx, req, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, operations.UpsertServiceRequest, ...operations.Option) *operations.UpsertServiceResponse); ok { + r0 = rf(ctx, req, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*operations.UpsertServiceResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, operations.UpsertServiceRequest, ...operations.Option) error); ok { + r1 = rf(ctx, req, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockServicesSDK_UpsertService_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpsertService' +type MockServicesSDK_UpsertService_Call struct { + *mock.Call +} + +// UpsertService is a helper method to define mock.On call +// - ctx context.Context +// - req operations.UpsertServiceRequest +// - opts ...operations.Option +func (_e *MockServicesSDK_Expecter) UpsertService(ctx interface{}, req interface{}, opts ...interface{}) *MockServicesSDK_UpsertService_Call { + return &MockServicesSDK_UpsertService_Call{Call: _e.mock.On("UpsertService", + append([]interface{}{ctx, req}, opts...)...)} +} + +func (_c *MockServicesSDK_UpsertService_Call) Run(run func(ctx context.Context, req operations.UpsertServiceRequest, opts ...operations.Option)) *MockServicesSDK_UpsertService_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]operations.Option, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(operations.Option) + } + } + run(args[0].(context.Context), args[1].(operations.UpsertServiceRequest), variadicArgs...) + }) + return _c +} + +func (_c *MockServicesSDK_UpsertService_Call) Return(_a0 *operations.UpsertServiceResponse, _a1 error) *MockServicesSDK_UpsertService_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockServicesSDK_UpsertService_Call) RunAndReturn(run func(context.Context, operations.UpsertServiceRequest, ...operations.Option) (*operations.UpsertServiceResponse, error)) *MockServicesSDK_UpsertService_Call { + _c.Call.Return(run) + return _c +} + +// NewMockServicesSDK creates a new instance of MockServicesSDK. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockServicesSDK(t interface { + mock.TestingT + Cleanup(func()) +}) *MockServicesSDK { + mock := &MockServicesSDK{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/controller/konnect/ops/ops.go b/controller/konnect/ops/ops.go index 91bebe1dc..332e9c051 100644 --- a/controller/konnect/ops/ops.go +++ b/controller/konnect/ops/ops.go @@ -148,7 +148,7 @@ func Update[ case *konnectv1alpha1.KonnectControlPlane: return ctrl.Result{}, updateControlPlane(ctx, sdk.ControlPlanes, ent) case *configurationv1alpha1.KongService: - return ctrl.Result{}, updateService(ctx, sdk.Services, cl, ent) + return ctrl.Result{}, updateService(ctx, sdk.Services, ent) case *configurationv1alpha1.KongRoute: return ctrl.Result{}, updateRoute(ctx, sdk.Routes, cl, ent) case *configurationv1.KongConsumer: diff --git a/controller/konnect/ops/ops_controlplane_test.go b/controller/konnect/ops/ops_controlplane_test.go index dc40c3b87..73c39fb42 100644 --- a/controller/konnect/ops/ops_controlplane_test.go +++ b/controller/konnect/ops/ops_controlplane_test.go @@ -53,7 +53,7 @@ func TestCreateControlPlane(t *testing.T) { return sdk, cp }, assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectControlPlane) { - assert.Equal(t, "12345", cp.Status.GetKonnectID()) + assert.Equal(t, "12345", cp.GetKonnectStatus().GetKonnectID()) cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) require.True(t, ok, "Programmed condition not set on KonnectControlPlane") assert.Equal(t, metav1.ConditionTrue, cond.Status) @@ -97,7 +97,7 @@ func TestCreateControlPlane(t *testing.T) { assert.Equal(t, metav1.ConditionFalse, cond.Status) assert.Equal(t, "FailedToCreate", cond.Reason) assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) - assert.Equal(t, "failed to create KonnectControlPlane default/cp-1: {\"status\":400,\"title\":\"\",\"instance\":\"\",\"detail\":\"bad request\",\"invalid_parameters\":null}", cond.Message) + assert.Equal(t, `failed to create KonnectControlPlane default/cp-1: {"status":400,"title":"","instance":"","detail":"bad request","invalid_parameters":null}`, cond.Message) }, expectedErr: true, }, @@ -197,7 +197,7 @@ func TestDeleteControlPlane(t *testing.T) { expectedErr: true, }, { - name: "not found error is ignore and considered a success when trying to delete", + name: "not found error is ignored and considered a success when trying to delete", mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { sdk := &MockControlPlaneSDK{} cp := &konnectv1alpha1.KonnectControlPlane{ @@ -358,7 +358,7 @@ func TestUpdateControlPlane(t *testing.T) { assert.Equal(t, metav1.ConditionFalse, cond.Status) assert.Equal(t, "FailedToUpdate", cond.Reason) assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) - assert.Equal(t, "failed to update KonnectControlPlane default/cp-1: {\"status\":400,\"title\":\"\",\"instance\":\"\",\"detail\":\"bad request\",\"invalid_parameters\":null}", cond.Message) + assert.Equal(t, `failed to update KonnectControlPlane default/cp-1: {"status":400,"title":"","instance":"","detail":"bad request","invalid_parameters":null}`, cond.Message) }, expectedErr: true, }, diff --git a/controller/konnect/ops/ops_kongservice.go b/controller/konnect/ops/ops_kongservice.go index 9dce8957b..9a69a1d15 100644 --- a/controller/konnect/ops/ops_kongservice.go +++ b/controller/konnect/ops/ops_kongservice.go @@ -9,7 +9,6 @@ import ( sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" @@ -17,7 +16,6 @@ import ( k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" - konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" ) func createService( @@ -26,7 +24,10 @@ func createService( svc *configurationv1alpha1.KongService, ) error { if svc.GetControlPlaneID() == "" { - return fmt.Errorf("can't create %T %s without a Konnect ControlPlane ID", svc, client.ObjectKeyFromObject(svc)) + return fmt.Errorf( + "can't create %T %s without a Konnect ControlPlane ID", + svc, client.ObjectKeyFromObject(svc), + ) } resp, err := sdk.CreateService(ctx, @@ -73,37 +74,19 @@ func createService( func updateService( ctx context.Context, sdk ServicesSDK, - cl client.Client, svc *configurationv1alpha1.KongService, ) error { - if svc.Spec.ControlPlaneRef == nil { - return fmt.Errorf("can't update %T without a ControlPlaneRef", svc) - } - - // TODO(pmalek) handle other types of CP ref - // TODO(pmalek) handle cross namespace refs - nnCP := types.NamespacedName{ - Namespace: svc.Namespace, - Name: svc.Spec.ControlPlaneRef.KonnectNamespacedRef.Name, - } - var cp konnectv1alpha1.KonnectControlPlane - if err := cl.Get(ctx, nnCP, &cp); err != nil { - return fmt.Errorf("failed to get KonnectControlPlane %s: for %T %s: %w", - nnCP, svc, client.ObjectKeyFromObject(svc), err, - ) - } - - if cp.Status.ID == "" { - return fmt.Errorf( - "can't update %T when referenced KonnectControlPlane %s does not have the Konnect ID", - svc, nnCP, + if svc.GetControlPlaneID() == "" { + return fmt.Errorf("can't update %T %s without a Konnect ControlPlane ID", + svc, client.ObjectKeyFromObject(svc), ) } + id := svc.GetKonnectStatus().GetKonnectID() resp, err := sdk.UpsertService(ctx, sdkkonnectgoops.UpsertServiceRequest{ - ControlPlaneID: cp.Status.ID, - ServiceID: svc.GetKonnectStatus().GetKonnectID(), + ControlPlaneID: svc.GetControlPlaneID(), + ServiceID: id, Service: kongServiceToSDKServiceInput(svc), }, ) @@ -112,11 +95,33 @@ func updateService( // Can't adopt it as it will cause conflicts between the controller // that created that entity and already manages it, hm if errWrapped := wrapErrIfKonnectOpFailed(err, UpdateOp, svc); errWrapped != nil { + // Service update operation returns an SDKError instead of a NotFoundError. + var sdkError *sdkkonnectgoerrs.SDKError + if errors.As(errWrapped, &sdkError) { + switch sdkError.StatusCode { + case 404: + if err := createService(ctx, sdk, svc); err != nil { + return FailedKonnectOpError[configurationv1alpha1.KongService]{ + Op: UpdateOp, + Err: err, + } + } + // Create succeeded, createService sets the status so no need to do this here. + + return nil + default: + return FailedKonnectOpError[configurationv1alpha1.KongService]{ + Op: UpdateOp, + Err: sdkError, + } + } + } + k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( conditions.KonnectEntityProgrammedConditionType, metav1.ConditionFalse, - "FailedToCreate", + "FailedToUpdate", errWrapped.Error(), svc.GetGeneration(), ), @@ -126,7 +131,7 @@ func updateService( } svc.Status.Konnect.SetKonnectID(*resp.Service.ID) - svc.Status.Konnect.SetControlPlaneID(cp.Status.ID) + svc.Status.Konnect.SetControlPlaneID(svc.GetControlPlaneID()) k8sutils.SetCondition( k8sutils.NewConditionWithGeneration( conditions.KonnectEntityProgrammedConditionType, diff --git a/controller/konnect/ops/ops_kongservice_test.go b/controller/konnect/ops/ops_kongservice_test.go new file mode 100644 index 000000000..2903ef7f4 --- /dev/null +++ b/controller/konnect/ops/ops_kongservice_test.go @@ -0,0 +1,501 @@ +package ops + +import ( + "context" + "testing" + + sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" + "github.com/samber/lo" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kong/gateway-operator/controller/konnect/conditions" + k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" + + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" +) + +func TestCreateKongService(t *testing.T) { + ctx := context.Background() + testCases := []struct { + name string + mockServicePair func() (*MockServicesSDK, *configurationv1alpha1.KongService) + expectedErr bool + assertions func(*testing.T, *configurationv1alpha1.KongService) + }{ + { + name: "success", + mockServicePair: func() (*MockServicesSDK, *configurationv1alpha1.KongService) { + sdk := &MockServicesSDK{} + svc := &configurationv1alpha1.KongService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "svc-1", + Namespace: "default", + }, + Spec: configurationv1alpha1.KongServiceSpec{ + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Name: lo.ToPtr("svc-1"), + Host: "example.com", + }, + }, + Status: configurationv1alpha1.KongServiceStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + ControlPlaneID: "123456789", + }, + }, + } + + sdk. + EXPECT(). + CreateService(ctx, "123456789", kongServiceToSDKServiceInput(svc)). + Return( + &sdkkonnectgoops.CreateServiceResponse{ + Service: &sdkkonnectgocomp.Service{ + ID: lo.ToPtr("12345"), + Host: "example.com", + Name: lo.ToPtr("svc-1"), + }, + }, + nil, + ) + + return sdk, svc + }, + assertions: func(t *testing.T, svc *configurationv1alpha1.KongService) { + assert.Equal(t, "12345", svc.GetKonnectStatus().GetKonnectID()) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, svc) + require.True(t, ok, "Programmed condition not set on KongService") + assert.Equal(t, metav1.ConditionTrue, cond.Status) + assert.Equal(t, conditions.KonnectEntityProgrammedReasonProgrammed, cond.Reason) + assert.Equal(t, svc.GetGeneration(), cond.ObservedGeneration) + }, + }, + { + name: "fail - no control plane ID in status returns an error and does not create the Service in Konnect", + mockServicePair: func() (*MockServicesSDK, *configurationv1alpha1.KongService) { + sdk := &MockServicesSDK{} + svc := &configurationv1alpha1.KongService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "svc-1", + Namespace: "default", + }, + Spec: configurationv1alpha1.KongServiceSpec{ + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Name: lo.ToPtr("svc-1"), + Host: "example.com", + }, + }, + } + + return sdk, svc + }, + assertions: func(t *testing.T, svc *configurationv1alpha1.KongService) { + assert.Equal(t, "", svc.GetKonnectStatus().GetKonnectID()) + // TODO: we should probably set a condition when the control plane ID is missing in the status. + }, + expectedErr: true, + }, + { + name: "fail", + mockServicePair: func() (*MockServicesSDK, *configurationv1alpha1.KongService) { + sdk := &MockServicesSDK{} + svc := &configurationv1alpha1.KongService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "svc-1", + Namespace: "default", + }, + Spec: configurationv1alpha1.KongServiceSpec{ + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Name: lo.ToPtr("svc-1"), + Host: "example.com", + }, + }, + Status: configurationv1alpha1.KongServiceStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + ControlPlaneID: "123456789", + }, + }, + } + + sdk. + EXPECT(). + CreateService(ctx, "123456789", kongServiceToSDKServiceInput(svc)). + Return( + nil, + &sdkkonnectgoerrs.BadRequestError{ + Status: 400, + Detail: "bad request", + }, + ) + + return sdk, svc + }, + assertions: func(t *testing.T, svc *configurationv1alpha1.KongService) { + assert.Equal(t, "", svc.GetKonnectStatus().GetKonnectID()) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, svc) + require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + assert.Equal(t, metav1.ConditionFalse, cond.Status) + assert.Equal(t, "FailedToCreate", cond.Reason) + assert.Equal(t, svc.GetGeneration(), cond.ObservedGeneration) + assert.Equal(t, `failed to create KongService default/svc-1: {"status":400,"title":"","instance":"","detail":"bad request","invalid_parameters":null}`, cond.Message) + }, + expectedErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sdk, svc := tc.mockServicePair() + + err := createService(ctx, sdk, svc) + t.Cleanup(func() { + assert.True(t, sdk.AssertExpectations(t)) + }) + + tc.assertions(t, svc) + + if tc.expectedErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + }) + } +} + +func TestDeleteKongService(t *testing.T) { + ctx := context.Background() + testCases := []struct { + name string + mockServicePair func() (*MockServicesSDK, *configurationv1alpha1.KongService) + expectedErr bool + assertions func(*testing.T, *configurationv1alpha1.KongService) + }{ + { + name: "success", + mockServicePair: func() (*MockServicesSDK, *configurationv1alpha1.KongService) { + sdk := &MockServicesSDK{} + svc := &configurationv1alpha1.KongService{ + Spec: configurationv1alpha1.KongServiceSpec{ + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Name: lo.ToPtr("svc-1"), + }, + }, + Status: configurationv1alpha1.KongServiceStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + ControlPlaneID: "12345", + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "123456789", + }, + }, + }, + } + sdk. + EXPECT(). + DeleteService(ctx, "12345", "123456789"). + Return( + &sdkkonnectgoops.DeleteServiceResponse{ + StatusCode: 200, + }, + nil, + ) + + return sdk, svc + }, + }, + { + name: "fail", + mockServicePair: func() (*MockServicesSDK, *configurationv1alpha1.KongService) { + sdk := &MockServicesSDK{} + svc := &configurationv1alpha1.KongService{ + Spec: configurationv1alpha1.KongServiceSpec{ + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Name: lo.ToPtr("svc-1"), + }, + }, + Status: configurationv1alpha1.KongServiceStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + ControlPlaneID: "12345", + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "123456789", + }, + }, + }, + } + sdk. + EXPECT(). + DeleteService(ctx, "12345", "123456789"). + Return( + nil, + &sdkkonnectgoerrs.BadRequestError{ + Status: 400, + Detail: "bad request", + }, + ) + + return sdk, svc + }, + expectedErr: true, + }, + { + name: "not found error is ignored and considered a success when trying to delete", + mockServicePair: func() (*MockServicesSDK, *configurationv1alpha1.KongService) { + sdk := &MockServicesSDK{} + svc := &configurationv1alpha1.KongService{ + Spec: configurationv1alpha1.KongServiceSpec{ + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Name: lo.ToPtr("svc-1"), + }, + }, + Status: configurationv1alpha1.KongServiceStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + ControlPlaneID: "12345", + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "123456789", + }, + }, + }, + } + sdk. + EXPECT(). + DeleteService(ctx, "12345", "123456789"). + Return( + nil, + &sdkkonnectgoerrs.SDKError{ + Message: "not found", + StatusCode: 404, + }, + ) + + return sdk, svc + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sdk, svc := tc.mockServicePair() + + err := deleteService(ctx, sdk, svc) + + if tc.assertions != nil { + tc.assertions(t, svc) + } + + if tc.expectedErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.True(t, sdk.AssertExpectations(t)) + }) + } +} + +func TestUpdateKongService(t *testing.T) { + ctx := context.Background() + testCases := []struct { + name string + mockServicePair func() (*MockServicesSDK, *configurationv1alpha1.KongService) + expectedErr bool + assertions func(*testing.T, *configurationv1alpha1.KongService) + }{ + { + name: "success", + mockServicePair: func() (*MockServicesSDK, *configurationv1alpha1.KongService) { + sdk := &MockServicesSDK{} + svc := &configurationv1alpha1.KongService{ + Spec: configurationv1alpha1.KongServiceSpec{ + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Name: lo.ToPtr("svc-1"), + }, + }, + Status: configurationv1alpha1.KongServiceStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + ControlPlaneID: "12345", + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "123456789", + }, + }, + }, + } + sdk. + EXPECT(). + UpsertService(ctx, + sdkkonnectgoops.UpsertServiceRequest{ + ControlPlaneID: "12345", + ServiceID: "123456789", + Service: kongServiceToSDKServiceInput(svc), + }, + ). + Return( + &sdkkonnectgoops.UpsertServiceResponse{ + StatusCode: 200, + Service: &sdkkonnectgocomp.Service{ + ID: lo.ToPtr("123456789"), + Name: lo.ToPtr("svc-1"), + }, + }, + nil, + ) + + return sdk, svc + }, + assertions: func(t *testing.T, svc *configurationv1alpha1.KongService) { + assert.Equal(t, "123456789", svc.GetKonnectStatus().GetKonnectID()) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, svc) + require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + assert.Equal(t, metav1.ConditionTrue, cond.Status) + assert.Equal(t, conditions.KonnectEntityProgrammedReasonProgrammed, cond.Reason) + assert.Equal(t, svc.GetGeneration(), cond.ObservedGeneration) + assert.Equal(t, "", cond.Message) + }, + }, + { + name: "fail", + mockServicePair: func() (*MockServicesSDK, *configurationv1alpha1.KongService) { + sdk := &MockServicesSDK{} + svc := &configurationv1alpha1.KongService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "svc-1", + Namespace: "default", + }, + Spec: configurationv1alpha1.KongServiceSpec{ + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Name: lo.ToPtr("svc-1"), + }, + }, + Status: configurationv1alpha1.KongServiceStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + ControlPlaneID: "12345", + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "123456789", + }, + }, + }, + } + sdk. + EXPECT(). + UpsertService(ctx, + sdkkonnectgoops.UpsertServiceRequest{ + ControlPlaneID: "12345", + ServiceID: "123456789", + Service: kongServiceToSDKServiceInput(svc), + }, + ). + Return( + nil, + &sdkkonnectgoerrs.BadRequestError{ + Status: 400, + Title: "bad request", + }, + ) + + return sdk, svc + }, + assertions: func(t *testing.T, svc *configurationv1alpha1.KongService) { + // TODO: When we fail to update a KongService, do we want to clear + // the Konnect ID from the status? Probably not. + // assert.Equal(t, "", svc.GetKonnectStatus().GetKonnectID()) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, svc) + require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + assert.Equal(t, metav1.ConditionFalse, cond.Status) + assert.Equal(t, "FailedToUpdate", cond.Reason) + assert.Equal(t, svc.GetGeneration(), cond.ObservedGeneration) + assert.Equal(t, `failed to update KongService default/svc-1: {"status":400,"title":"bad request","instance":"","detail":"","invalid_parameters":null}`, cond.Message) + }, + expectedErr: true, + }, + { + name: "when not found then try to create", + mockServicePair: func() (*MockServicesSDK, *configurationv1alpha1.KongService) { + sdk := &MockServicesSDK{} + svc := &configurationv1alpha1.KongService{ + ObjectMeta: metav1.ObjectMeta{ + Name: "svc-1", + Namespace: "default", + }, + Spec: configurationv1alpha1.KongServiceSpec{ + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Name: lo.ToPtr("svc-1"), + }, + }, + Status: configurationv1alpha1.KongServiceStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + ControlPlaneID: "12345", + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "123456789", + }, + }, + }, + } + sdk. + EXPECT(). + UpsertService(ctx, + sdkkonnectgoops.UpsertServiceRequest{ + ControlPlaneID: "12345", + ServiceID: "123456789", + Service: kongServiceToSDKServiceInput(svc), + }, + ). + Return( + nil, + &sdkkonnectgoerrs.SDKError{ + StatusCode: 404, + Message: "not found", + }, + ) + + sdk. + EXPECT(). + CreateService(ctx, "12345", kongServiceToSDKServiceInput(svc)). + Return( + &sdkkonnectgoops.CreateServiceResponse{ + Service: &sdkkonnectgocomp.Service{ + ID: lo.ToPtr("123456789"), + Name: lo.ToPtr("svc-1"), + }, + }, + nil, + ) + + return sdk, svc + }, + assertions: func(t *testing.T, svc *configurationv1alpha1.KongService) { + assert.Equal(t, "123456789", svc.GetKonnectStatus().GetKonnectID()) + cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, svc) + require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + assert.Equal(t, metav1.ConditionTrue, cond.Status) + assert.Equal(t, conditions.KonnectEntityProgrammedReasonProgrammed, cond.Reason) + assert.Equal(t, svc.GetGeneration(), cond.ObservedGeneration) + assert.Equal(t, "", cond.Message) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sdk, svc := tc.mockServicePair() + + err := updateService(ctx, sdk, svc) + + if tc.assertions != nil { + tc.assertions(t, svc) + } + + if tc.expectedErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.True(t, sdk.AssertExpectations(t)) + }) + } +} From 991388e80befac6555074f3f9715fa6070436e8c Mon Sep 17 00:00:00 2001 From: Tao Yi Date: Tue, 3 Sep 2024 18:31:32 +0800 Subject: [PATCH 4/6] Resolve conflict for "refactor(konnect): add SDK interface types and theirs mocks"(#507) (#542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(cli): configurable certgen images (#516) * feat(cli): configurable certgen images Add two new command line options to the manager. -webhook-certificate-config-base-image string The base image for the certgen Jobs. (default "registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.3.0") -webhook-certificate-config-shell-image string The shell image for the certgen Jobs. (default "busybox") Those are optional. If you omit them the defaults will be used which are the previous hard coded values. Signed-off-by: Arpad Kunszt * feat(cli): cleanup code per review Removed unnecessary variable assignments. Signed-off-by: Arpad Kunszt * feat(cli): add CHANGELOG entry Signed-off-by: Arpad Kunszt * feat(cli): unit tests handle new arguments Signed-off-by: Arpad Kunszt * feat(cli): move new configuration into Config Also created a constant for the shell image, so it is no more a hard coded string hidden in the code. Signed-off-by: Arpad Kunszt * feat(cli): added UT for the new arguments The new unit test tests only if the command line arguments are set in the configuration. The other cases, the default configuration, the environmental variable handling are already tested in previous cases. The test does not cover if the values from the configuration are actually used but at the moment there are no tests for that part of the code at all. Signed-off-by: Arpad Kunszt --------- Signed-off-by: Arpad Kunszt * chore(deps): bump github.com/kong/kubernetes-configuration (#523) Bumps [github.com/kong/kubernetes-configuration](https://github.com/kong/kubernetes-configuration) from 0.0.8 to 0.0.9. - [Commits](https://github.com/kong/kubernetes-configuration/compare/v0.0.8...v0.0.9) --- updated-dependencies: - dependency-name: github.com/kong/kubernetes-configuration dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github.com/kong/kubernetes-ingress-controller/v3 from 3.2.4 to 3.3.0 (#531) * chore(deps): bump github.com/kong/kubernetes-ingress-controller/v3 Bumps [github.com/kong/kubernetes-ingress-controller/v3](https://github.com/kong/kubernetes-ingress-controller) from 3.2.4 to 3.3.0. - [Release notes](https://github.com/kong/kubernetes-ingress-controller/releases) - [Changelog](https://github.com/Kong/kubernetes-ingress-controller/blob/main/CHANGELOG.md) - [Commits](https://github.com/kong/kubernetes-ingress-controller/compare/v3.2.4...v3.3.0) --- updated-dependencies: - dependency-name: github.com/kong/kubernetes-ingress-controller/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: go mod tidy --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jakub Warczarek * `KongPluginBinding` reconciler (#513) * feat: KongPluginBinding reconciler Signed-off-by: Mattia Lavacca * chore: fix linter Signed-off-by: Mattia Lavacca * chore: CHANGELOG updated Signed-off-by: Mattia Lavacca * Update controller/konnect/ops_kongpluginbinding.go Co-authored-by: Grzegorz Burzyński --------- Signed-off-by: Mattia Lavacca Co-authored-by: Grzegorz Burzyński * chore(deps): update kong/kubernetes-ingress-controller docker tag to v3.3.0 (#532) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update kong/kubernetes-ingress-controller docker tag to v3.3.1 (#534) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Add watch for KongPlugin and KongClusterPlugin in KongPluginBinding controller (#535) * chore(deps): bump github.com/kong/kubernetes-ingress-controller/v3 (#536) Bumps [github.com/kong/kubernetes-ingress-controller/v3](https://github.com/kong/kubernetes-ingress-controller) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/kong/kubernetes-ingress-controller/releases) - [Changelog](https://github.com/Kong/kubernetes-ingress-controller/blob/main/CHANGELOG.md) - [Commits](https://github.com/kong/kubernetes-ingress-controller/compare/v3.3.0...v3.3.1) --- updated-dependencies: - dependency-name: github.com/kong/kubernetes-ingress-controller/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github.com/Masterminds/sprig/v3 in /hack/generators (#539) Bumps [github.com/Masterminds/sprig/v3](https://github.com/Masterminds/sprig) from 3.2.3 to 3.3.0. - [Release notes](https://github.com/Masterminds/sprig/releases) - [Changelog](https://github.com/Masterminds/sprig/blob/master/CHANGELOG.md) - [Commits](https://github.com/Masterminds/sprig/compare/v3.2.3...v3.3.0) --- updated-dependencies: - dependency-name: github.com/Masterminds/sprig/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): update dependency kubernetes-sigs/controller-tools to v0.16.2 (#538) * chore(deps): update dependency kubernetes-sigs/controller-tools to v0.16.2 * chore: regenerate --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jakub Warczarek * chore(deps): bump github.com/gruntwork-io/terratest (#541) Bumps [github.com/gruntwork-io/terratest](https://github.com/gruntwork-io/terratest) from 0.47.0 to 0.47.1. - [Release notes](https://github.com/gruntwork-io/terratest/releases) - [Commits](https://github.com/gruntwork-io/terratest/compare/v0.47.0...v0.47.1) --- updated-dependencies: - dependency-name: github.com/gruntwork-io/terratest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * update golangci configuration --------- Signed-off-by: Arpad Kunszt Signed-off-by: dependabot[bot] Signed-off-by: Mattia Lavacca Co-authored-by: akunszt <32456696+akunszt@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jakub Warczarek Co-authored-by: Mattia Lavacca Co-authored-by: Grzegorz Burzyński Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .golangci.yaml | 6 +- .tools_versions.yaml | 2 +- CHANGELOG.md | 5 + ...ateway-operator.konghq.com_aigateways.yaml | 2 +- ...way-operator.konghq.com_controlplanes.yaml | 2 +- ...konghq.com_dataplanemetricsextensions.yaml | 2 +- ...ateway-operator.konghq.com_dataplanes.yaml | 2 +- ...ator.konghq.com_gatewayconfigurations.yaml | 2 +- ...or.konghq.com_kongplugininstallations.yaml | 2 +- ...ateway-operator.konghq.com_dataplanes.yaml | 2 +- config/rbac/role/role.yaml | 134 ++++---- config/samples/controlplane.yaml | 2 +- .../gateway-httproute-allowedroutes.yaml | 2 +- config/samples/gateway-httproute.yaml | 2 +- ...sabled-controlplane-admission-webhook.yaml | 2 +- config/samples/konnect-kongpluginbinding.yaml | 64 ++++ controller/konnect/constraints/constraints.go | 3 +- controller/konnect/index.go | 26 ++ controller/konnect/index_kongpluginbinding.go | 54 +++ controller/konnect/ops/controlplane.go | 10 +- controller/konnect/ops/kongconsumer.go | 10 +- controller/konnect/ops/kongconsumergroup.go | 10 +- controller/konnect/ops/kongroute.go | 10 +- controller/konnect/ops/kongservice.go | 10 +- controller/konnect/ops/ops.go | 6 + controller/konnect/ops/ops_controlplane.go | 14 +- .../konnect/ops/ops_controlplane_test.go | 58 ++-- controller/konnect/ops/ops_kongconsumer.go | 14 +- .../konnect/ops/ops_kongconsumergroup.go | 14 +- .../konnect/ops/ops_kongpluginbinding.go | 296 +++++++++++++++++ controller/konnect/ops/ops_kongroute.go | 18 +- controller/konnect/ops/ops_kongservice.go | 16 +- .../konnect/ops/ops_kongservice_test.go | 36 +- controller/konnect/reconciler_generic.go | 14 +- controller/konnect/reconciler_generic_rbac.go | 3 + controller/konnect/reconciler_generic_test.go | 6 + .../konnect/reconciler_konnectapiauth.go | 10 +- controller/konnect/sdkfactory.go | 4 +- controller/konnect/watch.go | 2 + controller/konnect/watch_kongpluginbinding.go | 308 ++++++++++++++++++ go.mod | 33 +- go.sum | 72 ++-- hack/generators/go.mod | 18 +- hack/generators/go.sum | 70 ++-- internal/versions/controlplane.go | 2 +- modules/cli/cli.go | 2 + modules/cli/cli_test.go | 15 + modules/manager/controller_setup.go | 9 + modules/manager/run.go | 4 +- modules/manager/webhook.go | 8 +- pkg/consts/consts.go | 2 + pkg/utils/kubernetes/resources/jobs.go | 9 +- 52 files changed, 1113 insertions(+), 316 deletions(-) create mode 100644 config/samples/konnect-kongpluginbinding.yaml create mode 100644 controller/konnect/index.go create mode 100644 controller/konnect/index_kongpluginbinding.go create mode 100644 controller/konnect/ops/ops_kongpluginbinding.go create mode 100644 controller/konnect/watch_kongpluginbinding.go diff --git a/.golangci.yaml b/.golangci.yaml index b5d4679ca..d4074e251 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -64,11 +64,11 @@ linters-settings: alias: gwtypes - pkg: github.com/Kong/sdk-konnect-go/models/components - alias: sdkkonnectgocomp + alias: sdkkonnectcomp - pkg: github.com/Kong/sdk-konnect-go/models/operations - alias: sdkkonnectgoops + alias: sdkkonnectops - pkg: github.com/Kong/sdk-konnect-go/models/sdkerrors - alias: sdkkonnectgoerrs + alias: sdkkonnecterrs revive: rules: - name: errorf diff --git a/.tools_versions.yaml b/.tools_versions.yaml index 5b62ad971..c94f35896 100644 --- a/.tools_versions.yaml +++ b/.tools_versions.yaml @@ -1,7 +1,7 @@ # renovate: datasource=github-tags depName=kubernetes/code-generator code-generator: "0.31.0" # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools -controller-tools: "0.16.1" +controller-tools: "0.16.2" # renovate: datasource=github-releases depName=kubernetes-sigs/kustomize kustomize: "5.4.3" # renovate: datasource=github-releases depName=golangci/golangci-lint diff --git a/CHANGELOG.md b/CHANGELOG.md index dba21596d..1075f9416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,11 @@ [#506](https://github.com/Kong/gateway-operator/pull/506) - Add `KongConsumerGroup` reconciler for Konnect control planes. [#510](https://github.com/Kong/gateway-operator/pull/510) +- Added command line flags to configure the certificate generator job's images. + [#516](https://github.com/Kong/gateway-operator/pull/516) +- Add `KongPluginBinding` reconciler for Konnect Plugins. + [#513](https://github.com/Kong/gateway-operator/pull/513) + [#535](https://github.com/Kong/gateway-operator/pull/535) ### Fixed diff --git a/config/crd/bases/gateway-operator.konghq.com_aigateways.yaml b/config/crd/bases/gateway-operator.konghq.com_aigateways.yaml index 9cea988bd..a10f602b9 100644 --- a/config/crd/bases/gateway-operator.konghq.com_aigateways.yaml +++ b/config/crd/bases/gateway-operator.konghq.com_aigateways.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.16.2 name: aigateways.gateway-operator.konghq.com spec: group: gateway-operator.konghq.com diff --git a/config/crd/bases/gateway-operator.konghq.com_controlplanes.yaml b/config/crd/bases/gateway-operator.konghq.com_controlplanes.yaml index 202c12f35..2ea6e571f 100644 --- a/config/crd/bases/gateway-operator.konghq.com_controlplanes.yaml +++ b/config/crd/bases/gateway-operator.konghq.com_controlplanes.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.16.2 name: controlplanes.gateway-operator.konghq.com spec: group: gateway-operator.konghq.com diff --git a/config/crd/bases/gateway-operator.konghq.com_dataplanemetricsextensions.yaml b/config/crd/bases/gateway-operator.konghq.com_dataplanemetricsextensions.yaml index 3ca263a2c..11f2bc192 100644 --- a/config/crd/bases/gateway-operator.konghq.com_dataplanemetricsextensions.yaml +++ b/config/crd/bases/gateway-operator.konghq.com_dataplanemetricsextensions.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.16.2 name: dataplanemetricsextensions.gateway-operator.konghq.com spec: group: gateway-operator.konghq.com diff --git a/config/crd/bases/gateway-operator.konghq.com_dataplanes.yaml b/config/crd/bases/gateway-operator.konghq.com_dataplanes.yaml index f84839d7b..677372ad1 100644 --- a/config/crd/bases/gateway-operator.konghq.com_dataplanes.yaml +++ b/config/crd/bases/gateway-operator.konghq.com_dataplanes.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.16.2 name: dataplanes.gateway-operator.konghq.com spec: group: gateway-operator.konghq.com diff --git a/config/crd/bases/gateway-operator.konghq.com_gatewayconfigurations.yaml b/config/crd/bases/gateway-operator.konghq.com_gatewayconfigurations.yaml index 539d83b3b..12ef88bc9 100644 --- a/config/crd/bases/gateway-operator.konghq.com_gatewayconfigurations.yaml +++ b/config/crd/bases/gateway-operator.konghq.com_gatewayconfigurations.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.16.2 name: gatewayconfigurations.gateway-operator.konghq.com spec: group: gateway-operator.konghq.com diff --git a/config/crd/bases/gateway-operator.konghq.com_kongplugininstallations.yaml b/config/crd/bases/gateway-operator.konghq.com_kongplugininstallations.yaml index 4329639a2..96b876561 100644 --- a/config/crd/bases/gateway-operator.konghq.com_kongplugininstallations.yaml +++ b/config/crd/bases/gateway-operator.konghq.com_kongplugininstallations.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.16.2 name: kongplugininstallations.gateway-operator.konghq.com spec: group: gateway-operator.konghq.com diff --git a/config/crd/dataplane/gateway-operator.konghq.com_dataplanes.yaml b/config/crd/dataplane/gateway-operator.konghq.com_dataplanes.yaml index f84839d7b..677372ad1 100644 --- a/config/crd/dataplane/gateway-operator.konghq.com_dataplanes.yaml +++ b/config/crd/dataplane/gateway-operator.konghq.com_dataplanes.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.16.2 name: dataplanes.gateway-operator.konghq.com spec: group: gateway-operator.konghq.com diff --git a/config/rbac/role/role.yaml b/config/rbac/role/role.yaml index 563dcbfec..bdc30f3e9 100644 --- a/config/rbac/role/role.yaml +++ b/config/rbac/role/role.yaml @@ -4,6 +4,68 @@ kind: ClusterRole metadata: name: manager-role rules: +- apiGroups: + - "" + resources: + - configmaps + - serviceaccounts + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps/status + - serviceaccounts/status + verbs: + - get +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - namespaces + - pods + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - watch +- apiGroups: + - "" + resources: + - services/status + verbs: + - get + - patch + - update - apiGroups: - admissionregistration.k8s.io resources: @@ -87,6 +149,7 @@ rules: - kongcustomentities/status - kongingresses/status - konglicenses/status + - kongpluginbindings/status - kongplugins/status - kongservices/status - kongupstreampolicies/status @@ -100,18 +163,7 @@ rules: - apiGroups: - configuration.konghq.com resources: - - kongplugins - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - configuration.konghq.com - resources: + - kongpluginbindings - kongservices verbs: - get @@ -120,9 +172,9 @@ rules: - update - watch - apiGroups: - - coordination.k8s.io + - configuration.konghq.com resources: - - leases + - kongplugins verbs: - create - delete @@ -132,11 +184,9 @@ rules: - update - watch - apiGroups: - - "" + - coordination.k8s.io resources: - - configmaps - - serviceaccounts - - services + - leases verbs: - create - delete @@ -145,54 +195,6 @@ rules: - patch - update - watch -- apiGroups: - - "" - resources: - - configmaps/status - - serviceaccounts/status - verbs: - - get -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - namespaces - - pods - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - nodes - verbs: - - list - - watch -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - watch -- apiGroups: - - "" - resources: - - services/status - verbs: - - get - - patch - - update - apiGroups: - discovery.k8s.io resources: diff --git a/config/samples/controlplane.yaml b/config/samples/controlplane.yaml index 747e441d2..93646f615 100644 --- a/config/samples/controlplane.yaml +++ b/config/samples/controlplane.yaml @@ -14,7 +14,7 @@ spec: containers: - name: controller # renovate: datasource=docker versioning=docker - image: kong/kubernetes-ingress-controller:3.2.4 + image: kong/kubernetes-ingress-controller:3.3.1 readinessProbe: initialDelaySeconds: 1 periodSeconds: 3 diff --git a/config/samples/gateway-httproute-allowedroutes.yaml b/config/samples/gateway-httproute-allowedroutes.yaml index 6b9457e6e..76ccc78c8 100644 --- a/config/samples/gateway-httproute-allowedroutes.yaml +++ b/config/samples/gateway-httproute-allowedroutes.yaml @@ -82,7 +82,7 @@ spec: containers: - name: controller # renovate: datasource=docker versioning=docker - image: kong/kubernetes-ingress-controller:3.2.4 + image: kong/kubernetes-ingress-controller:3.3.1 readinessProbe: initialDelaySeconds: 1 periodSeconds: 1 diff --git a/config/samples/gateway-httproute.yaml b/config/samples/gateway-httproute.yaml index 788132772..b1320d85a 100644 --- a/config/samples/gateway-httproute.yaml +++ b/config/samples/gateway-httproute.yaml @@ -143,7 +143,7 @@ spec: containers: - name: controller # renovate: datasource=docker versioning=docker - image: kong/kubernetes-ingress-controller:3.2.4 + image: kong/kubernetes-ingress-controller:3.3.1 readinessProbe: initialDelaySeconds: 1 periodSeconds: 1 diff --git a/config/samples/gateway-with-disabled-controlplane-admission-webhook.yaml b/config/samples/gateway-with-disabled-controlplane-admission-webhook.yaml index 766b4e164..04b5f7f1b 100644 --- a/config/samples/gateway-with-disabled-controlplane-admission-webhook.yaml +++ b/config/samples/gateway-with-disabled-controlplane-admission-webhook.yaml @@ -78,7 +78,7 @@ spec: containers: - name: controller # renovate: datasource=docker versioning=docker - image: kong/kubernetes-ingress-controller:3.2.4 + image: kong/kubernetes-ingress-controller:3.3.1 readinessProbe: initialDelaySeconds: 1 periodSeconds: 1 diff --git a/config/samples/konnect-kongpluginbinding.yaml b/config/samples/konnect-kongpluginbinding.yaml new file mode 100644 index 000000000..8ba32098e --- /dev/null +++ b/config/samples/konnect-kongpluginbinding.yaml @@ -0,0 +1,64 @@ +--- +kind: KonnectAPIAuthConfiguration +apiVersion: konnect.konghq.com/v1alpha1 +metadata: + name: demo-auth + namespace: default +spec: + type: token + token: kpat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + serverURL: eu.api.konghq.tech +--- +kind: KonnectControlPlane +apiVersion: konnect.konghq.com/v1alpha1 +metadata: + name: demo-cp + namespace: default +spec: + name: demo-cp + labels: + app: demo-cp + key1: demo-cp + konnect: + authRef: + name: demo-auth + # namespace not required if APIAuthConfiguration is in the same namespace +--- +kind: KongService +apiVersion: configuration.konghq.com/v1alpha1 +metadata: + name: service-1 + namespace: default +spec: + name: service-1 + host: example.com + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: demo-cp +--- +apiVersion: configuration.konghq.com/v1 +kind: KongPlugin +metadata: + name: rate-limit-5-min +config: + minute: 5 + policy: local +plugin: rate-limiting +--- +apiVersion: configuration.konghq.com/v1alpha1 +kind: KongPluginBinding +metadata: + name: plugin-binding-kongservice +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: demo-cp + pluginRef: + name: proxy-cache-all-endpoints + targets: + serviceRef: + name: service-1 + kind: KongService + group: configuration.konghq.com \ No newline at end of file diff --git a/controller/konnect/constraints/constraints.go b/controller/konnect/constraints/constraints.go index 7978beff9..a9f90270c 100644 --- a/controller/konnect/constraints/constraints.go +++ b/controller/konnect/constraints/constraints.go @@ -17,7 +17,8 @@ type SupportedKonnectEntityType interface { configurationv1alpha1.KongService | configurationv1alpha1.KongRoute | configurationv1.KongConsumer | - configurationv1beta1.KongConsumerGroup + configurationv1beta1.KongConsumerGroup | + configurationv1alpha1.KongPluginBinding // TODO: add other types GetTypeName() string diff --git a/controller/konnect/index.go b/controller/konnect/index.go new file mode 100644 index 000000000..b021a29cf --- /dev/null +++ b/controller/konnect/index.go @@ -0,0 +1,26 @@ +package konnect + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/kong/gateway-operator/controller/konnect/constraints" + + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" +) + +// ReconciliationIndexOption contains required options of index for a kind of object required for reconciliation. +type ReconciliationIndexOption struct { + IndexObject client.Object + IndexField string + ExtractValue client.IndexerFunc +} + +// ReconciliationIndexOptionsForEntity returns required index options for controller reconciliing the entity. +func ReconciliationIndexOptionsForEntity[T constraints.SupportedKonnectEntityType, + TEnt constraints.EntityType[T]](ent TEnt) []ReconciliationIndexOption { + switch any(ent).(type) { //nolint:gocritic // TODO: add index options required for other entities + case *configurationv1alpha1.KongPluginBinding: + return IndexOptionsForKongPluginBinding() + } + return nil +} diff --git a/controller/konnect/index_kongpluginbinding.go b/controller/konnect/index_kongpluginbinding.go new file mode 100644 index 000000000..a778e7fa6 --- /dev/null +++ b/controller/konnect/index_kongpluginbinding.go @@ -0,0 +1,54 @@ +package konnect + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" +) + +const ( + // IndexFieldKongPluginBindingKongPluginReference is the index field for KongPlugin -> KongPluginBinding. + IndexFieldKongPluginBindingKongPluginReference = "kongPluginRef" + // IndexFieldKongPluginBindingKongClusterPluginReference is the index field for KongClusterPlugin -> KongPluginBinding. + IndexFieldKongPluginBindingKongClusterPluginReference = "kongClusterPluginRef" +) + +// IndexOptionsForKongPluginBinding returns required Index options for KongPluginBinding reconclier. +func IndexOptionsForKongPluginBinding() []ReconciliationIndexOption { + return []ReconciliationIndexOption{ + { + IndexObject: &configurationv1alpha1.KongPluginBinding{}, + IndexField: IndexFieldKongPluginBindingKongClusterPluginReference, + ExtractValue: kongPluginReferencesFromKongPluginBinding, + }, + { + IndexObject: &configurationv1alpha1.KongPluginBinding{}, + IndexField: IndexFieldKongPluginBindingKongClusterPluginReference, + ExtractValue: kongClusterPluginReferencesFromKongPluginBinding, + }, + } +} + +// kongPluginReferencesFromKongPluginBinding returns namespace/name of referenced KongPlugin in KongPluginBinding spec. +func kongPluginReferencesFromKongPluginBinding(obj client.Object) []string { + binding, ok := obj.(*configurationv1alpha1.KongPluginBinding) + if !ok { + return nil + } + if binding.Spec.PluginReference.Kind != nil && *binding.Spec.PluginReference.Kind != "KongPlugin" { + return nil + } + return []string{binding.Namespace + "/" + binding.Spec.PluginReference.Name} +} + +// kongClusterPluginReferencesFromKongPluginBinding returns name of referenced KongClusterPlugin in KongPluginBinding spec. +func kongClusterPluginReferencesFromKongPluginBinding(obj client.Object) []string { + binding, ok := obj.(*configurationv1alpha1.KongPluginBinding) + if !ok { + return nil + } + if binding.Spec.PluginReference.Kind == nil || *binding.Spec.PluginReference.Kind != "KongClusterPlugin" { + return nil + } + return []string{binding.Spec.PluginReference.Name} +} diff --git a/controller/konnect/ops/controlplane.go b/controller/konnect/ops/controlplane.go index 2b58dfe50..4da05c1bd 100644 --- a/controller/konnect/ops/controlplane.go +++ b/controller/konnect/ops/controlplane.go @@ -3,13 +3,13 @@ package ops import ( "context" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" ) // ControlPlaneSDK is the interface for the Konnect ControlPlaneSDK SDK. type ControlPlaneSDK interface { - CreateControlPlane(ctx context.Context, req sdkkonnectgocomp.CreateControlPlaneRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateControlPlaneResponse, error) - DeleteControlPlane(ctx context.Context, id string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteControlPlaneResponse, error) - UpdateControlPlane(ctx context.Context, id string, req sdkkonnectgocomp.UpdateControlPlaneRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpdateControlPlaneResponse, error) + CreateControlPlane(ctx context.Context, req sdkkonnectcomp.CreateControlPlaneRequest, opts ...sdkkonnectops.Option) (*sdkkonnectops.CreateControlPlaneResponse, error) + DeleteControlPlane(ctx context.Context, id string, opts ...sdkkonnectops.Option) (*sdkkonnectops.DeleteControlPlaneResponse, error) + UpdateControlPlane(ctx context.Context, id string, req sdkkonnectcomp.UpdateControlPlaneRequest, opts ...sdkkonnectops.Option) (*sdkkonnectops.UpdateControlPlaneResponse, error) } diff --git a/controller/konnect/ops/kongconsumer.go b/controller/konnect/ops/kongconsumer.go index ba705bab4..547ac6480 100644 --- a/controller/konnect/ops/kongconsumer.go +++ b/controller/konnect/ops/kongconsumer.go @@ -3,13 +3,13 @@ package ops import ( "context" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" ) // ConsumersSDK is the interface for the Konnect Consumers SDK. type ConsumersSDK interface { - CreateConsumer(ctx context.Context, controlPlaneID string, consumerInput sdkkonnectgocomp.ConsumerInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateConsumerResponse, error) - UpsertConsumer(ctx context.Context, upsertConsumerRequest sdkkonnectgoops.UpsertConsumerRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertConsumerResponse, error) - DeleteConsumer(ctx context.Context, controlPlaneID string, consumerID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteConsumerResponse, error) + CreateConsumer(ctx context.Context, controlPlaneID string, consumerInput sdkkonnectcomp.ConsumerInput, opts ...sdkkonnectops.Option) (*sdkkonnectops.CreateConsumerResponse, error) + UpsertConsumer(ctx context.Context, upsertConsumerRequest sdkkonnectops.UpsertConsumerRequest, opts ...sdkkonnectops.Option) (*sdkkonnectops.UpsertConsumerResponse, error) + DeleteConsumer(ctx context.Context, controlPlaneID string, consumerID string, opts ...sdkkonnectops.Option) (*sdkkonnectops.DeleteConsumerResponse, error) } diff --git a/controller/konnect/ops/kongconsumergroup.go b/controller/konnect/ops/kongconsumergroup.go index c87cfc1a7..afebcd8fd 100644 --- a/controller/konnect/ops/kongconsumergroup.go +++ b/controller/konnect/ops/kongconsumergroup.go @@ -3,13 +3,13 @@ package ops import ( "context" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" ) // ConsumerGroupSDK is the interface for the Konnect ConsumerGroups SDK. type ConsumerGroupSDK interface { - CreateConsumerGroup(ctx context.Context, controlPlaneID string, consumerInput sdkkonnectgocomp.ConsumerGroupInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateConsumerGroupResponse, error) - UpsertConsumerGroup(ctx context.Context, upsertConsumerRequest sdkkonnectgoops.UpsertConsumerGroupRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertConsumerGroupResponse, error) - DeleteConsumerGroup(ctx context.Context, controlPlaneID string, consumerID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteConsumerGroupResponse, error) + CreateConsumerGroup(ctx context.Context, controlPlaneID string, consumerInput sdkkonnectcomp.ConsumerGroupInput, opts ...sdkkonnectops.Option) (*sdkkonnectops.CreateConsumerGroupResponse, error) + UpsertConsumerGroup(ctx context.Context, upsertConsumerRequest sdkkonnectops.UpsertConsumerGroupRequest, opts ...sdkkonnectops.Option) (*sdkkonnectops.UpsertConsumerGroupResponse, error) + DeleteConsumerGroup(ctx context.Context, controlPlaneID string, consumerID string, opts ...sdkkonnectops.Option) (*sdkkonnectops.DeleteConsumerGroupResponse, error) } diff --git a/controller/konnect/ops/kongroute.go b/controller/konnect/ops/kongroute.go index 0473cee1a..809382866 100644 --- a/controller/konnect/ops/kongroute.go +++ b/controller/konnect/ops/kongroute.go @@ -3,13 +3,13 @@ package ops import ( "context" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" ) // RoutesSDK is the interface for the Konnect Routes SDK. type RoutesSDK interface { - CreateRoute(ctx context.Context, controlPlaneID string, route sdkkonnectgocomp.RouteInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateRouteResponse, error) - UpsertRoute(ctx context.Context, req sdkkonnectgoops.UpsertRouteRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertRouteResponse, error) - DeleteRoute(ctx context.Context, controlPlaneID, routeID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteRouteResponse, error) + CreateRoute(ctx context.Context, controlPlaneID string, route sdkkonnectcomp.RouteInput, opts ...sdkkonnectops.Option) (*sdkkonnectops.CreateRouteResponse, error) + UpsertRoute(ctx context.Context, req sdkkonnectops.UpsertRouteRequest, opts ...sdkkonnectops.Option) (*sdkkonnectops.UpsertRouteResponse, error) + DeleteRoute(ctx context.Context, controlPlaneID, routeID string, opts ...sdkkonnectops.Option) (*sdkkonnectops.DeleteRouteResponse, error) } diff --git a/controller/konnect/ops/kongservice.go b/controller/konnect/ops/kongservice.go index 137082b9a..502ae5649 100644 --- a/controller/konnect/ops/kongservice.go +++ b/controller/konnect/ops/kongservice.go @@ -3,13 +3,13 @@ package ops import ( "context" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" ) // ServicesSDK is the interface for the Konnect Service SDK. type ServicesSDK interface { - CreateService(ctx context.Context, controlPlaneID string, service sdkkonnectgocomp.ServiceInput, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.CreateServiceResponse, error) - UpsertService(ctx context.Context, req sdkkonnectgoops.UpsertServiceRequest, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.UpsertServiceResponse, error) - DeleteService(ctx context.Context, controlPlaneID, serviceID string, opts ...sdkkonnectgoops.Option) (*sdkkonnectgoops.DeleteServiceResponse, error) + CreateService(ctx context.Context, controlPlaneID string, service sdkkonnectcomp.ServiceInput, opts ...sdkkonnectops.Option) (*sdkkonnectops.CreateServiceResponse, error) + UpsertService(ctx context.Context, req sdkkonnectops.UpsertServiceRequest, opts ...sdkkonnectops.Option) (*sdkkonnectops.UpsertServiceResponse, error) + DeleteService(ctx context.Context, controlPlaneID, serviceID string, opts ...sdkkonnectops.Option) (*sdkkonnectops.DeleteServiceResponse, error) } diff --git a/controller/konnect/ops/ops.go b/controller/konnect/ops/ops.go index 332e9c051..1906e1563 100644 --- a/controller/konnect/ops/ops.go +++ b/controller/konnect/ops/ops.go @@ -58,6 +58,8 @@ func Create[ return e, createConsumer(ctx, sdk.Consumers, ent) case *configurationv1beta1.KongConsumerGroup: return e, createConsumerGroup(ctx, sdk.ConsumerGroups, ent) + case *configurationv1alpha1.KongPluginBinding: + return e, createPlugin(ctx, cl, sdk, ent) // --------------------------------------------------------------------- // TODO: add other Konnect types @@ -94,6 +96,8 @@ func Delete[ return deleteConsumer(ctx, sdk.Consumers, ent) case *configurationv1beta1.KongConsumerGroup: return deleteConsumerGroup(ctx, sdk.ConsumerGroups, ent) + case *configurationv1alpha1.KongPluginBinding: + return deletePlugin(ctx, sdk, ent) // --------------------------------------------------------------------- // TODO: add other Konnect types @@ -155,6 +159,8 @@ func Update[ return ctrl.Result{}, updateConsumer(ctx, sdk.Consumers, cl, ent) case *configurationv1beta1.KongConsumerGroup: return ctrl.Result{}, updateConsumerGroup(ctx, sdk.ConsumerGroups, cl, ent) + case *configurationv1alpha1.KongPluginBinding: + return ctrl.Result{}, updatePlugin(ctx, sdk, cl, ent) // --------------------------------------------------------------------- // TODO: add other Konnect types diff --git a/controller/konnect/ops/ops_controlplane.go b/controller/konnect/ops/ops_controlplane.go index 9cc78fac4..50c4f8407 100644 --- a/controller/konnect/ops/ops_controlplane.go +++ b/controller/konnect/ops/ops_controlplane.go @@ -5,8 +5,8 @@ import ( "errors" sdkkonnectgo "github.com/Kong/sdk-konnect-go" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnecterrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" @@ -65,7 +65,7 @@ func deleteControlPlane( id := cp.GetKonnectStatus().GetKonnectID() _, err := sdk.DeleteControlPlane(ctx, id) if errWrap := wrapErrIfKonnectOpFailed(err, DeleteOp, cp); errWrap != nil { - var sdkNotFoundError *sdkkonnectgoerrs.NotFoundError + var sdkNotFoundError *sdkkonnecterrs.NotFoundError if errors.As(err, &sdkNotFoundError) { ctrllog.FromContext(ctx). Info("entity not found in Konnect, skipping delete", @@ -73,7 +73,7 @@ func deleteControlPlane( ) return nil } - var sdkError *sdkkonnectgoerrs.SDKError + var sdkError *sdkkonnecterrs.SDKError if errors.As(errWrap, &sdkError) { return FailedKonnectOpError[konnectv1alpha1.KonnectControlPlane]{ Op: DeleteOp, @@ -98,16 +98,16 @@ func updateControlPlane( cp *konnectv1alpha1.KonnectControlPlane, ) error { id := cp.GetKonnectStatus().GetKonnectID() - req := sdkkonnectgocomp.UpdateControlPlaneRequest{ + req := sdkkonnectcomp.UpdateControlPlaneRequest{ Name: sdkkonnectgo.String(cp.Spec.Name), Description: cp.Spec.Description, - AuthType: (*sdkkonnectgocomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), + AuthType: (*sdkkonnectcomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), ProxyUrls: cp.Spec.ProxyUrls, Labels: cp.Spec.Labels, } resp, err := sdk.UpdateControlPlane(ctx, id, req) - var sdkError *sdkkonnectgoerrs.NotFoundError + var sdkError *sdkkonnecterrs.NotFoundError if errors.As(err, &sdkError) { ctrllog.FromContext(ctx). Info("entity not found in Konnect, trying to recreate", diff --git a/controller/konnect/ops/ops_controlplane_test.go b/controller/konnect/ops/ops_controlplane_test.go index 73c39fb42..644ab52a1 100644 --- a/controller/konnect/ops/ops_controlplane_test.go +++ b/controller/konnect/ops/ops_controlplane_test.go @@ -5,9 +5,9 @@ import ( "testing" sdkkonnectgo "github.com/Kong/sdk-konnect-go" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" - sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnecterrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -32,7 +32,7 @@ func TestCreateControlPlane(t *testing.T) { sdk := &MockControlPlaneSDK{} cp := &konnectv1alpha1.KonnectControlPlane{ Spec: konnectv1alpha1.KonnectControlPlaneSpec{ - CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, @@ -42,8 +42,8 @@ func TestCreateControlPlane(t *testing.T) { EXPECT(). CreateControlPlane(ctx, cp.Spec.CreateControlPlaneRequest). Return( - &sdkkonnectgoops.CreateControlPlaneResponse{ - ControlPlane: &sdkkonnectgocomp.ControlPlane{ + &sdkkonnectops.CreateControlPlaneResponse{ + ControlPlane: &sdkkonnectcomp.ControlPlane{ ID: "12345", }, }, @@ -71,7 +71,7 @@ func TestCreateControlPlane(t *testing.T) { Namespace: "default", }, Spec: konnectv1alpha1.KonnectControlPlaneSpec{ - CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, @@ -82,7 +82,7 @@ func TestCreateControlPlane(t *testing.T) { CreateControlPlane(ctx, cp.Spec.CreateControlPlaneRequest). Return( nil, - &sdkkonnectgoerrs.BadRequestError{ + &sdkkonnecterrs.BadRequestError{ Status: 400, Detail: "bad request", }, @@ -138,7 +138,7 @@ func TestDeleteControlPlane(t *testing.T) { sdk := &MockControlPlaneSDK{} cp := &konnectv1alpha1.KonnectControlPlane{ Spec: konnectv1alpha1.KonnectControlPlaneSpec{ - CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, @@ -152,7 +152,7 @@ func TestDeleteControlPlane(t *testing.T) { EXPECT(). DeleteControlPlane(ctx, "12345"). Return( - &sdkkonnectgoops.DeleteControlPlaneResponse{ + &sdkkonnectops.DeleteControlPlaneResponse{ StatusCode: 200, }, nil, @@ -171,7 +171,7 @@ func TestDeleteControlPlane(t *testing.T) { Namespace: "default", }, Spec: konnectv1alpha1.KonnectControlPlaneSpec{ - CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, @@ -186,7 +186,7 @@ func TestDeleteControlPlane(t *testing.T) { DeleteControlPlane(ctx, "12345"). Return( nil, - &sdkkonnectgoerrs.BadRequestError{ + &sdkkonnecterrs.BadRequestError{ Status: 400, Detail: "bad request", }, @@ -206,7 +206,7 @@ func TestDeleteControlPlane(t *testing.T) { Namespace: "default", }, Spec: konnectv1alpha1.KonnectControlPlaneSpec{ - CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, @@ -221,7 +221,7 @@ func TestDeleteControlPlane(t *testing.T) { DeleteControlPlane(ctx, "12345"). Return( nil, - &sdkkonnectgoerrs.NotFoundError{ + &sdkkonnecterrs.NotFoundError{ Status: 404, Detail: "not found", }, @@ -267,7 +267,7 @@ func TestUpdateControlPlane(t *testing.T) { sdk := &MockControlPlaneSDK{} cp := &konnectv1alpha1.KonnectControlPlane{ Spec: konnectv1alpha1.KonnectControlPlaneSpec{ - CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, @@ -280,17 +280,17 @@ func TestUpdateControlPlane(t *testing.T) { sdk. EXPECT(). UpdateControlPlane(ctx, "12345", - sdkkonnectgocomp.UpdateControlPlaneRequest{ + sdkkonnectcomp.UpdateControlPlaneRequest{ Name: sdkkonnectgo.String(cp.Spec.Name), Description: cp.Spec.Description, - AuthType: (*sdkkonnectgocomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), + AuthType: (*sdkkonnectcomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), ProxyUrls: cp.Spec.ProxyUrls, Labels: cp.Spec.Labels, }, ). Return( - &sdkkonnectgoops.UpdateControlPlaneResponse{ - ControlPlane: &sdkkonnectgocomp.ControlPlane{ + &sdkkonnectops.UpdateControlPlaneResponse{ + ControlPlane: &sdkkonnectcomp.ControlPlane{ ID: "12345", }, }, @@ -319,7 +319,7 @@ func TestUpdateControlPlane(t *testing.T) { Namespace: "default", }, Spec: konnectv1alpha1.KonnectControlPlaneSpec{ - CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, @@ -333,17 +333,17 @@ func TestUpdateControlPlane(t *testing.T) { sdk. EXPECT(). UpdateControlPlane(ctx, "12345", - sdkkonnectgocomp.UpdateControlPlaneRequest{ + sdkkonnectcomp.UpdateControlPlaneRequest{ Name: sdkkonnectgo.String(cp.Spec.Name), Description: cp.Spec.Description, - AuthType: (*sdkkonnectgocomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), + AuthType: (*sdkkonnectcomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), ProxyUrls: cp.Spec.ProxyUrls, Labels: cp.Spec.Labels, }, ). Return( nil, - &sdkkonnectgoerrs.BadRequestError{ + &sdkkonnecterrs.BadRequestError{ Status: 400, Detail: "bad request", }, @@ -372,7 +372,7 @@ func TestUpdateControlPlane(t *testing.T) { Namespace: "default", }, Spec: konnectv1alpha1.KonnectControlPlaneSpec{ - CreateControlPlaneRequest: sdkkonnectgocomp.CreateControlPlaneRequest{ + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, @@ -386,17 +386,17 @@ func TestUpdateControlPlane(t *testing.T) { sdk. EXPECT(). UpdateControlPlane(ctx, "12345", - sdkkonnectgocomp.UpdateControlPlaneRequest{ + sdkkonnectcomp.UpdateControlPlaneRequest{ Name: sdkkonnectgo.String(cp.Spec.Name), Description: cp.Spec.Description, - AuthType: (*sdkkonnectgocomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), + AuthType: (*sdkkonnectcomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType), ProxyUrls: cp.Spec.ProxyUrls, Labels: cp.Spec.Labels, }, ). Return( nil, - &sdkkonnectgoerrs.NotFoundError{ + &sdkkonnecterrs.NotFoundError{ Status: 404, Detail: "not found", }, @@ -406,8 +406,8 @@ func TestUpdateControlPlane(t *testing.T) { EXPECT(). CreateControlPlane(ctx, cp.Spec.CreateControlPlaneRequest). Return( - &sdkkonnectgoops.CreateControlPlaneResponse{ - ControlPlane: &sdkkonnectgocomp.ControlPlane{ + &sdkkonnectops.CreateControlPlaneResponse{ + ControlPlane: &sdkkonnectcomp.ControlPlane{ ID: "12345", }, }, diff --git a/controller/konnect/ops/ops_kongconsumer.go b/controller/konnect/ops/ops_kongconsumer.go index 8aa3ff931..a3e7938f5 100644 --- a/controller/konnect/ops/ops_kongconsumer.go +++ b/controller/konnect/ops/ops_kongconsumer.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" - sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnecterrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -101,7 +101,7 @@ func updateConsumer( } resp, err := sdk.UpsertConsumer(ctx, - sdkkonnectgoops.UpsertConsumerRequest{ + sdkkonnectops.UpsertConsumerRequest{ ControlPlaneID: cp.Status.ID, ConsumerID: consumer.GetKonnectStatus().GetKonnectID(), Consumer: kongConsumerToSDKConsumerInput(consumer), @@ -153,7 +153,7 @@ func deleteConsumer( _, err := sdk.DeleteConsumer(ctx, consumer.Status.Konnect.ControlPlaneID, id) if errWrapped := wrapErrIfKonnectOpFailed(err, DeleteOp, consumer); errWrapped != nil { // Consumer delete operation returns an SDKError instead of a NotFoundError. - var sdkError *sdkkonnectgoerrs.SDKError + var sdkError *sdkkonnecterrs.SDKError if errors.As(errWrapped, &sdkError) { if sdkError.StatusCode == 404 { ctrllog.FromContext(ctx). @@ -178,8 +178,8 @@ func deleteConsumer( func kongConsumerToSDKConsumerInput( consumer *configurationv1.KongConsumer, -) sdkkonnectgocomp.ConsumerInput { - return sdkkonnectgocomp.ConsumerInput{ +) sdkkonnectcomp.ConsumerInput { + return sdkkonnectcomp.ConsumerInput{ CustomID: &consumer.CustomID, Tags: metadata.ExtractTags(consumer), Username: &consumer.Username, diff --git a/controller/konnect/ops/ops_kongconsumergroup.go b/controller/konnect/ops/ops_kongconsumergroup.go index 35fe60992..8b0d0896d 100644 --- a/controller/konnect/ops/ops_kongconsumergroup.go +++ b/controller/konnect/ops/ops_kongconsumergroup.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" - sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnecterrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -101,7 +101,7 @@ func updateConsumerGroup( } resp, err := sdk.UpsertConsumerGroup(ctx, - sdkkonnectgoops.UpsertConsumerGroupRequest{ + sdkkonnectops.UpsertConsumerGroupRequest{ ControlPlaneID: cp.Status.ID, ConsumerGroupID: group.GetKonnectStatus().GetKonnectID(), ConsumerGroup: kongConsumerGroupToSDKConsumerGroupInput(group), @@ -153,7 +153,7 @@ func deleteConsumerGroup( _, err := sdk.DeleteConsumerGroup(ctx, consumer.Status.Konnect.ControlPlaneID, id) if errWrapped := wrapErrIfKonnectOpFailed(err, DeleteOp, consumer); errWrapped != nil { // Consumer delete operation returns an SDKError instead of a NotFoundError. - var sdkError *sdkkonnectgoerrs.SDKError + var sdkError *sdkkonnecterrs.SDKError if errors.As(errWrapped, &sdkError) { if sdkError.StatusCode == 404 { ctrllog.FromContext(ctx). @@ -178,8 +178,8 @@ func deleteConsumerGroup( func kongConsumerGroupToSDKConsumerGroupInput( group *configurationv1beta1.KongConsumerGroup, -) sdkkonnectgocomp.ConsumerGroupInput { - return sdkkonnectgocomp.ConsumerGroupInput{ +) sdkkonnectcomp.ConsumerGroupInput { + return sdkkonnectcomp.ConsumerGroupInput{ Tags: metadata.ExtractTags(group), Name: group.Spec.Name, } diff --git a/controller/konnect/ops/ops_kongpluginbinding.go b/controller/konnect/ops/ops_kongpluginbinding.go new file mode 100644 index 000000000..09944a846 --- /dev/null +++ b/controller/konnect/ops/ops_kongpluginbinding.go @@ -0,0 +1,296 @@ +package ops + +import ( + "context" + "encoding/json" + "errors" + "fmt" + + sdkkonnectgo "github.com/Kong/sdk-konnect-go" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnecterrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" + "github.com/samber/lo" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/kong/gateway-operator/controller/konnect/conditions" + k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes" + + configurationv1 "github.com/kong/kubernetes-configuration/api/configuration/v1" + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" +) + +// ----------------------------------------------------------------------------- +// Konnect KongPlugin - ops functions +// ----------------------------------------------------------------------------- + +// createPlugin creates the Konnect Plugin entity. +func createPlugin( + ctx context.Context, + cl client.Client, + sdk *sdkkonnectgo.SDK, + pluginBinding *configurationv1alpha1.KongPluginBinding, +) error { + controlPlaneID := pluginBinding.GetControlPlaneID() + if controlPlaneID == "" { + return fmt.Errorf("can't create %T %s without a Konnect ControlPlane ID", pluginBinding, client.ObjectKeyFromObject(pluginBinding)) + } + pluginInput, err := getPluginInput(ctx, cl, pluginBinding) + if err != nil { + return err + } + + resp, err := sdk.Plugins.CreatePlugin(ctx, + controlPlaneID, + *pluginInput, + ) + + // TODO: handle already exists + // Can't adopt it as it will cause conflicts between the controller + // that created that entity and already manages it, hm + if errWrapped := wrapErrIfKonnectOpFailed[configurationv1alpha1.KongPluginBinding](err, CreateOp, pluginBinding); errWrapped != nil { + k8sutils.SetCondition( + k8sutils.NewConditionWithGeneration( + conditions.KonnectEntityProgrammedConditionType, + metav1.ConditionFalse, + "FailedToCreate", + errWrapped.Error(), + pluginBinding.GetGeneration(), + ), + pluginBinding, + ) + return errWrapped + } + + pluginBinding.SetKonnectID(*resp.Plugin.ID) + k8sutils.SetCondition( + k8sutils.NewConditionWithGeneration( + conditions.KonnectEntityProgrammedConditionType, + metav1.ConditionTrue, + conditions.KonnectEntityProgrammedReasonProgrammed, + "", + pluginBinding.GetGeneration(), + ), + pluginBinding, + ) + + return nil +} + +// updatePlugin updates the Konnect Plugin entity. +// It is assumed that provided KongPluginBinding has Konnect ID set in status. +// It returns an error if the KongPluginBinding does not have a ControlPlaneRef or +// if the operation fails. +func updatePlugin( + ctx context.Context, + sdk *sdkkonnectgo.SDK, + cl client.Client, + pb *configurationv1alpha1.KongPluginBinding, +) error { + controlPlaneID := pb.GetControlPlaneID() + if controlPlaneID == "" { + return fmt.Errorf("can't create %T %s without a Konnect ControlPlane ID", pb, client.ObjectKeyFromObject(pb)) + } + + // TODO(pmalek) handle other types of CP ref + // TODO(pmalek) handle cross namespace refs + nnCP := types.NamespacedName{ + Namespace: pb.Namespace, + Name: pb.Spec.ControlPlaneRef.KonnectNamespacedRef.Name, + } + var cp konnectv1alpha1.KonnectControlPlane + if err := cl.Get(ctx, nnCP, &cp); err != nil { + return fmt.Errorf("failed to get KonnectControlPlane %s: for %T %s: %w", + nnCP, pb, client.ObjectKeyFromObject(pb), err, + ) + } + + if cp.Status.ID == "" { + return fmt.Errorf( + "can't update %T when referenced KonnectControlPlane %s does not have the Konnect ID", + pb, nnCP, + ) + } + + pluginInput, err := getPluginInput(ctx, cl, pb) + if err != nil { + return err + } + + resp, err := sdk.Plugins.UpsertPlugin(ctx, + sdkkonnectops.UpsertPluginRequest{ + ControlPlaneID: controlPlaneID, + PluginID: pb.GetKonnectID(), + Plugin: *pluginInput, + }, + ) + + // TODO: handle already exists + // Can't adopt it as it will cause conflicts between the controller + // that created that entity and already manages it, hm + if errWrapped := wrapErrIfKonnectOpFailed[configurationv1alpha1.KongPluginBinding](err, UpdateOp, pb); errWrapped != nil { + k8sutils.SetCondition( + k8sutils.NewConditionWithGeneration( + conditions.KonnectEntityProgrammedConditionType, + metav1.ConditionFalse, + "FailedToCreate", + errWrapped.Error(), + pb.GetGeneration(), + ), + pb, + ) + return errWrapped + } + + pb.Status.Konnect.SetKonnectID(*resp.Plugin.ID) + pb.Status.Konnect.SetControlPlaneID(cp.Status.ID) + k8sutils.SetCondition( + k8sutils.NewConditionWithGeneration( + conditions.KonnectEntityProgrammedConditionType, + metav1.ConditionTrue, + conditions.KonnectEntityProgrammedReasonProgrammed, + "", + pb.GetGeneration(), + ), + pb, + ) + + return nil +} + +// deletePlugin deletes a plugin in Konnect. +// The KongPluginBinding is assumed to have a Konnect ID set in status. +// It returns an error if the operation fails. +func deletePlugin( + ctx context.Context, + sdk *sdkkonnectgo.SDK, + pb *configurationv1alpha1.KongPluginBinding, +) error { + id := pb.GetKonnectID() + _, err := sdk.Plugins.DeletePlugin(ctx, pb.GetControlPlaneID(), id) + if errWrapped := wrapErrIfKonnectOpFailed[configurationv1alpha1.KongPluginBinding](err, DeleteOp, pb); errWrapped != nil { + // plugin delete operation returns an SDKError instead of a NotFoundError. + var sdkError *sdkkonnecterrs.SDKError + if errors.As(errWrapped, &sdkError) && sdkError.StatusCode == 404 { + ctrllog.FromContext(ctx). + Info("entity not found in Konnect, skipping delete", + "op", DeleteOp, "type", pb.GetTypeName(), "id", id, + ) + return nil + } + return FailedKonnectOpError[configurationv1alpha1.KongPluginBinding]{ + Op: DeleteOp, + Err: errWrapped, + } + } + + return nil +} + +// ----------------------------------------------------------------------------- +// Konnect KongPlugin - ops helpers +// ----------------------------------------------------------------------------- + +// getPluginInput returns the SDK PluginInput for the KongPluginBinding. +func getPluginInput(ctx context.Context, cl client.Client, pluginBinding *configurationv1alpha1.KongPluginBinding) (*sdkkonnectcomp.PluginInput, error) { + plugin, err := getReferencedPlugin(ctx, cl, pluginBinding) + if err != nil { + return nil, err + } + + targets, err := getPluginBindingTargets(ctx, cl, pluginBinding) + if err != nil { + return nil, err + } + + return kongPluginBindingToSDKPluginInput(plugin, targets) +} + +// getPluginBindingTargets returns the list of client objects referenced +// by the kongPluginBInding.spec.targets field. +func getPluginBindingTargets( + ctx context.Context, + cl client.Client, + pluginBinding *configurationv1alpha1.KongPluginBinding, +) ([]client.Object, error) { + targets := pluginBinding.Spec.Targets + targetObjects := []client.Object{} + if targets.ServiceReference != nil { + if targets.ServiceReference.Kind != "KongService" { + return nil, fmt.Errorf("unsupported service target kind %q", targets.ServiceReference.Kind) + } + + kongService := configurationv1alpha1.KongService{} + kongService.SetName(targets.ServiceReference.Name) + kongService.SetNamespace(pluginBinding.GetNamespace()) + if err := cl.Get(ctx, client.ObjectKeyFromObject(&kongService), &kongService); err != nil { + return nil, err + } + targetObjects = append(targetObjects, &kongService) + } + + // TODO(mlavacca): add support for KongRoute + // TODO(mlavacca): add support for KongConsumer + + return targetObjects, nil +} + +// getReferencedPlugin returns the KongPlugin referenced by the KongPluginBinding.spec.pluginRef field. +func getReferencedPlugin(ctx context.Context, cl client.Client, pluginBinding *configurationv1alpha1.KongPluginBinding) (*configurationv1.KongPlugin, error) { + // TODO(mlavacca): add support for KongClusterPlugin + plugin := configurationv1.KongPlugin{} + plugin.SetName(pluginBinding.Spec.PluginReference.Name) + plugin.SetNamespace(pluginBinding.GetNamespace()) + + if err := cl.Get(ctx, client.ObjectKeyFromObject(&plugin), &plugin); err != nil { + return nil, err + } + + return &plugin, nil +} + +// kongPluginBindingToSDKPluginInput converts a kongPluginConfiguration to an SKD PluginInput. +func kongPluginBindingToSDKPluginInput( + plugin *configurationv1.KongPlugin, + targets []client.Object, +) (*sdkkonnectcomp.PluginInput, error) { + if len(targets) == 0 { + return nil, fmt.Errorf("no targets found for KongPluginBinding %s", client.ObjectKeyFromObject(plugin)) + } + + pluginConfig := map[string]any{} + if err := json.Unmarshal(plugin.Config.Raw, &pluginConfig); err != nil { + return nil, err + } + + pluginInput := &sdkkonnectcomp.PluginInput{ + Name: lo.ToPtr(plugin.PluginName), + Config: pluginConfig, + Enabled: lo.ToPtr(!plugin.Disabled), + } + + // TODO(mlavacca): check all the entities reference the same KonnectControlPlane + + for _, t := range targets { + switch t := t.(type) { + case *configurationv1alpha1.KongService: + id := t.GetKonnectID() + if id == "" { + return nil, fmt.Errorf("KongService %s is not configured in Konnect yet", client.ObjectKeyFromObject(t)) + } + pluginInput.Service = &sdkkonnectcomp.PluginService{ + ID: lo.ToPtr(t.GetKonnectStatus().ID), + } + // TODO(mlavacca): add support for KongRoute + // TODO(mlavacca): add support for KongConsumer + default: + return nil, fmt.Errorf("unsupported target type %T", t) + } + } + + return pluginInput, nil +} diff --git a/controller/konnect/ops/ops_kongroute.go b/controller/konnect/ops/ops_kongroute.go index 6b69a8a88..9cf41f1f6 100644 --- a/controller/konnect/ops/ops_kongroute.go +++ b/controller/konnect/ops/ops_kongroute.go @@ -6,9 +6,9 @@ import ( "fmt" sdkkonnectgo "github.com/Kong/sdk-konnect-go" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" - sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnecterrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -116,8 +116,8 @@ func updateRoute( ) } - resp, err := sdk.UpsertRoute(ctx, sdkkonnectgoops.UpsertRouteRequest{ - // resp, err := sdk.UpsertRoute(ctx, sdkkonnectgoops.UpsertRouteRequest{ + resp, err := sdk.UpsertRoute(ctx, sdkkonnectops.UpsertRouteRequest{ + // resp, err := sdk.UpsertRoute(ctx, sdkkonnectops.UpsertRouteRequest{ ControlPlaneID: cp.Status.ID, RouteID: route.Status.Konnect.ID, Route: kongRouteToSDKRouteInput(route), @@ -169,7 +169,7 @@ func deleteRoute( _, err := sdk.DeleteRoute(ctx, route.Status.Konnect.ControlPlaneID, id) if errWrapped := wrapErrIfKonnectOpFailed(err, DeleteOp, route); errWrapped != nil { // Service delete operation returns an SDKError instead of a NotFoundError. - var sdkError *sdkkonnectgoerrs.SDKError + var sdkError *sdkkonnecterrs.SDKError if errors.As(errWrapped, &sdkError) { if sdkError.StatusCode == 404 { ctrllog.FromContext(ctx). @@ -194,8 +194,8 @@ func deleteRoute( func kongRouteToSDKRouteInput( route *configurationv1alpha1.KongRoute, -) sdkkonnectgocomp.RouteInput { - return sdkkonnectgocomp.RouteInput{ +) sdkkonnectcomp.RouteInput { + return sdkkonnectcomp.RouteInput{ Destinations: route.Spec.KongRouteAPISpec.Destinations, Headers: route.Spec.KongRouteAPISpec.Headers, Hosts: route.Spec.KongRouteAPISpec.Hosts, @@ -213,7 +213,7 @@ func kongRouteToSDKRouteInput( Sources: route.Spec.KongRouteAPISpec.Sources, StripPath: route.Spec.KongRouteAPISpec.StripPath, Tags: route.Spec.KongRouteAPISpec.Tags, - Service: &sdkkonnectgocomp.RouteService{ + Service: &sdkkonnectcomp.RouteService{ ID: sdkkonnectgo.String(route.Status.Konnect.ServiceID), }, } diff --git a/controller/konnect/ops/ops_kongservice.go b/controller/konnect/ops/ops_kongservice.go index 9a69a1d15..44559029c 100644 --- a/controller/konnect/ops/ops_kongservice.go +++ b/controller/konnect/ops/ops_kongservice.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" - sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnecterrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" @@ -84,7 +84,7 @@ func updateService( id := svc.GetKonnectStatus().GetKonnectID() resp, err := sdk.UpsertService(ctx, - sdkkonnectgoops.UpsertServiceRequest{ + sdkkonnectops.UpsertServiceRequest{ ControlPlaneID: svc.GetControlPlaneID(), ServiceID: id, Service: kongServiceToSDKServiceInput(svc), @@ -96,7 +96,7 @@ func updateService( // that created that entity and already manages it, hm if errWrapped := wrapErrIfKonnectOpFailed(err, UpdateOp, svc); errWrapped != nil { // Service update operation returns an SDKError instead of a NotFoundError. - var sdkError *sdkkonnectgoerrs.SDKError + var sdkError *sdkkonnecterrs.SDKError if errors.As(errWrapped, &sdkError) { switch sdkError.StatusCode { case 404: @@ -158,7 +158,7 @@ func deleteService( _, err := sdk.DeleteService(ctx, svc.Status.Konnect.ControlPlaneID, id) if errWrapped := wrapErrIfKonnectOpFailed(err, DeleteOp, svc); errWrapped != nil { // Service delete operation returns an SDKError instead of a NotFoundError. - var sdkError *sdkkonnectgoerrs.SDKError + var sdkError *sdkkonnecterrs.SDKError if errors.As(errWrapped, &sdkError) { switch sdkError.StatusCode { case 404: @@ -185,8 +185,8 @@ func deleteService( func kongServiceToSDKServiceInput( svc *configurationv1alpha1.KongService, -) sdkkonnectgocomp.ServiceInput { - return sdkkonnectgocomp.ServiceInput{ +) sdkkonnectcomp.ServiceInput { + return sdkkonnectcomp.ServiceInput{ URL: svc.Spec.KongServiceAPISpec.URL, ConnectTimeout: svc.Spec.KongServiceAPISpec.ConnectTimeout, Enabled: svc.Spec.KongServiceAPISpec.Enabled, diff --git a/controller/konnect/ops/ops_kongservice_test.go b/controller/konnect/ops/ops_kongservice_test.go index 2903ef7f4..e1689cf35 100644 --- a/controller/konnect/ops/ops_kongservice_test.go +++ b/controller/konnect/ops/ops_kongservice_test.go @@ -4,9 +4,9 @@ import ( "context" "testing" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" - sdkkonnectgoerrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnecterrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -53,8 +53,8 @@ func TestCreateKongService(t *testing.T) { EXPECT(). CreateService(ctx, "123456789", kongServiceToSDKServiceInput(svc)). Return( - &sdkkonnectgoops.CreateServiceResponse{ - Service: &sdkkonnectgocomp.Service{ + &sdkkonnectops.CreateServiceResponse{ + Service: &sdkkonnectcomp.Service{ ID: lo.ToPtr("12345"), Host: "example.com", Name: lo.ToPtr("svc-1"), @@ -126,7 +126,7 @@ func TestCreateKongService(t *testing.T) { CreateService(ctx, "123456789", kongServiceToSDKServiceInput(svc)). Return( nil, - &sdkkonnectgoerrs.BadRequestError{ + &sdkkonnecterrs.BadRequestError{ Status: 400, Detail: "bad request", }, @@ -199,7 +199,7 @@ func TestDeleteKongService(t *testing.T) { EXPECT(). DeleteService(ctx, "12345", "123456789"). Return( - &sdkkonnectgoops.DeleteServiceResponse{ + &sdkkonnectops.DeleteServiceResponse{ StatusCode: 200, }, nil, @@ -232,7 +232,7 @@ func TestDeleteKongService(t *testing.T) { DeleteService(ctx, "12345", "123456789"). Return( nil, - &sdkkonnectgoerrs.BadRequestError{ + &sdkkonnecterrs.BadRequestError{ Status: 400, Detail: "bad request", }, @@ -266,7 +266,7 @@ func TestDeleteKongService(t *testing.T) { DeleteService(ctx, "12345", "123456789"). Return( nil, - &sdkkonnectgoerrs.SDKError{ + &sdkkonnecterrs.SDKError{ Message: "not found", StatusCode: 404, }, @@ -328,16 +328,16 @@ func TestUpdateKongService(t *testing.T) { sdk. EXPECT(). UpsertService(ctx, - sdkkonnectgoops.UpsertServiceRequest{ + sdkkonnectops.UpsertServiceRequest{ ControlPlaneID: "12345", ServiceID: "123456789", Service: kongServiceToSDKServiceInput(svc), }, ). Return( - &sdkkonnectgoops.UpsertServiceResponse{ + &sdkkonnectops.UpsertServiceResponse{ StatusCode: 200, - Service: &sdkkonnectgocomp.Service{ + Service: &sdkkonnectcomp.Service{ ID: lo.ToPtr("123456789"), Name: lo.ToPtr("svc-1"), }, @@ -383,7 +383,7 @@ func TestUpdateKongService(t *testing.T) { sdk. EXPECT(). UpsertService(ctx, - sdkkonnectgoops.UpsertServiceRequest{ + sdkkonnectops.UpsertServiceRequest{ ControlPlaneID: "12345", ServiceID: "123456789", Service: kongServiceToSDKServiceInput(svc), @@ -391,7 +391,7 @@ func TestUpdateKongService(t *testing.T) { ). Return( nil, - &sdkkonnectgoerrs.BadRequestError{ + &sdkkonnecterrs.BadRequestError{ Status: 400, Title: "bad request", }, @@ -438,7 +438,7 @@ func TestUpdateKongService(t *testing.T) { sdk. EXPECT(). UpsertService(ctx, - sdkkonnectgoops.UpsertServiceRequest{ + sdkkonnectops.UpsertServiceRequest{ ControlPlaneID: "12345", ServiceID: "123456789", Service: kongServiceToSDKServiceInput(svc), @@ -446,7 +446,7 @@ func TestUpdateKongService(t *testing.T) { ). Return( nil, - &sdkkonnectgoerrs.SDKError{ + &sdkkonnecterrs.SDKError{ StatusCode: 404, Message: "not found", }, @@ -456,8 +456,8 @@ func TestUpdateKongService(t *testing.T) { EXPECT(). CreateService(ctx, "12345", kongServiceToSDKServiceInput(svc)). Return( - &sdkkonnectgoops.CreateServiceResponse{ - Service: &sdkkonnectgocomp.Service{ + &sdkkonnectops.CreateServiceResponse{ + Service: &sdkkonnectcomp.Service{ ID: lo.ToPtr("123456789"), Name: lo.ToPtr("svc-1"), }, diff --git a/controller/konnect/reconciler_generic.go b/controller/konnect/reconciler_generic.go index 1fb787a51..5ff4fbed2 100644 --- a/controller/konnect/reconciler_generic.go +++ b/controller/konnect/reconciler_generic.go @@ -101,6 +101,13 @@ func (r *KonnectEntityReconciler[T, TEnt]) SetupWithManager(mgr ctrl.Manager) er }) ) + for _, ind := range ReconciliationIndexOptionsForEntity(ent) { + if err := mgr.GetCache().IndexField(context.Background(), ind.IndexObject, ind.IndexField, ind.ExtractValue); err != nil { + fmt.Println("Create index:", err) + return err + } + } + for _, dep := range ReconciliationWatchOptionsForEntity(r.Client, ent) { b = dep(b) } @@ -577,7 +584,7 @@ func handleKongServiceRef[T constraints.SupportedKonnectEntityType, TEnt constra return res, errStatus } - return ctrl.Result{}, fmt.Errorf("Can't get the referenced KongService %s: %w", nn, err) + return ctrl.Result{}, fmt.Errorf("can't get the referenced KongService %s: %w", nn, err) } // If referenced KongService is being deleted, return an error so that we @@ -721,6 +728,11 @@ func getControlPlaneRef[T constraints.SupportedKonnectEntityType, TEnt constrain return mo.None[configurationv1alpha1.ControlPlaneRef]() } return mo.Some(*e.Spec.ControlPlaneRef) + case *configurationv1alpha1.KongPluginBinding: + if e.Spec.ControlPlaneRef == nil { + return mo.None[configurationv1alpha1.ControlPlaneRef]() + } + return mo.Some(*e.Spec.ControlPlaneRef) default: panic(fmt.Sprintf("unsupported entity type %T", e)) } diff --git a/controller/konnect/reconciler_generic_rbac.go b/controller/konnect/reconciler_generic_rbac.go index fba53439a..acbbc900d 100644 --- a/controller/konnect/reconciler_generic_rbac.go +++ b/controller/konnect/reconciler_generic_rbac.go @@ -9,4 +9,7 @@ package konnect //+kubebuilder:rbac:groups=configuration.konghq.com,resources=kongservices,verbs=get;list;watch;update;patch //+kubebuilder:rbac:groups=configuration.konghq.com,resources=kongservices/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=configuration.konghq.com,resources=kongpluginbindings,verbs=get;list;watch;update;patch +//+kubebuilder:rbac:groups=configuration.konghq.com,resources=kongpluginbindings/status,verbs=get;update;patch + //+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch diff --git a/controller/konnect/reconciler_generic_test.go b/controller/konnect/reconciler_generic_test.go index 44f8f4d07..b079ebe32 100644 --- a/controller/konnect/reconciler_generic_test.go +++ b/controller/konnect/reconciler_generic_test.go @@ -23,6 +23,10 @@ func TestNewKonnectEntityReconciler(t *testing.T) { testNewKonnectEntityReconciler(t, configurationv1.KongConsumer{}) testNewKonnectEntityReconciler(t, configurationv1alpha1.KongRoute{}) testNewKonnectEntityReconciler(t, configurationv1beta1.KongConsumerGroup{}) + // TODO: Reconcilers setting index require a real k8s API server: + // https://github.com/kubernetes-sigs/controller-runtime/issues/657 + // Maybe we should import envtest. + // testNewKonnectEntityReconciler(t, configurationv1alpha1.KongPluginBinding{}) } func testNewKonnectEntityReconciler[ @@ -39,9 +43,11 @@ func testNewKonnectEntityReconciler[ t.Run(ent.GetTypeName(), func(t *testing.T) { cl := fakectrlruntimeclient.NewFakeClient() mgr, err := ctrl.NewManager(&rest.Config{}, ctrl.Options{ + Scheme: scheme.Get(), }) require.NoError(t, err) + reconciler := NewKonnectEntityReconciler[T, TEnt](sdkFactory, false, cl) require.NoError(t, reconciler.SetupWithManager(mgr)) }) diff --git a/controller/konnect/reconciler_konnectapiauth.go b/controller/konnect/reconciler_konnectapiauth.go index 82c97c9f2..ac03deece 100644 --- a/controller/konnect/reconciler_konnectapiauth.go +++ b/controller/konnect/reconciler_konnectapiauth.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - sdkkonnectgoops "github.com/Kong/sdk-konnect-go/models/operations" + sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -144,7 +144,7 @@ func (r *KonnectAPIAuthConfigurationReconciler) Reconcile( // NOTE: This is needed because currently the SDK only lists the prod global API as supported: // https://github.com/Kong/sdk-konnect-go/blob/999d9a987e1aa7d2e09ac11b1450f4563adf21ea/models/operations/getorganizationsme.go#L10-L12 - respOrg, err := sdk.Me.GetOrganizationsMe(ctx, sdkkonnectgoops.WithServerURL("https://"+apiAuth.Spec.ServerURL)) + respOrg, err := sdk.Me.GetOrganizationsMe(ctx, sdkkonnectops.WithServerURL("https://"+apiAuth.Spec.ServerURL)) if err != nil { logger.Error(err, "failed to get organization info from Konnect") if cond, ok := k8sutils.GetCondition(conditions.KonnectEntityAPIAuthConfigurationValidConditionType, &apiAuth); !ok || @@ -232,13 +232,13 @@ func getTokenFromKonnectAPIAuthConfiguration( return "", fmt.Errorf("failed to get Secret %s: %w", nn, err) } if secret.Labels == nil || secret.Labels[SecretCredentialLabel] != SecretCredentialLabelValueKonnect { - return "", fmt.Errorf("Secret %s does not have label %s: %s", nn, SecretCredentialLabel, SecretCredentialLabelValueKonnect) + return "", fmt.Errorf("secret %s does not have label %s: %s", nn, SecretCredentialLabel, SecretCredentialLabelValueKonnect) } if secret.Data == nil { - return "", fmt.Errorf("Secret %s has no data", nn) + return "", fmt.Errorf("secret %s has no data", nn) } if _, ok := secret.Data[SecretTokenKey]; !ok { - return "", fmt.Errorf("Secret %s does not have key %s", nn, SecretTokenKey) + return "", fmt.Errorf("secret %s does not have key %s", nn, SecretTokenKey) } return string(secret.Data[SecretTokenKey]), nil } diff --git a/controller/konnect/sdkfactory.go b/controller/konnect/sdkfactory.go index bf34e1164..07b6fb7cd 100644 --- a/controller/konnect/sdkfactory.go +++ b/controller/konnect/sdkfactory.go @@ -2,7 +2,7 @@ package konnect import ( sdkkonnectgo "github.com/Kong/sdk-konnect-go" - sdkkonnectgocomp "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" ) // SDKToken is a token used to authenticate with the Konnect SDK. @@ -24,7 +24,7 @@ func NewSDKFactory() SDKFactory { func (f sdkFactory) NewKonnectSDK(serverURL string, token SDKToken) *sdkkonnectgo.SDK { return sdkkonnectgo.New( sdkkonnectgo.WithSecurity( - sdkkonnectgocomp.Security{ + sdkkonnectcomp.Security{ PersonalAccessToken: sdkkonnectgo.String(string(token)), }, ), diff --git a/controller/konnect/watch.go b/controller/konnect/watch.go index 5973a43b2..450fdb7b7 100644 --- a/controller/konnect/watch.go +++ b/controller/konnect/watch.go @@ -34,6 +34,8 @@ func ReconciliationWatchOptionsForEntity[ return KongServiceReconciliationWatchOptions(cl) case *konnectv1alpha1.KonnectControlPlane: return KonnectControlPlaneReconciliationWatchOptions(cl) + case *configurationv1alpha1.KongPluginBinding: + return KongPluginBindingReconciliationWatchOptions(cl) default: panic(fmt.Sprintf("unsupported entity type %T", ent)) } diff --git a/controller/konnect/watch_kongpluginbinding.go b/controller/konnect/watch_kongpluginbinding.go new file mode 100644 index 000000000..9b6572aad --- /dev/null +++ b/controller/konnect/watch_kongpluginbinding.go @@ -0,0 +1,308 @@ +package konnect + +import ( + "context" + "fmt" + "reflect" + + "github.com/samber/lo" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + operatorerrors "github.com/kong/gateway-operator/internal/errors" + "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" + konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" +) + +// ----------------------------------------------------------------------------- +// KongPluginBinding reconciler - Watch Builder +// ----------------------------------------------------------------------------- + +// TODO(pmalek): this can be extracted and used in reconciler.go +// as every Konnect entity will have a reference to the KonnectAPIAuthConfiguration. +// This would require: +// - mapping function from non List types to List types +// - a function on each Konnect entity type to get the API Auth configuration +// reference from the object +// - lists have their items stored in Items field, not returned via a method + +// KongPluginBindingReconciliationWatchOptions returns the watch options for +// the KongPluginBinding. +func KongPluginBindingReconciliationWatchOptions( + cl client.Client, +) []func(*ctrl.Builder) *ctrl.Builder { + return []func(*ctrl.Builder) *ctrl.Builder{ + func(b *ctrl.Builder) *ctrl.Builder { + return b.For(&configurationv1alpha1.KongPluginBinding{}, + builder.WithPredicates( + predicate.NewPredicateFuncs(kongPluginBindingRefersToKonnectControlPlane), + ), + ) + }, + func(b *ctrl.Builder) *ctrl.Builder { + return b.Watches( + &konnectv1alpha1.KonnectAPIAuthConfiguration{}, + handler.EnqueueRequestsFromMapFunc( + enqueueKongPluginBindingForKonnectAPIAuthConfiguration(cl), + ), + ) + }, + func(b *ctrl.Builder) *ctrl.Builder { + return b.Watches( + &konnectv1alpha1.KonnectControlPlane{}, + handler.EnqueueRequestsFromMapFunc( + enqueueKongPluginBindingForKonnectControlPlane(cl), + ), + ) + }, + func(b *ctrl.Builder) *ctrl.Builder { + return b.Watches( + &configurationv1.KongPlugin{}, + handler.EnqueueRequestsFromMapFunc( + enqueueKongPluginBindingForKongPlugin(cl), + ), + ) + }, + func(b *ctrl.Builder) *ctrl.Builder { + return b.Watches( + &configurationv1.KongClusterPlugin{}, + handler.EnqueueRequestsFromMapFunc( + enqueueKongPluginBindingForKongClusterPlugin(cl), + ), + ) + }, + // TODO(mlavacca): add KongService watch + // TODO(mlavacca): add KongConsumer watch + // TODO(mlavacca): add KongRoute watch + } +} + +// ----------------------------------------------------------------------------- +// KongPluginBinding reconciler - Watch Predicates +// ----------------------------------------------------------------------------- + +// kongPluginBindingRefersToKonnectControlPlane returns true if the KongPluginBinding +// refers to a KonnectControlPlane. +func kongPluginBindingRefersToKonnectControlPlane(obj client.Object) bool { + kongPB, ok := obj.(*configurationv1alpha1.KongPluginBinding) + if !ok { + ctrllog.FromContext(context.Background()).Error( + operatorerrors.ErrUnexpectedObject, + "failed to run predicate function", + "expected", "KongPluginBinding", "found", reflect.TypeOf(obj), + ) + return false + } + + cpRef := kongPB.Spec.ControlPlaneRef + return cpRef != nil && cpRef.Type == configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef +} + +// ----------------------------------------------------------------------------- +// KongPluginBinding reconciler - Watch Mappers +// ----------------------------------------------------------------------------- + +func enqueueKongPluginBindingForKonnectAPIAuthConfiguration( + cl client.Client, +) func(ctx context.Context, obj client.Object) []reconcile.Request { + return func(ctx context.Context, obj client.Object) []reconcile.Request { + auth, ok := obj.(*konnectv1alpha1.KonnectAPIAuthConfiguration) + if !ok { + return nil + } + var l configurationv1alpha1.KongPluginBindingList + if err := cl.List(ctx, &l, &client.ListOptions{ + // TODO: change this when cross namespace refs are allowed. + Namespace: auth.GetNamespace(), + }); err != nil { + return nil + } + + var ret []reconcile.Request + for _, pb := range l.Items { + cpRef, ok := getControlPlaneRef(&pb).Get() + if !ok { + continue + } + + switch cpRef.Type { + case configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef: + nn := types.NamespacedName{ + Name: cpRef.KonnectNamespacedRef.Name, + Namespace: pb.Namespace, + } + // TODO: change this when cross namespace refs are allowed. + if nn.Namespace != auth.Namespace { + continue + } + var cp konnectv1alpha1.KonnectControlPlane + if err := cl.Get(ctx, nn, &cp); err != nil { + ctrllog.FromContext(ctx).Error( + err, + "failed to get KonnectControlPlane", + "KonnectControlPlane", 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: pb.Namespace, + Name: pb.Name, + }, + }) + + case configurationv1alpha1.ControlPlaneRefKonnectID: + ctrllog.FromContext(ctx).Error( + fmt.Errorf("unimplemented ControlPlaneRef type %q", cpRef.Type), + "unimplemented ControlPlaneRef for KongPluginBinding", + "KongPluginBinding", pb, "refType", cpRef.Type, + ) + continue + + default: + ctrllog.FromContext(ctx).V(logging.DebugLevel.Value()).Info( + "unsupported ControlPlaneRef for KongPluginBinding", + "KongPluginBinding", pb, "refType", cpRef.Type, + ) + continue + } + } + return ret + } +} + +func enqueueKongPluginBindingForKonnectControlPlane( + cl client.Client, +) func(ctx context.Context, obj client.Object) []reconcile.Request { + return func(ctx context.Context, obj client.Object) []reconcile.Request { + cp, ok := obj.(*konnectv1alpha1.KonnectControlPlane) + if !ok { + return nil + } + var l configurationv1alpha1.KongPluginBindingList + if err := cl.List(ctx, &l, &client.ListOptions{ + // TODO: change this when cross namespace refs are allowed. + Namespace: cp.GetNamespace(), + }); err != nil { + return nil + } + + var ret []reconcile.Request + for _, pb := range l.Items { + switch pb.Spec.ControlPlaneRef.Type { + case configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef: + // TODO: change this when cross namespace refs are allowed. + if pb.Spec.ControlPlaneRef.KonnectNamespacedRef.Name != cp.Name { + continue + } + + ret = append(ret, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: pb.Namespace, + Name: pb.Name, + }, + }) + + case configurationv1alpha1.ControlPlaneRefKonnectID: + ctrllog.FromContext(ctx).Error( + fmt.Errorf("unimplemented ControlPlaneRef type %q", pb.Spec.ControlPlaneRef.Type), + "unimplemented ControlPlaneRef for KongPluginBinding", + "KongPluginBinding", pb, "refType", pb.Spec.ControlPlaneRef.Type, + ) + continue + + default: + ctrllog.FromContext(ctx).V(logging.DebugLevel.Value()).Info( + "unsupported ControlPlaneRef for KongPluginBinding", + "KongPluginBinding", pb, "refType", pb.Spec.ControlPlaneRef.Type, + ) + continue + } + } + return ret + } +} + +func enqueueKongPluginBindingForKongPlugin(cl client.Client) func( + ctx context.Context, obj client.Object) []reconcile.Request { + return func(ctx context.Context, obj client.Object) []reconcile.Request { + plugin, ok := obj.(*configurationv1.KongPlugin) + if !ok { + return nil + } + + pluginBindingList := configurationv1alpha1.KongPluginBindingList{} + err := cl.List(ctx, &pluginBindingList, + // Currently KongPlugin and KongPluginBinding must be in the same namespace to reference the plugin. + client.InNamespace(plugin.Namespace), + client.MatchingFields{ + IndexFieldKongPluginBindingKongPluginReference: plugin.Namespace + "/" + plugin.Name, + }, + ) + if err != nil { + ctrllog.FromContext(ctx).Error(err, "failed to list KongPluginBindings referencing KongPlugin") + return nil + } + + return lo.FilterMap(pluginBindingList.Items, func(pb configurationv1alpha1.KongPluginBinding, _ int) (reconcile.Request, bool) { + // Only put KongPluginBindings referencing to a Konnect control plane, + if pb.Spec.ControlPlaneRef == nil || pb.Spec.ControlPlaneRef.Type != configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef { + return reconcile.Request{}, false + } + return reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: pb.Namespace, + Name: pb.Name, + }, + }, true + }) + } +} + +func enqueueKongPluginBindingForKongClusterPlugin(cl client.Client) func( + ctx context.Context, obj client.Object) []reconcile.Request { + return func(ctx context.Context, obj client.Object) []reconcile.Request { + plugin, ok := obj.(*configurationv1.KongClusterPlugin) + if !ok { + return nil + } + + pluginBindingList := configurationv1alpha1.KongPluginBindingList{} + err := cl.List(ctx, &pluginBindingList, + client.MatchingFields{ + IndexFieldKongPluginBindingKongClusterPluginReference: plugin.Name, + }, + ) + if err != nil { + ctrllog.FromContext(ctx).Error(err, "failed to list KongPluginBindings referencing KongClusterPlugin") + } + + return lo.FilterMap(pluginBindingList.Items, func(pb configurationv1alpha1.KongPluginBinding, _ int) (reconcile.Request, bool) { + // Only put KongPluginBindings referencing to a Konnect control plane, + if pb.Spec.ControlPlaneRef == nil || pb.Spec.ControlPlaneRef.Type != configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef { + return reconcile.Request{}, false + } + return reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: pb.Namespace, + Name: pb.Name, + }, + }, true + }) + } +} diff --git a/go.mod b/go.mod index 2c276a3a2..bdf621894 100644 --- a/go.mod +++ b/go.mod @@ -18,8 +18,8 @@ require ( github.com/go-logr/logr v1.4.2 github.com/google/go-containerregistry v0.20.2 github.com/google/uuid v1.6.0 - github.com/kong/kubernetes-configuration v0.0.8 - github.com/kong/kubernetes-ingress-controller/v3 v3.2.4 + github.com/kong/kubernetes-configuration v0.0.9 + github.com/kong/kubernetes-ingress-controller/v3 v3.3.1 github.com/kong/kubernetes-telemetry v0.1.5 github.com/kong/kubernetes-testing-framework v0.47.2 github.com/kong/semver/v4 v4.0.1 @@ -35,8 +35,8 @@ require ( ) require ( - cloud.google.com/go/auth v0.8.1 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect + cloud.google.com/go/auth v0.9.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect github.com/BurntSushi/toml v1.4.0 // indirect github.com/avast/retry-go/v4 v4.6.0 // indirect github.com/aws/aws-sdk-go v1.49.13 // indirect @@ -44,7 +44,7 @@ require ( github.com/boombuler/barcode v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect - github.com/distribution/reference v0.5.0 // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/ericlagergren/decimal v0.0.0-20240411145413-00de7ca16731 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect @@ -67,6 +67,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/homeport/dyff v1.6.0 // indirect github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/kong/go-kong v0.57.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect @@ -102,8 +103,8 @@ require ( golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect k8s.io/apiserver v0.31.0 // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect @@ -111,7 +112,7 @@ require ( require ( cloud.google.com/go/compute/metadata v0.5.0 // indirect - cloud.google.com/go/container v1.38.1 // indirect + cloud.google.com/go/container v1.39.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/gammazero/deque v0.2.0 // indirect github.com/gammazero/workerpool v1.1.3 // indirect @@ -129,9 +130,9 @@ require ( github.com/zmap/zlint/v3 v3.5.0 // indirect go.opencensus.io v0.24.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect - golang.org/x/crypto v0.25.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/sync v0.8.0 // indirect - google.golang.org/api v0.192.0 // indirect + google.golang.org/api v0.194.0 // indirect google.golang.org/grpc v1.65.0 // indirect sigs.k8s.io/kind v0.24.0 // indirect sigs.k8s.io/kustomize/api v0.17.3 @@ -139,7 +140,7 @@ require ( ) require ( - github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -159,7 +160,7 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.6.0 github.com/google/gofuzz v1.2.0 // indirect - github.com/gruntwork-io/terratest v0.47.0 + github.com/gruntwork-io/terratest v0.47.1 github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -171,7 +172,7 @@ require ( github.com/opencontainers/image-spec v1.1.0 github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_golang v1.20.2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -181,11 +182,11 @@ require ( github.com/tidwall/pretty v1.2.1 go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 - golang.org/x/net v0.27.0 // indirect + golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect - golang.org/x/sys v0.23.0 // indirect + golang.org/x/sys v0.24.0 // indirect golang.org/x/term v0.23.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.6.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.34.2 // indirect diff --git a/go.sum b/go.sum index 88b267c5f..6456c34f6 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,14 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= -cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= -cloud.google.com/go/auth v0.8.1 h1:QZW9FjC5lZzN864p13YxvAtGUlQ+KgRL+8Sg45Z6vxo= -cloud.google.com/go/auth v0.8.1/go.mod h1:qGVp/Y3kDRSDZ5gFD/XPUfYQ9xW1iI7q8RIRoCyBbJc= -cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI= -cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= +cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= +cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= +cloud.google.com/go/auth v0.9.1 h1:+pMtLEV2k0AXKvs/tGZojuj6QaioxfUjOpMsG5Gtx+w= +cloud.google.com/go/auth v0.9.1/go.mod h1:Sw8ocT5mhhXxFklyhT12Eiy0ed6tTrPMCJjSI8KhYLk= +cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= +cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= -cloud.google.com/go/container v1.38.1 h1:Pb0GbZIg/KS4A9gbF3J4JHmrgPpBA2y+4v9N04aJkOs= -cloud.google.com/go/container v1.38.1/go.mod h1:2r4Qiz6IG2LhRFfWhPNmrYD7yzdE2B2kghigVWoSw/g= +cloud.google.com/go/container v1.39.0 h1:Q1oW01ENxkkG3uf1oYoTmHPdvP+yhFCIuCJ4mk2RwkQ= +cloud.google.com/go/container v1.39.0/go.mod h1:gNgnvs1cRHXjYxrotVm+0nxDfZkqzBbXCffh5WtqieI= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -19,8 +19,8 @@ github.com/Kong/sdk-konnect-go v0.0.6 h1:dbE8p96ys/GJEsmnC5EoJ/qSJxG3pEVCRPO2hpo github.com/Kong/sdk-konnect-go v0.0.6/go.mod h1:ipu67aQNnwDzu/LXKePG46cVqkkZnAHKWpsbhTEI8xE= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA= @@ -62,8 +62,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v27.1.2+incompatible h1:AhGzR1xaQIy53qCkxARaFluI00WPGtXn0AJuoQsVYTY= github.com/docker/docker v27.1.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -197,8 +197,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/gruntwork-io/go-commons v0.8.0 h1:k/yypwrPqSeYHevLlEDmvmgQzcyTwrlZGRaxEM6G0ro= github.com/gruntwork-io/go-commons v0.8.0/go.mod h1:gtp0yTtIBExIZp7vyIV9I0XQkVwiQZze678hvDXof78= -github.com/gruntwork-io/terratest v0.47.0 h1:xIy1pT7NbGVlMLDZEHl3+3iSnvffh8tN2pL6idn448c= -github.com/gruntwork-io/terratest v0.47.0/go.mod h1:oywHw1cFKXSYvKPm27U7quZVzDUlA22H2xUrKCe26xM= +github.com/gruntwork-io/terratest v0.47.1 h1:qOaxnL7Su5+KpDHYUN/ek1jn8ImvCKtOkaY4OSMS4tI= +github.com/gruntwork-io/terratest v0.47.1/go.mod h1:LnYX8BN5WxUMpDr8rtD39oToSL4CBERWSCusbJ0d/64= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -220,12 +220,14 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kong/go-kong v0.57.1 h1:PATDsjrRr0SMMtN433ONoZqm+7UKmv49zgqQyvWbfPY= github.com/kong/go-kong v0.57.1/go.mod h1:lG2zDVU0yynwV9S7gJXDwwi6zVY+MgS8aUX4QhzpDz4= -github.com/kong/kubernetes-configuration v0.0.8 h1:FxfCjG7MGO0UvIWRKAnO+G0hrxG7UjUweG38cD+iNLw= -github.com/kong/kubernetes-configuration v0.0.8/go.mod h1:Hers1gtlaaEqlg73xt/SS+W8UPj9fnxQkdZfAjVeW7M= -github.com/kong/kubernetes-ingress-controller/v3 v3.2.4 h1:5QKzRMHWAePwa9HGj3xtvgridzeG72xd3BftDP+PcjU= -github.com/kong/kubernetes-ingress-controller/v3 v3.2.4/go.mod h1:kbbsyyNw/2HdURecav7iFcjAOtNXoUP5CfYFYJgiJq4= +github.com/kong/kubernetes-configuration v0.0.9 h1:sNw/Y2e51qtBHTnA+CS+aS66lrAtg2ndpBW3zv9ObYE= +github.com/kong/kubernetes-configuration v0.0.9/go.mod h1:5RTuYhTVoyfjKFaeJ6dziN/8dqVHcLnHR6wAyT/WfVU= +github.com/kong/kubernetes-ingress-controller/v3 v3.3.1 h1:uWlcwz5oAnVyUZdtDV9p2l9CdlHhLNTKey3AcHF/Jxs= +github.com/kong/kubernetes-ingress-controller/v3 v3.3.1/go.mod h1:2CBAJ7/J+FyAFn7Y8OLoTO3ApM+qiGIgNLbCyy98Vqk= github.com/kong/kubernetes-telemetry v0.1.5 h1:xHwU1q0IvfEYqpj03po73ZKbVarnFPUwzkoFkdVnr9w= github.com/kong/kubernetes-telemetry v0.1.5/go.mod h1:1UXyZ6N3e8Fl6YguToQ6tKNveonkhjSqxzY7HVW+Ba4= github.com/kong/kubernetes-testing-framework v0.47.2 h1:+2Z9anTpbV/hwNeN+NFQz53BMU+g3QJydkweBp3tULo= @@ -241,6 +243,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= @@ -311,8 +315,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= +github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= @@ -434,8 +438,8 @@ golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= @@ -464,8 +468,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -494,8 +498,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -510,8 +514,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -532,17 +536,17 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/api v0.192.0 h1:PljqpNAfZaaSpS+TnANfnNAXKdzHM/B9bKhwRlo7JP0= -google.golang.org/api v0.192.0/go.mod h1:9VcphjvAxPKLmSxVSzPlSRXy/5ARMEw5bf58WoVXafQ= +google.golang.org/api v0.194.0 h1:dztZKG9HgtIpbI35FhfuSNR/zmaMVdxNlntHj1sIS4s= +google.golang.org/api v0.194.0/go.mod h1:AgvUFdojGANh3vI+P7EVnxj3AISHllxGCJSFmggmnd0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f h1:b1Ln/PG8orm0SsBbHZWke8dDp2lrCD4jSmfglFpTZbk= -google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= diff --git a/hack/generators/go.mod b/hack/generators/go.mod index 1ccededde..7707901ab 100644 --- a/hack/generators/go.mod +++ b/hack/generators/go.mod @@ -8,7 +8,7 @@ replace github.com/kong/gateway-operator => ../../ require ( github.com/Masterminds/semver v1.5.0 - github.com/Masterminds/sprig/v3 v3.2.3 + github.com/Masterminds/sprig/v3 v3.3.0 github.com/hashicorp/go-retryablehttp v0.7.7 github.com/kong/gateway-operator v0.0.0-00010101000000-000000000000 github.com/kong/semver/v4 v4.0.1 @@ -19,27 +19,27 @@ require ( ) require ( + dario.cat/mergo v1.0.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/huandu/xstrings v1.4.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/shopspring/decimal v1.2.0 // indirect - github.com/spf13/cast v1.3.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/spf13/cast v1.7.0 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.25.0 // indirect - golang.org/x/net v0.27.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/text v0.17.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect diff --git a/hack/generators/go.sum b/hack/generators/go.sum index 81c32113f..42d8a6586 100644 --- a/hack/generators/go.sum +++ b/hack/generators/go.sum @@ -1,17 +1,21 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= -github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= @@ -24,7 +28,6 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -33,12 +36,8 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1 github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -53,10 +52,8 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -71,70 +68,49 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -144,9 +120,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/versions/controlplane.go b/internal/versions/controlplane.go index 32a689a9d..f80889520 100644 --- a/internal/versions/controlplane.go +++ b/internal/versions/controlplane.go @@ -13,7 +13,7 @@ const ( // and those tests create KIC's URLs for things like roles or CRDs. // Since KIC only defines the full tags in its repo (as expected) we cannot use // a partial version here, as it would not match KIC's tag. - DefaultControlPlaneVersion = "3.2.4" // renovate: datasource=docker depName=kong/kubernetes-ingress-controller + DefaultControlPlaneVersion = "3.3.1" // renovate: datasource=docker depName=kong/kubernetes-ingress-controller ) // minimumControlPlaneVersion indicates the bare minimum version of the diff --git a/modules/cli/cli.go b/modules/cli/cli.go index 7966098fd..9257884e4 100644 --- a/modules/cli/cli.go +++ b/modules/cli/cli.go @@ -52,6 +52,8 @@ func New(m metadata.Info) *CLI { // webhook and validation options flagSet.BoolVar(&deferCfg.ValidatingWebhookEnabled, "enable-validating-webhook", true, "Enable the validating webhook.") + flagSet.StringVar(&cfg.WebhookCertificateConfigBaseImage, "webhook-certificate-config-base-image", consts.WebhookCertificateConfigBaseImage, "The base image for the certgen Jobs.") + flagSet.StringVar(&cfg.WebhookCertificateConfigShellImage, "webhook-certificate-config-shell-image", consts.WebhookCertificateConfigShellImage, "The shell image for the certgen Jobs.") flagSet.BoolVar(&deferCfg.Version, "version", false, "Print version information.") diff --git a/modules/cli/cli_test.go b/modules/cli/cli_test.go index d5bcc036e..8e6496859 100644 --- a/modules/cli/cli_test.go +++ b/modules/cli/cli_test.go @@ -78,6 +78,19 @@ func TestParse(t *testing.T) { return cfg }, }, + { + name: "webhook certificate configuration arguments are set", + args: []string{ + "--webhook-certificate-config-base-image=mybaseimage:42", + "--webhook-certificate-config-shell-image=shellimg", + }, + expectedCfg: func() manager.Config { + cfg := expectedDefaultCfg() + cfg.WebhookCertificateConfigBaseImage = "mybaseimage:42" + cfg.WebhookCertificateConfigShellImage = "shellimg" + return cfg + }, + }, } for _, tC := range testCases { @@ -153,6 +166,8 @@ func expectedDefaultCfg() manager.Config { KonnectSyncPeriod: consts.DefaultKonnectSyncPeriod, KongPluginInstallationControllerEnabled: false, ValidatingWebhookEnabled: true, + WebhookCertificateConfigBaseImage: consts.WebhookCertificateConfigBaseImage, + WebhookCertificateConfigShellImage: consts.WebhookCertificateConfigShellImage, LoggerOpts: &zap.Options{}, } } diff --git a/modules/manager/controller_setup.go b/modules/manager/controller_setup.go index 9ea5232f4..1ddc817b3 100644 --- a/modules/manager/controller_setup.go +++ b/modules/manager/controller_setup.go @@ -360,6 +360,15 @@ func SetupControllers(mgr manager.Manager, c *Config) (map[string]ControllerDef, konnect.WithKonnectEntitySyncPeriod[configurationv1beta1.KongConsumerGroup](c.KonnectSyncPeriod), ), } + controllers[KongConsumerControllerName] = ControllerDef{ + Enabled: c.KonnectControllersEnabled, + Controller: konnect.NewKonnectEntityReconciler[configurationv1alpha1.KongPluginBinding]( + sdkFactory, + c.DevelopmentMode, + mgr.GetClient(), + konnect.WithKonnectEntitySyncPeriod[configurationv1alpha1.KongPluginBinding](c.KonnectSyncPeriod), + ), + } } return controllers, nil diff --git a/modules/manager/run.go b/modules/manager/run.go index eb8b18315..798e387f1 100644 --- a/modules/manager/run.go +++ b/modules/manager/run.go @@ -92,7 +92,9 @@ type Config struct { KonnectControllersEnabled bool // webhook and validation options - ValidatingWebhookEnabled bool + ValidatingWebhookEnabled bool + WebhookCertificateConfigBaseImage string + WebhookCertificateConfigShellImage string } // DefaultConfig returns a default configuration for the manager. diff --git a/modules/manager/webhook.go b/modules/manager/webhook.go index b0e6de9fe..0da275d74 100644 --- a/modules/manager/webhook.go +++ b/modules/manager/webhook.go @@ -256,16 +256,18 @@ func (m *webhookManager) createWebhookResources(ctx context.Context) error { } func (m *webhookManager) createCertificateConfigJobs(ctx context.Context) error { - jobCertificateConfigImage := consts.WebhookCertificateConfigBaseImage + jobCertificateConfigBaseImage := m.cfg.WebhookCertificateConfigBaseImage + jobCertificateConfigShellImage := m.cfg.WebhookCertificateConfigShellImage if relatedJobImage := os.Getenv("RELATED_IMAGE_CERTIFICATE_CONFIG"); relatedJobImage != "" { // RELATED_IMAGE_CERTIFICATE_CONFIG is set by the operator-sdk when building the operator bundle. // https://github.com/Kong/gateway-operator-archive/issues/261 - jobCertificateConfigImage = relatedJobImage + jobCertificateConfigBaseImage = relatedJobImage } job := k8sresources.GenerateNewWebhookCertificateConfigJob( m.cfg.ControllerNamespace, consts.WebhookCertificateConfigName, - jobCertificateConfigImage, + jobCertificateConfigBaseImage, + jobCertificateConfigShellImage, consts.WebhookCertificateConfigSecretName, consts.WebhookName, ) diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go index d0c3c1c7a..23576b6ed 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -128,6 +128,8 @@ const ( const ( // WebhookCertificateConfigBaseImage is the image to use by the certificate config Jobs. WebhookCertificateConfigBaseImage = "registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.3.0" + // WebhookCertificateConfigShellImage is the image to use by the certificate config Jobs. + WebhookCertificateConfigShellImage = "busybox" // WebhookName is the ValidatingWebhookConfiguration name. WebhookName = "gateway-operator-validation.konghq.com" // WebhookCertificateConfigSecretName is the name of the secret containing the webhook certificate. diff --git a/pkg/utils/kubernetes/resources/jobs.go b/pkg/utils/kubernetes/resources/jobs.go index 41261f195..f2cd3264b 100644 --- a/pkg/utils/kubernetes/resources/jobs.go +++ b/pkg/utils/kubernetes/resources/jobs.go @@ -18,7 +18,8 @@ import ( // GenerateNewWebhookCertificateConfigJob generates the create and patch jobs for the certificateConfig func GenerateNewWebhookCertificateConfigJob(namespace, serviceAccountName, - imageName, + baseImageName, + shellImageName, secretName, webhookName string, ) *batchv1.Job { @@ -39,7 +40,7 @@ func GenerateNewWebhookCertificateConfigJob(namespace, fmt.Sprintf("--namespace=%s", namespace), fmt.Sprintf("--secret-name=%s", secretName), }, - Image: imageName, + Image: baseImageName, ImagePullPolicy: corev1.PullIfNotPresent, }, { @@ -53,7 +54,7 @@ func GenerateNewWebhookCertificateConfigJob(namespace, fmt.Sprintf("--secret-name=%s", secretName), "--patch-failure-policy=Fail", }, - Image: imageName, + Image: baseImageName, ImagePullPolicy: corev1.PullIfNotPresent, }, } @@ -61,7 +62,7 @@ func GenerateNewWebhookCertificateConfigJob(namespace, j.Spec.Template.Spec.Containers = []corev1.Container{ { Name: "done", - Image: "busybox", + Image: shellImageName, Args: []string{"echo", "done"}, ImagePullPolicy: corev1.PullIfNotPresent, }, From a0bc5d31830c2fd1ed83ec2e52f828d3b08c5854 Mon Sep 17 00:00:00 2001 From: Yi Tao Date: Wed, 4 Sep 2024 11:00:31 +0800 Subject: [PATCH 5/6] fix lint --- test/integration/test_konnect_entities.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/test_konnect_entities.go b/test/integration/test_konnect_entities.go index e629df7f2..a80f84eab 100644 --- a/test/integration/test_konnect_entities.go +++ b/test/integration/test_konnect_entities.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/Kong/sdk-konnect-go/models/components" + sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" "github.com/google/uuid" "github.com/samber/lo" "github.com/stretchr/testify/assert" @@ -61,9 +61,9 @@ func TestKonnectEntities(t *testing.T) { Namespace: ns.Name, }, Spec: konnectv1alpha1.KonnectControlPlaneSpec{ - CreateControlPlaneRequest: components.CreateControlPlaneRequest{ + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: cpName, - ClusterType: lo.ToPtr(components.ClusterTypeClusterTypeControlPlane), + ClusterType: lo.ToPtr(sdkkonnectcomp.ClusterTypeClusterTypeControlPlane), Labels: map[string]string{"test_id": testID}, }, KonnectConfiguration: konnectv1alpha1.KonnectConfiguration{ From d83cb9069bfa6669be42aa52936126bc9f05e8d5 Mon Sep 17 00:00:00 2001 From: Yi Tao Date: Thu, 5 Sep 2024 11:07:02 +0800 Subject: [PATCH 6/6] replace KonnnectControlPlane to KonnectGatewayControlPlane --- .../konnect/ops/ops_controlplane_test.go | 96 +++++++++---------- .../konnect/ops/ops_kongservice_test.go | 8 +- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/controller/konnect/ops/ops_controlplane_test.go b/controller/konnect/ops/ops_controlplane_test.go index 644ab52a1..afff2cbce 100644 --- a/controller/konnect/ops/ops_controlplane_test.go +++ b/controller/konnect/ops/ops_controlplane_test.go @@ -22,16 +22,16 @@ func TestCreateControlPlane(t *testing.T) { ctx := context.Background() testCases := []struct { name string - mockCPPair func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) + mockCPPair func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectGatewayControlPlane) expectedErr bool - assertions func(*testing.T, *konnectv1alpha1.KonnectControlPlane) + assertions func(*testing.T, *konnectv1alpha1.KonnectGatewayControlPlane) }{ { name: "success", - mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectGatewayControlPlane) { sdk := &MockControlPlaneSDK{} - cp := &konnectv1alpha1.KonnectControlPlane{ - Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + cp := &konnectv1alpha1.KonnectGatewayControlPlane{ + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, @@ -52,10 +52,10 @@ func TestCreateControlPlane(t *testing.T) { return sdk, cp }, - assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectControlPlane) { + assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectGatewayControlPlane) { assert.Equal(t, "12345", cp.GetKonnectStatus().GetKonnectID()) cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) - require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + require.True(t, ok, "Programmed condition not set on KonnectGatewayControlPlane") assert.Equal(t, metav1.ConditionTrue, cond.Status) assert.Equal(t, conditions.KonnectEntityProgrammedReasonProgrammed, cond.Reason) assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) @@ -63,14 +63,14 @@ func TestCreateControlPlane(t *testing.T) { }, { name: "fail", - mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectGatewayControlPlane) { sdk := &MockControlPlaneSDK{} - cp := &konnectv1alpha1.KonnectControlPlane{ + cp := &konnectv1alpha1.KonnectGatewayControlPlane{ ObjectMeta: metav1.ObjectMeta{ Name: "cp-1", Namespace: "default", }, - Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, @@ -90,14 +90,14 @@ func TestCreateControlPlane(t *testing.T) { return sdk, cp }, - assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectControlPlane) { + assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectGatewayControlPlane) { assert.Equal(t, "", cp.Status.GetKonnectID()) cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) - require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + require.True(t, ok, "Programmed condition not set on KonnectGatewayControlPlane") assert.Equal(t, metav1.ConditionFalse, cond.Status) assert.Equal(t, "FailedToCreate", cond.Reason) assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) - assert.Equal(t, `failed to create KonnectControlPlane default/cp-1: {"status":400,"title":"","instance":"","detail":"bad request","invalid_parameters":null}`, cond.Message) + assert.Equal(t, `failed to create KonnectGatewayControlPlane default/cp-1: {"status":400,"title":"","instance":"","detail":"bad request","invalid_parameters":null}`, cond.Message) }, expectedErr: true, }, @@ -128,21 +128,21 @@ func TestDeleteControlPlane(t *testing.T) { ctx := context.Background() testCases := []struct { name string - mockCPPair func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) + mockCPPair func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectGatewayControlPlane) expectedErr bool - assertions func(*testing.T, *konnectv1alpha1.KonnectControlPlane) + assertions func(*testing.T, *konnectv1alpha1.KonnectGatewayControlPlane) }{ { name: "success", - mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectGatewayControlPlane) { sdk := &MockControlPlaneSDK{} - cp := &konnectv1alpha1.KonnectControlPlane{ - Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + cp := &konnectv1alpha1.KonnectGatewayControlPlane{ + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, - Status: konnectv1alpha1.KonnectControlPlaneStatus{ + Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{ KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ ID: "12345", }, @@ -163,19 +163,19 @@ func TestDeleteControlPlane(t *testing.T) { }, { name: "fail", - mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectGatewayControlPlane) { sdk := &MockControlPlaneSDK{} - cp := &konnectv1alpha1.KonnectControlPlane{ + cp := &konnectv1alpha1.KonnectGatewayControlPlane{ ObjectMeta: metav1.ObjectMeta{ Name: "cp-1", Namespace: "default", }, - Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, - Status: konnectv1alpha1.KonnectControlPlaneStatus{ + Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{ KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ ID: "12345", }, @@ -198,19 +198,19 @@ func TestDeleteControlPlane(t *testing.T) { }, { name: "not found error is ignored and considered a success when trying to delete", - mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectGatewayControlPlane) { sdk := &MockControlPlaneSDK{} - cp := &konnectv1alpha1.KonnectControlPlane{ + cp := &konnectv1alpha1.KonnectGatewayControlPlane{ ObjectMeta: metav1.ObjectMeta{ Name: "cp-1", Namespace: "default", }, - Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, - Status: konnectv1alpha1.KonnectControlPlaneStatus{ + Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{ KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ ID: "12345", }, @@ -257,21 +257,21 @@ func TestUpdateControlPlane(t *testing.T) { ctx := context.Background() testCases := []struct { name string - mockCPPair func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) + mockCPPair func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectGatewayControlPlane) expectedErr bool - assertions func(*testing.T, *konnectv1alpha1.KonnectControlPlane) + assertions func(*testing.T, *konnectv1alpha1.KonnectGatewayControlPlane) }{ { name: "success", - mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectGatewayControlPlane) { sdk := &MockControlPlaneSDK{} - cp := &konnectv1alpha1.KonnectControlPlane{ - Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + cp := &konnectv1alpha1.KonnectGatewayControlPlane{ + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, - Status: konnectv1alpha1.KonnectControlPlaneStatus{ + Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{ KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ ID: "12345", }, @@ -299,10 +299,10 @@ func TestUpdateControlPlane(t *testing.T) { return sdk, cp }, - assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectControlPlane) { + assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectGatewayControlPlane) { assert.Equal(t, "12345", cp.Status.GetKonnectID()) cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) - require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + require.True(t, ok, "Programmed condition not set on KonnectGatewayControlPlane") assert.Equal(t, metav1.ConditionTrue, cond.Status) assert.Equal(t, conditions.KonnectEntityProgrammedReasonProgrammed, cond.Reason) assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) @@ -311,19 +311,19 @@ func TestUpdateControlPlane(t *testing.T) { }, { name: "fail", - mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectGatewayControlPlane) { sdk := &MockControlPlaneSDK{} - cp := &konnectv1alpha1.KonnectControlPlane{ + cp := &konnectv1alpha1.KonnectGatewayControlPlane{ ObjectMeta: metav1.ObjectMeta{ Name: "cp-1", Namespace: "default", }, - Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, - Status: konnectv1alpha1.KonnectControlPlaneStatus{ + Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{ KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ ID: "12345", }, @@ -351,32 +351,32 @@ func TestUpdateControlPlane(t *testing.T) { return sdk, cp }, - assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectControlPlane) { + assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectGatewayControlPlane) { assert.Equal(t, "12345", cp.Status.GetKonnectID()) cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) - require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + require.True(t, ok, "Programmed condition not set on KonnectGatewayControlPlane") assert.Equal(t, metav1.ConditionFalse, cond.Status) assert.Equal(t, "FailedToUpdate", cond.Reason) assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) - assert.Equal(t, `failed to update KonnectControlPlane default/cp-1: {"status":400,"title":"","instance":"","detail":"bad request","invalid_parameters":null}`, cond.Message) + assert.Equal(t, `failed to update KonnectGatewayControlPlane default/cp-1: {"status":400,"title":"","instance":"","detail":"bad request","invalid_parameters":null}`, cond.Message) }, expectedErr: true, }, { name: "when not found then try to create", - mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectControlPlane) { + mockCPPair: func() (*MockControlPlaneSDK, *konnectv1alpha1.KonnectGatewayControlPlane) { sdk := &MockControlPlaneSDK{} - cp := &konnectv1alpha1.KonnectControlPlane{ + cp := &konnectv1alpha1.KonnectGatewayControlPlane{ ObjectMeta: metav1.ObjectMeta{ Name: "cp-1", Namespace: "default", }, - Spec: konnectv1alpha1.KonnectControlPlaneSpec{ + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ Name: "cp-1", }, }, - Status: konnectv1alpha1.KonnectControlPlaneStatus{ + Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{ KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ ID: "12345", }, @@ -416,10 +416,10 @@ func TestUpdateControlPlane(t *testing.T) { return sdk, cp }, - assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectControlPlane) { + assertions: func(t *testing.T, cp *konnectv1alpha1.KonnectGatewayControlPlane) { assert.Equal(t, "12345", cp.Status.GetKonnectID()) cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, cp) - require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + require.True(t, ok, "Programmed condition not set on KonnectGatewayControlPlane") assert.Equal(t, metav1.ConditionTrue, cond.Status) assert.Equal(t, conditions.KonnectEntityProgrammedReasonProgrammed, cond.Reason) assert.Equal(t, cp.GetGeneration(), cond.ObservedGeneration) diff --git a/controller/konnect/ops/ops_kongservice_test.go b/controller/konnect/ops/ops_kongservice_test.go index e1689cf35..fa0e6421e 100644 --- a/controller/konnect/ops/ops_kongservice_test.go +++ b/controller/konnect/ops/ops_kongservice_test.go @@ -137,7 +137,7 @@ func TestCreateKongService(t *testing.T) { assertions: func(t *testing.T, svc *configurationv1alpha1.KongService) { assert.Equal(t, "", svc.GetKonnectStatus().GetKonnectID()) cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, svc) - require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + require.True(t, ok, "Programmed condition not set on KonnectGatewayControlPlane") assert.Equal(t, metav1.ConditionFalse, cond.Status) assert.Equal(t, "FailedToCreate", cond.Reason) assert.Equal(t, svc.GetGeneration(), cond.ObservedGeneration) @@ -350,7 +350,7 @@ func TestUpdateKongService(t *testing.T) { assertions: func(t *testing.T, svc *configurationv1alpha1.KongService) { assert.Equal(t, "123456789", svc.GetKonnectStatus().GetKonnectID()) cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, svc) - require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + require.True(t, ok, "Programmed condition not set on KonnectGatewayControlPlane") assert.Equal(t, metav1.ConditionTrue, cond.Status) assert.Equal(t, conditions.KonnectEntityProgrammedReasonProgrammed, cond.Reason) assert.Equal(t, svc.GetGeneration(), cond.ObservedGeneration) @@ -404,7 +404,7 @@ func TestUpdateKongService(t *testing.T) { // the Konnect ID from the status? Probably not. // assert.Equal(t, "", svc.GetKonnectStatus().GetKonnectID()) cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, svc) - require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + require.True(t, ok, "Programmed condition not set on KonnectGatewayControlPlane") assert.Equal(t, metav1.ConditionFalse, cond.Status) assert.Equal(t, "FailedToUpdate", cond.Reason) assert.Equal(t, svc.GetGeneration(), cond.ObservedGeneration) @@ -470,7 +470,7 @@ func TestUpdateKongService(t *testing.T) { assertions: func(t *testing.T, svc *configurationv1alpha1.KongService) { assert.Equal(t, "123456789", svc.GetKonnectStatus().GetKonnectID()) cond, ok := k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, svc) - require.True(t, ok, "Programmed condition not set on KonnectControlPlane") + require.True(t, ok, "Programmed condition not set on KonnectGatewayControlPlane") assert.Equal(t, metav1.ConditionTrue, cond.Status) assert.Equal(t, conditions.KonnectEntityProgrammedReasonProgrammed, cond.Reason) assert.Equal(t, svc.GetGeneration(), cond.ObservedGeneration)