Skip to content

Commit

Permalink
Feat: add opentofu support (#718)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomerHeber committed Oct 15, 2023
1 parent f1fb29d commit ea8518f
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 2 deletions.
9 changes: 9 additions & 0 deletions client/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type Template struct {
UpdatedAt string `json:"updatedAt"`
TerraformVersion string `json:"terraformVersion" tfschema:",omitempty"`
TerragruntVersion string `json:"terragruntVersion,omitempty" tfschema:",omitempty"`
OpentofuVersion string `json:"opentofuVersion,omitempty" tfschema:",omitempty"`
IsDeleted bool `json:"isDeleted,omitempty"`
BitbucketClientKey string `json:"bitbucketClientKey" tfschema:",omitempty"`
IsGithubEnterprise bool `json:"isGitHubEnterprise"`
Expand Down Expand Up @@ -80,6 +81,7 @@ type TemplateCreatePayload struct {
OrganizationId string `json:"organizationId"`
TerraformVersion string `json:"terraformVersion,omitempty"`
TerragruntVersion string `json:"terragruntVersion,omitempty"`
OpentofuVersion string `json:"opentofuVersion,omitempty"`
IsGitlabEnterprise bool `json:"isGitLabEnterprise"`
BitbucketClientKey string `json:"bitbucketClientKey,omitempty"`
IsGithubEnterprise bool `json:"isGitHubEnterprise"`
Expand Down Expand Up @@ -122,6 +124,9 @@ func (payload *TemplateCreatePayload) Invalidate() error {
if payload.Type == "terragrunt" && payload.TerragruntVersion == "" {
return errors.New("must supply terragrunt version")
}
if payload.Type == "opentofu" && payload.OpentofuVersion == "" {
return errors.New("must supply opentofu version")
}

if payload.IsTerragruntRunAll {
if payload.Type != "terragrunt" {
Expand Down Expand Up @@ -163,6 +168,10 @@ func (payload *TemplateCreatePayload) Invalidate() error {
payload.TerraformVersion = ""
}

if payload.Type != "opentofu" {
payload.OpentofuVersion = ""
}

return nil
}

Expand Down
7 changes: 7 additions & 0 deletions env0/resource_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var allowedTemplateTypes = []string{
"workflow",
"cloudformation",
"helm",
"opentofu",
}

func getTemplateSchema(prefix string) map[string]*schema.Schema {
Expand Down Expand Up @@ -172,6 +173,12 @@ func getTemplateSchema(prefix string) map[string]*schema.Schema {
ValidateDiagFunc: NewRegexValidator(`^[0-9]\.[0-9]{1,2}\.[0-9]{1,2}$`),
Optional: true,
},
"opentofu_version": {
Type: schema.TypeString,
Description: "the Opentofu version to use (example: 0.36.5)",
ValidateDiagFunc: NewRegexValidator(`^(?:[0-9]\.[0-9]{1,2}\.[0-9]{1,2})|1\.6\.0-alpha$`),
Optional: true,
},
"is_gitlab_enterprise": {
Type: schema.TypeBool,
Description: "true if this template uses gitlab enterprise repository",
Expand Down
94 changes: 92 additions & 2 deletions env0/resource_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,45 @@ func TestUnitTemplateResource(t *testing.T) {
TerraformVersion: "0.12.24",
}

opentofuTemplate := client.Template{
Id: "opentofu",
Name: "template0",
Description: "description0",
Repository: "env0/repo",
Type: "opentofu",
OpentofuVersion: "1.6.0-alpha",
TerraformVersion: "0.15.1",
Retry: client.TemplateRetry{
OnDeploy: &client.TemplateRetryOn{
Times: 2,
ErrorRegex: "RetryMeForDeploy.*",
},
OnDestroy: &client.TemplateRetryOn{
Times: 1,
ErrorRegex: "RetryMeForDestroy.*",
},
},
}
opentofuUpdatedTemplate := client.Template{
Id: opentofuTemplate.Id,
Name: "new-name",
Description: "new-description",
Repository: "env0/repo-new",
Type: "opentofu",
OpentofuVersion: "1.7.0",
TerraformVersion: "0.15.1",
Retry: client.TemplateRetry{
OnDeploy: &client.TemplateRetryOn{
Times: 2,
ErrorRegex: "RetryMeForDeploy.*",
},
OnDestroy: &client.TemplateRetryOn{
Times: 1,
ErrorRegex: "RetryMeForDestroy.*",
},
},
}

fullTemplateResourceConfig := func(resourceType string, resourceName string, template client.Template) string {
templateAsDictionary := map[string]interface{}{
"name": template.Name,
Expand Down Expand Up @@ -440,6 +479,9 @@ func TestUnitTemplateResource(t *testing.T) {
if template.TerraformVersion != "" {
templateAsDictionary["terraform_version"] = template.TerraformVersion
}
if template.OpentofuVersion != "" {
templateAsDictionary["opentofu_version"] = template.OpentofuVersion
}
if template.TokenId != "" {
templateAsDictionary["token_id"] = template.TokenId
}
Expand Down Expand Up @@ -516,6 +558,11 @@ func TestUnitTemplateResource(t *testing.T) {
terragruntVersionAssertion = resource.TestCheckNoResourceAttr(resourceFullName, "terragrunt_version")
}

opentofuVersionAssertion := resource.TestCheckResourceAttr(resourceFullName, "opentofu_version", template.OpentofuVersion)
if template.OpentofuVersion == "" {
opentofuVersionAssertion = resource.TestCheckNoResourceAttr(resourceFullName, "opentofu_version")
}

githubInstallationIdAssertion := resource.TestCheckResourceAttr(resourceFullName, "github_installation_id", strconv.Itoa(template.GithubInstallationId))
if template.GithubInstallationId == 0 {
githubInstallationIdAssertion = resource.TestCheckNoResourceAttr(resourceFullName, "github_installation_id")
Expand Down Expand Up @@ -544,6 +591,7 @@ func TestUnitTemplateResource(t *testing.T) {
githubInstallationIdAssertion,
helmChartNameAssertion,
pathAssertion,
opentofuVersionAssertion,
resource.TestCheckResourceAttr(resourceFullName, "terraform_version", template.TerraformVersion),
resource.TestCheckResourceAttr(resourceFullName, "is_terragrunt_run_all", strconv.FormatBool(template.IsTerragruntRunAll)),
resource.TestCheckResourceAttr(resourceFullName, "is_azure_devops", strconv.FormatBool(template.IsAzureDevOps)),
Expand All @@ -565,6 +613,7 @@ func TestUnitTemplateResource(t *testing.T) {
{"Cloudformation", cloudformationTemplate, cloudformationUpdatedTemplate},
{"Azure DevOps", azureDevOpsTemplate, azureDevOpsUpdatedTemplate},
{"Helm Chart", helmTemplate, helmUpdatedTemplate},
{"Opentofu", opentofuTemplate, opentofuUpdatedTemplate},
}
for _, templateUseCase := range templateUseCases {
t.Run("Full "+templateUseCase.vcs+" template (without SSH keys)", func(t *testing.T) {
Expand Down Expand Up @@ -599,6 +648,7 @@ func TestUnitTemplateResource(t *testing.T) {
IsAzureDevOps: templateUseCase.template.IsAzureDevOps,
IsHelmRepository: templateUseCase.template.IsHelmRepository,
HelmChartName: templateUseCase.template.HelmChartName,
OpentofuVersion: templateUseCase.template.OpentofuVersion,
}

updateTemplateCreateTemplate := client.TemplateCreatePayload{
Expand All @@ -622,8 +672,9 @@ func TestUnitTemplateResource(t *testing.T) {
TerragruntVersion: templateUseCase.updatedTemplate.TerragruntVersion,
IsTerragruntRunAll: templateUseCase.updatedTemplate.IsTerragruntRunAll,
IsAzureDevOps: templateUseCase.updatedTemplate.IsAzureDevOps,
IsHelmRepository: templateUseCase.template.IsHelmRepository,
HelmChartName: templateUseCase.template.HelmChartName,
IsHelmRepository: templateUseCase.updatedTemplate.IsHelmRepository,
HelmChartName: templateUseCase.updatedTemplate.HelmChartName,
OpentofuVersion: templateUseCase.updatedTemplate.OpentofuVersion,
}

if templateUseCase.template.Type != "terraform" && templateUseCase.template.Type != "terragrunt" {
Expand Down Expand Up @@ -1110,6 +1161,45 @@ func TestUnitTemplateResource(t *testing.T) {
runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) {})
})

t.Run("Invalid Opentofu Version", func(t *testing.T) {
testCase := resource.TestCase{
Steps: []resource.TestStep{
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"name": "template0",
"repository": "env0/repo",
"type": "opentofu",
"gitlab_project_id": 123456,
"token_id": "abcdefg",
"opentofu_version": "v0.20.1",
}),
ExpectError: regexp.MustCompile("must match pattern"),
},
},
}

runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) {})
})

t.Run("Opentofu type with no Opentofu version", func(t *testing.T) {
testCase := resource.TestCase{
Steps: []resource.TestStep{
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"name": "template0",
"repository": "env0/repo",
"type": "opentofu",
"gitlab_project_id": 123456,
"token_id": "abcdefg",
}),
ExpectError: regexp.MustCompile("must supply opentofu version"),
},
},
}

runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) {})
})

t.Run("Cloudformation type with no file_name", func(t *testing.T) {
testCase := resource.TestCase{
Steps: []resource.TestStep{
Expand Down
13 changes: 13 additions & 0 deletions tests/integration/004_template/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ resource "env0_template" "template_tg" {
terragrunt_version = "0.35.0"
}

resource "env0_template" "template_opentofu" {
name = "Opentofu-${random_string.random.result}"
description = "Template description - OpenTofu and GitHub"
type = "opentofu"
repository = data.env0_template.github_template.repository
github_installation_id = data.env0_template.github_template.github_installation_id
path = "/misc/null-resource"
retries_on_deploy = 3
retry_on_deploy_only_when_matches_regex = "abc"
retries_on_destroy = 1
opentofu_version = "1.6.0"
}

resource "env0_configuration_variable" "in_a_template" {
name = "fake_key"
value = "fake value"
Expand Down

0 comments on commit ea8518f

Please sign in to comment.