Skip to content
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

Feat: add opentofu support #718

Merged
merged 4 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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$`),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job in including the alpha, could have been easily missed 😅

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
Loading