From fc2754ded4c2f3f6512dfa9dfcd5dc60cc063bd1 Mon Sep 17 00:00:00 2001 From: Carl Henrik Lunde Date: Tue, 25 Nov 2025 22:42:45 +0100 Subject: [PATCH] Use TypedExternalConnector and remove type assertions Signed-off-by: Carl Henrik Lunde --- internal/controller/mytype/mytype.go | 44 +++++------------------ internal/controller/mytype/mytype_test.go | 7 ++-- 2 files changed, 13 insertions(+), 38 deletions(-) diff --git a/internal/controller/mytype/mytype.go b/internal/controller/mytype/mytype.go index b730fe1..7151f16 100644 --- a/internal/controller/mytype/mytype.go +++ b/internal/controller/mytype/mytype.go @@ -39,7 +39,6 @@ import ( ) const ( - errNotMyType = "managed resource is not a MyType custom resource" errTrackPCUsage = "cannot track ProviderConfig usage" errGetPC = "cannot get ProviderConfig" errGetCPC = "cannot get ClusterProviderConfig" @@ -70,7 +69,7 @@ func Setup(mgr ctrl.Manager, o controller.Options) error { name := managed.ControllerName(v1alpha1.MyTypeGroupKind) opts := []managed.ReconcilerOption{ - managed.WithExternalConnector(&connector{ + managed.WithTypedExternalConnector[*v1alpha1.MyType](&connector{ kube: mgr.GetClient(), usage: resource.NewProviderConfigUsageTracker(mgr.GetClient(), &apisv1alpha1.ProviderConfigUsage{}), newServiceFn: newNoOpService}), @@ -123,26 +122,19 @@ type connector struct { // 2. Getting the managed resource's ProviderConfig. // 3. Getting the credentials specified by the ProviderConfig. // 4. Using the credentials to form a client. -func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { - cr, ok := mg.(*v1alpha1.MyType) - if !ok { - return nil, errors.New(errNotMyType) - } - +func (c *connector) Connect(ctx context.Context, cr *v1alpha1.MyType) (managed.TypedExternalClient[*v1alpha1.MyType], error) { if err := c.usage.Track(ctx, cr); err != nil { return nil, errors.Wrap(err, errTrackPCUsage) } var cd apisv1alpha1.ProviderCredentials - // Switch to ModernManaged resource to get ProviderConfigRef - m := mg.(resource.ModernManaged) - ref := m.GetProviderConfigReference() + ref := cr.GetProviderConfigReference() switch ref.Kind { case "ProviderConfig": pc := &apisv1alpha1.ProviderConfig{} - if err := c.kube.Get(ctx, types.NamespacedName{Name: ref.Name, Namespace: m.GetNamespace()}, pc); err != nil { + if err := c.kube.Get(ctx, types.NamespacedName{Name: ref.Name, Namespace: cr.GetNamespace()}, pc); err != nil { return nil, errors.Wrap(err, errGetPC) } cd = pc.Spec.Credentials @@ -177,21 +169,16 @@ type external struct { service interface{} } -func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) { +func (c *external) Observe(ctx context.Context, cr *v1alpha1.MyType) (managed.ExternalObservation, error) { // If the managed resource is marked for deletion then deleted it. // Because there is no external resource to observe, we return false for // ResourceExists. - if meta.WasDeleted(mg) { + if meta.WasDeleted(cr) { return managed.ExternalObservation{ ResourceExists: false, }, nil } - cr, ok := mg.(*v1alpha1.MyType) - if !ok { - return managed.ExternalObservation{}, errors.New(errNotMyType) - } - // These fmt statements should be removed in the real implementation. fmt.Printf("Observing: %+v", cr) @@ -230,12 +217,8 @@ func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex }, nil } -func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.ExternalCreation, error) { - cr, ok := mg.(*v1alpha1.MyType) +func (c *external) Create(ctx context.Context, cr *v1alpha1.MyType) (managed.ExternalCreation, error) { cr.Status.SetConditions(xpv1.Creating()) - if !ok { - return managed.ExternalCreation{}, errors.New(errNotMyType) - } fmt.Printf("Creating: %+v", cr) @@ -250,12 +233,7 @@ func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.Ext }, nil } -func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) { - cr, ok := mg.(*v1alpha1.MyType) - if !ok { - return managed.ExternalUpdate{}, errors.New(errNotMyType) - } - +func (c *external) Update(ctx context.Context, cr *v1alpha1.MyType) (managed.ExternalUpdate, error) { fmt.Printf("Updating: %+v", cr) // Copy ConfigurableField to AtProvider and complete the update. @@ -268,12 +246,8 @@ func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.Ext }, nil } -func (c *external) Delete(ctx context.Context, mg resource.Managed) (managed.ExternalDelete, error) { - cr, ok := mg.(*v1alpha1.MyType) +func (c *external) Delete(ctx context.Context, cr *v1alpha1.MyType) (managed.ExternalDelete, error) { cr.Status.SetConditions(xpv1.Deleting()) - if !ok { - return managed.ExternalDelete{}, errors.New(errNotMyType) - } fmt.Printf("Deleting: %+v", cr) diff --git a/internal/controller/mytype/mytype_test.go b/internal/controller/mytype/mytype_test.go index 0ad0aad..4010319 100644 --- a/internal/controller/mytype/mytype_test.go +++ b/internal/controller/mytype/mytype_test.go @@ -23,8 +23,9 @@ import ( "github.com/google/go-cmp/cmp" "github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed" - "github.com/crossplane/crossplane-runtime/v2/pkg/resource" "github.com/crossplane/crossplane-runtime/v2/pkg/test" + + "github.com/crossplane/provider-template/apis/sample/v1alpha1" ) // Unlike many Kubernetes projects Crossplane does not use third party testing @@ -42,7 +43,7 @@ func TestObserve(t *testing.T) { type args struct { ctx context.Context - mg resource.Managed + cr *v1alpha1.MyType } type want struct { @@ -62,7 +63,7 @@ func TestObserve(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { e := external{service: tc.fields.service} - got, err := e.Observe(tc.args.ctx, tc.args.mg) + got, err := e.Observe(tc.args.ctx, tc.args.cr) if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { t.Errorf("\n%s\ne.Observe(...): -want error, +got error:\n%s\n", tc.reason, diff) }