Skip to content

Commit

Permalink
refactor SDKFactory to allow using mock SDK in reconcilers
Browse files Browse the repository at this point in the history
  • Loading branch information
randmonkey committed Sep 5, 2024
1 parent 0b5df8e commit 778a9d9
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 64 deletions.
12 changes: 12 additions & 0 deletions controller/konnect/ops/me.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ops

import (
"context"

sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations"
)

// MeSDK is the interface for Konnect "Me" SDK to get current organization.
type MeSDK interface {
GetOrganizationsMe(ctx context.Context, opts ...sdkkonnectops.Option) (*sdkkonnectops.GetOrganizationsMeResponse, error)
}
43 changes: 21 additions & 22 deletions controller/konnect/ops/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"time"

sdkkonnectgo "github.com/Kong/sdk-konnect-go"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -46,25 +45,25 @@ func Create[
TEnt constraints.EntityType[T],
](
ctx context.Context,
sdk *sdkkonnectgo.SDK,
sdk SDKWrapper,
cl client.Client,
e *T,
) (*T, error) {
defer logOpComplete[T, TEnt](ctx, time.Now(), CreateOp, e)

switch ent := any(e).(type) {
case *konnectv1alpha1.KonnectGatewayControlPlane:
return e, createControlPlane(ctx, sdk.ControlPlanes, ent)
return e, createControlPlane(ctx, sdk.GetControlPlaneSDK(), ent)
case *configurationv1alpha1.KongService:
return e, createService(ctx, sdk.Services, ent)
return e, createService(ctx, sdk.GetServicesSDK(), ent)
case *configurationv1alpha1.KongRoute:
return e, createRoute(ctx, sdk.Routes, ent)
return e, createRoute(ctx, sdk.GetRoutesSDK(), ent)
case *configurationv1.KongConsumer:
return e, createConsumer(ctx, sdk.Consumers, ent)
return e, createConsumer(ctx, sdk.GetConsumersSDK(), ent)
case *configurationv1beta1.KongConsumerGroup:
return e, createConsumerGroup(ctx, sdk.ConsumerGroups, ent)
return e, createConsumerGroup(ctx, sdk.GetConsumerGroupsSDK(), ent)
case *configurationv1alpha1.KongPluginBinding:
return e, createPlugin(ctx, cl, sdk.Plugins, ent)
return e, createPlugin(ctx, cl, sdk.GetPluginSDK(), ent)

// ---------------------------------------------------------------------
// TODO: add other Konnect types
Expand All @@ -79,7 +78,7 @@ func Create[
func Delete[
T constraints.SupportedKonnectEntityType,
TEnt constraints.EntityType[T],
](ctx context.Context, sdk *sdkkonnectgo.SDK, cl client.Client, e *T) error {
](ctx context.Context, sdk SDKWrapper, cl client.Client, e *T) error {
ent := TEnt(e)
if ent.GetKonnectStatus().GetKonnectID() == "" {
return fmt.Errorf(
Expand All @@ -92,17 +91,17 @@ func Delete[

switch ent := any(e).(type) {
case *konnectv1alpha1.KonnectGatewayControlPlane:
return deleteControlPlane(ctx, sdk.ControlPlanes, ent)
return deleteControlPlane(ctx, sdk.GetControlPlaneSDK(), ent)
case *configurationv1alpha1.KongService:
return deleteService(ctx, sdk.Services, ent)
return deleteService(ctx, sdk.GetServicesSDK(), ent)
case *configurationv1alpha1.KongRoute:
return deleteRoute(ctx, sdk.Routes, ent)
return deleteRoute(ctx, sdk.GetRoutesSDK(), ent)
case *configurationv1.KongConsumer:
return deleteConsumer(ctx, sdk.Consumers, ent)
return deleteConsumer(ctx, sdk.GetConsumersSDK(), ent)
case *configurationv1beta1.KongConsumerGroup:
return deleteConsumerGroup(ctx, sdk.ConsumerGroups, ent)
return deleteConsumerGroup(ctx, sdk.GetConsumerGroupsSDK(), ent)
case *configurationv1alpha1.KongPluginBinding:
return deletePlugin(ctx, sdk.Plugins, ent)
return deletePlugin(ctx, sdk.GetPluginSDK(), ent)

// ---------------------------------------------------------------------
// TODO: add other Konnect types
Expand All @@ -117,7 +116,7 @@ func Delete[
func Update[
T constraints.SupportedKonnectEntityType,
TEnt constraints.EntityType[T],
](ctx context.Context, sdk *sdkkonnectgo.SDK, syncPeriod time.Duration, cl client.Client, e *T) (ctrl.Result, error) {
](ctx context.Context, sdk SDKWrapper, syncPeriod time.Duration, cl client.Client, e *T) (ctrl.Result, error) {
var (
ent = TEnt(e)
condProgrammed, ok = k8sutils.GetCondition(conditions.KonnectEntityProgrammedConditionType, ent)
Expand Down Expand Up @@ -155,17 +154,17 @@ func Update[

switch ent := any(e).(type) {
case *konnectv1alpha1.KonnectGatewayControlPlane:
return ctrl.Result{}, updateControlPlane(ctx, sdk.ControlPlanes, ent)
return ctrl.Result{}, updateControlPlane(ctx, sdk.GetControlPlaneSDK(), ent)
case *configurationv1alpha1.KongService:
return ctrl.Result{}, updateService(ctx, sdk.Services, ent)
return ctrl.Result{}, updateService(ctx, sdk.GetServicesSDK(), ent)
case *configurationv1alpha1.KongRoute:
return ctrl.Result{}, updateRoute(ctx, sdk.Routes, cl, ent)
return ctrl.Result{}, updateRoute(ctx, sdk.GetRoutesSDK(), cl, ent)
case *configurationv1.KongConsumer:
return ctrl.Result{}, updateConsumer(ctx, sdk.Consumers, cl, ent)
return ctrl.Result{}, updateConsumer(ctx, sdk.GetConsumersSDK(), cl, ent)
case *configurationv1beta1.KongConsumerGroup:
return ctrl.Result{}, updateConsumerGroup(ctx, sdk.ConsumerGroups, cl, ent)
return ctrl.Result{}, updateConsumerGroup(ctx, sdk.GetConsumerGroupsSDK(), cl, ent)
case *configurationv1alpha1.KongPluginBinding:
return ctrl.Result{}, updatePlugin(ctx, sdk.Plugins, cl, ent)
return ctrl.Result{}, updatePlugin(ctx, sdk.GetPluginSDK(), cl, ent)

// ---------------------------------------------------------------------
// TODO: add other Konnect types
Expand Down
1 change: 1 addition & 0 deletions controller/konnect/ops/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations"
)

// PluginSDK is the interface for Konnect plugin SDK.
type PluginSDK interface {
CreatePlugin(ctx context.Context, controlPlaneID string, plugin sdkkonnectcomp.PluginInput, opts ...sdkkonnectops.Option) (*sdkkonnectops.CreatePluginResponse, error)
UpsertPlugin(ctx context.Context, request sdkkonnectops.UpsertPluginRequest, opts ...sdkkonnectops.Option) (*sdkkonnectops.UpsertPluginResponse, error)
Expand Down
87 changes: 87 additions & 0 deletions controller/konnect/ops/sdkfactory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package ops

import (
sdkkonnectgo "github.com/Kong/sdk-konnect-go"
sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components"
)

// SDKWrapper is a wrapper of Konnect SDK to allow using mock SDKs in tests.
type SDKWrapper interface {
GetControlPlaneSDK() ControlPlaneSDK
GetServicesSDK() ServicesSDK
GetRoutesSDK() RoutesSDK
GetConsumersSDK() ConsumersSDK
GetConsumerGroupsSDK() ConsumerGroupSDK
GetPluginSDK() PluginSDK
GetMeSDK() MeSDK
}

type sdkWrapper struct {
sdk *sdkkonnectgo.SDK
}

var _ SDKWrapper = sdkWrapper{}

// GetControlPlaneSDK returns the SDK to operate Konenct control planes.
func (w sdkWrapper) GetControlPlaneSDK() ControlPlaneSDK {
return w.sdk.ControlPlanes
}

// GetServicesSDK returns the SDK to operate Kong services.
func (w sdkWrapper) GetServicesSDK() ServicesSDK {
return w.sdk.Services
}

// GetRoutesSDK returns the SDK to operate Kong routes.
func (w sdkWrapper) GetRoutesSDK() RoutesSDK {
return w.sdk.Routes
}

// GetConsumersSDK returns the SDK to operate Kong consumers.
func (w sdkWrapper) GetConsumersSDK() ConsumersSDK {
return w.sdk.Consumers
}

// GetConsumerGroupsSDK returns the SDK to operate Kong consumer groups.
func (w sdkWrapper) GetConsumerGroupsSDK() ConsumerGroupSDK {
return w.sdk.ConsumerGroups
}

// GetPluginSDK returns the SDK to operate plugins.
func (w sdkWrapper) GetPluginSDK() PluginSDK {
return w.sdk.Plugins
}

// GetMeSDK returns the "me" SDK to get current organization.
func (w sdkWrapper) GetMeSDK() MeSDK {
return w.sdk.Me
}

// SDKToken is a token used to authenticate with the Konnect SDK.
type SDKToken string

// SDKFactory is a factory for creating Konnect SDKs.
type SDKFactory interface {
NewKonnectSDK(serverURL string, token SDKToken) SDKWrapper
}

type sdkFactory struct{}

// NewSDKFactory creates a new SDKFactory.
func NewSDKFactory() SDKFactory {
return sdkFactory{}
}

// NewKonnectSDK creates a new Konnect SDK.
func (f sdkFactory) NewKonnectSDK(serverURL string, token SDKToken) SDKWrapper {
return sdkWrapper{
sdk: sdkkonnectgo.New(
sdkkonnectgo.WithSecurity(
sdkkonnectcomp.Security{
PersonalAccessToken: sdkkonnectgo.String(string(token)),
},
),
sdkkonnectgo.WithServerURL(serverURL),
),
}
}
6 changes: 3 additions & 3 deletions controller/konnect/reconciler_generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const (
// KonnectEntityReconciler reconciles a Konnect entities.
// It uses the generic type constraints to constrain the supported types.
type KonnectEntityReconciler[T constraints.SupportedKonnectEntityType, TEnt constraints.EntityType[T]] struct {
sdkFactory SDKFactory
sdkFactory ops.SDKFactory
DevelopmentMode bool
Client client.Client
SyncPeriod time.Duration
Expand All @@ -66,7 +66,7 @@ func NewKonnectEntityReconciler[
T constraints.SupportedKonnectEntityType,
TEnt constraints.EntityType[T],
](
sdkFactory SDKFactory,
sdkFactory ops.SDKFactory,
developmentMode bool,
client client.Client,
opts ...KonnectEntityReconcilerOption[T, TEnt],
Expand Down Expand Up @@ -288,7 +288,7 @@ func (r *KonnectEntityReconciler[T, TEnt]) Reconcile(
// because the token is retrieved in runtime through KonnectAPIAuthConfiguration.
sdk := r.sdkFactory.NewKonnectSDK(
"https://"+apiAuth.Spec.ServerURL,
SDKToken(token),
ops.SDKToken(token),
)

if delTimestamp := ent.GetDeletionTimestamp(); !delTimestamp.IsZero() {
Expand Down
3 changes: 2 additions & 1 deletion controller/konnect/reconciler_generic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
fakectrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"

"github.com/kong/gateway-operator/controller/konnect/constraints"
"github.com/kong/gateway-operator/controller/konnect/ops"
"github.com/kong/gateway-operator/modules/manager/scheme"

configurationv1 "github.com/kong/kubernetes-configuration/api/configuration/v1"
Expand Down Expand Up @@ -38,7 +39,7 @@ func testNewKonnectEntityReconciler[
) {
t.Helper()

sdkFactory := NewSDKFactory()
sdkFactory := ops.NewSDKFactory()

t.Run(ent.GetTypeName(), func(t *testing.T) {
cl := fakectrlruntimeclient.NewFakeClient()
Expand Down
9 changes: 5 additions & 4 deletions controller/konnect/reconciler_konnectapiauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/predicate"

"github.com/kong/gateway-operator/controller/konnect/conditions"
"github.com/kong/gateway-operator/controller/konnect/ops"
"github.com/kong/gateway-operator/controller/pkg/log"
k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes"

Expand All @@ -27,7 +28,7 @@ import (

// KonnectAPIAuthConfigurationReconciler reconciles a KonnectAPIAuthConfiguration object.
type KonnectAPIAuthConfigurationReconciler struct {
SDKFactory SDKFactory
SDKFactory ops.SDKFactory
DevelopmentMode bool
Client client.Client
}
Expand All @@ -45,7 +46,7 @@ const (

// NewKonnectAPIAuthConfigurationReconciler creates a new KonnectAPIAuthConfigurationReconciler.
func NewKonnectAPIAuthConfigurationReconciler(
sdkFactory SDKFactory,
sdkFactory ops.SDKFactory,
developmentMode bool,
client client.Client,
) *KonnectAPIAuthConfigurationReconciler {
Expand Down Expand Up @@ -138,7 +139,7 @@ func (r *KonnectAPIAuthConfigurationReconciler) Reconcile(
}
sdk := r.SDKFactory.NewKonnectSDK(
serverURL,
SDKToken(token),
ops.SDKToken(token),
)

// TODO(pmalek): check if api auth config has a valid status condition
Expand All @@ -150,7 +151,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, sdkkonnectops.WithServerURL("https://"+apiAuth.Spec.ServerURL))
respOrg, err := sdk.GetMeSDK().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 ||
Expand Down
33 changes: 0 additions & 33 deletions controller/konnect/sdkfactory.go

This file was deleted.

3 changes: 2 additions & 1 deletion modules/manager/controller_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/kong/gateway-operator/controller/gatewayclass"
"github.com/kong/gateway-operator/controller/kongplugininstallation"
"github.com/kong/gateway-operator/controller/konnect"
konnectops "github.com/kong/gateway-operator/controller/konnect/ops"
"github.com/kong/gateway-operator/controller/specialized"
"github.com/kong/gateway-operator/internal/utils/index"
dataplanevalidator "github.com/kong/gateway-operator/internal/validation/dataplane"
Expand Down Expand Up @@ -305,7 +306,7 @@ func SetupControllers(mgr manager.Manager, c *Config) (map[string]ControllerDef,

// Konnect controllers
if c.KonnectControllersEnabled {
sdkFactory := konnect.NewSDKFactory()
sdkFactory := konnectops.NewSDKFactory()

controllers[KonnectAPIAuthConfigurationControllerName] = ControllerDef{
Enabled: c.KonnectControllersEnabled,
Expand Down

0 comments on commit 778a9d9

Please sign in to comment.