backend/remote: Filter environment variables when loading context (#23358)
* backend/remote: Filter environment variables when loading context Following up on #23122, the remote system (Terraform Cloud or Enterprise) serves environment and Terraform variables using a single type of object. We only should load Terraform variables into the Terraform context. Fixes https://github.com/hashicorp/terraform/issues/23283.
This commit is contained in:
parent
b09626b0cc
commit
af77d1d22c
|
@ -116,11 +116,13 @@ func (b *Remote) Context(op *backend.Operation) (*terraform.Context, statemgr.Fu
|
|||
op.Variables = make(map[string]backend.UnparsedVariableValue)
|
||||
}
|
||||
for _, v := range tfeVariables.Items {
|
||||
if v.Category == tfe.CategoryTerraform {
|
||||
op.Variables[v.Key] = &remoteStoredVariableValue{
|
||||
definition: v,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if op.Variables != nil {
|
||||
variables, varDiags := backend.ParseVariableValues(op.Variables, config.Module.Variables)
|
||||
|
|
|
@ -4,7 +4,9 @@ import (
|
|||
"testing"
|
||||
|
||||
tfe "github.com/hashicorp/go-tfe"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/hashicorp/terraform/internal/initwd"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
|
@ -141,3 +143,72 @@ func TestRemoteStoredVariableValue(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteContextWithVars(t *testing.T) {
|
||||
catTerraform := tfe.CategoryTerraform
|
||||
catEnv := tfe.CategoryEnv
|
||||
|
||||
tests := map[string]struct {
|
||||
Opts *tfe.VariableCreateOptions
|
||||
WantError string
|
||||
}{
|
||||
"Terraform variable": {
|
||||
&tfe.VariableCreateOptions{
|
||||
Category: &catTerraform,
|
||||
},
|
||||
`Value for undeclared variable: A variable named "key" was assigned a value, but the root module does not declare a variable of that name. To use this value, add a "variable" block to the configuration.`,
|
||||
},
|
||||
"environment variable": {
|
||||
&tfe.VariableCreateOptions{
|
||||
Category: &catEnv,
|
||||
},
|
||||
``,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
configDir := "./testdata/empty"
|
||||
|
||||
b, bCleanup := testBackendDefault(t)
|
||||
defer bCleanup()
|
||||
|
||||
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir)
|
||||
defer configCleanup()
|
||||
|
||||
op := &backend.Operation{
|
||||
ConfigDir: configDir,
|
||||
ConfigLoader: configLoader,
|
||||
Workspace: backend.DefaultStateName,
|
||||
}
|
||||
|
||||
v := test.Opts
|
||||
if v.Key == nil {
|
||||
key := "key"
|
||||
v.Key = &key
|
||||
}
|
||||
if v.Workspace == nil {
|
||||
v.Workspace = &tfe.Workspace{
|
||||
Name: b.workspace,
|
||||
}
|
||||
}
|
||||
b.client.Variables.Create(nil, *v)
|
||||
|
||||
_, _, diags := b.Context(op)
|
||||
|
||||
if test.WantError != "" {
|
||||
if !diags.HasErrors() {
|
||||
t.Fatalf("missing expected error\ngot: <no error>\nwant: %s", test.WantError)
|
||||
}
|
||||
errStr := diags.Err().Error()
|
||||
if errStr != test.WantError {
|
||||
t.Fatalf("wrong error\ngot: %s\nwant: %s", errStr, test.WantError)
|
||||
}
|
||||
} else {
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("unexpected error\ngot: %s\nwant: <no error>", diags.Err().Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ type mockClient struct {
|
|||
PolicyChecks *mockPolicyChecks
|
||||
Runs *mockRuns
|
||||
StateVersions *mockStateVersions
|
||||
Variables *mockVariables
|
||||
Workspaces *mockWorkspaces
|
||||
}
|
||||
|
||||
|
@ -40,6 +41,7 @@ func newMockClient() *mockClient {
|
|||
c.PolicyChecks = newMockPolicyChecks(c)
|
||||
c.Runs = newMockRuns(c)
|
||||
c.StateVersions = newMockStateVersions(c)
|
||||
c.Variables = newMockVariables(c)
|
||||
c.Workspaces = newMockWorkspaces(c)
|
||||
return c
|
||||
}
|
||||
|
@ -945,6 +947,63 @@ func (m *mockStateVersions) Download(ctx context.Context, url string) ([]byte, e
|
|||
return state, nil
|
||||
}
|
||||
|
||||
type mockVariables struct {
|
||||
client *mockClient
|
||||
workspaces map[string]*tfe.VariableList
|
||||
}
|
||||
|
||||
func newMockVariables(client *mockClient) *mockVariables {
|
||||
return &mockVariables{
|
||||
client: client,
|
||||
workspaces: make(map[string]*tfe.VariableList),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockVariables) List(ctx context.Context, options tfe.VariableListOptions) (*tfe.VariableList, error) {
|
||||
vl := m.workspaces[*options.Workspace]
|
||||
return vl, nil
|
||||
}
|
||||
|
||||
func (m *mockVariables) Create(ctx context.Context, options tfe.VariableCreateOptions) (*tfe.Variable, error) {
|
||||
v := &tfe.Variable{
|
||||
ID: generateID("var-"),
|
||||
Key: *options.Key,
|
||||
Category: *options.Category,
|
||||
}
|
||||
if options.Value != nil {
|
||||
v.Value = *options.Value
|
||||
}
|
||||
if options.HCL != nil {
|
||||
v.HCL = *options.HCL
|
||||
}
|
||||
if options.Sensitive != nil {
|
||||
v.Sensitive = *options.Sensitive
|
||||
}
|
||||
|
||||
workspace := options.Workspace.Name
|
||||
|
||||
if m.workspaces[workspace] == nil {
|
||||
m.workspaces[workspace] = &tfe.VariableList{}
|
||||
}
|
||||
|
||||
vl := m.workspaces[workspace]
|
||||
vl.Items = append(vl.Items, v)
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (m *mockVariables) Read(ctx context.Context, variableID string) (*tfe.Variable, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (m *mockVariables) Update(ctx context.Context, variableID string, options tfe.VariableUpdateOptions) (*tfe.Variable, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (m *mockVariables) Delete(ctx context.Context, variableID string) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
type mockWorkspaces struct {
|
||||
client *mockClient
|
||||
workspaceIDs map[string]*tfe.Workspace
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"testing"
|
||||
|
||||
tfe "github.com/hashicorp/go-tfe"
|
||||
"github.com/hashicorp/terraform-svchost"
|
||||
svchost "github.com/hashicorp/terraform-svchost"
|
||||
"github.com/hashicorp/terraform-svchost/auth"
|
||||
"github.com/hashicorp/terraform-svchost/disco"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
|
@ -123,6 +123,7 @@ func testBackend(t *testing.T, obj cty.Value) (*Remote, func()) {
|
|||
b.client.PolicyChecks = mc.PolicyChecks
|
||||
b.client.Runs = mc.Runs
|
||||
b.client.StateVersions = mc.StateVersions
|
||||
b.client.Variables = mc.Variables
|
||||
b.client.Workspaces = mc.Workspaces
|
||||
|
||||
b.ShowDiagnostics = func(vals ...interface{}) {
|
||||
|
|
Loading…
Reference in New Issue