Skip to content

Commit

Permalink
Merge pull request #555 from hashicorp/gs/add-pre-plan-changes
Browse files Browse the repository at this point in the history
Add workspace run task stage property
  • Loading branch information
brandonc committed Aug 18, 2022
2 parents 3d95210 + f3754e3 commit 1fc3e38
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 14 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,8 @@
## Unreleased

FEATURES:
* r/tfe_workspace_run_task, d/tfe_workspace_run_task: Add `stage` attribute to workspace run tasks. ([#555](https://github.com/hashicorp/terraform-provider-tfe/pull/555))

## v0.36.0 (August 16th, 2022)

FEATURES:
Expand Down
22 changes: 16 additions & 6 deletions tfe/data_source_workspace_run_task.go
Expand Up @@ -13,18 +13,27 @@ func dataSourceTFEWorkspaceRunTask() *schema.Resource {

Schema: map[string]*schema.Schema{
"workspace_id": {
Type: schema.TypeString,
Required: true,
Description: "The id of the workspace.",
Type: schema.TypeString,
Required: true,
},

"task_id": {
Type: schema.TypeString,
Required: true,
Description: "The id of the run task.",
Type: schema.TypeString,
Required: true,
},

"enforcement_level": {
Type: schema.TypeString,
Computed: true,
Description: "The enforcement level of the task.",
Type: schema.TypeString,
Computed: true,
},

"stage": {
Description: "Which stage the task will run in.",
Type: schema.TypeString,
Computed: true,
},
},
}
Expand All @@ -47,6 +56,7 @@ func dataSourceTFEWorkspaceRunTaskRead(d *schema.ResourceData, meta interface{})
for _, wstask := range list.Items {
if wstask.RunTask.ID == taskID {
d.Set("enforcement_level", string(wstask.EnforcementLevel))
d.Set("stage", string(wstask.Stage))
d.SetId(wstask.ID)
return nil
}
Expand Down
1 change: 1 addition & 0 deletions tfe/data_source_workspace_run_task_test.go
Expand Up @@ -25,6 +25,7 @@ func TestAccTFEWorkspaceRunTaskDataSource_basic(t *testing.T) {
Config: testAccTFEWorkspaceRunTaskDataSourceConfig(orgName, rInt, runTasksURL()),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.tfe_workspace_run_task.foobar", "enforcement_level", "advisory"),
resource.TestCheckResourceAttrSet("data.tfe_workspace_run_task.foobar", "stage"),
resource.TestCheckResourceAttrSet("data.tfe_workspace_run_task.foobar", "id"),
resource.TestCheckResourceAttrSet("data.tfe_workspace_run_task.foobar", "task_id"),
resource.TestCheckResourceAttrSet("data.tfe_workspace_run_task.foobar", "workspace_id"),
Expand Down
73 changes: 66 additions & 7 deletions tfe/resource_tfe_workspace_run_task.go
Expand Up @@ -10,6 +10,36 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func workspaceRunTaskEnforcementLevels() []string {
return []string{
string(tfe.Advisory),
string(tfe.Mandatory),
}
}

func workspaceRunTaskStages() []string {
return []string{
string(tfe.PrePlan),
string(tfe.PostPlan),
}
}

// Helper function to turn a slice of strings into an english sentence for documentation
func sentenceList(items []string, prefix string, suffix string, conjunction string) string {
var b strings.Builder
for i, v := range items {
fmt.Fprint(&b, prefix, v, suffix)
if i < len(items)-1 {
if i < len(items)-2 {
fmt.Fprint(&b, ", ")
} else {
fmt.Fprintf(&b, " %s ", conjunction)
}
}
}
return b.String()
}

func resourceTFEWorkspaceRunTask() *schema.Resource {
return &schema.Resource{
Create: resourceTFEWorkspaceRunTaskCreate,
Expand All @@ -22,25 +52,47 @@ func resourceTFEWorkspaceRunTask() *schema.Resource {

Schema: map[string]*schema.Schema{
"workspace_id": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
Description: "The id of the workspace to associate the Run task to.",
Type: schema.TypeString,
ForceNew: true,
Required: true,
},

"task_id": {
Description: "The id of the Run task to associate to the Workspace.",

Type: schema.TypeString,
ForceNew: true,
Required: true,
},

"enforcement_level": {
Description: fmt.Sprintf("The enforcement level of the task. Valid values are %s.", sentenceList(
workspaceRunTaskEnforcementLevels(),
"`",
"`",
"and",
)),
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(
[]string{
string(tfe.Advisory),
string(tfe.Mandatory),
},
workspaceRunTaskEnforcementLevels(),
false,
),
},

"stage": {
Description: fmt.Sprintf("This is currently in BETA. The stage to run the task in. Valid values are %s.", sentenceList(
workspaceRunTaskStages(),
"`",
"`",
"and",
)),
Type: schema.TypeString,
Optional: true,
Default: tfe.PostPlan,
ValidateFunc: validation.StringInSlice(
workspaceRunTaskStages(),
false,
),
},
Expand All @@ -65,10 +117,12 @@ func resourceTFEWorkspaceRunTaskCreate(d *schema.ResourceData, meta interface{})
return fmt.Errorf(
"Error retrieving workspace %s: %w", workspaceID, err)
}
stage := tfe.Stage(d.Get("stage").(string))

options := tfe.WorkspaceRunTaskCreateOptions{
RunTask: task,
EnforcementLevel: tfe.TaskEnforcementLevel(d.Get("enforcement_level").(string)),
Stage: &stage,
}

log.Printf("[DEBUG] Create task %s in workspace %s", task.ID, ws.ID)
Expand Down Expand Up @@ -108,6 +162,10 @@ func resourceTFEWorkspaceRunTaskUpdate(d *schema.ResourceData, meta interface{})
if d.HasChange("enforcement_level") {
options.EnforcementLevel = tfe.TaskEnforcementLevel(d.Get("enforcement_level").(string))
}
if d.HasChange("stage") {
stage := tfe.Stage(d.Get("stage").(string))
options.Stage = &stage
}

log.Printf("[DEBUG] Update configuration of task %s in workspace %s", d.Id(), workspaceID)
_, err := tfeClient.WorkspaceRunTasks.Update(ctx, workspaceID, d.Id(), options)
Expand Down Expand Up @@ -138,6 +196,7 @@ func resourceTFEWorkspaceRunTaskRead(d *schema.ResourceData, meta interface{}) e
d.Set("workspace_id", wstask.Workspace.ID)
d.Set("task_id", wstask.RunTask.ID)
d.Set("enforcement_level", string(wstask.EnforcementLevel))
d.Set("stage", string(wstask.Stage))

return nil
}
Expand Down
88 changes: 88 additions & 0 deletions tfe/resource_tfe_workspace_run_task_test.go
Expand Up @@ -42,6 +42,40 @@ func TestAccTFEWorkspaceRunTask_create(t *testing.T) {
})
}

func TestAccTFEWorkspaceRunTask_beta_create(t *testing.T) {
skipUnlessRunTasksDefined(t)
skipUnlessBeta(t)
skipIfFreeOnly(t) // Run Tasks requires TFE or a TFC paid/trial subscription

workspaceTask := &tfe.WorkspaceRunTask{}
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
orgName := fmt.Sprintf("tst-terraform-%d", rInt)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckTFEWorkspaceRunTaskDestroy,
Steps: []resource.TestStep{
testCheckCreateOrgWithRunTasks(orgName),
{
Config: testAccTFEWorkspaceRunTask_beta_basic(orgName, runTasksURL()),
Check: resource.ComposeTestCheckFunc(
testAccCheckTFEWorkspaceRunTaskExists("tfe_workspace_run_task.foobar", workspaceTask),
resource.TestCheckResourceAttr("tfe_workspace_run_task.foobar", "enforcement_level", "advisory"),
resource.TestCheckResourceAttr("tfe_workspace_run_task.foobar", "stage", "post_plan"),
),
},
{
Config: testAccTFEWorkspaceRunTask_beta_update(orgName, runTasksURL()),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("tfe_workspace_run_task.foobar", "enforcement_level", "mandatory"),
resource.TestCheckResourceAttr("tfe_workspace_run_task.foobar", "stage", "pre_plan"),
),
},
},
})
}

