-
Notifications
You must be signed in to change notification settings - Fork 157
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add ability to attach a workspace to an existing policy set
- Loading branch information
Showing
8 changed files
with
385 additions
and
3 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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,169 @@ | ||
package tfe | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"log" | ||
"strings" | ||
|
||
tfe "github.com/hashicorp/go-tfe" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
func resourceTFEWorkspacePolicySet() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceTFEWorkspacePolicySetCreate, | ||
Read: resourceTFEWorkspacePolicySetRead, | ||
Delete: resourceTFEWorkspacePolicySetDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: resourceTFEWorkspacePolicySetImporter, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"policy_set_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
|
||
"workspace_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceTFEWorkspacePolicySetCreate(d *schema.ResourceData, meta interface{}) error { | ||
tfeClient := meta.(*tfe.Client) | ||
|
||
policySetID := d.Get("policy_set_id").(string) | ||
workspaceID := d.Get("workspace_id").(string) | ||
|
||
policySetAddWorkspacesOptions := tfe.PolicySetAddWorkspacesOptions{} | ||
policySetAddWorkspacesOptions.Workspaces = append(policySetAddWorkspacesOptions.Workspaces, &tfe.Workspace{ID: workspaceID}) | ||
|
||
err := tfeClient.PolicySets.AddWorkspaces(ctx, policySetID, policySetAddWorkspacesOptions) | ||
if err != nil { | ||
return fmt.Errorf( | ||
"Error attaching policy set id %s to workspace %s: %w", policySetID, workspaceID, err) | ||
} | ||
|
||
d.SetId(fmt.Sprintf("%s_%s", workspaceID, policySetID)) | ||
|
||
return resourceTFEWorkspacePolicySetRead(d, meta) | ||
} | ||
|
||
func resourceTFEWorkspacePolicySetRead(d *schema.ResourceData, meta interface{}) error { | ||
tfeClient := meta.(*tfe.Client) | ||
|
||
policySetID := d.Get("policy_set_id").(string) | ||
workspaceID := d.Get("workspace_id").(string) | ||
|
||
log.Printf("[DEBUG] Read configuration of workspace policy set: %s", policySetID) | ||
policySet, err := tfeClient.PolicySets.ReadWithOptions(ctx, policySetID, &tfe.PolicySetReadOptions{ | ||
Include: []tfe.PolicySetIncludeOpt{tfe.PolicySetWorkspaces}, | ||
}) | ||
if err != nil { | ||
if errors.Is(err, tfe.ErrResourceNotFound) { | ||
log.Printf("[DEBUG] Policy set %s no longer exists", policySetID) | ||
d.SetId("") | ||
return nil | ||
} | ||
return fmt.Errorf("Error reading configuration of policy set %s: %w", policySetID, err) | ||
} | ||
|
||
isWorkspaceAttached := false | ||
for _, workspace := range policySet.Workspaces { | ||
if workspace.ID == workspaceID { | ||
isWorkspaceAttached = true | ||
d.Set("workspace_id", workspaceID) | ||
break | ||
} | ||
} | ||
|
||
if !isWorkspaceAttached { | ||
log.Printf("[DEBUG] Workspace %s not attached to policy set %s. Removing from state.", workspaceID, policySetID) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
d.Set("policy_set_id", policySetID) | ||
return nil | ||
} | ||
|
||
func resourceTFEWorkspacePolicySetDelete(d *schema.ResourceData, meta interface{}) error { | ||
tfeClient := meta.(*tfe.Client) | ||
|
||
policySetID := d.Get("policy_set_id").(string) | ||
workspaceID := d.Get("workspace_id").(string) | ||
|
||
log.Printf("[DEBUG] Detaching workspace (%s) from policy set (%s)", workspaceID, policySetID) | ||
policySetRemoveWorkspacesOptions := tfe.PolicySetRemoveWorkspacesOptions{} | ||
policySetRemoveWorkspacesOptions.Workspaces = append(policySetRemoveWorkspacesOptions.Workspaces, &tfe.Workspace{ID: workspaceID}) | ||
|
||
err := tfeClient.PolicySets.RemoveWorkspaces(ctx, policySetID, policySetRemoveWorkspacesOptions) | ||
if err != nil { | ||
return fmt.Errorf( | ||
"Error detaching workspace %s from policy set %s: %w", workspaceID, policySetID, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceTFEWorkspacePolicySetImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { | ||
// The format of the import ID is <ORGANIZATION/WORKSPACE NAME/POLICYSET NAME> | ||
splitID := strings.SplitN(d.Id(), "/", 3) | ||
if len(splitID) != 3 { | ||
return nil, fmt.Errorf( | ||
"invalid workspace policy set input format: %s (expected <ORGANIZATION>/<WORKSPACE NAME>/<POLICYSET NAME>)", | ||
splitID, | ||
) | ||
} | ||
|
||
organization, wsName, pSName := splitID[0], splitID[1], splitID[2] | ||
|
||
tfeClient := meta.(*tfe.Client) | ||
|
||
// Ensure the named workspace exists before fetching all the policy sets in the org | ||
_, err := tfeClient.Workspaces.Read(ctx, organization, wsName) | ||
if err != nil { | ||
return nil, fmt.Errorf("error reading configuration of workspace %s in organization %s: %w", wsName, organization, err) | ||
} | ||
|
||
options := &tfe.PolicySetListOptions{Include: []tfe.PolicySetIncludeOpt{tfe.PolicySetWorkspaces}} | ||
for { | ||
list, err := tfeClient.PolicySets.List(ctx, organization, options) | ||
if err != nil { | ||
return nil, fmt.Errorf("Error retrieving policy sets: %w", err) | ||
} | ||
for _, policySet := range list.Items { | ||
if policySet.Name != pSName { | ||
continue | ||
} | ||
|
||
for _, ws := range policySet.Workspaces { | ||
if ws.Name != wsName { | ||
continue | ||
} | ||
|
||
d.Set("workspace_id", ws.ID) | ||
d.Set("policy_set_id", policySet.ID) | ||
d.SetId(fmt.Sprintf("%s_%s", ws.ID, policySet.ID)) | ||
|
||
return []*schema.ResourceData{d}, nil | ||
} | ||
} | ||
|
||
// Exit the loop when we've seen all pages. | ||
if list.CurrentPage >= list.TotalPages { | ||
break | ||
} | ||
|
||
// Update the page number to get the next page. | ||
options.PageNumber = list.NextPage | ||
} | ||
|
||
return nil, fmt.Errorf("workspace %s has not been assigned to policy set %s", wsName, pSName) | ||
} |
This file contains 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,151 @@ | ||
package tfe | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
"regexp" | ||
"testing" | ||
"time" | ||
|
||
tfe "github.com/hashicorp/go-tfe" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform" | ||
) | ||
|
||
func TestAccTFEWorkspacePolicySet_basic(t *testing.T) { | ||
skipIfFreeOnly(t) | ||
|
||
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckTFEWorkspacePolicySetDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccTFEWorkspacePolicySet_basic(rInt), | ||
ExpectNonEmptyPlan: true, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckTFEWorkspacePolicySetExists( | ||
"tfe_workspace_policy_set.test"), | ||
), | ||
}, | ||
{ | ||
ResourceName: "tfe_workspace_policy_set.test", | ||
ImportState: true, | ||
ImportStateId: fmt.Sprintf("tst-terraform-%d/tst-terraform-%d/tst-policy-set-%d", rInt, rInt, rInt), | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccTFEWorkspacePolicySet_incorrectImportSyntax(t *testing.T) { | ||
skipIfFreeOnly(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: testAccTFEWorkspacePolicySet_basic(rInt), | ||
ExpectNonEmptyPlan: true, | ||
}, | ||
{ | ||
ResourceName: "tfe_workspace_policy_set.test", | ||
ImportState: true, | ||
ImportStateId: fmt.Sprintf("tst-terraform-%d/tst-terraform-%d", rInt, rInt), | ||
ExpectError: regexp.MustCompile(`Error: invalid workspace policy set input format`), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckTFEWorkspacePolicySetExists(n string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
tfeClient := testAccProvider.Meta().(*tfe.Client) | ||
|
||
rs, ok := s.RootModule().Resources[n] | ||
if !ok { | ||
return fmt.Errorf("Not found: %s", n) | ||
} | ||
|
||
id := rs.Primary.ID | ||
if id == "" { | ||
return fmt.Errorf("No ID is set") | ||
} | ||
|
||
policySetID := rs.Primary.Attributes["policy_set_id"] | ||
if policySetID == "" { | ||
return fmt.Errorf("No policy set id set") | ||
} | ||
|
||
workspaceID := rs.Primary.Attributes["workspace_id"] | ||
if workspaceID == "" { | ||
return fmt.Errorf("No workspace id set") | ||
} | ||
|
||
policySet, err := tfeClient.PolicySets.ReadWithOptions(ctx, policySetID, &tfe.PolicySetReadOptions{ | ||
Include: []tfe.PolicySetIncludeOpt{tfe.PolicySetWorkspaces}, | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("error reading polciy set %s: %w", policySetID, err) | ||
} | ||
for _, workspace := range policySet.Workspaces { | ||
if workspace.ID == workspaceID { | ||
return nil | ||
} | ||
} | ||
|
||
return fmt.Errorf("Workspace (%s) is not attached to policy set (%s).", workspaceID, policySetID) | ||
} | ||
} | ||
|
||
func testAccCheckTFEWorkspacePolicySetDestroy(s *terraform.State) error { | ||
tfeClient := testAccProvider.Meta().(*tfe.Client) | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "tfe_policy_set" { | ||
continue | ||
} | ||
|
||
if rs.Primary.ID == "" { | ||
return fmt.Errorf("No instance ID is set") | ||
} | ||
|
||
_, err := tfeClient.PolicySets.Read(ctx, rs.Primary.ID) | ||
if err == nil { | ||
return fmt.Errorf("Policy Set %s still exists", rs.Primary.ID) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func testAccTFEWorkspacePolicySet_basic(rInt int) string { | ||
return fmt.Sprintf(` | ||
resource "tfe_organization" "test" { | ||
name = "tst-terraform-%d" | ||
email = "admin@company.com" | ||
} | ||
resource "tfe_workspace" "test" { | ||
name = "tst-terraform-%d" | ||
organization = tfe_organization.test.id | ||
auto_apply = true | ||
tag_names = ["test"] | ||
} | ||
resource "tfe_policy_set" "test" { | ||
name = "tst-policy-set-%d" | ||
description = "Policy Set" | ||
organization = tfe_organization.test.id | ||
} | ||
resource "tfe_workspace_policy_set" "test" { | ||
policy_set_id = tfe_policy_set.test.id | ||
workspace_id = tfe_workspace.test.id | ||
}`, rInt, rInt, rInt) | ||
} |
Oops, something went wrong.