Order of Configure() calls in framework >= v0.12.0

I’m working with framework v0.13.0 using the comments on pull #472 as a guide. The Configure() method on my datasource.DataSource implementation includes something like this:

		providerData, ok := req.ProviderData.(*dataSourceProviderData)
		if !ok {
			resp.Diagnostics.AddError("invalid configure data",
				fmt.Sprintf("unable to type assert ProviderData to '%T'", dataSourceProviderData{}))
			return
		}

terraform plan has been blowing up here because req.ProviderData is nil.

Debugs indicate that the provider’s MetaData(), GetSchema(), Resources() and DataSources() methods are all getting invoked, but never Configure(), so it’s no wonder that req.ProviderData is nil at this point.

Not knowing what to do about this but wanting to make short-term progress, I re-worked the data source Configure() to read data from disk when req.ProviderData is nil.

Next thing I know, the provider’s Configure() method is running! It’s never done that before!

It turns out, the data source Configure() is invoked multiple times, both before and after the provider Configure() runs.

Is this expected behavior? What’s the right thing for a data source or resource to do when it cannot configure itself? It feels strange for that function to just return as though nothing is wrong…

Should data source and resource Configure() methods just… quietly return if they discover they’re running before the provider’s been configured?

My instinct here:

  1. Provider, DataSource and Resource implementations each get a configured bool struct element to mark successful run of their respective Configure() methods.
  2. DataSource and Resource Configure() methods return quietly, leaving configured == false if they determine the provider is un-configured (blind faith they’ll be re-invoked for another try later).
  3. CRUD methods on DataSource and Resource objects check their configured flag. Ultimately configuration completeness is critical only for these methods.

Am I on the right track?

@bflad, I love the refactoring that came with v0.12.0. It’s really a wonderful improvement.

update: The strategy I outlined above seems to be working.

Hi again @bumble :wave:

Currently your suspicions here are correct; the data source or resource Configure() method may get called before the provider Configure() method is called. In particular, I think I remember when doing this refactoring that the ValidateDataSourceConfig and ValidateResourceConfig RPCs may wind up calling the data source and resource Configure() methods while the validation phase in Terraform is currently intended to be an offline operation, so never configuring the provider.

Whether this is a framework bug or a feature is an interesting question – there could theoretically be use cases where configuring the data source or resource doesn’t require provider level data, so having the Configure() method executed without the provider level data would be necessary. It is also possible that even during planning or apply phases, that the provider Configure() method exits early due to unknown provider configuration values, etc. so it’s not necessarily a situation we can always protect against in the framework itself.

The best recommendations I can provide at the moment are:

  • Use a pointer type within the data source or resource type for whatever provider data/client may be expected
  • Use a nil check and early return at the top of the data source or resource Configure method
  • If the data/client happens to be unexpectedly absent during Create or other methods and cause a panic, doing a nil check on the data source or resource data/client field to raise an error diagnostic instead

Putting it all together:

type MyResource struct {
  client *example.Client
}

func (r *MyResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
  if req.ProviderData == nil {
    return
  }

  client, ok := req.ProviderData.(*example.Client)

  if !ok {
    resp.Diagnostics.AddError(/*...*/)
    return
  }

  r.client = client
}

func (r MyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
  if r.client == nil {
    resp.Diagnostics.AddError(/*...*/)
    return
  }
}

Hope this helps.

Hi @bflad,

Got it. Using the pointer type, and checking its nil-ness beats my idea of carrying around the configured boolean, so thanks for that.

I want to mention again how much I like the changes that came in 0.12.0. Great stuff, thank you for it.

Thank you for the positive feedback. If there is anything else you wish was “easier”/“better” please definitely reach out in the GitHub issues.

For this particular issue, I did notice that the upfront Configure() method nil check happens to be documented with the latest version documentation:

If there would be another place you would expect this type of documentation or if there are other potential improvements here, please let us know.