From 0de03f8b8bdd2a78c6a074119594c24555e77e92 Mon Sep 17 00:00:00 2001 From: Tomer Heber Date: Mon, 8 Jul 2024 07:50:21 -0500 Subject: [PATCH] Feat: add auto approve field to sub environment configuration (#871) * Feat: add auto approve field to sub environment configuration * remove unrelated change * fix test * fix approve_plan issue --- client/environment.go | 1 + client/remote_state_access_test.go | 2 +- client/user_environment_assignment_test.go | 2 +- env0/provider.go | 2 +- env0/resource_environment.go | 35 ++++++++++++++-------- env0/resource_environment_test.go | 7 ++++- env0/resource_project_test.go | 4 +-- env0/resource_variable_set.go | 6 ++-- env0/utils_test.go | 21 +++++++------ tests/integration/012_environment/main.tf | 5 ++-- 10 files changed, 52 insertions(+), 33 deletions(-) diff --git a/client/environment.go b/client/environment.go index 0c64ce84..a619e4f7 100644 --- a/client/environment.go +++ b/client/environment.go @@ -62,6 +62,7 @@ type SubEnvironment struct { Revision string `json:"revision,omitempty"` Workspace string `json:"workspaceName,omitempty"` ConfigurationChanges ConfigurationChanges `json:"configurationChanges"` + UserRequiresApproval bool `json:"userRequiresApproval"` } type DeployRequest struct { diff --git a/client/remote_state_access_test.go b/client/remote_state_access_test.go index fdb68ab3..60d058f4 100644 --- a/client/remote_state_access_test.go +++ b/client/remote_state_access_test.go @@ -8,7 +8,7 @@ import ( ) var _ = Describe("RemoteStateAccess", func() { - environmentId := "environmnet_id" + environmentId := "environment_id" remoteStateAccess := RemoteStateAccessConfiguration{ EnvironmentId: environmentId, diff --git a/client/user_environment_assignment_test.go b/client/user_environment_assignment_test.go index 2cfb3ce4..dad96381 100644 --- a/client/user_environment_assignment_test.go +++ b/client/user_environment_assignment_test.go @@ -9,7 +9,7 @@ import ( "go.uber.org/mock/gomock" ) -var _ = Describe("User Envrionment Assignment", func() { +var _ = Describe("User Environment Assignment", func() { environmentId := "environmentId" userId := "userId" diff --git a/env0/provider.go b/env0/provider.go index 891d3ff6..bd1b47b1 100644 --- a/env0/provider.go +++ b/env0/provider.go @@ -199,7 +199,7 @@ func configureProvider(version string, p *schema.Provider) schema.ConfigureConte AddRetryAfterErrorCondition(). AddRetryCondition(func(r *resty.Response, err error) bool { if r == nil { - // No response. Possiblly a networking issue (E.g. DNS lookup failure). + // No response. Possibly a networking issue (E.g. DNS lookup failure). tflog.SubsystemWarn(subCtx, "env0_api_client", "No response, retrying request") return true } diff --git a/env0/resource_environment.go b/env0/resource_environment.go index c5a84823..1aa9debd 100644 --- a/env0/resource_environment.go +++ b/env0/resource_environment.go @@ -16,12 +16,13 @@ import ( ) type SubEnvironment struct { - Id string - Alias string - Revision string - Workflow string - Workspace string - Configuration client.ConfigurationChanges `tfschema:"-"` + Id string + Alias string + Revision string + Workflow string + Workspace string + Configuration client.ConfigurationChanges `tfschema:"-"` + ApprovePlanAutomatically bool } func getSubEnvironments(d *schema.ResourceData) ([]SubEnvironment, error) { @@ -290,7 +291,7 @@ func resourceEnvironment() *schema.Resource { }, "sub_environment_configuration": { Type: schema.TypeList, - Description: "the subenvironments for a workflow enviornment. Template type must be 'workflow'. Must match the configuration as defined in 'env0.workflow.yml'", + Description: "the subenvironments for a workflow environment. Template type must be 'workflow'. Must match the configuration as defined in 'env0.workflow.yml'", Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -320,6 +321,12 @@ func resourceEnvironment() *schema.Resource { Optional: true, Elem: configurationSchema, }, + "approve_plan_automatically": { + Type: schema.TypeBool, + Description: "when 'true' (default) plans are approved automatically, otherwise ('false') deployment require manual approval", + Optional: true, + Default: true, + }, }, }, }, @@ -408,16 +415,16 @@ func setEnvironmentSchema(ctx context.Context, d *schema.ResourceData, environme var newSubEnvironments []interface{} for i, iSubEnvironment := range iSubEnvironments.([]interface{}) { - subEnviornment := iSubEnvironment.(map[string]interface{}) + subEnvironment := iSubEnvironment.(map[string]interface{}) alias := d.Get(fmt.Sprintf("sub_environment_configuration.%d.alias", i)).(string) workkflowSubEnvironment, ok := environment.LatestDeploymentLog.WorkflowFile.Environments[alias] if ok { - subEnviornment["id"] = workkflowSubEnvironment.EnvironmentId + subEnvironment["id"] = workkflowSubEnvironment.EnvironmentId } - newSubEnvironments = append(newSubEnvironments, subEnviornment) + newSubEnvironments = append(newSubEnvironments, subEnvironment) } d.Set("sub_environment_configuration", newSubEnvironments) @@ -789,6 +796,7 @@ func deploy(d *schema.ResourceData, apiClient client.ApiClientInterface) diag.Di Revision: subEnvironment.Revision, Workspace: subEnvironment.Workspace, ConfigurationChanges: configurationChanges, + UserRequiresApproval: !subEnvironment.ApprovePlanAutomatically, } } } @@ -952,6 +960,7 @@ func getCreatePayload(d *schema.ResourceData, apiClient client.ApiClientInterfac Revision: subEnvironment.Revision, ConfigurationChanges: subEnvironment.Configuration, Workspace: subEnvironment.Workspace, + UserRequiresApproval: !subEnvironment.ApprovePlanAutomatically, } } } @@ -1111,9 +1120,9 @@ func getDeployPayload(d *schema.ResourceData, apiClient client.ApiClientInterfac } } - if userRequiresApproval, ok := d.GetOk("requires_approval"); ok { - userRequiresApproval := userRequiresApproval.(bool) - payload.UserRequiresApproval = &userRequiresApproval + //lint:ignore SA1019 reason: https://github.com/hashicorp/terraform-plugin-sdk/issues/817 + if val, exists := d.GetOkExists("approve_plan_automatically"); exists { + payload.UserRequiresApproval = boolPtr(!val.(bool)) } return payload, nil diff --git a/env0/resource_environment_test.go b/env0/resource_environment_test.go index 3617b66c..21e131ef 100644 --- a/env0/resource_environment_test.go +++ b/env0/resource_environment_test.go @@ -520,7 +520,8 @@ func TestUnitEnvironmentResource(t *testing.T) { ProjectId: environment.ProjectId, DeployRequest: &client.DeployRequest{ - BlueprintId: templateId, + BlueprintId: templateId, + UserRequiresApproval: boolPtr(false), }, IsRemoteBackend: environment.IsRemoteBackend, RequiresApproval: environment.RequiresApproval, @@ -2554,6 +2555,7 @@ func TestUnitEnvironmentWithSubEnvironment(t *testing.T) { } updatedSubEnvironment := subEnvironment + updatedSubEnvironment.ApprovePlanAutomatically = false updatedSubEnvironment.Configuration = append(updatedSubEnvironment.Configuration, client.ConfigurationVariable{ Name: "name2", Value: "value2", @@ -2638,6 +2640,7 @@ func TestUnitEnvironmentWithSubEnvironment(t *testing.T) { Revision: subEnvironment.Revision, Workspace: updatedSubEnvironment.Workspace, ConfigurationChanges: updatedSubEnvironment.Configuration, + UserRequiresApproval: true, }, }, } @@ -2700,6 +2703,7 @@ func TestUnitEnvironmentWithSubEnvironment(t *testing.T) { alias = "%s" revision = "%s" workspace = "%s" + approve_plan_automatically = false configuration { name = "%s" value = "%s" @@ -2731,6 +2735,7 @@ func TestUnitEnvironmentWithSubEnvironment(t *testing.T) { resource.TestCheckResourceAttr(accessor, "sub_environment_configuration.0.alias", updatedSubEnvironment.Alias), resource.TestCheckResourceAttr(accessor, "sub_environment_configuration.0.revision", updatedSubEnvironment.Revision), resource.TestCheckResourceAttr(accessor, "sub_environment_configuration.0.workspace", updatedSubEnvironment.Workspace), + resource.TestCheckResourceAttr(accessor, "sub_environment_configuration.0.approve_plan_automatically", "false"), resource.TestCheckResourceAttr(accessor, "sub_environment_configuration.0.configuration.0.name", updatedSubEnvironment.Configuration[0].Name), resource.TestCheckResourceAttr(accessor, "sub_environment_configuration.0.configuration.0.value", updatedSubEnvironment.Configuration[0].Value), resource.TestCheckResourceAttr(accessor, "sub_environment_configuration.0.configuration.1.name", updatedSubEnvironment.Configuration[1].Name), diff --git a/env0/resource_project_test.go b/env0/resource_project_test.go index 6411495a..a8e503ce 100644 --- a/env0/resource_project_test.go +++ b/env0/resource_project_test.go @@ -170,7 +170,7 @@ func TestUnitProjectResourceDestroyWithEnvironments(t *testing.T) { Name: "name1", } - t.Run("Success With Force Destory", func(t *testing.T) { + t.Run("Success With Force Destroy", func(t *testing.T) { testCase := resource.TestCase{ Steps: []resource.TestStep{ { @@ -199,7 +199,7 @@ func TestUnitProjectResourceDestroyWithEnvironments(t *testing.T) { }) }) - t.Run("Failure Without Force Destory", func(t *testing.T) { + t.Run("Failure Without Force Destroy", func(t *testing.T) { testCase := resource.TestCase{ Steps: []resource.TestStep{ { diff --git a/env0/resource_variable_set.go b/env0/resource_variable_set.go index f706ffe7..9f8722a4 100644 --- a/env0/resource_variable_set.go +++ b/env0/resource_variable_set.go @@ -39,7 +39,7 @@ var variableSetVariableSchema *schema.Resource = &schema.Resource{ }, "is_sensitive": { Type: schema.TypeBool, - Description: "is the value sensitive (defaults to 'false'). Note: 'dropdown' value format cannot be senstive.", + Description: "is the value sensitive (defaults to 'false'). Note: 'dropdown' value format cannot be sensitive.", Optional: true, Default: false, }, @@ -293,7 +293,7 @@ type mergedVariables struct { func mergeVariables(schema []client.ConfigurationVariable, api []client.ConfigurationVariable) *mergedVariables { var res mergedVariables - // To avoid false drifts, keep the order of the 'currentVariables' list similiar to the schema as much as possible. + // To avoid false drifts, keep the order of the 'currentVariables' list similar to the schema as much as possible. for _, svariable := range schema { found := false @@ -301,7 +301,7 @@ func mergeVariables(schema []client.ConfigurationVariable, api []client.Configur if svariable.Name == avariable.Name && *svariable.Type == *avariable.Type { found = true if avariable.IsSensitive != nil && *avariable.IsSensitive { - // Senstive - to avoid drift use the value from the schema + // Sensitive - to avoid drift use the value from the schema avariable.Value = svariable.Value } res.currentVariables = append(res.currentVariables, avariable) diff --git a/env0/utils_test.go b/env0/utils_test.go index c76f0051..b0ce4be9 100644 --- a/env0/utils_test.go +++ b/env0/utils_test.go @@ -343,14 +343,16 @@ func TestWriteResourceDataOmitEmpty(t *testing.T) { func TestReadSubEnvironment(t *testing.T) { expectedSubEnvironments := []SubEnvironment{ { - Id: "id1", - Alias: "alias1", - Revision: "revision1", + Id: "id1", + Alias: "alias1", + Revision: "revision1", + ApprovePlanAutomatically: true, }, { - Id: "id2", - Alias: "alias2", - Revision: "revision2", + Id: "id2", + Alias: "alias2", + Revision: "revision2", + ApprovePlanAutomatically: false, Configuration: client.ConfigurationChanges{ { Name: "name1", @@ -388,9 +390,10 @@ func TestReadSubEnvironment(t *testing.T) { "revision": expectedSubEnvironments[0].Revision, }, map[string]interface{}{ - "id": expectedSubEnvironments[1].Id, - "alias": expectedSubEnvironments[1].Alias, - "revision": expectedSubEnvironments[1].Revision, + "id": expectedSubEnvironments[1].Id, + "alias": expectedSubEnvironments[1].Alias, + "revision": expectedSubEnvironments[1].Revision, + "approve_plan_automatically": expectedSubEnvironments[1].ApprovePlanAutomatically, "configuration": []interface{}{ map[string]interface{}{ "name": expectedSubEnvironments[1].Configuration[0].Name, diff --git a/tests/integration/012_environment/main.tf b/tests/integration/012_environment/main.tf index 8e185e7d..347ba8d3 100644 --- a/tests/integration/012_environment/main.tf +++ b/tests/integration/012_environment/main.tf @@ -256,8 +256,9 @@ resource "env0_environment" "workflow-environment" { variable_sets = var.second_run ? [env0_variable_set.variable_set2.id] : [env0_variable_set.variable_set1.id] sub_environment_configuration { - alias = "rootService1" - revision = "master" + alias = "rootService1" + revision = "master" + approve_plan_automatically = var.second_run ? true : false configuration { name = "sub_env1_var1" value = "hello"