From 26b675288ba8a64da7c3b034ed65925ee81c9ffd Mon Sep 17 00:00:00 2001 From: Tomer Heber Date: Wed, 5 Jun 2024 11:38:17 -0500 Subject: [PATCH 1/3] Feat: importing templateless environment causes the configuration to be unplannable --- client/environment.go | 2 ++ env0/resource_environment.go | 19 +++++++++++- env0/resource_environment_test.go | 48 ++++++++++++++++++++++++++++++- env0/resource_template.go | 19 ++++++------ 4 files changed, 78 insertions(+), 10 deletions(-) diff --git a/client/environment.go b/client/environment.go index 0f55cf57..5a1dbe3a 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 { @@ -120,6 +121,7 @@ type Environment struct { IsArchived *bool `json:"isArchived" tfschema:"-"` IsRemoteApplyEnabled bool `json:"isRemoteApplyEnabled"` K8sNamespace string `json:"k8s_namespace"` + IsSingleUseBlueprint bool `json:"isSingleUseBlueprint" tfschema:"-"` } type EnvironmentCreate struct { diff --git a/env0/resource_environment.go b/env0/resource_environment.go index 63efa627..914375f1 100644 --- a/env0/resource_environment.go +++ b/env0/resource_environment.go @@ -623,7 +623,7 @@ func resourceEnvironmentRead(ctx context.Context, d *schema.ResourceData, meta i return diag.Errorf("could not get template: %v", err) } - if err := templateRead("without_template_settings", template, d); err != nil { + if err := templateRead("without_template_settings", template, d, false); err != nil { return diag.Errorf("schema resource data serialization failed: %v", err) } } @@ -1189,6 +1189,23 @@ func resourceEnvironmentImporter(ctx context.Context, d *schema.ResourceData, me } d.Set("deployment_id", environment.LatestDeploymentLogId) + + if environment.IsSingleUseBlueprint { + templateId := environment.BlueprintId + if templateId == "" { + templateId = environment.LatestDeploymentLog.BlueprintId + } + + template, err := apiClient.Template(templateId) + if err != nil { + return nil, fmt.Errorf("failed to get template with id %s: %w", templateId, err) + } + + if err := templateRead("without_template_settings", template, d, true); err != nil { + return nil, fmt.Errorf("failed to write template to schema: %w", err) + } + } + setEnvironmentSchema(ctx, d, environment, environmentConfigurationVariables) if environment.IsRemoteBackend != nil { diff --git a/env0/resource_environment_test.go b/env0/resource_environment_test.go index d25ddfda..51989857 100644 --- a/env0/resource_environment_test.go +++ b/env0/resource_environment_test.go @@ -1959,9 +1959,10 @@ func TestUnitEnvironmentWithoutTemplateResource(t *testing.T) { resourceType := "env0_environment" resourceName := "test" accessor := resourceAccessor(resourceType, resourceName) + resourceNameImport := resourceType + "." + resourceName environment := client.Environment{ - Id: "id0", + Id: uuid.New().String(), Name: "my-environment", ProjectId: "project-id", WorkspaceName: "workspace-name", @@ -1970,6 +1971,7 @@ func TestUnitEnvironmentWithoutTemplateResource(t *testing.T) { LatestDeploymentLog: client.DeploymentLog{ BlueprintId: "id-template-0", }, + IsSingleUseBlueprint: true, } environmentWithBluePrint := environment @@ -2262,6 +2264,50 @@ func TestUnitEnvironmentWithoutTemplateResource(t *testing.T) { mock.EXPECT().EnvironmentDestroy(environment.Id).Times(1), ) }) + + }) + + t.Run("Import By Id", func(t *testing.T) { + testCase := resource.TestCase{ + Steps: []resource.TestStep{ + { + Config: createEnvironmentResourceConfig(environment, template), + }, + { + ResourceName: resourceNameImport, + ImportState: true, + ImportStateId: environment.Id, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, + }, + } + + runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { + + gomock.InOrder( + // Create + mock.EXPECT().EnvironmentCreateWithoutTemplate(createPayload).Times(1).Return(environmentWithBluePrint, nil), + + // Read + mock.EXPECT().Environment(environment.Id).Times(1).Return(environment, nil), + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, environment.Id).Times(1).Return(client.ConfigurationChanges{}, nil), + mock.EXPECT().Template(template.Id).Times(1).Return(template, nil), + + // Import + mock.EXPECT().Environment(environment.Id).Times(1).Return(environment, nil), + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, environment.Id).Times(1).Return(client.ConfigurationChanges{}, nil), + mock.EXPECT().Template(template.Id).Times(1).Return(template, nil), + + // Read + mock.EXPECT().Environment(environment.Id).Times(1).Return(environment, nil), + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, environment.Id).Times(1).Return(client.ConfigurationChanges{}, nil), + mock.EXPECT().Template(template.Id).Times(1).Return(template, nil), + + // Destroy + mock.EXPECT().EnvironmentDestroy(environment.Id).Times(1), + ) + }) }) t.Run("Revision conflict", func(t *testing.T) { diff --git a/env0/resource_template.go b/env0/resource_template.go index 6b3bdd04..52eb2338 100644 --- a/env0/resource_template.go +++ b/env0/resource_template.go @@ -301,7 +301,7 @@ func resourceTemplateRead(ctx context.Context, d *schema.ResourceData, meta inte return nil } - if err := templateRead("", template, d); err != nil { + if err := templateRead("", template, d, false); err != nil { return diag.Errorf("%v", err) } @@ -422,7 +422,7 @@ func templateCreatePayloadFromParameters(prefix string, d *schema.ResourceData) } // Reads template and writes to the resource data. -func templateRead(prefix string, template client.Template, d *schema.ResourceData) error { +func templateRead(prefix string, template client.Template, d *schema.ResourceData, isImport bool) error { pathPrefix := "path" terragruntTfBinaryPrefix := "terragrunt_tf_binary" terraformVersionPrefix := "terraform_version" @@ -440,12 +440,15 @@ func templateRead(prefix string, template client.Template, d *schema.ResourceDat // If this value isn't set, ignore whatever is returned from the response. // This helps avoid drifts when defaulting to 'opentofu' for new 'terragrunt' templates, and 'terraform' for existing 'terragrunt' templates. // 'template.TerragruntTfBinary' field is set to 'omitempty'. Therefore, the state isn't modified if `template.TerragruntTfBinary` is an empty string. - if terragruntTfBinary == "" { - template.TerragruntTfBinary = "" - } - // Same explanation as above. - if terraformVersion == "" { - template.TerraformVersion = "" + // This is not true for imports - because the shcema is empty irrespective in that case. + if !isImport { + if terragruntTfBinary == "" { + template.TerragruntTfBinary = "" + } + // Same explanation as above. + if terraformVersion == "" { + template.TerraformVersion = "" + } } if err := writeResourceDataEx(prefix, &template, d); err != nil { From a7527f3913cb37652e7d3087909feb9c2df9f699 Mon Sep 17 00:00:00 2001 From: Tomer Heber Date: Wed, 5 Jun 2024 11:42:49 -0500 Subject: [PATCH 2/3] update comments --- env0/resource_environment.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/env0/resource_environment.go b/env0/resource_environment.go index 914375f1..5b34f053 100644 --- a/env0/resource_environment.go +++ b/env0/resource_environment.go @@ -279,7 +279,7 @@ func resourceEnvironment() *schema.Resource { }, "without_template_settings": { Type: schema.TypeList, - Description: "settings for creating an environment without a template. Is not imported when running the import command", + Description: "settings for creating an environment without a template", Optional: true, MinItems: 1, MaxItems: 1, From 2609813cf84d3937e8bef5ecafa11973811d591f Mon Sep 17 00:00:00 2001 From: Tomer Heber Date: Wed, 5 Jun 2024 11:45:36 -0500 Subject: [PATCH 3/3] remove mixup --- client/environment.go | 1 - 1 file changed, 1 deletion(-) diff --git a/client/environment.go b/client/environment.go index 5a1dbe3a..9346c5dd 100644 --- a/client/environment.go +++ b/client/environment.go @@ -62,7 +62,6 @@ type SubEnvironment struct { Revision string `json:"revision,omitempty"` Workspace string `json:"workspaceName,omitempty"` ConfigurationChanges ConfigurationChanges `json:"configurationChanges"` - UserRequiresApproval bool `json:"userRequiresApproval"` } type DeployRequest struct {