func TestAccTFEWorkspaceRunTask_import(t *testing.T) {
skipUnlessRunTasksDefined(t)
skipIfFreeOnly(t) // Run Tasks requires TFE or a TFC paid/trial subscription
Expand Down Expand Up @@ -175,3 +209,57 @@ resource "tfe_workspace_run_task" "foobar" {
}
`, orgName, runTaskURL)
}

func testAccTFEWorkspaceRunTask_beta_basic(orgName, runTaskURL string) string {
return fmt.Sprintf(`
resource "tfe_organization" "foobar" {
name = "%s"
email = "admin@company.com"
}
resource "tfe_organization_run_task" "foobar" {
organization = tfe_organization.foobar.id
url = "%s"
name = "foobar-task"
}
resource "tfe_workspace" "foobar" {
name = "workspace-test"
organization = tfe_organization.foobar.id
}
resource "tfe_workspace_run_task" "foobar" {
workspace_id = resource.tfe_workspace.foobar.id
task_id = resource.tfe_organization_run_task.foobar.id
enforcement_level = "advisory"
stage = "post_plan"
}
`, orgName, runTaskURL)
}

func testAccTFEWorkspaceRunTask_beta_update(orgName, runTaskURL string) string {
return fmt.Sprintf(`
resource "tfe_organization" "foobar" {
name = "%s"
email = "admin@company.com"
}
resource "tfe_organization_run_task" "foobar" {
organization = tfe_organization.foobar.id
url = "%s"
name = "foobar-task"
}
resource "tfe_workspace" "foobar" {
name = "workspace-test"
organization = tfe_organization.foobar.id
}
resource "tfe_workspace_run_task" "foobar" {
workspace_id = resource.tfe_workspace.foobar.id
task_id = resource.tfe_organization_run_task.foobar.id
enforcement_level = "mandatory"
stage = "pre_plan"
}
`, orgName, runTaskURL)
}
11 changes: 11 additions & 0 deletions tfe/testing.go
Expand Up @@ -67,6 +67,12 @@ func skipUnlessRunTasksDefined(t *testing.T) {
}
}

func skipUnlessBeta(t *testing.T) {
if !betaFeaturesEnabled() {
t.Skip("Skipping test related to a Terraform Cloud/Enterprise beta feature. Set ENABLE_BETA=1 to run.")
}
}

func enterpriseEnabled() bool {
return os.Getenv("ENABLE_TFE") == "1"
}
Expand All @@ -79,6 +85,11 @@ func runTasksURL() string {
return os.Getenv(RunTasksURLEnvName)
}

// Checks to see if ENABLE_BETA is set to 1, thereby enabling tests for beta features.
func betaFeaturesEnabled() bool {
return os.Getenv("ENABLE_BETA") == "1"
}

// Most tests rely on terraform-plugin-sdk/helper/resource.Test to run. That test helper ensures
// that TF_ACC=1 or else it skips. In some rare cases, however, tests do not use the SDK helper and
// are acceptance tests.
Expand Down
3 changes: 2 additions & 1 deletion website/docs/d/workspace_run_task.html.markdown
Expand Up @@ -25,7 +25,7 @@ data "tfe_workspace_run_task" "foobar" {

The following arguments are supported:

* `task_id` - (Required) The id of the Run task.
* `task_id` - (Required) The id of the run task.
* `workspace_id` - (Required) The id of the workspace.

## Attributes Reference
Expand All @@ -34,3 +34,4 @@ In addition to all arguments above, the following attributes are exported:

* `enforcement_level` - The enforcement level of the task.
* `id` - The ID of the Workspace Run task.
* `stage` - Which stage the task will run in.
1 change: 1 addition & 0 deletions website/docs/r/workspace_run_task.html.markdown
Expand Up @@ -31,6 +31,7 @@ The following arguments are supported:
* `enforcement_level` - (Required) The enforcement level of the task. Valid values are `advisory` and `mandatory`.
* `task_id` - (Required) The id of the Run task to associate to the Workspace.
* `workspace_id` - (Required) The id of the workspace to associate the Run task to.
* `stage` - (Optional) This is currently in BETA. The stage to run the task in. Valid values are `pre-plan` and `post-plan`.

## Attributes Reference

Expand Down

0 comments on commit 1fc3e38

Please sign in to comment.