You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Don't create custom Context types or use interfaces other than Context in function signatures.
If you have application data to pass around, put it in a parameter, in the receiver, in globals, or, if it truly belongs there, in a Context value.
I wish this guidance went into more detail around why to avoid custom Context types, but at least in our case the custom provider.Context type makes it difficult (impossible?) to unit test CRUD operations.
Specifically, if a CRUD operation needs access to the provider's configuration it must use infer.GetConfig to get it. GetConfig requires a provider.Context from a Wrap'd provider, but once you have that provider you can't get a provider.Context from it because ctx is private. Instead, you need a server available (ResourceProviderServer or integration.Server) to generate a usable context.
This is unfortunate because we lose one of the primary benefits of this library, namely the ability to work with simple structs instead of the lower-level resource.PropertyMap.
Solution
One solution would be to export provider.ctx(...) → provider.Ctx(...) so users can create a provider.Context more easily. This is an easy fix, but it still requires me to have a provider available, with configuration inferred, and so on. In other words, the framework is still "in the way," even for the simplest of test cases.
Instead, I would strongly recommend an alternative API that uses the standard library's context.Context. Consider some CRUD signatures with context.Context instead:
We can immediately see how to unit test these because it's just a matter of instantiating a context.Background(), a &Resource{}, some inputs, and expected outputs.
But what to do about the methods currently on provider.Context? The Log* and RuntimeInformation methods could instead be made context-aware and embedded in the resource controller:
We can do this in such a way that the user doesn't need to instantiate anything they don't require, so the framework can get completely out of the way:
We can also leverage this Controller concept to make it easier to grab the provider's configuration. Instead of cfg := infer.GetConfig[Config](ctx) we could have cfg := c.Controller.ProviderConfiguration().
Similarly, we could expose things like any options attached to the resource:
This gives us the flexibility to easily override as little or as much as we need, without needing to deal with servers or test harnesses. If we want to test log output, we can inject a test logger. If we want to inject a mock client, we can inject that with the provider's configuration. Etc.
This would be a backwards-incompatible change, but the library currently makes no stability guarantees so this is OK. Updating an existing provider to use the new API would be a mechanical change.
The text was updated successfully, but these errors were encountered:
Problem
https://github.com/golang/go/wiki/CodeReviewComments#contexts
I wish this guidance went into more detail around why to avoid custom Context types, but at least in our case the custom
provider.Context
type makes it difficult (impossible?) to unit test CRUD operations.Specifically, if a CRUD operation needs access to the provider's configuration it must use
infer.GetConfig
to get it.GetConfig
requires aprovider.Context
from aWrap
'd provider, but once you have that provider you can't get aprovider.Context
from it becausectx
is private. Instead, you need a server available (ResourceProviderServer
orintegration.Server
) to generate a usable context.pulumi-go-provider/infer/provider.go
Lines 129 to 134 in ab33426
pulumi-go-provider/infer/provider.go
Lines 118 to 120 in ab33426
pulumi-go-provider/provider.go
Lines 587 to 589 in ab33426
In other words, as a user I want to be be able to unit test this function:
but I require a server to produce a valid
provider.Context
, so I'm actually only able to test at a higher layer:This is unfortunate because we lose one of the primary benefits of this library, namely the ability to work with simple structs instead of the lower-level
resource.PropertyMap
.Solution
One solution would be to export
provider.ctx(...)
→provider.Ctx(...)
so users can create aprovider.Context
more easily. This is an easy fix, but it still requires me to have a provider available, with configuration inferred, and so on. In other words, the framework is still "in the way," even for the simplest of test cases.Instead, I would strongly recommend an alternative API that uses the standard library's
context.Context
. Consider some CRUD signatures withcontext.Context
instead:We can immediately see how to unit test these because it's just a matter of instantiating a
context.Background()
, a&Resource{}
, some inputs, and expected outputs.But what to do about the methods currently on
provider.Context
? TheLog*
andRuntimeInformation
methods could instead be made context-aware and embedded in the resource controller:We can do this in such a way that the user doesn't need to instantiate anything they don't require, so the framework can get completely out of the way:
We can also leverage this
Controller
concept to make it easier to grab the provider's configuration. Instead ofcfg := infer.GetConfig[Config](ctx)
we could havecfg := c.Controller.ProviderConfiguration()
.Similarly, we could expose things like any options attached to the resource:
(AFAICT these controllers are scoped to individual resources, and we don't currently expose things like retainOnDelete.)
Taken all together, a more complex unit test could look something like this:
This gives us the flexibility to easily override as little or as much as we need, without needing to deal with servers or test harnesses. If we want to test log output, we can inject a test logger. If we want to inject a mock client, we can inject that with the provider's configuration. Etc.
This would be a backwards-incompatible change, but the library currently makes no stability guarantees so this is OK. Updating an existing provider to use the new API would be a mechanical change.
The text was updated successfully, but these errors were encountered: