From d93d1f0e3549e8e4bd7eee1f107681512452daa9 Mon Sep 17 00:00:00 2001 From: Tomer Heber Date: Wed, 7 Aug 2024 11:40:16 -0500 Subject: [PATCH] Fix: using git token_id on Templates always assumes GitLab (#929) --- .golangci.yaml | 31 +++++++++ client/template.go | 4 +- env0/cloud_configuration.go | 12 ++-- env0/credentials.go | 11 +++- env0/data_agent_values.go | 4 +- env0/data_api_key.go | 3 + env0/data_cloud_credentials.go | 4 +- env0/data_configuration_variable.go | 14 ++-- env0/data_credentials.go | 2 + env0/data_custom_roles.go | 4 +- env0/data_ip_ranges.go | 4 +- env0/data_notifications.go | 4 +- env0/data_organization.go | 4 +- env0/data_project.go | 13 ++-- env0/data_project_cloud_credentials.go | 1 + env0/resource_approval_policy_test.go | 5 +- env0/resource_configuration_variable_test.go | 4 -- env0/resource_environment.go | 10 +-- env0/resource_environment_test.go | 4 +- env0/resource_team_organization_assignment.go | 4 +- env0/resource_template.go | 17 ++--- env0/resource_template_test.go | 9 ++- env0/resource_variable_set.go | 4 +- env0/resource_variable_set_assignment.go | 4 +- env0/resource_workflow_triggers.go | 1 + env0/utils.go | 12 +++- env0/utils_test.go | 66 ++++++++++--------- 27 files changed, 147 insertions(+), 108 deletions(-) create mode 100644 .golangci.yaml diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 00000000..25cada2c --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,31 @@ +linters: + enable: + - errname + - errorlint + - gocheckcompilerdirectives + - gochecknoglobals + - gochecknoinits + - goconst + - gocritic + - misspell + - nilerr + - nilnil + - nlreturn + - perfsprint + - prealloc + - predeclared + - reassign + - sloglint + - spancheck + - testifylint + - unparam + - unused + - usestdlibvars + - wsl + +linters-settings: + errorlint: + asserts: false + errcheck: + exclude-functions: + - (*github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema.ResourceData).Set diff --git a/client/template.go b/client/template.go index ec1a3ae1..e6fcffdf 100644 --- a/client/template.go +++ b/client/template.go @@ -61,7 +61,7 @@ type Template struct { IsAzureDevOps bool `json:"isAzureDevOps" tfschema:"is_azure_devops"` IsHelmRepository bool `json:"isHelmRepository"` HelmChartName string `json:"helmChartName" tfschema:",omitempty"` - IsGitLab bool `json:"isGitLab" tfschema:"is_gitlab"` + IsGitlab bool `json:"isGitLab"` TerragruntTfBinary string `json:"terragruntTfBinary" tfschema:",omitempty"` TokenName string `json:"tokenName" tfschema:",omitempty"` GitlabProjectId int `json:"gitlabProjectId" tfschema:",omitempty"` @@ -75,7 +75,7 @@ type TemplateCreatePayload struct { Name string `json:"name"` Repository string `json:"repository"` Path string `json:"path,omitempty"` - IsGitLab bool `json:"isGitLab"` + IsGitlab bool `json:"isGitLab"` TokenName string `json:"tokenName,omitempty"` TokenId string `json:"tokenId,omitempty"` GithubInstallationId int `json:"githubInstallationId,omitempty"` diff --git a/env0/cloud_configuration.go b/env0/cloud_configuration.go index 18ed5d23..62edc595 100644 --- a/env0/cloud_configuration.go +++ b/env0/cloud_configuration.go @@ -24,7 +24,7 @@ func getCloudConfigurationFromSchema(d *schema.ResourceData, provider string) (i } if err := readResourceData(configuration, d); err != nil { - return nil, fmt.Errorf("schema resource data deserialization failed: %v", err) + return nil, fmt.Errorf("schema resource data deserialization failed: %w", err) } return configuration, nil @@ -47,6 +47,7 @@ func getCloudConfigurationByNameFromApi(apiClient client.ApiClientInterface, nam func getCloudConfigurationFromApi(apiClient client.ApiClientInterface, id string) (*client.CloudAccount, error) { var err error + var cloudAccount *client.CloudAccount if _, parseErr := uuid.Parse(id); parseErr != nil { @@ -108,9 +109,8 @@ func createCloudConfiguration(d *schema.ResourceData, meta interface{}, provider return diag.Errorf("failed to create a cloud configuration: %v", err) } - if err := d.Set("health", cloudAccount.Health); err != nil { - return diag.Errorf("failed to set health: %v", err) - } + d.Set("health", cloudAccount.Health) + d.SetId(cloudAccount.Id) return nil @@ -156,9 +156,7 @@ func updateCloudConfiguration(d *schema.ResourceData, meta interface{}, provider return diag.Errorf("failed to update cloud configuration: %v", err) } - if err := d.Set("health", cloudAccount.Health); err != nil { - return diag.Errorf("failed to set health: %v", err) - } + d.Set("health", cloudAccount.Health) return nil } diff --git a/env0/credentials.go b/env0/credentials.go index d303b4fe..abcd3f83 100644 --- a/env0/credentials.go +++ b/env0/credentials.go @@ -57,6 +57,7 @@ func getCredentialsByName(name string, prefixList []string, meta interface{}) (c } var foundCredentials []client.Credentials + for _, credentials := range credentialsList { if credentials.Name == name && credentials.HasPrefix(prefixList) { foundCredentials = append(foundCredentials, credentials) @@ -76,11 +77,13 @@ func getCredentialsByName(name string, prefixList []string, meta interface{}) (c func getCredentialsById(id string, prefixList []string, meta interface{}) (client.Credentials, error) { apiClient := meta.(client.ApiClientInterface) + credentials, err := apiClient.CloudCredentials(id) if err != nil { if _, ok := err.(*client.NotFoundError); ok { return client.Credentials{}, errors.New("credentials not found") } + return client.Credentials{}, err } @@ -93,11 +96,14 @@ func getCredentialsById(id string, prefixList []string, meta interface{}) (clien func getCredentials(ctx context.Context, id string, prefixList []string, meta interface{}) (client.Credentials, error) { _, err := uuid.Parse(id) + if err == nil { tflog.Info(ctx, "Resolving credentials by id", map[string]interface{}{"id": id}) + return getCredentialsById(id, prefixList, meta) } else { tflog.Info(ctx, "Resolving credentials by name", map[string]interface{}{"name": id}) + return getCredentialsByName(id, prefixList, meta) } } @@ -106,10 +112,12 @@ func resourceCredentialsDelete(ctx context.Context, d *schema.ResourceData, meta apiClient := meta.(client.ApiClientInterface) id := d.Id() + err := apiClient.CloudCredentialsDelete(id) if err != nil { return diag.Errorf("could not delete credentials: %v", err) } + return nil } @@ -137,11 +145,12 @@ func resourceCredentialsImport(cloudType CloudType) schema.StateContextFunc { if _, ok := err.(*client.NotFoundError); ok { return nil, fmt.Errorf(string(cloudType)+" credentials resource with id %v not found", d.Id()) } + return nil, err } if err := writeResourceData(&credentials, d); err != nil { - return nil, fmt.Errorf("schema resource data serialization failed: %v", err) + return nil, fmt.Errorf("schema resource data serialization failed: %w", err) } return []*schema.ResourceData{d}, nil diff --git a/env0/data_agent_values.go b/env0/data_agent_values.go index 248eb2c9..cfbcac36 100644 --- a/env0/data_agent_values.go +++ b/env0/data_agent_values.go @@ -37,9 +37,7 @@ func dataAgentValuesRead(ctx context.Context, d *schema.ResourceData, meta inter return diag.Errorf("could not get agent values: %v", err) } - if err := d.Set("values", values); err != nil { - return diag.FromErr(err) - } + d.Set("values", values) d.SetId(agentKey) diff --git a/env0/data_api_key.go b/env0/data_api_key.go index 20897196..bec38965 100644 --- a/env0/data_api_key.go +++ b/env0/data_api_key.go @@ -36,14 +36,17 @@ func dataApiKey() *schema.Resource { func dataApiKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var apiKey *client.ApiKey + var err error id, ok := d.GetOk("id") if ok { apiKey, err = getApiKeyById(id.(string), meta) + if err != nil { return diag.Errorf("could not read api key: %v", err) } + if apiKey == nil { return diag.Errorf("could not read api key: id %v not found", id) } diff --git a/env0/data_cloud_credentials.go b/env0/data_cloud_credentials.go index 4e97ea01..cbe6ce9e 100644 --- a/env0/data_cloud_credentials.go +++ b/env0/data_cloud_credentials.go @@ -65,9 +65,7 @@ func dataCloudCredentialsRead(ctx context.Context, d *schema.ResourceData, meta data = append(data, credentials.Name) } - if err := d.Set("names", data); err != nil { - return diag.FromErr(err) - } + d.Set("names", data) // Not really needed. But required by Terraform SDK - https://github.com/hashicorp/terraform-plugin-sdk/issues/541 d.SetId("all_cloud_credential_names") diff --git a/env0/data_configuration_variable.go b/env0/data_configuration_variable.go index e0f79a12..b3383104 100644 --- a/env0/data_configuration_variable.go +++ b/env0/data_configuration_variable.go @@ -136,13 +136,10 @@ func dataConfigurationVariableRead(ctx context.Context, d *schema.ResourceData, return diag.Errorf("schema resource data serialization failed: %v", err) } - if err := d.Set("enum", variable.Schema.Enum); err != nil { - return diag.FromErr(err) - } + d.Set("enum", variable.Schema.Enum) + if variable.Schema.Format != client.Text { - if err := d.Set("format", string(variable.Schema.Format)); err != nil { - return diag.FromErr(err) - } + d.Set("format", string(variable.Schema.Format)) } return nil @@ -151,22 +148,27 @@ func dataConfigurationVariableRead(ctx context.Context, d *schema.ResourceData, func getScopeAndId(d *schema.ResourceData) (client.Scope, string) { scope := client.ScopeGlobal scopeId := "" + if projectId, ok := d.GetOk("project_id"); ok { scope = client.ScopeProject scopeId = projectId.(string) } + if templateId, ok := d.GetOk("template_id"); ok { scope = client.ScopeTemplate scopeId = templateId.(string) } + if environmentId, ok := d.GetOk("environment_id"); ok { scope = client.ScopeEnvironment scopeId = environmentId.(string) } + if deploymentLogId, ok := d.GetOk("deployment_log_id"); ok { scope = client.ScopeDeploymentLog scopeId = deploymentLogId.(string) } + return scope, scopeId } diff --git a/env0/data_credentials.go b/env0/data_credentials.go index 849b871c..911f2003 100644 --- a/env0/data_credentials.go +++ b/env0/data_credentials.go @@ -32,6 +32,7 @@ func dataCredentials(cloudType CloudType) *schema.Resource { func dataCredentialsRead(cloudType CloudType) schema.ReadContextFunc { return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var err error + var credentials client.Credentials prefixList := credentialsTypeToPrefixList[cloudType] @@ -47,6 +48,7 @@ func dataCredentialsRead(cloudType CloudType) schema.ReadContextFunc { if !ok { return diag.Errorf("either 'name' or 'id' must be specified") } + credentials, err = getCredentialsByName(name.(string), prefixList, meta) if err != nil { return diag.Errorf("could not query %s credentials by name: %v", cloudType, err) diff --git a/env0/data_custom_roles.go b/env0/data_custom_roles.go index e7f1b739..3d35a9f9 100644 --- a/env0/data_custom_roles.go +++ b/env0/data_custom_roles.go @@ -41,9 +41,7 @@ func dataCustomRolesRead(ctx context.Context, d *schema.ResourceData, meta inter } } - if err := d.Set("names", data); err != nil { - return diag.FromErr(err) - } + d.Set("names", data) // Not really needed. But required by Terraform SDK - https://github.com/hashicorp/terraform-plugin-sdk/issues/541 d.SetId("all_roles_names") diff --git a/env0/data_ip_ranges.go b/env0/data_ip_ranges.go index fded8e19..00a35bc7 100644 --- a/env0/data_ip_ranges.go +++ b/env0/data_ip_ranges.go @@ -47,9 +47,7 @@ func dataIpRangesRead(ctx context.Context, d *schema.ResourceData, meta interfac "54.165.19.49/32", } - if err := d.Set("ipv4", ipv4s); err != nil { - return diag.FromErr(err) - } + d.Set("ipv4", ipv4s) d.SetId("ip_ranges") diff --git a/env0/data_notifications.go b/env0/data_notifications.go index 38c9abc6..71b7132a 100644 --- a/env0/data_notifications.go +++ b/env0/data_notifications.go @@ -39,9 +39,7 @@ func dataNotificationsRead(ctx context.Context, d *schema.ResourceData, meta int names = append(names, notification.Name) } - if err := d.Set("names", names); err != nil { - return diag.FromErr(err) - } + d.Set("names", names) d.SetId("all_notification_names") diff --git a/env0/data_organization.go b/env0/data_organization.go index 372bbf80..9ea9ebc8 100644 --- a/env0/data_organization.go +++ b/env0/data_organization.go @@ -56,9 +56,7 @@ func dataOrganizationRead(ctx context.Context, d *schema.ResourceData, meta inte oidcSub, err := apiClient.OidcSub() if err == nil { - if err := d.Set("oidc_sub", oidcSub); err != nil { - return diag.FromErr(err) - } + d.Set("oidc_sub", oidcSub) } return nil diff --git a/env0/data_project.go b/env0/data_project.go index fee6b7f2..2d580ac5 100644 --- a/env0/data_project.go +++ b/env0/data_project.go @@ -92,7 +92,7 @@ func dataProjectRead(ctx context.Context, d *schema.ResourceData, meta interface return nil } -func filterByParentProjectId(parentId string, projects []client.Project) ([]client.Project, error) { +func filterByParentProjectId(parentId string, projects []client.Project) []client.Project { filteredProjects := make([]client.Project, 0) for _, project := range projects { if len(project.ParentProjectId) == 0 { @@ -104,7 +104,7 @@ func filterByParentProjectId(parentId string, projects []client.Project) ([]clie } } - return filteredProjects, nil + return filteredProjects } func filterByParentProjectName(parentName string, projects []client.Project, meta interface{}) ([]client.Project, error) { @@ -131,7 +131,7 @@ func getProjectByName(name string, parentId string, parentName string, meta inte apiClient := meta.(client.ApiClientInterface) projects, err := apiClient.Projects() if err != nil { - return client.Project{}, fmt.Errorf("could not query project by name: %v", err) + return client.Project{}, fmt.Errorf("could not query project by name: %w", err) } projectsByName := make([]client.Project, 0) @@ -142,10 +142,7 @@ func getProjectByName(name string, parentId string, parentName string, meta inte } if len(parentId) > 0 { // Use parentId filter to reduce the results. - projectsByName, err = filterByParentProjectId(parentId, projectsByName) - if err != nil { - return client.Project{}, err - } + projectsByName = filterByParentProjectId(parentId, projectsByName) } else if len(parentName) > 0 { // Use parentName filter to reduce the results. projectsByName, err = filterByParentProjectName(parentName, projectsByName, meta) @@ -172,7 +169,7 @@ func getProjectById(id string, meta interface{}) (client.Project, error) { if frerr, ok := err.(*http.FailedResponseError); ok && frerr.NotFound() { return client.Project{}, fmt.Errorf("could not find a project with id: %s", id) } - return client.Project{}, fmt.Errorf("could not query project: %v", err) + return client.Project{}, fmt.Errorf("could not query project: %w", err) } return project, nil } diff --git a/env0/data_project_cloud_credentials.go b/env0/data_project_cloud_credentials.go index 3f073484..dad87ae0 100644 --- a/env0/data_project_cloud_credentials.go +++ b/env0/data_project_cloud_credentials.go @@ -43,6 +43,7 @@ func dataProjectCloudCredentialsRead(ctx context.Context, d *schema.ResourceData } d.Set("ids", credentialIds) + d.SetId(projectId) return nil diff --git a/env0/resource_approval_policy_test.go b/env0/resource_approval_policy_test.go index 5b0aeb52..522c5594 100644 --- a/env0/resource_approval_policy_test.go +++ b/env0/resource_approval_policy_test.go @@ -12,6 +12,7 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/jinzhu/copier" + "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -33,7 +34,7 @@ func TestUnitApprovalPolicyResource(t *testing.T) { } var template client.Template - copier.Copy(&template, &approvalPolicy) + require.NoError(t, copier.Copy(&template, &approvalPolicy)) template.Type = string(ApprovalPolicy) deletedTemplate := template @@ -53,7 +54,7 @@ func TestUnitApprovalPolicyResource(t *testing.T) { } var updatedTemplate client.Template - copier.Copy(&updatedTemplate, &updatedApprovalPolicy) + require.NoError(t, copier.Copy(&updatedTemplate, &updatedApprovalPolicy)) updatedTemplate.Type = string(ApprovalPolicy) createPayload := client.ApprovalPolicyCreatePayload{ diff --git a/env0/resource_configuration_variable_test.go b/env0/resource_configuration_variable_test.go index cd57c385..26613e2e 100644 --- a/env0/resource_configuration_variable_test.go +++ b/env0/resource_configuration_variable_test.go @@ -549,12 +549,9 @@ resource "%s" "test" { }) t.Run("Update with wrong type", func(t *testing.T) { - wrongType := client.ConfigurationVariableType(6) newConfigVar := client.ConfigurationVariable{ - Id: configVar.Id, Name: configVar.Name, Value: "I want to be the config value", - Type: &wrongType, } updateTestCase := resource.TestCase{ @@ -682,7 +679,6 @@ resource "%s" "test" { t.Run("cant be empty value when isRequired and isReadOnly are true", func(t *testing.T) { trueVariable := true configVar := client.ConfigurationVariable{ - Id: "id0", Name: "name0", Description: "desc0", Value: "", diff --git a/env0/resource_environment.go b/env0/resource_environment.go index 130a190b..a2382a92 100644 --- a/env0/resource_environment.go +++ b/env0/resource_environment.go @@ -473,9 +473,7 @@ func setEnvironmentSchema(ctx context.Context, d *schema.ResourceData, environme } } - if err := d.Set("variable_sets", sortedVariablesSet); err != nil { - return fmt.Errorf("failed to set variable_sets value: %w", err) - } + d.Set("variable_sets", sortedVariablesSet) } return nil @@ -630,7 +628,7 @@ func resourceEnvironmentCreate(ctx context.Context, d *schema.ResourceData, meta EnvironmentCreate: environmentPayload, TemplateCreate: templatePayload, } - // Note: the blueprint id field of the environment is returned only during creation of a template without envrionment. + // Note: the blueprint id field of the environment is returned only during creation of a template without environment. // Afterward, it will be omitted from future response. // setEnvironmentSchema() (several lines below) sets the blueprint id in the resource (under "without_template_settings.0.id"). environment, err = apiClient.EnvironmentCreateWithoutTemplate(payload) @@ -707,7 +705,7 @@ func resourceEnvironmentRead(ctx context.Context, d *schema.ResourceData, meta i } if isTemplateless(d) { - // envrionment with no template. + // environment with no template. templateId := d.Get("without_template_settings.0.id").(string) template, err := apiClient.Template(templateId) if err != nil { @@ -870,6 +868,7 @@ func deploy(d *schema.ResourceData, apiClient client.ApiClientInterface) diag.Di return diag.Errorf("failed deploying environment: %v", err) } d.Set("deployment_id", deployResponse.Id) + return nil } @@ -1257,6 +1256,7 @@ func linkToExistConfigurationVariables(configurationChanges client.Configuration if isExist, existVariable := isVariableExist(existVariables, change); isExist { change.Id = existVariable.Id } + updateConfigurationChanges = append(updateConfigurationChanges, change) } return updateConfigurationChanges diff --git a/env0/resource_environment_test.go b/env0/resource_environment_test.go index e2457d99..8506d8ce 100644 --- a/env0/resource_environment_test.go +++ b/env0/resource_environment_test.go @@ -2348,7 +2348,7 @@ func TestUnitEnvironmentWithoutTemplateResource(t *testing.T) { Description: template.Description, GithubInstallationId: template.GithubInstallationId, IsGitlabEnterprise: template.IsGitlabEnterprise, - IsGitLab: template.TokenId != "", + IsGitlab: template.IsGitlab, TokenId: template.TokenId, Path: template.Path, Revision: template.Revision, @@ -2369,7 +2369,7 @@ func TestUnitEnvironmentWithoutTemplateResource(t *testing.T) { Description: updatedTemplate.Description, GithubInstallationId: updatedTemplate.GithubInstallationId, IsGitlabEnterprise: updatedTemplate.IsGitlabEnterprise, - IsGitLab: updatedTemplate.TokenId != "", + IsGitlab: updatedTemplate.IsGitlab, TokenId: updatedTemplate.TokenId, Path: updatedTemplate.Path, Revision: updatedTemplate.Revision, diff --git a/env0/resource_team_organization_assignment.go b/env0/resource_team_organization_assignment.go index a011fabe..5089be7c 100644 --- a/env0/resource_team_organization_assignment.go +++ b/env0/resource_team_organization_assignment.go @@ -65,10 +65,10 @@ func resourceTeamOrganizationAssignmentRead(ctx context.Context, d *schema.Resou organizationId, err := apiClient.OrganizationId() if err != nil { return diag.Errorf("could not get organization id: %v", err) - } var payload client.TeamRoleAssignmentListPayload + payload.OrganizationId = organizationId id := d.Id() @@ -104,10 +104,10 @@ func resourceTeamOrganizationAssignmentDelete(ctx context.Context, d *schema.Res organizationId, err := apiClient.OrganizationId() if err != nil { return diag.Errorf("could not get organization id: %v", err) - } payload.OrganizationId = organizationId + payload.TeamId = d.Get("team_id").(string) if err := apiClient.TeamRoleAssignmentDelete(&payload); err != nil { diff --git a/env0/resource_template.go b/env0/resource_template.go index 80e39c3e..24a720ad 100644 --- a/env0/resource_template.go +++ b/env0/resource_template.go @@ -249,6 +249,12 @@ func getTemplateSchema(prefix string) map[string]*schema.Schema { Optional: true, Description: "token name for Gitlab", }, + "is_gitlab": { + Type: schema.TypeBool, + Description: "set to 'true' if the repository is Gitlab", + Optional: true, + Default: false, + }, } if prefix == "" { @@ -385,14 +391,10 @@ func templateCreatePayloadFromParameters(prefix string, d *schema.ResourceData) isNew := d.IsNewResource() - tokenIdKey := "token_id" - isAzureDevOpsKey := "is_azure_devops" terragruntTfBinaryKey := "terragrunt_tf_binary" templateTypeKey := "type" if prefix != "" { - tokenIdKey = prefix + "." + tokenIdKey - isAzureDevOpsKey = prefix + "." + isAzureDevOpsKey terragruntTfBinaryKey = prefix + "." + terragruntTfBinaryKey templateTypeKey = prefix + "." + templateTypeKey } @@ -409,13 +411,6 @@ func templateCreatePayloadFromParameters(prefix string, d *schema.ResourceData) } } - // IsGitLab is implicitly assumed to be true if tokenId is non-empty. Unless AzureDevOps is explicitly used. - if tokenId, ok := d.GetOk(tokenIdKey); ok { - if isAzureDevOps := d.Get(isAzureDevOpsKey); !isAzureDevOps.(bool) { - payload.IsGitLab = tokenId != "" - } - } - templateCreatePayloadRetryOnHelper(prefix, d, "deploy", &payload.Retry.OnDeploy) templateCreatePayloadRetryOnHelper(prefix, d, "destroy", &payload.Retry.OnDestroy) diff --git a/env0/resource_template_test.go b/env0/resource_template_test.go index de69ee59..aacf6465 100644 --- a/env0/resource_template_test.go +++ b/env0/resource_template_test.go @@ -86,6 +86,7 @@ func TestUnitTemplateResource(t *testing.T) { TerraformVersion: "0.12.24", TokenName: "token_name", GitlabProjectId: 1234, + IsGitlab: true, } gitlabUpdatedTemplate := client.Template{ Id: gitlabTemplate.Id, @@ -528,6 +529,9 @@ func TestUnitTemplateResource(t *testing.T) { if template.HelmChartName != "" { templateAsDictionary["helm_chart_name"] = template.HelmChartName } + if template.IsGitlab { + templateAsDictionary["is_gitlab"] = true + } return resourceConfigCreate(resourceType, resourceName, templateAsDictionary) } @@ -601,6 +605,7 @@ func TestUnitTemplateResource(t *testing.T) { resource.TestCheckResourceAttr(resourceFullName, "is_azure_devops", strconv.FormatBool(template.IsAzureDevOps)), resource.TestCheckResourceAttr(resourceFullName, "is_helm_repository", strconv.FormatBool(template.IsHelmRepository)), tokenNameAssertion, + resource.TestCheckResourceAttr(resourceFullName, "is_gitlab", strconv.FormatBool(template.IsGitlab)), ) } @@ -628,7 +633,7 @@ func TestUnitTemplateResource(t *testing.T) { Description: templateUseCase.template.Description, GithubInstallationId: templateUseCase.template.GithubInstallationId, IsGitlabEnterprise: templateUseCase.template.IsGitlabEnterprise, - IsGitLab: templateUseCase.template.TokenId != "" && !templateUseCase.template.IsAzureDevOps, + IsGitlab: templateUseCase.template.IsGitlab, GitlabProjectId: templateUseCase.template.GitlabProjectId, TokenId: templateUseCase.template.TokenId, Path: templateUseCase.template.Path, @@ -655,7 +660,7 @@ func TestUnitTemplateResource(t *testing.T) { Description: templateUseCase.updatedTemplate.Description, GithubInstallationId: templateUseCase.updatedTemplate.GithubInstallationId, IsGitlabEnterprise: templateUseCase.updatedTemplate.IsGitlabEnterprise, - IsGitLab: templateUseCase.updatedTemplate.TokenId != "" && !templateUseCase.updatedTemplate.IsAzureDevOps, + IsGitlab: templateUseCase.updatedTemplate.IsGitlab, GitlabProjectId: templateUseCase.updatedTemplate.GitlabProjectId, TokenId: templateUseCase.updatedTemplate.TokenId, Path: templateUseCase.updatedTemplate.Path, diff --git a/env0/resource_variable_set.go b/env0/resource_variable_set.go index 9f8722a4..c604c607 100644 --- a/env0/resource_variable_set.go +++ b/env0/resource_variable_set.go @@ -368,9 +368,7 @@ func resourceVariableSetRead(ctx context.Context, d *schema.ResourceData, meta i return diag.Errorf("failed to get schema from variables: %v", err) } - if err := d.Set("variable", ivariables); err != nil { - return diag.Errorf("failed to set variables in schema: %v", err) - } + d.Set("variable", ivariables) return nil } diff --git a/env0/resource_variable_set_assignment.go b/env0/resource_variable_set_assignment.go index 67d1b7c0..26f08ee5 100644 --- a/env0/resource_variable_set_assignment.go +++ b/env0/resource_variable_set_assignment.go @@ -207,9 +207,7 @@ func resourceVariableSetAssignmentRead(ctx context.Context, d *schema.ResourceDa } } - if err := d.Set("set_ids", newSchemaSetIds); err != nil { - return diag.Errorf("failed to set 'set_ids': %v", err) - } + d.Set("set_ids", newSchemaSetIds) return nil } diff --git a/env0/resource_workflow_triggers.go b/env0/resource_workflow_triggers.go index 4773d727..236c9b73 100644 --- a/env0/resource_workflow_triggers.go +++ b/env0/resource_workflow_triggers.go @@ -61,6 +61,7 @@ func resourceWorkflowTriggersCreateOrUpdate(ctx context.Context, d *schema.Resou apiClient := meta.(client.ApiClientInterface) environmentId := d.Get("environment_id").(string) rawDownstreamIds := d.Get("downstream_environment_ids").([]interface{}) + var requestDownstreamIds []string for _, rawId := range rawDownstreamIds { diff --git a/env0/utils.go b/env0/utils.go index b3b4153c..52ef70bb 100644 --- a/env0/utils.go +++ b/env0/utils.go @@ -92,7 +92,6 @@ func reasourceDataGetValue(fieldName string, omitEmpty bool, d *schema.ResourceD func readResourceDataEx(prefix string, i interface{}, d *schema.ResourceData) error { // TODO: add a mechanism that returns an error if fields were set in the resourceData but not in the struct. // Blocked by: https://github.com/hashicorp/terraform-plugin-sdk/issues/910 - val := reflect.ValueOf(i).Elem() for i := 0; i < val.NumField(); i++ { parsedField := getFieldName(val.Type().Field(i)) @@ -120,9 +119,11 @@ func readResourceDataEx(prefix string, i interface{}, d *schema.ResourceData) er // Init the field a valid value (instead of nil). field.Set(reflect.New(field.Type().Elem())) } + if err := field.Interface().(CustomResourceDataField).ReadResourceData(fieldName, d); err != nil { return err } + continue } @@ -131,6 +132,7 @@ func readResourceDataEx(prefix string, i interface{}, d *schema.ResourceData) er if err := customField.ReadResourceData(fieldName, d); err != nil { return err } + continue } @@ -173,7 +175,6 @@ func readResourceDataSliceStructHelper(field reflect.Value, resource interface{} m := resource.(map[string]interface{}) for i := 0; i < val.NumField(); i++ { - parsedField := getFieldName(val.Type().Field(i)) if parsedField.skip { continue @@ -198,6 +199,7 @@ func readResourceDataSliceEx(field reflect.Value, resources []interface{}) error for _, resource := range resources { var val reflect.Value + switch elemType.Kind() { case reflect.String: val = reflect.ValueOf(resource.(string)) @@ -212,6 +214,7 @@ func readResourceDataSliceEx(field reflect.Value, resources []interface{}) error default: return fmt.Errorf("internal error - unhandled slice element kind %v", elemType.Kind()) } + vals = reflect.Append(vals, val) } @@ -233,6 +236,7 @@ func getFieldName(field reflect.StructField) *parsedField { // Assumes golang is CamalCase and Terraform is snake_case. // This behavior can be overrided be used in the 'tfschema' tag. res.name = toSnakeCase(field.Name) + if tag, ok := field.Tag.Lookup("tfschema"); ok { if tag == "-" { res.skip = true @@ -274,7 +278,9 @@ func writeResourceData(i interface{}, d *schema.ResourceData) error { if len(id) == 0 { return errors.New("id is empty") } + d.SetId(id) + continue } @@ -286,6 +292,7 @@ func writeResourceData(i interface{}, d *schema.ResourceData) error { if err := customField.WriteResourceData(fieldName, d); err != nil { return err } + continue } @@ -399,6 +406,7 @@ func getResourceDataSliceStructValue(val reflect.Value, name string, d *schema.R if err := writer.ResourceDataSliceStructValueWrite(value); err != nil { return nil, err } + continue } diff --git a/env0/utils_test.go b/env0/utils_test.go index b0ce4be9..ab8f3967 100644 --- a/env0/utils_test.go +++ b/env0/utils_test.go @@ -37,7 +37,8 @@ func TestReadResourceDataModule(t *testing.T) { var payload client.ModuleCreatePayload - assert.Nil(t, readResourceData(&payload, d)) + require.NoError(t, readResourceData(&payload, d)) + assert.Equal(t, expectedPayload, payload) } @@ -60,7 +61,7 @@ func TestWriteResourceDataModule(t *testing.T) { }, } - assert.Nil(t, writeResourceData(&m, d)) + require.NoError(t, writeResourceData(&m, d)) assert.Equal(t, "id", d.Id()) assert.Equal(t, "module_name", d.Get("module_name")) @@ -90,7 +91,7 @@ func TestReadResourceDataNotification(t *testing.T) { var payload client.NotificationCreatePayload - assert.Nil(t, readResourceData(&payload, d)) + require.NoError(t, readResourceData(&payload, d)) assert.Equal(t, expectedPayload, payload) } @@ -106,7 +107,7 @@ func TestReadResourceDataWithTag(t *testing.T) { var payload client.AwsCredentialsValuePayload - assert.Nil(t, readResourceData(&payload, d)) + require.NoError(t, readResourceData(&payload, d)) assert.Equal(t, expectedPayload, payload) } @@ -120,7 +121,7 @@ func TestWriteResourceDataNotification(t *testing.T) { Value: "value", } - assert.Nil(t, writeResourceData(&n, d)) + require.NoError(t, writeResourceData(&n, d)) assert.Equal(t, "id", d.Id()) assert.Equal(t, "name", d.Get("name")) @@ -144,7 +145,9 @@ func TestReadResourceDataNotificationProjectAssignment(t *testing.T) { } var payload client.NotificationProjectAssignmentUpdatePayload - assert.Nil(t, readResourceData(&payload, d)) + + require.NoError(t, readResourceData(&payload, d)) + assert.Equal(t, expectedPayload, payload) } @@ -159,7 +162,7 @@ func TestWriteResourceDataNotificationProjectAssignment(t *testing.T) { }, } - assert.Nil(t, writeResourceData(&a, d)) + require.NoError(t, writeResourceData(&a, d)) assert.Equal(t, "id", d.Id()) assert.Equal(t, "nid", d.Get("notification_endpoint_id")) @@ -187,7 +190,7 @@ func TestWriteCustomResourceData(t *testing.T) { Regex: "regex", } - assert.Nil(t, writeResourceData(&configurationVariable, d)) + require.NoError(t, writeResourceData(&configurationVariable, d)) assert.Equal(t, configurationVariable.Id, d.Id()) assert.Equal(t, configurationVariable.Name, d.Get("name")) @@ -209,11 +212,11 @@ func TestReadByValueCustomResourceData(t *testing.T) { params := client.ConfigurationVariableCreateParams{} - assert.Nil(t, readResourceData(¶ms, d)) + require.NoError(t, readResourceData(¶ms, d)) - assert.Equal(t, params.Name, "name") - assert.Equal(t, int(params.Type), 1) - assert.Equal(t, params.Description, "description") + assert.Equal(t, "name", params.Name) + assert.Equal(t, 1, int(params.Type)) + assert.Equal(t, "description", params.Description) } func TestReadByPointerCustomResourceData(t *testing.T) { @@ -225,11 +228,11 @@ func TestReadByPointerCustomResourceData(t *testing.T) { params := client.ConfigurationVariable{} - assert.Nil(t, readResourceData(¶ms, d)) + require.NoError(t, readResourceData(¶ms, d)) - assert.Equal(t, params.Name, "name") - assert.Equal(t, int(*params.Type), 1) - assert.Equal(t, params.Description, "description") + assert.Equal(t, "name", params.Name) + assert.Equal(t, 1, int(*params.Type)) + assert.Equal(t, "description", params.Description) } func TestReadByPointerNilCustomResourceData(t *testing.T) { @@ -240,11 +243,11 @@ func TestReadByPointerNilCustomResourceData(t *testing.T) { params := client.ConfigurationVariable{} - assert.Nil(t, readResourceData(¶ms, d)) + require.NoError(t, readResourceData(¶ms, d)) - assert.Equal(t, params.Name, "name") + assert.Equal(t, "name", params.Name) assert.Nil(t, params.Type) - assert.Equal(t, params.Description, "description") + assert.Equal(t, "description", params.Description) } func TestWriteResourceDataSliceVariablesAgents(t *testing.T) { @@ -260,7 +263,8 @@ func TestWriteResourceDataSliceVariablesAgents(t *testing.T) { vars := []client.Agent{agent1, agent2} - assert.Nil(t, writeResourceDataSlice(vars, "agents", d)) + require.NoError(t, writeResourceDataSlice(vars, "agents", d)) + assert.Equal(t, agent1.AgentKey, d.Get("agents.0.agent_key")) assert.Equal(t, agent2.AgentKey, d.Get("agents.1.agent_key")) } @@ -297,7 +301,8 @@ func TestWriteResourceDataSliceVariablesConfigurationVariable(t *testing.T) { vars := []client.ConfigurationVariable{var1, var2} - assert.Nil(t, writeResourceDataSlice(vars, "variables", d)) + require.NoError(t, writeResourceDataSlice(vars, "variables", d)) + assert.Equal(t, var1.Name, d.Get("variables.0.name")) assert.Equal(t, var2.Name, d.Get("variables.1.name")) assert.Equal(t, var1.Value, d.Get("variables.0.value")) @@ -317,7 +322,7 @@ func TestWriteResourceDataOmitEmpty(t *testing.T) { Revision: "branch-zero", } - assert.Nil(t, writeResourceData(&template, d)) + require.NoError(t, writeResourceData(&template, d)) attr := d.State().Attributes @@ -334,7 +339,8 @@ func TestWriteResourceDataOmitEmpty(t *testing.T) { template.TokenId = "tokenid" - assert.Nil(t, writeResourceData(&template, d)) + require.NoError(t, writeResourceData(&template, d)) + attr = d.State().Attributes _, ok = attr["token_id"] assert.True(t, ok, "token_id should be set") @@ -410,7 +416,7 @@ func TestReadSubEnvironment(t *testing.T) { subEnvironments, err := getSubEnvironments(d) - require.Nil(t, err) + require.NoError(t, err) require.Len(t, subEnvironments, 2) require.Equal(t, expectedSubEnvironments, subEnvironments) } @@ -418,37 +424,37 @@ func TestReadSubEnvironment(t *testing.T) { func TestTTLToDuration(t *testing.T) { t.Run("hours", func(t *testing.T) { duration, err := ttlToDuration(stringPtr("2-h")) - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, time.Duration(3600*2*1000000000), duration) }) t.Run("days", func(t *testing.T) { duration, err := ttlToDuration(stringPtr("1-d")) - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, time.Duration(3600*24*1000000000), duration) }) t.Run("weeks", func(t *testing.T) { duration, err := ttlToDuration(stringPtr("3-w")) - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, time.Duration(21*3600*24*1000000000), duration) }) t.Run("months", func(t *testing.T) { duration, err := ttlToDuration(stringPtr("1-M")) - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, time.Duration(30*3600*24*1000000000), duration) }) t.Run("inherit", func(t *testing.T) { duration, err := ttlToDuration(stringPtr("inherit")) - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, time.Duration(math.MaxInt64), duration) }) t.Run("Infinite", func(t *testing.T) { duration, err := ttlToDuration(stringPtr("Infinite")) - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, time.Duration(math.MaxInt64), duration) })