-
Couldn't load subscription status.
- Fork 10.1k
First step to enable use of TF_REATTACH_PROVIDERS with PSS #37634
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
Merged
Merged
Changes from 10 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
de93629
Add test coverage for `parseReattachProviders`
SarahFrench 7463879
Add unhappy path test cases to parseReattachProviders test
SarahFrench 3f982c0
Add `isProviderReattached` function, to help identify when a reattach…
SarahFrench 3a8f8ee
Move reattach config-related code into its own package
SarahFrench aabb8c9
Make calling code not need to know what the ENV is for reattach provi…
SarahFrench 7926a73
Refactor IsProviderReattached to use pre-existing logic
SarahFrench 0a4379e
Add test case, make spell check happy with US English
SarahFrench 000235a
Add headers
SarahFrench 12f8da6
Make calling code responsible for reading in the related ENV
SarahFrench 6116caa
Add godoc comment to TF_REATTACH_PROVIDERS const
SarahFrench c43a2b5
Update internal/getproviders/reattach/reattach.go
SarahFrench File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| // Copyright (c) HashiCorp, Inc. | ||
| // SPDX-License-Identifier: BUSL-1.1 | ||
|
|
||
| package reattach | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| "net" | ||
|
|
||
| "github.com/hashicorp/go-plugin" | ||
| "github.com/hashicorp/terraform/internal/addrs" | ||
| ) | ||
|
|
||
| // TF_REATTACH_PROVIDERS is JSON string, containing a map of provider source to reattachment config. | ||
| // | ||
| // E.g this corresponds to a provider with source 'registry.terraform.io/hashicorp/foobar': | ||
| /* | ||
| { | ||
| "foobar": { | ||
| "Protocol": "grpc", | ||
| "ProtocolVersion": 6, | ||
| "Pid": 12345, | ||
| "Test": true, | ||
| "Addr": { | ||
| "Network": "unix", | ||
| "String":"/var/folders/xx/abcde12345/T/plugin12345" | ||
| } | ||
| } | ||
| } | ||
| */ | ||
| const TF_REATTACH_PROVIDERS = "TF_REATTACH_PROVIDERS" | ||
|
|
||
| // ParseReattachProviders parses information used for reattaching to unmanaged providers out of a | ||
| // JSON-encoded environment variable (TF_REATTACH_PROVIDERS). | ||
| // | ||
| // Calling code is expected to pass in the value of os.Getenv("TF_REATTACH_PROVIDERS") | ||
| func ParseReattachProviders(in string) (map[addrs.Provider]*plugin.ReattachConfig, error) { | ||
| unmanagedProviders := map[addrs.Provider]*plugin.ReattachConfig{} | ||
| if in != "" { | ||
| type reattachConfig struct { | ||
| Protocol string | ||
| ProtocolVersion int | ||
| Addr struct { | ||
| Network string | ||
| String string | ||
| } | ||
| Pid int | ||
| Test bool | ||
| } | ||
| var m map[string]reattachConfig | ||
| err := json.Unmarshal([]byte(in), &m) | ||
| if err != nil { | ||
| return unmanagedProviders, fmt.Errorf("Invalid format for %s: %w", TF_REATTACH_PROVIDERS, err) | ||
| } | ||
| for p, c := range m { | ||
| a, diags := addrs.ParseProviderSourceString(p) | ||
| if diags.HasErrors() { | ||
| return unmanagedProviders, fmt.Errorf("Error parsing %q as a provider address: %w", a, diags.Err()) | ||
| } | ||
| var addr net.Addr | ||
| switch c.Addr.Network { | ||
| case "unix": | ||
| addr, err = net.ResolveUnixAddr("unix", c.Addr.String) | ||
| if err != nil { | ||
| return unmanagedProviders, fmt.Errorf("Invalid unix socket path %q for %q: %w", c.Addr.String, p, err) | ||
| } | ||
| case "tcp": | ||
| addr, err = net.ResolveTCPAddr("tcp", c.Addr.String) | ||
| if err != nil { | ||
| return unmanagedProviders, fmt.Errorf("Invalid TCP address %q for %q: %w", c.Addr.String, p, err) | ||
| } | ||
| default: | ||
| return unmanagedProviders, fmt.Errorf("Unknown address type %q for %q", c.Addr.Network, p) | ||
| } | ||
| unmanagedProviders[a] = &plugin.ReattachConfig{ | ||
| Protocol: plugin.Protocol(c.Protocol), | ||
| ProtocolVersion: c.ProtocolVersion, | ||
| Pid: c.Pid, | ||
| Test: c.Test, | ||
| Addr: addr, | ||
| } | ||
| } | ||
| } | ||
| return unmanagedProviders, nil | ||
| } | ||
|
|
||
| // IsProviderReattached determines if a given provider is being supplied to Terraform via the TF_REATTACH_PROVIDERS | ||
| // environment variable. | ||
| // | ||
| // Calling code is expected to pass in a provider address and the value of os.Getenv("TF_REATTACH_PROVIDERS") | ||
| func IsProviderReattached(provider addrs.Provider, in string) (bool, error) { | ||
| providers, err := ParseReattachProviders(in) | ||
| if err != nil { | ||
| return false, err | ||
| } | ||
|
|
||
| _, ok := providers[provider] | ||
| return ok, nil | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,288 @@ | ||
| // Copyright (c) HashiCorp, Inc. | ||
| // SPDX-License-Identifier: BUSL-1.1 | ||
|
|
||
| package reattach | ||
|
|
||
| import ( | ||
| "net" | ||
| "testing" | ||
|
|
||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/hashicorp/go-plugin" | ||
| tfaddr "github.com/hashicorp/terraform-registry-address" | ||
| "github.com/hashicorp/terraform/internal/addrs" | ||
| ) | ||
|
|
||
| func Test_parseReattachProviders(t *testing.T) { | ||
| cases := map[string]struct { | ||
| reattachProviders string | ||
| expectedOutput map[addrs.Provider]*plugin.ReattachConfig | ||
| expectErr bool | ||
| }{ | ||
| "simple parse - 1 provider": { | ||
| reattachProviders: `{ | ||
| "test": { | ||
| "Protocol": "grpc", | ||
| "ProtocolVersion": 6, | ||
| "Pid": 12345, | ||
| "Test": true, | ||
| "Addr": { | ||
| "Network": "unix", | ||
| "String":"/var/folders/xx/abcde12345/T/plugin12345" | ||
| } | ||
| } | ||
| }`, | ||
| expectedOutput: map[addrs.Provider]*plugin.ReattachConfig{ | ||
| tfaddr.NewProvider(tfaddr.DefaultProviderRegistryHost, "hashicorp", "test"): func() *plugin.ReattachConfig { | ||
| addr, err := net.ResolveUnixAddr("unix", "/var/folders/xx/abcde12345/T/plugin12345") | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| return &plugin.ReattachConfig{ | ||
| Protocol: plugin.Protocol("grpc"), | ||
| ProtocolVersion: 6, | ||
| Pid: 12345, | ||
| Test: true, | ||
| Addr: addr, | ||
| } | ||
| }(), | ||
| }, | ||
| }, | ||
| "complex parse - 2 providers via different protocols etc": { | ||
| reattachProviders: `{ | ||
| "test-grpc": { | ||
| "Protocol": "grpc", | ||
| "ProtocolVersion": 6, | ||
| "Pid": 12345, | ||
| "Test": true, | ||
| "Addr": { | ||
| "Network": "unix", | ||
| "String": "/var/folders/xx/abcde12345/T/plugin12345" | ||
| } | ||
| }, | ||
| "test-netrpc": { | ||
| "Protocol": "netrpc", | ||
| "ProtocolVersion": 5, | ||
| "Pid": 6789, | ||
| "Test": false, | ||
| "Addr": { | ||
| "Network": "tcp", | ||
| "String":"127.0.0.1:1337" | ||
| } | ||
| } | ||
| }`, | ||
| expectedOutput: map[addrs.Provider]*plugin.ReattachConfig{ | ||
| //test-grpc | ||
| tfaddr.NewProvider(tfaddr.DefaultProviderRegistryHost, "hashicorp", "test-grpc"): func() *plugin.ReattachConfig { | ||
| addr, err := net.ResolveUnixAddr("unix", "/var/folders/xx/abcde12345/T/plugin12345") | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| return &plugin.ReattachConfig{ | ||
| Protocol: plugin.Protocol("grpc"), | ||
| ProtocolVersion: 6, | ||
| Pid: 12345, | ||
| Test: true, | ||
| Addr: addr, | ||
| } | ||
| }(), | ||
| //test-netrpc | ||
| tfaddr.NewProvider(tfaddr.DefaultProviderRegistryHost, "hashicorp", "test-netrpc"): func() *plugin.ReattachConfig { | ||
| addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:1337") | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| return &plugin.ReattachConfig{ | ||
| Protocol: plugin.Protocol("netrpc"), | ||
| ProtocolVersion: 5, | ||
| Pid: 6789, | ||
| Test: false, | ||
| Addr: addr, | ||
| } | ||
| }(), | ||
| }, | ||
| }, | ||
| "can specify the providers host and namespace": { | ||
| // The key here has host and namespace data, vs. just "test" | ||
| reattachProviders: `{ | ||
| "example.com/my-org/test": { | ||
| "Protocol": "grpc", | ||
| "ProtocolVersion": 6, | ||
| "Pid": 12345, | ||
| "Test": true, | ||
| "Addr": { | ||
| "Network": "unix", | ||
| "String":"/var/folders/xx/abcde12345/T/plugin12345" | ||
| } | ||
| } | ||
| }`, | ||
| expectedOutput: map[addrs.Provider]*plugin.ReattachConfig{ | ||
| tfaddr.NewProvider("example.com", "my-org", "test"): func() *plugin.ReattachConfig { | ||
| addr, err := net.ResolveUnixAddr("unix", "/var/folders/xx/abcde12345/T/plugin12345") | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| return &plugin.ReattachConfig{ | ||
| Protocol: plugin.Protocol("grpc"), | ||
| ProtocolVersion: 6, | ||
| Pid: 12345, | ||
| Test: true, | ||
| Addr: addr, | ||
| } | ||
| }(), | ||
| }, | ||
| }, | ||
| "error - bad JSON": { | ||
| // Missing closing brace | ||
| reattachProviders: `{ | ||
| "test": { | ||
| "Protocol": "grpc", | ||
| "ProtocolVersion": 6, | ||
| "Pid": 12345, | ||
| "Test": true, | ||
| "Addr": { | ||
| "Network": "unix", | ||
| "String":"/var/folders/xx/abcde12345/T/plugin12345" | ||
| } | ||
| } | ||
| `, | ||
| expectErr: true, | ||
| }, | ||
| "error - bad provider address": { | ||
| reattachProviders: `{ | ||
| "bad provider addr": { | ||
| "Protocol": "grpc", | ||
| "ProtocolVersion": 6, | ||
| "Pid": 12345, | ||
| "Test": true, | ||
| "Addr": { | ||
| "Network": "unix", | ||
| "String":"/var/folders/xx/abcde12345/T/plugin12345" | ||
| } | ||
| } | ||
| }`, | ||
| expectErr: true, | ||
| }, | ||
| "error - unrecognized protocol": { | ||
| reattachProviders: `{ | ||
| "test": { | ||
| "Protocol": "carrier-pigeon", | ||
| "ProtocolVersion": 6, | ||
| "Pid": 12345, | ||
| "Test": true, | ||
| "Addr": { | ||
| "Network": "pigeon", | ||
| "String":"fly home little pigeon" | ||
| } | ||
| } | ||
| }`, | ||
| expectErr: true, | ||
| }, | ||
| "error - unrecognized network": { | ||
| reattachProviders: `{ | ||
| "test": { | ||
| "Protocol": "grpc", | ||
| "ProtocolVersion": 6, | ||
| "Pid": 12345, | ||
| "Test": true, | ||
| "Addr": { | ||
| "Network": "linkedin", | ||
| "String":"http://www.linkedin.com/" | ||
| } | ||
| } | ||
| }`, | ||
| expectErr: true, | ||
| }, | ||
| "error - bad tcp address": { | ||
| // Addr.String has no port at the end | ||
| reattachProviders: `{ | ||
| "test": { | ||
| "Protocol": "grpc", | ||
| "ProtocolVersion": 6, | ||
| "Pid": 12345, | ||
| "Test": true, | ||
| "Addr": { | ||
| "Network": "tcp", | ||
| "String":"127.0.0.1" | ||
| } | ||
| } | ||
| }`, | ||
| expectErr: true, | ||
| }, | ||
| } | ||
|
|
||
| for tn, tc := range cases { | ||
| t.Run(tn, func(t *testing.T) { | ||
| output, err := ParseReattachProviders(tc.reattachProviders) | ||
| if err != nil { | ||
| if !tc.expectErr { | ||
| t.Fatal(err) | ||
| } | ||
| // an expected error occurred | ||
| return | ||
| } | ||
| if err == nil && tc.expectErr { | ||
| t.Fatal("expected error but there was none") | ||
| } | ||
| if diff := cmp.Diff(output, tc.expectedOutput); diff != "" { | ||
| t.Fatalf("expected diff:\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func Test_isProviderReattached(t *testing.T) { | ||
| cases := map[string]struct { | ||
| provider addrs.Provider | ||
| reattachProviders string | ||
| expectedOutput bool | ||
| }{ | ||
| "identifies when a matching provider is present in TF_REATTACH_PROVIDERS": { | ||
| // Note that the source in the TF_REATTACH_PROVIDERS value is just the provider name. | ||
| // It'll be assumed to be under the default registry host and in the 'hashicorp' namespace. | ||
| reattachProviders: `{ | ||
| "test": { | ||
| "Protocol": "grpc", | ||
| "ProtocolVersion": 6, | ||
| "Pid": 12345, | ||
| "Test": true, | ||
| "Addr": { | ||
| "Network": "unix", | ||
| "String":"/var/folders/xx/abcde12345/T/plugin12345" | ||
| } | ||
| } | ||
| }`, | ||
| provider: tfaddr.NewProvider(tfaddr.DefaultProviderRegistryHost, "hashicorp", "test"), | ||
| expectedOutput: true, | ||
| }, | ||
| "identifies when a provider doesn't have a match in TF_REATTACH_PROVIDERS": { | ||
| // Note the mismatch on namespace | ||
| reattachProviders: `{ | ||
| "hashicorp/test": { | ||
| "Protocol": "grpc", | ||
| "ProtocolVersion": 6, | ||
| "Pid": 12345, | ||
| "Test": true, | ||
| "Addr": { | ||
| "Network": "unix", | ||
| "String":"/var/folders/xx/abcde12345/T/plugin12345" | ||
| } | ||
| } | ||
| }`, | ||
| provider: tfaddr.NewProvider(tfaddr.DefaultProviderRegistryHost, "dadgarcorp", "test"), | ||
| expectedOutput: false, | ||
| }, | ||
| } | ||
|
|
||
| for tn, tc := range cases { | ||
| t.Run(tn, func(t *testing.T) { | ||
| output, err := IsProviderReattached(tc.provider, tc.reattachProviders) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| if output != tc.expectedOutput { | ||
| t.Fatalf("expected returned value to be %v, got %v", tc.expectedOutput, output) | ||
| } | ||
| }) | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.