-
Notifications
You must be signed in to change notification settings - Fork 161
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
Manage Variable Sets #452
Manage Variable Sets #452
Changes from all commits
8e4fd3e
065b3b3
e69468b
925bdd6
376ab18
6c06a70
5b99284
06f5e5d
2b22db2
f1da8bc
730bce2
bfd3f61
917a235
59818cc
02bd402
379451a
690c2cf
100c706
5d2f599
362e6bb
b5ee652
e5b056b
3b32606
b67577b
8533d09
b5d4a74
5b900b8
4e3bd4d
4c76a56
1064a22
873a7ac
9e0aa84
566ebe5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package tfe | ||
|
||
import ( | ||
"fmt" | ||
|
||
tfe "github.com/hashicorp/go-tfe" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
func dataSourceTFEVariableSet() *schema.Resource { | ||
return &schema.Resource{ | ||
Read: dataSourceTFEVariableSetRead, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
|
||
"organization": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
|
||
"description": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
|
||
"global": { | ||
Type: schema.TypeBool, | ||
Computed: true, | ||
}, | ||
|
||
"workspace_ids": { | ||
Type: schema.TypeSet, | ||
Optional: true, | ||
Computed: true, | ||
Elem: &schema.Schema{Type: schema.TypeString}, | ||
}, | ||
|
||
"variable_ids": { | ||
Type: schema.TypeSet, | ||
Optional: true, | ||
Computed: true, | ||
Elem: &schema.Schema{Type: schema.TypeString}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func dataSourceTFEVariableSetRead(d *schema.ResourceData, meta interface{}) error { | ||
tfeClient := meta.(*tfe.Client) | ||
|
||
// Get the name and organization. | ||
name := d.Get("name").(string) | ||
organization := d.Get("organization").(string) | ||
|
||
// Create an options struct. | ||
options := tfe.VariableSetListOptions{} | ||
|
||
for { | ||
// Variable Set relations, vars and workspaces, are omitted from the querying until | ||
// we find the desired variable set. | ||
l, err := tfeClient.VariableSets.List(ctx, organization, &options) | ||
if err != nil { | ||
if err == tfe.ErrResourceNotFound { | ||
return fmt.Errorf("could not find variable set%s/%s", organization, name) | ||
} | ||
return fmt.Errorf("Error retrieving variable set: %v", err) | ||
} | ||
|
||
for _, vs := range l.Items { | ||
if vs.Name == name { | ||
d.Set("name", vs.Name) | ||
d.Set("description", vs.Description) | ||
d.Set("global", vs.Global) | ||
|
||
//Only now include vars and workspaces to cut down on request load. | ||
readOptions := tfe.VariableSetReadOptions{ | ||
Include: &[]tfe.VariableSetIncludeOpt{tfe.VariableSetWorkspaces, tfe.VariableSetVars}, | ||
} | ||
|
||
vs, err = tfeClient.VariableSets.Read(ctx, vs.ID, &readOptions) | ||
if err != nil { | ||
return fmt.Errorf("Error retrieving variable set relations: %v", err) | ||
} | ||
|
||
var workspaces []interface{} | ||
for _, workspace := range vs.Workspaces { | ||
workspaces = append(workspaces, workspace.ID) | ||
} | ||
d.Set("workspace_ids", workspaces) | ||
|
||
var variables []interface{} | ||
for _, variable := range vs.Variables { | ||
variables = append(variables, variable.ID) | ||
} | ||
d.Set("variable_ids", variables) | ||
|
||
d.SetId(vs.ID) | ||
return nil | ||
} | ||
} | ||
|
||
// Exit the loop when we've seen all pages. | ||
if l.CurrentPage >= l.TotalPages { | ||
break | ||
} | ||
|
||
// Update the page number to get the next page. | ||
options.PageNumber = l.NextPage | ||
} | ||
|
||
return fmt.Errorf("Could not find variable set %s/%s", organization, name) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package tfe | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
"testing" | ||
"time" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
) | ||
|
||
func TestAccTFEVariableSetsDataSource_basic(t *testing.T) { | ||
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() | ||
orgName := fmt.Sprintf("org-%d", rInt) | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccTFEVariableSetsDataSourceConfig_basic(rInt), | ||
Check: resource.ComposeAggregateTestCheckFunc( | ||
resource.TestCheckResourceAttrSet("data.tfe_variable_set.foobar", "id"), | ||
resource.TestCheckResourceAttr( | ||
"data.tfe_variable_set.foobar", "name", fmt.Sprintf("varset-foo-%d", rInt)), | ||
resource.TestCheckResourceAttr( | ||
"data.tfe_variable_set.foobar", "description", "a description"), | ||
resource.TestCheckResourceAttr( | ||
"data.tfe_variable_set.foobar", "global", "false"), | ||
resource.TestCheckResourceAttr( | ||
"data.tfe_variable_set.foobar", "organization", orgName), | ||
), | ||
}, | ||
}, | ||
}, | ||
) | ||
} | ||
|
||
func TestAccTFEVariableSetsDataSource_full(t *testing.T) { | ||
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccTFEVariableSetsDataSourceConfig_full(rInt), | ||
Check: resource.ComposeAggregateTestCheckFunc( | ||
resource.TestCheckResourceAttrSet("data.tfe_variable_set.foobar", "id"), | ||
resource.TestCheckResourceAttr( | ||
"data.tfe_variable_set.foobar", "name", fmt.Sprintf("varset-foo-%d", rInt)), | ||
resource.TestCheckResourceAttr( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add a test check for the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the ids are determined at run time how can I reference them from inside the test func declaration? Looking for prior art and coming up short. |
||
"data.tfe_variable_set.foobar", "workspace_ids.#", "1"), | ||
resource.TestCheckResourceAttr( | ||
"data.tfe_variable_set.foobar", "variable_ids.#", "1"), | ||
), | ||
}, | ||
}, | ||
}, | ||
) | ||
} | ||
|
||
func testAccTFEVariableSetsDataSourceConfig_basic(rInt int) string { | ||
return fmt.Sprintf(` | ||
resource "tfe_organization" "foobar" { | ||
name = "org-%d" | ||
email = "[email protected]" | ||
} | ||
|
||
resource "tfe_variable_set" "foobar" { | ||
name = "varset-foo-%d" | ||
description = "a description" | ||
organization = tfe_organization.foobar.id | ||
} | ||
|
||
data "tfe_variable_set" "foobar" { | ||
name = tfe_variable_set.foobar.name | ||
organization = tfe_variable_set.foobar.organization | ||
}`, rInt, rInt) | ||
} | ||
|
||
func testAccTFEVariableSetsDataSourceConfig_full(rInt int) string { | ||
return fmt.Sprintf(` | ||
resource "tfe_organization" "foobar" { | ||
name = "org-%d" | ||
email = "[email protected]" | ||
} | ||
|
||
resource "tfe_workspace" "foobar" { | ||
name = "workspace-foo-%d" | ||
organization = tfe_organization.foobar.id | ||
} | ||
|
||
resource "tfe_variable_set" "foobar" { | ||
name = "varset-foo-%d" | ||
description = "a description" | ||
organization = tfe_organization.foobar.id | ||
workspace_ids = [tfe_workspace.foobar.id] | ||
} | ||
|
||
resource "tfe_variable" "envfoo" { | ||
key = "vfoo" | ||
value = "bar" | ||
category = "env" | ||
variable_set_id = tfe_variable_set.foobar.id | ||
} | ||
|
||
data "tfe_variable_set" "foobar" { | ||
name = tfe_variable_set.foobar.name | ||
organization = tfe_variable_set.foobar.organization | ||
}`, rInt, rInt, rInt) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,14 +62,26 @@ func dataSourceTFEWorkspaceVariables() *schema.Resource { | |
}, | ||
}, | ||
"workspace_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ExactlyOneOf: []string{"workspace_id", "variable_set_id"}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add a ValidateFunc: validation.StringMatch(
workspaceIdRegexp,
"must be a valid workspace ID (ws-<RANDOM STRING>)",
), There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you recommend this for the |
||
}, | ||
"variable_set_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ExactlyOneOf: []string{"workspace_id", "variable_set_id"}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func dataSourceVariableRead(d *schema.ResourceData, meta interface{}) error { | ||
//Switch to variable set variable logic | ||
_, variableSetIdProvided := d.GetOk("variable_set_id") | ||
if variableSetIdProvided { | ||
return dataSourceVariableSetVariableRead(d, meta) | ||
} | ||
|
||
tfeClient := meta.(*tfe.Client) | ||
|
||
// Get the name and organization. | ||
|
@@ -122,3 +134,57 @@ func dataSourceVariableRead(d *schema.ResourceData, meta interface{}) error { | |
d.Set("env", totalEnvVariables) | ||
return nil | ||
} | ||
|
||
func dataSourceVariableSetVariableRead(d *schema.ResourceData, meta interface{}) error { | ||
tfeClient := meta.(*tfe.Client) | ||
|
||
// Get the id. | ||
variableSetId := d.Get("variable_set_id").(string) | ||
|
||
log.Printf("[DEBUG] Read configuration of variable set: %s", variableSetId) | ||
|
||
totalEnvVariables := make([]interface{}, 0) | ||
totalTerraformVariables := make([]interface{}, 0) | ||
|
||
options := tfe.VariableSetVariableListOptions{} | ||
|
||
for { | ||
variableList, err := tfeClient.VariableSetVariables.List(ctx, variableSetId, &options) | ||
if err != nil { | ||
return fmt.Errorf("Error retrieving variable list: %w", err) | ||
} | ||
terraformVars := make([]interface{}, 0) | ||
envVars := make([]interface{}, 0) | ||
for _, variable := range variableList.Items { | ||
result := make(map[string]interface{}) | ||
result["id"] = variable.ID | ||
result["category"] = variable.Category | ||
result["hcl"] = variable.HCL | ||
result["name"] = variable.Key | ||
result["sensitive"] = variable.Sensitive | ||
result["value"] = variable.Value | ||
if variable.Category == "terraform" { | ||
terraformVars = append(terraformVars, result) | ||
} else if variable.Category == "env" { | ||
envVars = append(envVars, result) | ||
} | ||
} | ||
|
||
totalEnvVariables = append(totalEnvVariables, envVars...) | ||
totalTerraformVariables = append(totalTerraformVariables, terraformVars...) | ||
|
||
// Exit the loop when we've seen all pages. | ||
if variableList.CurrentPage >= variableList.TotalPages { | ||
break | ||
} | ||
|
||
// Update the page number to get the next page. | ||
options.PageNumber = variableList.NextPage | ||
} | ||
|
||
d.SetId(fmt.Sprintf("variables/%v", variableSetId)) | ||
d.Set("variables", append(totalTerraformVariables, totalEnvVariables...)) | ||
d.Set("terraform", totalTerraformVariables) | ||
d.Set("env", totalEnvVariables) | ||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be wrong on this, but from what I've read in the API docs workspace and variable IDs are included in the response body for the list endpoint? So you can simply loop through the
Workspaces
field without the need to make an extra API call:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thinking here was to not put the extra load of looking up and serializing all the vars and workspaces until we know which one we care about. There are realistic scenarios where I believe the List call could become quite slow if asked to collect all that extra data. Happy to chat more about it if that seems unfounded.