-
Notifications
You must be signed in to change notification settings - Fork 91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support muxing with SDKv2 #22
Comments
While a lot of the implementation work for this will likely happen in terraform-plugin-mux, I noticed that we may need to add a function or method that can return a |
Reference: #22 Provider developers have a few reasons to directly need a tfprotov6.ProviderServer implementation: - When implementing acceptance testing with terraform-plugin-sdk helper/resource. - When using terraform-plugin-mux tf6muxserver.NewMuxServer(). - When using terraform-plugin-mux tf6to5server.DowngradeServer(). The current `NewProtocol6Server(Provider)` function enables these use cases, however the implementation is less than ergonomic for provider developers as a wrapping function is required in all cases: ```go // helper/resource acceptance testing resource.TestCase{ ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ "example": func() (tfprotov6.ProviderServer, error) { return tfsdk.NewProtocol6Server(New()), nil }, }, // ... } // tf6muxserver.NewMuxServer() providers := []func() tfprotov6.ProviderServer{ func() tfprotov6.ProviderServer { return tfsdk.NewProtocol6Server(provider1.New()) }, func() tfprotov6.ProviderServer { return tfsdk.NewProtocol6Server(provider2.New()) }, } muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...) // tf6to5server.DowngradeServer() downgradeServer, err := tf6to5server.DowngradeServer(ctx, func() tfprotov6.ProviderServer { return tfsdk.NewProtocol6Server(provider.New()) }) ``` The new functions simplify these implementations and get provider developers closer to not directly importing terraform-plugin-go (with a few other targeted changes, such as type aliases), e.g. ```go // helper/resource acceptance testing resource.TestCase{ ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ "example": tfsdk.NewProtocol6ProviderServerWithError(New()), }, // ... } // tf6muxserver.NewMuxServer() providers := []func() tfprotov6.ProviderServer{ tfsdk.NewProtocol6ProviderServer(provider1.New()), tfsdk.NewProtocol6ProviderServer(provider2.New()), } muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...) // tf6to5server.DowngradeServer() downgradeServer, err := tf6to5server.DowngradeServer(ctx, tfsdk.NewProtocol6ProviderServer(provider.New())) ``` This change prefers deprecation over straight replacement to give some lead time for various documentation updates across multiple projects. The naming is less than ideal, however it feels necessary to remain generic instead of picking any particular details related to their current usage (e.g. "NewTestServer") as this can/will change over time. The "Protocol6" naming is important should a new major protocol version 7 be released, which this framework must support.
Reference: #22 Provider developers have a few reasons to directly need a tfprotov6.ProviderServer implementation: - When implementing acceptance testing with terraform-plugin-sdk helper/resource. - When using terraform-plugin-mux tf6muxserver.NewMuxServer(). - When using terraform-plugin-mux tf6to5server.DowngradeServer(). The current `NewProtocol6Server(Provider)` function enables these use cases, however the implementation is less than ergonomic for provider developers as a wrapping function is required in all cases: ```go // helper/resource acceptance testing resource.TestCase{ ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ "example": func() (tfprotov6.ProviderServer, error) { return tfsdk.NewProtocol6Server(New()), nil }, }, // ... } // tf6muxserver.NewMuxServer() providers := []func() tfprotov6.ProviderServer{ func() tfprotov6.ProviderServer { return tfsdk.NewProtocol6Server(provider1.New()) }, func() tfprotov6.ProviderServer { return tfsdk.NewProtocol6Server(provider2.New()) }, } muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...) // tf6to5server.DowngradeServer() downgradeServer, err := tf6to5server.DowngradeServer(ctx, func() tfprotov6.ProviderServer { return tfsdk.NewProtocol6Server(provider.New()) }) ``` The new functions simplify these implementations and get provider developers closer to not directly importing terraform-plugin-go (with a few other targeted changes, such as type aliases), e.g. ```go // helper/resource acceptance testing resource.TestCase{ ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ "example": tfsdk.NewProtocol6ProviderServerWithError(New()), }, // ... } // tf6muxserver.NewMuxServer() providers := []func() tfprotov6.ProviderServer{ tfsdk.NewProtocol6ProviderServer(provider1.New()), tfsdk.NewProtocol6ProviderServer(provider2.New()), } muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...) // tf6to5server.DowngradeServer() downgradeServer, err := tf6to5server.DowngradeServer(ctx, tfsdk.NewProtocol6ProviderServer(provider.New())) ``` This change prefers deprecation over straight replacement to give some lead time for various documentation updates across multiple projects. The naming is less than ideal, however it feels necessary to remain generic instead of picking any particular details related to their current usage (e.g. "NewTestServer") as this can/will change over time. The "Protocol6" naming is important should a new major protocol version 7 be released, which this framework must support.
As of v0.6.1, all the underlying pieces for muxing with SDKv2 are in place. Refer to Combining and Translating Providers documentation around the various terraform-plugin-mux packages which can be used to mix and match protocol version 5 and version 6 provider servers. #158 will be used for tracking the creation of a specific guide for migrating from SDKv2 -> framework, with optionally using a mux implementation during the process to migrate individual resources at a time. Framework providers in v0.6.1 can use the following for mux implementations: // tf6muxserver.NewMuxServer()
providers := []func() tfprotov6.ProviderServer{
func() tfprotov6.ProviderServer {
return tfsdk.NewProtocol6Server(provider1.New())
},
func() tfprotov6.ProviderServer {
return tfsdk.NewProtocol6Server(provider2.New())
},
}
muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...)
// tf6to5server.DowngradeServer()
downgradeServer, err := tf6to5server.DowngradeServer(ctx, func() tfprotov6.ProviderServer {
return tfsdk.NewProtocol6Server(provider.New())
}) As of the upcoming v0.7.0, its slightly simplified so framework providers can use the following for mux implementations: // tf6muxserver.NewMuxServer()
providers := []func() tfprotov6.ProviderServer{
tfsdk.NewProtocol6ProviderServer(provider1.New()),
tfsdk.NewProtocol6ProviderServer(provider2.New()),
}
muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...)
// tf6to5server.DowngradeServer()
downgradeServer, err := tf6to5server.DowngradeServer(ctx, tfsdk.NewProtocol6ProviderServer(provider.New())) |
I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. |
Module version
Use-cases
Mux was born out of the desire to make sure that this new framework could be adopted piecemeal, that moving over wasn't a Big Bang rewrite project. That one resource at a time could be rewritten into or implemented in the new framework, and older SDKv2 code would continue to work.
Attempted Solutions
The fundamental problem is that SDKv2 uses protocol version 5, and this framework is being built on protocol version 6. On the face of it, that's not that big a deal; hashicorp/terraform-plugin-go-contrib#7 talks about a way to transparently upgrade providers built on version 5 of the protocol to version 6 of the protocol, without any loss of fidelity. So in theory an SDKv2 provider could be just wrapped in that helper, and then muxed with a provider using this framework as two protov6 providers. No big deal.
And this should, in theory, work fine.
As long as the provider configuration (the schema for the provider itself, not the resources) doesn't use any nested blocks or nested attributes.
Because both muxed providers need to respond to the same ConfigureProvider RPC to get their configuration, they need to agree on what the schema for that call is. SDKv2 is on protocol version 5, which has no concept of nested attributes. This framework is choosing not to expose sub-blocks, because we don't think they're needed anymore and are confusing for provider developers, so we're steering people away from them. This means there exist a great number of provider configuration schemas that can't be modeled by both SDKv2 and the framework, meaning those providers would not be able to be muxed.
We have a few options:
GetMuxedProviderSchema
or something) to the provider that allowed it to return a schema of a different type, with support for nested blocks. This would limit the impact to only the area needed for compatibility, and would let providers who don't need that compatibility (new providers, completely rewritten providers, etc.) avoid the cognitive overhead entirely.Proposal
I think my personal preference is number 4, but I think that's a lightly held preference at this point. I think either 3 or 4 is the way to solve this, honestly.
The text was updated successfully, but these errors were encountered: