Skip to content

Commit

Permalink
Add support for workflow tools and OpenTofu to stack and module resou…
Browse files Browse the repository at this point in the history
…rces

Added support for using a custom workflow tool or OpenTofu to Terraform stacks and modules.

- Added `terraform_workflow_tool` support to the stack resource.
- Added `workflow_tool` support to the module resource.
- Removed code from the stack resource that was setting the `terraform_version`. This isn't required because it gets set anyway in the `getVendorConfig()` function.
  • Loading branch information
adamconnelly authored Oct 4, 2023
1 parent 0d9812b commit 2b66265
Show file tree
Hide file tree
Showing 17 changed files with 348 additions and 28 deletions.
1 change: 1 addition & 0 deletions docs/data-sources/module.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ data "spacelift_module" "k8s-module" {
- `space_id` (String) ID (slug) of the space the module is in
- `terraform_provider` (String) The module provider will by default be inferred from the repository name if it follows the terraform-provider-name naming convention. However, if the repository doesn't follow this convention, or you gave the module a custom name, you can provide the provider name here.
- `worker_pool_id` (String) ID of the worker pool to use
- `workflow_tool` (String) Defines the tool that will be used to execute the workflow. This can be one of `OPEN_TOFU`, `TERRAFORM_FOSS` or `CUSTOM`.

<a id="nestedatt--azure_devops"></a>
### Nested Schema for `azure_devops`
Expand Down
1 change: 1 addition & 0 deletions docs/data-sources/stack.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ data "spacelift_stack" "k8s-core" {
- `terraform_external_state_access` (Boolean) Indicates whether you can access the Stack state file from other stacks or outside of Spacelift.
- `terraform_smart_sanitization` (Boolean) Indicates whether runs on this will use terraform's sensitive value system to sanitize the outputs of Terraform state and plans in spacelift instead of sanitizing all fields.
- `terraform_version` (String) Terraform version to use
- `terraform_workflow_tool` (String) Defines the tool that will be used to execute the workflow. This can be one of `OPEN_TOFU`, `TERRAFORM_FOSS` or `CUSTOM`.
- `terraform_workspace` (String) Terraform workspace to select
- `worker_pool_id` (String) ID of the worker pool to use

Expand Down
1 change: 1 addition & 0 deletions docs/data-sources/stacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ Read-Only:
- `terraform_external_state_access` (Boolean)
- `terraform_smart_sanitization` (Boolean)
- `terraform_version` (String)
- `terraform_workflow_tool` (String)
- `terraform_workspace` (String)
- `worker_pool_id` (String)

Expand Down
1 change: 1 addition & 0 deletions docs/resources/module.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ resource "spacelift_module" "example-module" {
- `space_id` (String) ID (slug) of the space the module is in
- `terraform_provider` (String) The module provider will by default be inferred from the repository name if it follows the terraform-provider-name naming convention. However, if the repository doesn't follow this convention, or you gave the module a custom name, you can provide the provider name here.
- `worker_pool_id` (String) ID of the worker pool to use. NOTE: worker_pool_id is required when using a self-hosted instance of Spacelift.
- `workflow_tool` (String) Defines the tool that will be used to execute the workflow. This can be one of `OPEN_TOFU`, `TERRAFORM_FOSS` or `CUSTOM`. Defaults to `TERRAFORM_FOSS`.

### Read-Only

Expand Down
1 change: 1 addition & 0 deletions docs/resources/stack.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ resource "spacelift_stack" "ansible-stack" {
- `terraform_external_state_access` (Boolean) Indicates whether you can access the Stack state file from other stacks or outside of Spacelift. Defaults to `false`.
- `terraform_smart_sanitization` (Boolean) Indicates whether runs on this will use terraform's sensitive value system to sanitize the outputs of Terraform state and plans in spacelift instead of sanitizing all fields. Note: Requires the terraform version to be v1.0.1 or above. Defaults to `false`.
- `terraform_version` (String) Terraform version to use
- `terraform_workflow_tool` (String) Defines the tool that will be used to execute the workflow. This can be one of `OPEN_TOFU`, `TERRAFORM_FOSS` or `CUSTOM`. Defaults to `TERRAFORM_FOSS`.
- `terraform_workspace` (String) Terraform workspace to select
- `terragrunt` (Block List, Max: 1) Terragrunt-specific configuration. Presence means this Stack is an Terragrunt Stack. (see [below for nested schema](#nestedblock--terragrunt))
- `worker_pool_id` (String) ID of the worker pool to use. NOTE: worker_pool_id is required when using a self-hosted instance of Spacelift.
Expand Down
9 changes: 9 additions & 0 deletions spacelift/data_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ func dataModule() *schema.Resource {
Description: "ID of the worker pool to use",
Computed: true,
},
"workflow_tool": {
Type: schema.TypeString,
Description: "Defines the tool that will be used to execute the workflow. This can be one of `OPEN_TOFU`, `TERRAFORM_FOSS` or `CUSTOM`.",
Computed: true,
},
},
}
}
Expand Down Expand Up @@ -233,5 +238,9 @@ func dataModuleRead(ctx context.Context, d *schema.ResourceData, meta interface{
d.Set("worker_pool_id", nil)
}

if workflowTool := module.WorkflowTool; workflowTool != nil {
d.Set("workflow_tool", *workflowTool)
}

return nil
}
85 changes: 68 additions & 17 deletions spacelift/data_module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import (
)

func TestModuleData(t *testing.T) {
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
t.Run("basic test", func(t *testing.T) {
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)

testSteps(t, []resource.TestStep{{
Config: fmt.Sprintf(`
testSteps(t, []resource.TestStep{{
Config: fmt.Sprintf(`
resource "spacelift_module" "test" {
name = "test-module-%s"
administrative = true
Expand All @@ -28,20 +29,70 @@ func TestModuleData(t *testing.T) {
module_id = spacelift_module.test.id
}
`, randomID),
Check: Resource(
"data.spacelift_module.test",
Attribute("id", Equals(fmt.Sprintf("test-module-%s", randomID))),
Attribute("administrative", Equals("true")),
Attribute("branch", Equals("master")),
Attribute("description", Equals("description")),
SetEquals("labels", "one", "two"),
Attribute("name", Equals(fmt.Sprintf("test-module-%s", randomID))),
Attribute("project_root", Equals("")),
Attribute("repository", Equals("terraform-bacon-tasty")),
SetEquals("shared_accounts", "bar-subdomain", "foo-subdomain"),
Attribute("terraform_provider", Equals("default")),
),
}})
Check: Resource(
"data.spacelift_module.test",
Attribute("id", Equals(fmt.Sprintf("test-module-%s", randomID))),
Attribute("administrative", Equals("true")),
Attribute("branch", Equals("master")),
Attribute("description", Equals("description")),
SetEquals("labels", "one", "two"),
Attribute("name", Equals(fmt.Sprintf("test-module-%s", randomID))),
Attribute("project_root", Equals("")),
Attribute("repository", Equals("terraform-bacon-tasty")),
SetEquals("shared_accounts", "bar-subdomain", "foo-subdomain"),
Attribute("terraform_provider", Equals("default")),
),
}})
})

t.Run("with terraform_workflow_tool defaulted", func(t *testing.T) {
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)

testSteps(t, []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "spacelift_module" "test" {
name = "test-module-%s"
administrative = true
branch = "master"
repository = "terraform-bacon-tasty"
}
data "spacelift_module" "test" {
module_id = spacelift_module.test.id
}
`, randomID),
Check: Resource(
"data.spacelift_module.test",
Attribute("workflow_tool", Equals("TERRAFORM_FOSS")),
),
},
})
})

t.Run("with terraform_workflow_tool set", func(t *testing.T) {
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)

testSteps(t, []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "spacelift_module" "test" {
name = "test-module-%s"
administrative = true
branch = "master"
repository = "terraform-bacon-tasty"
workflow_tool = "CUSTOM"
}
data "spacelift_module" "test" {
module_id = spacelift_module.test.id
}
`, randomID),
Check: Resource(
"data.spacelift_module.test",
Attribute("workflow_tool", Equals("CUSTOM")),
),
},
})
})
}

func TestModuleDataSpace(t *testing.T) {
Expand Down
6 changes: 6 additions & 0 deletions spacelift/data_stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,11 @@ func dataStack() *schema.Resource {
Description: "Terraform version to use",
Computed: true,
},
"terraform_workflow_tool": {
Type: schema.TypeString,
Description: "Defines the tool that will be used to execute the workflow. This can be one of `OPEN_TOFU`, `TERRAFORM_FOSS` or `CUSTOM`.",
Computed: true,
},
"terraform_workspace": {
Type: schema.TypeString,
Description: "Terraform workspace to select",
Expand Down Expand Up @@ -455,6 +460,7 @@ func dataStackRead(ctx context.Context, d *schema.ResourceData, meta interface{}
d.Set("terraform_workspace", stack.VendorConfig.Terraform.Workspace)
d.Set("terraform_smart_sanitization", stack.VendorConfig.Terraform.UseSmartSanitization)
d.Set("terraform_external_state_access", stack.VendorConfig.Terraform.ExternalStateAccessEnabled)
d.Set("terraform_workflow_tool", stack.VendorConfig.Terraform.WorkflowTool)
}

if workerPool := stack.WorkerPool; workerPool != nil {
Expand Down
49 changes: 49 additions & 0 deletions spacelift/data_stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,55 @@ func TestStackData(t *testing.T) {
),
}})
})

t.Run("with terraform_workflow_tool defaulted", func(t *testing.T) {
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)

testSteps(t, []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "spacelift_stack" "test" {
branch = "master"
name = "Provider test stack workflow_tool default %s"
project_root = "root"
repository = "demo"
}
data "spacelift_stack" "test" {
stack_id = spacelift_stack.test.id
}
`, randomID),
Check: Resource(
"data.spacelift_stack.test",
Attribute("terraform_workflow_tool", Equals("TERRAFORM_FOSS")),
),
},
})
})

t.Run("with terraform_workflow_tool set", func(t *testing.T) {
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)

testSteps(t, []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "spacelift_stack" "test" {
branch = "master"
name = "Provider test stack workflow_tool default %s"
project_root = "root"
repository = "demo"
terraform_workflow_tool = "CUSTOM"
}
data "spacelift_stack" "test" {
stack_id = spacelift_stack.test.id
}
`, randomID),
Check: Resource(
"data.spacelift_stack.test",
Attribute("terraform_workflow_tool", Equals("CUSTOM")),
),
},
})
})
}

func TestStackDataSpace(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions spacelift/internal/structs/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Module struct {
WorkerPool *struct {
ID string `graphql:"id"`
} `graphql:"workerPool"`
WorkflowTool *string `graphql:"workflowTool"`
}

// ExportVCSSettings exports VCS settings into Terraform schema.
Expand Down
2 changes: 2 additions & 0 deletions spacelift/internal/structs/module_input.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type ModuleCreateInput struct {
Repository graphql.String `json:"repository"`
TerraformProvider *graphql.String `json:"terraformProvider"`
Space *graphql.String `json:"space"`
WorkflowTool *graphql.String `json:"workflowTool"`
}

// ModuleUpdateInput represents the input required to update a Module.
Expand Down Expand Up @@ -43,4 +44,5 @@ type ModuleUpdateV2Input struct {
SharedAccounts *[]graphql.String `json:"sharedAccounts"`
Space *graphql.String `json:"space"`
WorkerPool *graphql.ID `json:"workerPool"`
WorkflowTool *graphql.String `json:"workflowTool"`
}
2 changes: 2 additions & 0 deletions spacelift/internal/structs/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type Stack struct {
Terraform struct {
UseSmartSanitization bool `graphql:"useSmartSanitization"`
Version *string `graphql:"version"`
WorkflowTool *string `graphql:"workflowTool"`
Workspace *string `graphql:"workspace"`
ExternalStateAccessEnabled bool `graphql:"externalStateAccessEnabled"`
} `graphql:"... on StackConfigVendorTerraform"`
Expand Down Expand Up @@ -222,6 +223,7 @@ func PopulateStack(d *schema.ResourceData, stack *Stack) error {
default:
d.Set("terraform_smart_sanitization", stack.VendorConfig.Terraform.UseSmartSanitization)
d.Set("terraform_version", stack.VendorConfig.Terraform.Version)
d.Set("terraform_workflow_tool", stack.VendorConfig.Terraform.WorkflowTool)
d.Set("terraform_workspace", stack.VendorConfig.Terraform.Workspace)
d.Set("terraform_external_state_access", stack.VendorConfig.Terraform.ExternalStateAccessEnabled)
}
Expand Down
1 change: 1 addition & 0 deletions spacelift/internal/structs/stack_input.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type TerragruntInput struct {
type TerraformInput struct {
UseSmartSanitization *graphql.Boolean `json:"useSmartSanitization"`
Version *graphql.String `json:"version"`
WorkflowTool *graphql.String `json:"workflowTool"`
Workspace *graphql.String `json:"workspace"`
ExternalStateAccessEnabled *graphql.Boolean `json:"externalStateAccessEnabled"`
}
18 changes: 18 additions & 0 deletions spacelift/resource_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ func resourceModule() *schema.Resource {
Description: "ID of the worker pool to use. NOTE: worker_pool_id is required when using a self-hosted instance of Spacelift.",
Optional: true,
},
"workflow_tool": {
Type: schema.TypeString,
Description: "Defines the tool that will be used to execute the workflow. This can be one of `OPEN_TOFU`, `TERRAFORM_FOSS` or `CUSTOM`. Defaults to `TERRAFORM_FOSS`.",
Optional: true,
Computed: true,
},
},
}
}
Expand Down Expand Up @@ -281,6 +287,10 @@ func resourceModuleRead(ctx context.Context, d *schema.ResourceData, meta interf
d.Set("worker_pool_id", nil)
}

if workflowTool := module.WorkflowTool; workflowTool != nil {
d.Set("workflow_tool", *workflowTool)
}

return nil
}

Expand Down Expand Up @@ -385,6 +395,10 @@ func moduleCreateInput(d *schema.ResourceData) structs.ModuleCreateInput {
ret.TerraformProvider = toOptionalString(terraformProvider)
}

if workflowTool, ok := d.GetOk("workflow_tool"); ok {
ret.WorkflowTool = toOptionalString(workflowTool)
}

return ret
}

Expand Down Expand Up @@ -475,5 +489,9 @@ func moduleUpdateV2Input(d *schema.ResourceData) structs.ModuleUpdateV2Input {
ret.WorkerPool = graphql.NewID(workerPoolID)
}

if workflowTool, ok := d.GetOk("workflow_tool"); ok {
ret.WorkflowTool = toOptionalString(workflowTool)
}

return ret
}
Loading

0 comments on commit 2b66265

Please sign in to comment.