mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 18:01:01 -06:00
Add TF_ORGANIZATION env var support
TF_ORGANIZATION will serve as a fallback for configuring the organization in the `cloud` block. This is the first step to make it easier for users wanting to configure Terraform programmatically.
This commit is contained in:
parent
7e849473a4
commit
45357f5004
@ -113,7 +113,7 @@ func (b *Cloud) ConfigSchema() *configschema.Block {
|
||||
},
|
||||
"organization": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
Optional: true,
|
||||
Description: schemaDescriptionOrganization,
|
||||
},
|
||||
"token": {
|
||||
@ -152,8 +152,13 @@ func (b *Cloud) PrepareConfig(obj cty.Value) (cty.Value, tfdiags.Diagnostics) {
|
||||
return obj, diags
|
||||
}
|
||||
|
||||
// check if organization is specified in the config.
|
||||
if val := obj.GetAttr("organization"); val.IsNull() || val.AsString() == "" {
|
||||
diags = diags.Append(invalidOrganizationConfigMissingValue)
|
||||
// organization is specified in the config but is invalid, so
|
||||
// we'll fallback on TF_ORGANIZATION
|
||||
if val := os.Getenv("TF_ORGANIZATION"); val == "" {
|
||||
diags = diags.Append(invalidOrganizationConfigMissingValue)
|
||||
}
|
||||
}
|
||||
|
||||
WorkspaceMapping := WorkspaceMapping{}
|
||||
@ -342,8 +347,14 @@ func (b *Cloud) setConfigurationFields(obj cty.Value) tfdiags.Diagnostics {
|
||||
b.hostname = defaultHostname
|
||||
}
|
||||
|
||||
// Get the organization.
|
||||
if val := obj.GetAttr("organization"); !val.IsNull() {
|
||||
// We can have two options, setting the organization via the config
|
||||
// or using TF_ORGANIZATION. Since PrepareConfig() validates that one of these
|
||||
// values must exist, we'll initially set it to the env var and override it if
|
||||
// specified in the configuration.
|
||||
b.organization = os.Getenv("TF_ORGANIZATION")
|
||||
|
||||
// Check if the organization is present and valid in the config.
|
||||
if val := obj.GetAttr("organization"); !val.IsNull() && val.AsString() != "" {
|
||||
b.organization = val.AsString()
|
||||
}
|
||||
|
||||
|
@ -146,6 +146,119 @@ func TestCloud_PrepareConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloud_PrepareConfigWithEnvVars(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
config cty.Value
|
||||
vars map[string]string
|
||||
expectedErr string
|
||||
}{
|
||||
"with no organization": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
"organization": cty.NullVal(cty.String),
|
||||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
vars: map[string]string{
|
||||
"TF_ORGANIZATION": "example-org",
|
||||
},
|
||||
},
|
||||
"with no organization attribute or env var": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
"organization": cty.NullVal(cty.String),
|
||||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
vars: map[string]string{},
|
||||
expectedErr: `Invalid organization value: The "organization" attribute value must not be empty.`,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
s := testServer(t)
|
||||
b := New(testDisco(s))
|
||||
|
||||
for k, v := range tc.vars {
|
||||
os.Setenv(k, v)
|
||||
defer os.Unsetenv(k)
|
||||
}
|
||||
|
||||
_, valDiags := b.PrepareConfig(tc.config)
|
||||
if valDiags.Err() != nil && tc.expectedErr != "" {
|
||||
actualErr := valDiags.Err().Error()
|
||||
if !strings.Contains(actualErr, tc.expectedErr) {
|
||||
t.Fatalf("%s: unexpected validation result: %v", name, valDiags.Err())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloud_configWithEnvVars(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
config cty.Value
|
||||
vars map[string]string
|
||||
expectedOrganization string
|
||||
}{
|
||||
"with no organization specified": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
"hostname": cty.NullVal(cty.String),
|
||||
"token": cty.NullVal(cty.String),
|
||||
"organization": cty.NullVal(cty.String),
|
||||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
vars: map[string]string{
|
||||
"TF_ORGANIZATION": "hashicorp",
|
||||
},
|
||||
expectedOrganization: "hashicorp",
|
||||
},
|
||||
"with both organization and env var specified": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
"hostname": cty.NullVal(cty.String),
|
||||
"token": cty.NullVal(cty.String),
|
||||
"organization": cty.StringVal("hashicorp"),
|
||||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
vars: map[string]string{
|
||||
"TF_ORGANIZATION": "we-should-not-see-this",
|
||||
},
|
||||
expectedOrganization: "hashicorp",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
s := testServer(t)
|
||||
b := New(testDisco(s))
|
||||
|
||||
for k, v := range tc.vars {
|
||||
os.Setenv(k, v)
|
||||
defer os.Unsetenv(k)
|
||||
}
|
||||
|
||||
_, valDiags := b.PrepareConfig(tc.config)
|
||||
if valDiags.Err() != nil {
|
||||
t.Fatalf("%s: unexpected validation result: %v", name, valDiags.Err())
|
||||
}
|
||||
|
||||
diags := b.Configure(tc.config)
|
||||
if diags.Err() != nil {
|
||||
t.Fatalf("%s: unexpected configuration result: %v", name, valDiags.Err())
|
||||
}
|
||||
|
||||
if tc.expectedOrganization != "" && tc.expectedOrganization != b.organization {
|
||||
t.Fatalf("%s: organization not valid: %s, expected: %s", name, b.organization, tc.expectedOrganization)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloud_config(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
config cty.Value
|
||||
|
Loading…
Reference in New Issue
Block a user