Migrated to framework, provider Configure only being called for data sources?

I started migrating one of my providers to use the ‘framework’ from sdkv2, but found in my Resource.Configure function that the req.ProviderData was always nil.

After quite some confused flailing around, and setting up delve (though that’s a great win, should’ve done that long ago), I found it’s because the Provider.Configure function isn’t being called at all. After even more confused flailing, I discovered by chance that it does get called for a data source! (I tried a minimal terraform config, and accidentally used data source instead of resource, it worked; but then changing it to resource didn’t.)

What on Earth have I done wrong for that to happen?

func (p *MyProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
    // ...
    resp.DataSourceData = client
    resp.ResourceData = client

which doesn’t get called (Itried with or without pointer ‘receiver’, I’m not that familiar with Go but as far as I can tell it’s not important, matches the interface either way?) and:

func (r resourceMyResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
    client, ok := req.ProviderData.(*myapi.Client)
    // ...

which does get called, but req.Provider is nil.

I’m using:

github.com/hashicorp/terraform-plugin-framework v1.1.1


Terraform v1.3.6
on linux_amd64

Ah, I found this which seems similar symptoms for the same ‘bug or feature’:

In my testing though, it would consistently work for the data source and not the resource, not the random race that seems to imply.

I’m not sure I understand the solution though - is Configure called multiple times if it exits without error or otherwise modifying the response? Otherwise, this doesn’t help, since I do need it to be configured - I just can’t do that if the provider configure hasn’t already run.

For what it’s worth, I think if you try to access ProviderData and it’s not set, but only because of the call order which is out of your control… If not a bug that’s a mis-feature, or one that could do with a clearer API. The docs do provide exactly this example use case of configuring a client on the provider, and then passing to resources/data sources to Configure.

For example - this will be hand-wavy because I don’t spend a lot of time with Go - if CRUD functions instead somehow declared or called a waiting function to say they needConfigure, the resource Configure could similarly say it needs provider Configure, and if either hasn’t already run it waits for it, even triggering it if necessary. Something along this line I think could fix the issue, but also push the flexibility of ‘it might not need it’ down further to the CRUD operations, not waiting for it to be configured if not necessary. (Delete for example is sometimes state-only even when others aren’t, and so wouldn’t need a client.)

The conversation you cited relates only to looping (count and for_each) resources/datasources (I think) and it’s a case where their Configure() method is called extra times, possibly before the provider’s Configure() is called.

You can work around that situation by bailing out of the resource/datasource Configure() method when it’s called without a properly configured provider:

	if req.ProviderData == nil {

(edit: the check above used to say return nil … fixed)

If you’re not already doing this, you might consider adding a compile-time check for the optional interfaces you intend to fulfill:

var _ resource.ResourceWithConfigure = &resourceMyResource{}

not that familiar with Go

I didn’t understand the line above the first time I encountered it, so I’ll explain. This part…

var _ resource.ResourceWithConfigure

…declares that the blank identifier (throwaway variable _) should fulfill the resource.ResourceWithConfigure interface. By further assigning a value to it (a pointer to an anonymous instantiation of resourceMyResource{}), you get a compile-time check that you haven’t screwed up the function signature, and resourceMyResource truly fulfills the ResourceWithConfigure interface.

1 Like

That’s a good tip, thanks, I’ll certainly do that (and for ResourceWithImportState too).

Since it’s Provider.Configure that’s not being called (in time?) though it won’t find any smoking gun I think? And I must surely be implementing Provider correctly for anything to work, nevermind that Provider.Configure itself was even working with the data source.

The only time I’ve had the “bonus method (configure, validate, whatever) not being called” problem is when I’ve screwed up the function signature.

…That or cases where it would have gotten called eventually, but the order was surprising, so the provider crashed before the function got called (the ProviderData nil check solved that problem).

The key fact to that second case is that there were extraneous calls to the resource/datasource Configure() methods. It’s safe to bail out when ProviderData is nil because the resource/datasource will be Configure()d again before it really matters.

That was my experience, anyway.

1 Like

Thank you, I wasn’t expecting it to be called multiple times, I still don’t understand really but at least that works.

However, with a ‘pointer receiver’ my resource Configure never gets called (at least, not before Read, and if that returns if not configured, then the operation just finishes, that isn’t called repeatedly). It satisfies the compiler with your tip above either way, but as:

func (r resourceMyResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {

it’s called, and as:

func (r *resourceMyResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {

it isn’t.

I’m sure this is just my lack of Go, but …eh?

I’ve verified the compile-time check is working, since if I make other methods ‘pointer received’ (e.g. Create, Read, …) it fails.

I need it to be a pointer, since I need to set the client on the resource (from the provider data) and have it available in CRUD fns.

Oh, got it, need to define them all with ‘pointer receivers’, and then the Provider.Resources returned functions must return &MyResource{}s, not values.

1 Like