From 1b09ddaed6eb7261a8df591d09195a032f90392a Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Thu, 17 Aug 2023 15:50:12 +0100 Subject: [PATCH 1/7] feat: Migrate DataSource: mongodbatlas_project_ip_access_list to Terraform Plugin Framework --- ...rce_mongodbatlas_project_ip_access_list.go | 107 ----------- .../framework/validator/cidr_validator.go | 50 ++++++ .../validator/cidr_validator_test.go | 61 +++++++ .../validator/duration_validator_test.go | 88 ++++++++++ .../validator/json_string_validator_test.go | 65 +++++++ ...rce_mongodbatlas_project_ip_access_list.go | 166 ++++++++++++++++++ mongodbatlas/fw_provider.go | 1 + mongodbatlas/provider.go | 1 - 8 files changed, 431 insertions(+), 108 deletions(-) delete mode 100644 mongodbatlas/data_source_mongodbatlas_project_ip_access_list.go create mode 100644 mongodbatlas/framework/validator/cidr_validator.go create mode 100644 mongodbatlas/framework/validator/cidr_validator_test.go create mode 100644 mongodbatlas/framework/validator/duration_validator_test.go create mode 100644 mongodbatlas/framework/validator/json_string_validator_test.go create mode 100644 mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go diff --git a/mongodbatlas/data_source_mongodbatlas_project_ip_access_list.go b/mongodbatlas/data_source_mongodbatlas_project_ip_access_list.go deleted file mode 100644 index 0ec42663e2..0000000000 --- a/mongodbatlas/data_source_mongodbatlas_project_ip_access_list.go +++ /dev/null @@ -1,107 +0,0 @@ -package mongodbatlas - -import ( - "bytes" - "context" - "errors" - "fmt" - "net" - - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func dataSourceMongoDBAtlasProjectIPAccessList() *schema.Resource { - return &schema.Resource{ - ReadContext: dataSourceMongoDBAtlasProjectIPAccessListRead, - Schema: map[string]*schema.Schema{ - "project_id": { - Type: schema.TypeString, - Required: true, - }, - "cidr_block": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ConflictsWith: []string{"aws_security_group", "ip_address"}, - ValidateFunc: func(i interface{}, k string) (s []string, es []error) { - v, ok := i.(string) - if !ok { - es = append(es, fmt.Errorf("expected type of %s to be string", k)) - return - } - - _, ipnet, err := net.ParseCIDR(v) - if err != nil { - es = append(es, fmt.Errorf("expected %s to contain a valid CIDR, got: %s with err: %s", k, v, err)) - return - } - - if ipnet == nil || v != ipnet.String() { - es = append(es, fmt.Errorf("expected %s to contain a valid network CIDR, expected %s, got %s", k, ipnet, v)) - return - } - return - }, - }, - "ip_address": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ConflictsWith: []string{"aws_security_group", "cidr_block"}, - ValidateFunc: validation.IsIPAddress, - }, - "aws_security_group": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ConflictsWith: []string{"ip_address", "cidr_block"}, - }, - "comment": { - Type: schema.TypeString, - Computed: true, - }, - }, - } -} - -func dataSourceMongoDBAtlasProjectIPAccessListRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*MongoDBClient).Atlas - projectID := d.Get("project_id").(string) - cidrBlock := d.Get("cidr_block").(string) - ipAddress := d.Get("ip_address").(string) - awsSecurityGroup := d.Get("aws_security_group").(string) - - if cidrBlock == "" && ipAddress == "" && awsSecurityGroup == "" { - return diag.FromErr(errors.New("cidr_block, ip_address or aws_security_group needs to contain a value")) - } - var entry bytes.Buffer - - entry.WriteString(cidrBlock) - entry.WriteString(ipAddress) - entry.WriteString(awsSecurityGroup) - - accessList, _, err := conn.ProjectIPAccessList.Get(ctx, projectID, entry.String()) - if err != nil { - return diag.FromErr(fmt.Errorf("error getting access list information: %s", err)) - } - - if err := d.Set("cidr_block", accessList.CIDRBlock); err != nil { - return diag.FromErr(fmt.Errorf("error setting `cidr_block` for the project access list: %s", err)) - } - if err := d.Set("ip_address", accessList.IPAddress); err != nil { - return diag.FromErr(fmt.Errorf("error setting `ip_address` for the project access list: %s", err)) - } - if err := d.Set("aws_security_group", accessList.AwsSecurityGroup); err != nil { - return diag.FromErr(fmt.Errorf("error setting `aws_security_group` for the project access list: %s", err)) - } - if err := d.Set("comment", accessList.Comment); err != nil { - return diag.FromErr(fmt.Errorf("error setting `comment` for the project access list: %s", err)) - } - - d.SetId(id.UniqueId()) - - return nil -} diff --git a/mongodbatlas/framework/validator/cidr_validator.go b/mongodbatlas/framework/validator/cidr_validator.go new file mode 100644 index 0000000000..eab389e526 --- /dev/null +++ b/mongodbatlas/framework/validator/cidr_validator.go @@ -0,0 +1,50 @@ +package validator + +import ( + "context" + "net" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +type CIDRValidator struct{} + +func (v CIDRValidator) Description(_ context.Context) string { + return "string value must be defined as a valid cidr." +} + +func (v CIDRValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v CIDRValidator) ValidateString(ctx context.Context, req validator.StringRequest, response *validator.StringResponse) { + // If the value is unknown or null, there is nothing to validate. + if req.ConfigValue.IsUnknown() || req.ConfigValue.IsNull() { + return + } + + value := req.ConfigValue.ValueString() + _, ipnet, err := net.ParseCIDR(value) + if err != nil { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + req.Path, + v.Description(ctx), + req.ConfigValue.ValueString(), + )) + return + } + + if ipnet == nil || ipnet.String() != value { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + req.Path, + v.Description(ctx), + req.ConfigValue.ValueString(), + )) + return + } +} + +func ValidCIDR() validator.String { + return CIDRValidator{} +} diff --git a/mongodbatlas/framework/validator/cidr_validator_test.go b/mongodbatlas/framework/validator/cidr_validator_test.go new file mode 100644 index 0000000000..585976fbfb --- /dev/null +++ b/mongodbatlas/framework/validator/cidr_validator_test.go @@ -0,0 +1,61 @@ +package validator + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestValidCIDR(t *testing.T) { + tests := []struct { + name string + cidr string + wantErr bool + }{ + { + name: "Valid URL", + cidr: "192.0.0.0/28", + wantErr: false, + }, + { + name: "invalid value", + cidr: "12312321", + wantErr: true, + }, + { + name: "missing slash", + cidr: "192.0.0.8", + wantErr: true, + }, + { + name: "empty", + cidr: "", + wantErr: true, + }, + } + for _, tt := range tests { + val := tt.cidr + wantErr := tt.wantErr + cidrValidator := CIDRValidator{} + + validatorRequest := validator.StringRequest{ + ConfigValue: types.StringValue(val), + } + + validatorResponse := validator.StringResponse{ + Diagnostics: diag.Diagnostics{}, + } + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + cidrValidator.ValidateString(context.Background(), validatorRequest, &validatorResponse) + + if validatorResponse.Diagnostics.HasError() && !wantErr { + t.Errorf("URL() error = %v, wantErr %v", validatorResponse.Diagnostics.Errors(), wantErr) + } + }) + } +} diff --git a/mongodbatlas/framework/validator/duration_validator_test.go b/mongodbatlas/framework/validator/duration_validator_test.go new file mode 100644 index 0000000000..d7db9f2a0a --- /dev/null +++ b/mongodbatlas/framework/validator/duration_validator_test.go @@ -0,0 +1,88 @@ +package validator + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestValidDurationBetween(t *testing.T) { + tests := []struct { + name string + minutes string + maxMinutes int + minMinutes int + wantErr bool + }{ + { + name: "valid minutes", + minutes: "11m", + minMinutes: 10, + maxMinutes: 12, + wantErr: false, + }, + { + name: "out of range", + minutes: "11h45m", + minMinutes: 10, + maxMinutes: 12, + wantErr: true, + }, + { + name: "unvalid minutes", + minutes: "1m", + minMinutes: 10, + maxMinutes: 12, + wantErr: true, + }, + { + name: "max minutes smaller than min minutes", + minutes: "11", + minMinutes: 10, + maxMinutes: 1, + wantErr: true, + }, + { + name: "negative number", + minutes: "-11", + minMinutes: 10, + maxMinutes: 1, + wantErr: true, + }, + { + name: "empty", + minutes: "", + minMinutes: 10, + maxMinutes: 12, + wantErr: true, + }, + } + for _, tt := range tests { + wantErr := tt.wantErr + cidrValidator := durationValidator{ + MinMinutes: tt.minMinutes, + MaxMinutes: tt.maxMinutes, + } + + val := tt.minutes + validatorRequest := validator.StringRequest{ + ConfigValue: types.StringValue(val), + } + + validatorResponse := validator.StringResponse{ + Diagnostics: diag.Diagnostics{}, + } + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + cidrValidator.ValidateString(context.Background(), validatorRequest, &validatorResponse) + + if validatorResponse.Diagnostics.HasError() && !wantErr { + t.Errorf("URL() error = %v, wantErr %v", validatorResponse.Diagnostics.Errors(), wantErr) + } + }) + } +} diff --git a/mongodbatlas/framework/validator/json_string_validator_test.go b/mongodbatlas/framework/validator/json_string_validator_test.go new file mode 100644 index 0000000000..2361b25e36 --- /dev/null +++ b/mongodbatlas/framework/validator/json_string_validator_test.go @@ -0,0 +1,65 @@ +package validator + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestStringIsJSON(t *testing.T) { + tests := []struct { + name string + json string + wantErr bool + }{ + { + name: "Valid JSON", + json: `{ + "test": "value" + }`, + wantErr: false, + }, + { + name: "invalid value", + json: "12312321", + wantErr: true, + }, + { + name: "missing comma", + json: `{ + "test" "value" + }`, + wantErr: true, + }, + { + name: "empty", + json: "", + wantErr: true, + }, + } + for _, tt := range tests { + val := tt.json + wantErr := tt.wantErr + cidrValidator := jsonStringValidator{} + + validatorRequest := validator.StringRequest{ + ConfigValue: types.StringValue(val), + } + + validatorResponse := validator.StringResponse{ + Diagnostics: diag.Diagnostics{}, + } + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + cidrValidator.ValidateString(context.Background(), validatorRequest, &validatorResponse) + + if validatorResponse.Diagnostics.HasError() && !wantErr { + t.Errorf("URL() error = %v, wantErr %v", validatorResponse.Diagnostics.Errors(), wantErr) + } + }) + } +} diff --git a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go new file mode 100644 index 0000000000..5fae511bc1 --- /dev/null +++ b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go @@ -0,0 +1,166 @@ +package mongodbatlas + +import ( + "bytes" + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + cstmvalidator "github.com/mongodb/terraform-provider-mongodbatlas/mongodbatlas/framework/validator" + matlas "go.mongodb.org/atlas/mongodbatlas" +) + +const ( + projectIPAccessList = "project_ip_access_list" +) + +type ProjectIPAccessListDS struct { + client *MongoDBClient +} + +func NewProjectIPAccessListDS() datasource.DataSource { + return &ProjectIPAccessListDS{} +} + +var _ datasource.DataSource = &ProjectIPAccessListDS{} +var _ datasource.DataSourceWithConfigure = &ProjectIPAccessListDS{} + +func (d *ProjectIPAccessListDS) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = fmt.Sprintf("%s_%s", req.ProviderTypeName, projectIPAccessList) +} + +func (d *ProjectIPAccessListDS) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*MongoDBClient) + + if !ok { + resp.Diagnostics.AddError(errorConfigureSummary, fmt.Sprintf(errorConfigure, req.ProviderData)) + return + } + d.client = client +} + +type tfProjectIPAccessListDSModel struct { + ID types.String `tfsdk:"id"` + ProjectID types.String `tfsdk:"project_id"` + CIDRBlock types.String `tfsdk:"cidr_block"` + IPAddress types.String `tfsdk:"ip_address"` + AWSSecurityGroup types.String `tfsdk:"aws_security_group"` + Comment types.String `tfsdk:"comment"` +} + +func (d *ProjectIPAccessListDS) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + "project_id": schema.StringAttribute{ + Required: true, + }, + "cidr_block": schema.StringAttribute{ + Optional: true, + Computed: true, + Validators: []validator.String{ + cstmvalidator.ValidCIDR(), + stringvalidator.ConflictsWith(path.Expressions{ + path.MatchRelative().AtParent().AtName("aws_security_group"), + path.MatchRelative().AtParent().AtName("ip_address"), + }...), + }, + }, + "ip_address": schema.StringAttribute{ + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.Expressions{ + path.MatchRelative().AtParent().AtName("aws_security_group"), + path.MatchRelative().AtParent().AtName("cidr_block"), + }...), + }, + }, + "aws_security_group": schema.StringAttribute{ + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.Expressions{ + path.MatchRelative().AtParent().AtName("aws_security_group"), + path.MatchRelative().AtParent().AtName("cidr_block"), + }...), + }, + }, + "comment": schema.StringAttribute{ + Computed: true, + }, + }, + } +} + +func (d *ProjectIPAccessListDS) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var databaseDSUserConfig *tfProjectIPAccessListDSModel + var err error + resp.Diagnostics.Append(req.Config.Get(ctx, &databaseDSUserConfig)...) + if resp.Diagnostics.HasError() { + return + } + + if databaseDSUserConfig.CIDRBlock.IsNull() && databaseDSUserConfig.IPAddress.IsNull() && databaseDSUserConfig.AWSSecurityGroup.IsNull() { + resp.Diagnostics.Append(diag.NewErrorDiagnostic("validation error", "Once between cidr_block, ip_address or aws_security_group needs to contain a value")) + return + } + + var entry bytes.Buffer + entry.WriteString(databaseDSUserConfig.CIDRBlock.ValueString()) + if !databaseDSUserConfig.IPAddress.IsNull() { + entry.WriteString(databaseDSUserConfig.IPAddress.ValueString()) + } else if !databaseDSUserConfig.AWSSecurityGroup.IsNull() { + entry.WriteString(databaseDSUserConfig.AWSSecurityGroup.ValueString()) + } + + conn := d.client.Atlas + accessList, _, err := conn.ProjectIPAccessList.Get(ctx, databaseDSUserConfig.ProjectID.ValueString(), entry.String()) + if err != nil { + resp.Diagnostics.AddError("error getting access list entry", err.Error()) + return + } + + accessListEntry, diagnostic := newTFProjectIPAccessListDSModel(ctx, accessList) + resp.Diagnostics.Append(diagnostic...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &accessListEntry)...) + if resp.Diagnostics.HasError() { + return + } +} + +func newTFProjectIPAccessListDSModel(ctx context.Context, accessList *matlas.ProjectIPAccessList) (*tfProjectIPAccessListDSModel, diag.Diagnostics) { + databaseUserModel := &tfProjectIPAccessListDSModel{ + ProjectID: types.StringValue(accessList.GroupID), + Comment: types.StringValue(accessList.Comment), + CIDRBlock: types.StringValue(accessList.CIDRBlock), + IPAddress: types.StringValue(accessList.IPAddress), + AWSSecurityGroup: types.StringValue(accessList.AwsSecurityGroup), + } + + entry := accessList.CIDRBlock + if accessList.IPAddress != "" { + entry = accessList.IPAddress + } else if accessList.AwsSecurityGroup != "" { + entry = accessList.AwsSecurityGroup + } + + id := fmt.Sprintf("%s-%s", accessList.GroupID, entry) + databaseUserModel.ID = types.StringValue(id) + return databaseUserModel, nil +} diff --git a/mongodbatlas/fw_provider.go b/mongodbatlas/fw_provider.go index 9f7cfcb1f4..f77cdcc91f 100644 --- a/mongodbatlas/fw_provider.go +++ b/mongodbatlas/fw_provider.go @@ -367,6 +367,7 @@ func (p *MongodbtlasProvider) DataSources(context.Context) []func() datasource.D return []func() datasource.DataSource{ NewProjectDS, NewProjectsDS, + NewProjectIPAccessListDS, } } diff --git a/mongodbatlas/provider.go b/mongodbatlas/provider.go index 1c45a1ad43..782f8a2e0c 100644 --- a/mongodbatlas/provider.go +++ b/mongodbatlas/provider.go @@ -164,7 +164,6 @@ func getDataSourcesMap() map[string]*schema.Resource { "mongodbatlas_cloud_backup_schedule": dataSourceMongoDBAtlasCloudBackupSchedule(), "mongodbatlas_third_party_integrations": dataSourceMongoDBAtlasThirdPartyIntegrations(), "mongodbatlas_third_party_integration": dataSourceMongoDBAtlasThirdPartyIntegration(), - "mongodbatlas_project_ip_access_list": dataSourceMongoDBAtlasProjectIPAccessList(), "mongodbatlas_cloud_provider_access": dataSourceMongoDBAtlasCloudProviderAccessList(), "mongodbatlas_cloud_provider_access_setup": dataSourceMongoDBAtlasCloudProviderAccessSetup(), "mongodbatlas_custom_dns_configuration_cluster_aws": dataSourceMongoDBAtlasCustomDNSConfigurationAWS(), From 673049e276ffb9edb79b480567e675677cac01f4 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 18 Aug 2023 10:00:33 +0100 Subject: [PATCH 2/7] Added tests --- ...rce_mongodbatlas_project_ip_access_list.go | 9 +- ...s_project_ip_access_list_migration_test.go | 141 ++++++++++++++++++ ...ngodbatlas_project_ip_access_list_test.go} | 0 ...ongodbatlas_project_ip_access_list_test.go | 4 +- 4 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go rename mongodbatlas/{data_source_mongodbatlas_project_ip_access_list_test.go => fw_data_source_mongodbatlas_project_ip_access_list_test.go} (100%) diff --git a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go index 5fae511bc1..1e00063082 100644 --- a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go +++ b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go @@ -36,15 +36,12 @@ func (d *ProjectIPAccessListDS) Metadata(ctx context.Context, req datasource.Met } func (d *ProjectIPAccessListDS) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { - if req.ProviderData == nil { + client, err := ConfigureClientInResource(req.ProviderData) + if err != nil { + resp.Diagnostics.AddError(errorConfigureSummary, err.Error()) return } - client, ok := req.ProviderData.(*MongoDBClient) - if !ok { - resp.Diagnostics.AddError(errorConfigureSummary, fmt.Sprintf(errorConfigure, req.ProviderData)) - return - } d.client = client } diff --git a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go new file mode 100644 index 0000000000..f2b038e63e --- /dev/null +++ b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go @@ -0,0 +1,141 @@ +package mongodbatlas + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" +) + +func TestAccProjectDSProjectIPAccessList_Migration_SettingIPAddress(t *testing.T) { + resourceName := "mongodbatlas_project_ip_access_list.test" + orgID := os.Getenv("MONGODB_ATLAS_ORG_ID") + projectName := acctest.RandomWithPrefix("test-acc") + ipAddress := fmt.Sprintf("179.154.226.%d", acctest.RandIntRange(0, 255)) + comment := fmt.Sprintf("TestAcc for ipAddress (%s)", ipAddress) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckBasic(t) }, + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "mongodbatlas": { + VersionConstraint: "1.11.0", + Source: "mongodb/mongodbatlas", + }, + }, + Config: testAccDataMongoDBAtlasProjectIPAccessListConfigSettingIPAddress(orgID, projectName, ipAddress, comment), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "project_id"), + resource.TestCheckResourceAttrSet(resourceName, "ip_address"), + resource.TestCheckResourceAttrSet(resourceName, "comment"), + resource.TestCheckResourceAttr(resourceName, "ip_address", ipAddress), + resource.TestCheckResourceAttr(resourceName, "comment", comment), + ), + }, + { + ProtoV6ProviderFactories: testAccProviderV6Factories, + Config: testAccDataMongoDBAtlasProjectIPAccessListConfigSettingIPAddress(orgID, projectName, ipAddress, comment), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PostApplyPreRefresh: []plancheck.PlanCheck{ + DebugPlan(), + }, + }, + PlanOnly: true, + }, + }, + }) +} + +func TestAccProjectDSProjectIPAccessList_Migration_SettingCIDRBlock(t *testing.T) { + resourceName := "mongodbatlas_project_ip_access_list.test" + orgID := os.Getenv("MONGODB_ATLAS_ORG_ID") + projectName := acctest.RandomWithPrefix("test-acc") + cidrBlock := fmt.Sprintf("179.154.226.%d/32", acctest.RandIntRange(0, 255)) + comment := fmt.Sprintf("TestAcc for cidrBlock (%s)", cidrBlock) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckBasic(t) }, + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "mongodbatlas": { + VersionConstraint: "1.11.0", + Source: "mongodb/mongodbatlas", + }, + }, + Config: testAccDataMongoDBAtlasProjectIPAccessListConfigSettingCIDRBlock(orgID, projectName, cidrBlock, comment), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasProjectIPAccessListExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "project_id"), + resource.TestCheckResourceAttrSet(resourceName, "cidr_block"), + resource.TestCheckResourceAttrSet(resourceName, "comment"), + resource.TestCheckResourceAttr(resourceName, "cidr_block", cidrBlock), + resource.TestCheckResourceAttr(resourceName, "comment", comment), + ), + }, + { + ProtoV6ProviderFactories: testAccProviderV6Factories, + Config: testAccDataMongoDBAtlasProjectIPAccessListConfigSettingCIDRBlock(orgID, projectName, cidrBlock, comment), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PostApplyPreRefresh: []plancheck.PlanCheck{ + DebugPlan(), + }, + }, + PlanOnly: true, + }, + }, + }) +} + +func TestAccProjectDSProjectIPAccessList_Migration_SettingAWSSecurityGroup(t *testing.T) { + SkipTestExtCred(t) + resourceName := "mongodbatlas_project_ip_access_list.test" + vpcID := os.Getenv("AWS_VPC_ID") + vpcCIDRBlock := os.Getenv("AWS_VPC_CIDR_BLOCK") + awsAccountID := os.Getenv("AWS_ACCOUNT_ID") + awsRegion := os.Getenv("AWS_REGION") + providerName := "AWS" + + projectID := os.Getenv("MONGODB_ATLAS_PROJECT_ID") + awsSGroup := os.Getenv("AWS_SECURITY_GROUP_ID") + comment := fmt.Sprintf("TestAcc for awsSecurityGroup (%s)", awsSGroup) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "mongodbatlas": { + VersionConstraint: "1.11.0", + Source: "mongodb/mongodbatlas", + }, + }, + Config: testAccDataMongoDBAtlasProjectIPAccessListConfigSettingAWSSecurityGroup(projectID, providerName, vpcID, awsAccountID, vpcCIDRBlock, awsRegion, awsSGroup, comment), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasProjectIPAccessListExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "project_id"), + resource.TestCheckResourceAttrSet(resourceName, "aws_security_group"), + resource.TestCheckResourceAttrSet(resourceName, "comment"), + + resource.TestCheckResourceAttr(resourceName, "project_id", projectID), + resource.TestCheckResourceAttr(resourceName, "aws_security_group", awsSGroup), + resource.TestCheckResourceAttr(resourceName, "comment", comment), + ), + }, + { + ProtoV6ProviderFactories: testAccProviderV6Factories, + Config: testAccDataMongoDBAtlasProjectIPAccessListConfigSettingAWSSecurityGroup(projectID, providerName, vpcID, awsAccountID, vpcCIDRBlock, awsRegion, awsSGroup, comment), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PostApplyPreRefresh: []plancheck.PlanCheck{ + DebugPlan(), + }, + }, + PlanOnly: true, + }, + }, + }) +} diff --git a/mongodbatlas/data_source_mongodbatlas_project_ip_access_list_test.go b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_test.go similarity index 100% rename from mongodbatlas/data_source_mongodbatlas_project_ip_access_list_test.go rename to mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_test.go diff --git a/mongodbatlas/resource_mongodbatlas_project_ip_access_list_test.go b/mongodbatlas/resource_mongodbatlas_project_ip_access_list_test.go index 2b43b3291d..009c4ce52a 100644 --- a/mongodbatlas/resource_mongodbatlas_project_ip_access_list_test.go +++ b/mongodbatlas/resource_mongodbatlas_project_ip_access_list_test.go @@ -223,7 +223,7 @@ func TestAccProjectRSProjectIPAccessList_importBasic(t *testing.T) { func testAccCheckMongoDBAtlasProjectIPAccessListExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := testAccProviderSdkV2.Meta().(*MongoDBClient).Atlas + conn := testMongoDBClient.(*MongoDBClient).Atlas rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -246,7 +246,7 @@ func testAccCheckMongoDBAtlasProjectIPAccessListExists(resourceName string) reso } func testAccCheckMongoDBAtlasProjectIPAccessListDestroy(s *terraform.State) error { - conn := testAccProviderSdkV2.Meta().(*MongoDBClient).Atlas + conn := testMongoDBClient.(*MongoDBClient).Atlas for _, rs := range s.RootModule().Resources { if rs.Type != "mongodbatlas_project_ip_access_list" { From 38a4e927f4a3be6776c5d5494babf3751ee1879d Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 18 Aug 2023 10:02:19 +0100 Subject: [PATCH 3/7] Added test --- .github/workflows/acceptance-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/acceptance-tests.yml b/.github/workflows/acceptance-tests.yml index 26e9ba32df..9022f4b2bf 100644 --- a/.github/workflows/acceptance-tests.yml +++ b/.github/workflows/acceptance-tests.yml @@ -57,7 +57,7 @@ jobs: - 'mongodbatlas/**backup_schedule**.go' project: - 'mongodbatlas/data_source_mongodbatlas_project_invitation*.go' - - 'mongodbatlas/data_source_mongodbatlas_project_ip_access_list*.go' + - 'mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list*.go' - 'mongodbatlas/data_source_mongodbatlas_project.go' - 'mongodbatlas/data_source_mongodbatlas_projects.go' - 'mongodbatlas/resource_mongodbatlas_access_list_api_key*.go' From 39651ca85bb6332dcb3af8e27e983556c31b868f Mon Sep 17 00:00:00 2001 From: Andrea Angiolillo Date: Mon, 21 Aug 2023 08:52:34 +0100 Subject: [PATCH 4/7] Update mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go Co-authored-by: maastha <122359335+maastha@users.noreply.github.com> --- .../fw_data_source_mongodbatlas_project_ip_access_list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go index dbd4a0d9bd..96c2b71fb8 100644 --- a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go +++ b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go @@ -110,7 +110,7 @@ func (d *ProjectIPAccessListDS) Read(ctx context.Context, req datasource.ReadReq } if databaseDSUserConfig.CIDRBlock.IsNull() && databaseDSUserConfig.IPAddress.IsNull() && databaseDSUserConfig.AWSSecurityGroup.IsNull() { - resp.Diagnostics.Append(diag.NewErrorDiagnostic("validation error", "Once between cidr_block, ip_address or aws_security_group needs to contain a value")) + resp.Diagnostics.Append(diag.NewErrorDiagnostic("validation error", "One of cidr_block, ip_address or aws_security_group needs to contain a value")) return } From b48472335c24511906f77f1ed76a76f6cfb8fa43 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Mon, 21 Aug 2023 12:38:41 +0100 Subject: [PATCH 5/7] Addressed PR comments (Part-1) --- ...rce_mongodbatlas_project_ip_access_list.go | 2 +- ...s_project_ip_access_list_migration_test.go | 42 +++++++++---------- mongodbatlas/fw_provider.go | 2 + 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go index 96c2b71fb8..e9e583a82d 100644 --- a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go +++ b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go @@ -89,7 +89,7 @@ func (d *ProjectIPAccessListDS) Schema(ctx context.Context, req datasource.Schem Computed: true, Validators: []validator.String{ stringvalidator.ConflictsWith(path.Expressions{ - path.MatchRelative().AtParent().AtName("aws_security_group"), + path.MatchRelative().AtParent().AtName("ip_address"), path.MatchRelative().AtParent().AtName("cidr_block"), }...), }, diff --git a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go index f2b038e63e..50a868bce2 100644 --- a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go +++ b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go @@ -11,7 +11,7 @@ import ( ) func TestAccProjectDSProjectIPAccessList_Migration_SettingIPAddress(t *testing.T) { - resourceName := "mongodbatlas_project_ip_access_list.test" + dataSourceName := "data.mongodbatlas_project_ip_access_list.test" orgID := os.Getenv("MONGODB_ATLAS_ORG_ID") projectName := acctest.RandomWithPrefix("test-acc") ipAddress := fmt.Sprintf("179.154.226.%d", acctest.RandIntRange(0, 255)) @@ -29,11 +29,11 @@ func TestAccProjectDSProjectIPAccessList_Migration_SettingIPAddress(t *testing.T }, Config: testAccDataMongoDBAtlasProjectIPAccessListConfigSettingIPAddress(orgID, projectName, ipAddress, comment), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(resourceName, "project_id"), - resource.TestCheckResourceAttrSet(resourceName, "ip_address"), - resource.TestCheckResourceAttrSet(resourceName, "comment"), - resource.TestCheckResourceAttr(resourceName, "ip_address", ipAddress), - resource.TestCheckResourceAttr(resourceName, "comment", comment), + resource.TestCheckResourceAttrSet(dataSourceName, "project_id"), + resource.TestCheckResourceAttrSet(dataSourceName, "ip_address"), + resource.TestCheckResourceAttrSet(dataSourceName, "comment"), + resource.TestCheckResourceAttr(dataSourceName, "ip_address", ipAddress), + resource.TestCheckResourceAttr(dataSourceName, "comment", comment), ), }, { @@ -51,7 +51,7 @@ func TestAccProjectDSProjectIPAccessList_Migration_SettingIPAddress(t *testing.T } func TestAccProjectDSProjectIPAccessList_Migration_SettingCIDRBlock(t *testing.T) { - resourceName := "mongodbatlas_project_ip_access_list.test" + dataSourceName := "data.mongodbatlas_project_ip_access_list.test" orgID := os.Getenv("MONGODB_ATLAS_ORG_ID") projectName := acctest.RandomWithPrefix("test-acc") cidrBlock := fmt.Sprintf("179.154.226.%d/32", acctest.RandIntRange(0, 255)) @@ -69,12 +69,12 @@ func TestAccProjectDSProjectIPAccessList_Migration_SettingCIDRBlock(t *testing.T }, Config: testAccDataMongoDBAtlasProjectIPAccessListConfigSettingCIDRBlock(orgID, projectName, cidrBlock, comment), Check: resource.ComposeTestCheckFunc( - testAccCheckMongoDBAtlasProjectIPAccessListExists(resourceName), - resource.TestCheckResourceAttrSet(resourceName, "project_id"), - resource.TestCheckResourceAttrSet(resourceName, "cidr_block"), - resource.TestCheckResourceAttrSet(resourceName, "comment"), - resource.TestCheckResourceAttr(resourceName, "cidr_block", cidrBlock), - resource.TestCheckResourceAttr(resourceName, "comment", comment), + testAccCheckMongoDBAtlasProjectIPAccessListExists(dataSourceName), + resource.TestCheckResourceAttrSet(dataSourceName, "project_id"), + resource.TestCheckResourceAttrSet(dataSourceName, "cidr_block"), + resource.TestCheckResourceAttrSet(dataSourceName, "comment"), + resource.TestCheckResourceAttr(dataSourceName, "cidr_block", cidrBlock), + resource.TestCheckResourceAttr(dataSourceName, "comment", comment), ), }, { @@ -93,7 +93,7 @@ func TestAccProjectDSProjectIPAccessList_Migration_SettingCIDRBlock(t *testing.T func TestAccProjectDSProjectIPAccessList_Migration_SettingAWSSecurityGroup(t *testing.T) { SkipTestExtCred(t) - resourceName := "mongodbatlas_project_ip_access_list.test" + dataSourceName := "data.mongodbatlas_project_ip_access_list.test" vpcID := os.Getenv("AWS_VPC_ID") vpcCIDRBlock := os.Getenv("AWS_VPC_CIDR_BLOCK") awsAccountID := os.Getenv("AWS_ACCOUNT_ID") @@ -116,14 +116,14 @@ func TestAccProjectDSProjectIPAccessList_Migration_SettingAWSSecurityGroup(t *te }, Config: testAccDataMongoDBAtlasProjectIPAccessListConfigSettingAWSSecurityGroup(projectID, providerName, vpcID, awsAccountID, vpcCIDRBlock, awsRegion, awsSGroup, comment), Check: resource.ComposeTestCheckFunc( - testAccCheckMongoDBAtlasProjectIPAccessListExists(resourceName), - resource.TestCheckResourceAttrSet(resourceName, "project_id"), - resource.TestCheckResourceAttrSet(resourceName, "aws_security_group"), - resource.TestCheckResourceAttrSet(resourceName, "comment"), + testAccCheckMongoDBAtlasProjectIPAccessListExists(dataSourceName), + resource.TestCheckResourceAttrSet(dataSourceName, "project_id"), + resource.TestCheckResourceAttrSet(dataSourceName, "aws_security_group"), + resource.TestCheckResourceAttrSet(dataSourceName, "comment"), - resource.TestCheckResourceAttr(resourceName, "project_id", projectID), - resource.TestCheckResourceAttr(resourceName, "aws_security_group", awsSGroup), - resource.TestCheckResourceAttr(resourceName, "comment", comment), + resource.TestCheckResourceAttr(dataSourceName, "project_id", projectID), + resource.TestCheckResourceAttr(dataSourceName, "aws_security_group", awsSGroup), + resource.TestCheckResourceAttr(dataSourceName, "comment", comment), ), }, { diff --git a/mongodbatlas/fw_provider.go b/mongodbatlas/fw_provider.go index c9164bbb8e..19f0debdfd 100644 --- a/mongodbatlas/fw_provider.go +++ b/mongodbatlas/fw_provider.go @@ -367,6 +367,8 @@ func (p *MongodbtlasProvider) DataSources(context.Context) []func() datasource.D return []func() datasource.DataSource{ NewProjectDS, NewProjectsDS, + NewDatabaseUserDS, + NewDatabaseUsersDS, NewProjectIPAccessListDS, } } From 1f731ef8b4850558946221f080020767336be376 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Mon, 21 Aug 2023 16:34:37 +0100 Subject: [PATCH 6/7] fix --- ..._source_mongodbatlas_project_ip_access_list_migration_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go index 50a868bce2..df640a9041 100644 --- a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go +++ b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list_migration_test.go @@ -69,7 +69,6 @@ func TestAccProjectDSProjectIPAccessList_Migration_SettingCIDRBlock(t *testing.T }, Config: testAccDataMongoDBAtlasProjectIPAccessListConfigSettingCIDRBlock(orgID, projectName, cidrBlock, comment), Check: resource.ComposeTestCheckFunc( - testAccCheckMongoDBAtlasProjectIPAccessListExists(dataSourceName), resource.TestCheckResourceAttrSet(dataSourceName, "project_id"), resource.TestCheckResourceAttrSet(dataSourceName, "cidr_block"), resource.TestCheckResourceAttrSet(dataSourceName, "comment"), From 37a363e9559554cd5e87dc5fc0c8883d441b3b3c Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Mon, 21 Aug 2023 17:35:58 +0100 Subject: [PATCH 7/7] Addressed PR comments (Part 2) --- .../validator/cidr_validator_test.go | 2 +- .../framework/validator/ip_validator.go | 40 +++++++++++ .../framework/validator/ip_validator_test.go | 66 +++++++++++++++++++ ...rce_mongodbatlas_project_ip_access_list.go | 1 + 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 mongodbatlas/framework/validator/ip_validator.go create mode 100644 mongodbatlas/framework/validator/ip_validator_test.go diff --git a/mongodbatlas/framework/validator/cidr_validator_test.go b/mongodbatlas/framework/validator/cidr_validator_test.go index 585976fbfb..2461e20040 100644 --- a/mongodbatlas/framework/validator/cidr_validator_test.go +++ b/mongodbatlas/framework/validator/cidr_validator_test.go @@ -16,7 +16,7 @@ func TestValidCIDR(t *testing.T) { wantErr bool }{ { - name: "Valid URL", + name: "Valid Value", cidr: "192.0.0.0/28", wantErr: false, }, diff --git a/mongodbatlas/framework/validator/ip_validator.go b/mongodbatlas/framework/validator/ip_validator.go new file mode 100644 index 0000000000..c3e599467f --- /dev/null +++ b/mongodbatlas/framework/validator/ip_validator.go @@ -0,0 +1,40 @@ +package validator + +import ( + "context" + "net" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +type IPValidator struct{} + +func (v IPValidator) Description(_ context.Context) string { + return "string value must be defined as a valid IP Address." +} + +func (v IPValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v IPValidator) ValidateString(ctx context.Context, req validator.StringRequest, response *validator.StringResponse) { + // If the value is unknown or null, there is nothing to validate. + if req.ConfigValue.IsUnknown() || req.ConfigValue.IsNull() { + return + } + + value := req.ConfigValue.ValueString() + ip := net.ParseIP(value) + if ip == nil { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + req.Path, + v.Description(ctx), + req.ConfigValue.ValueString(), + )) + } +} + +func ValidIP() validator.String { + return IPValidator{} +} diff --git a/mongodbatlas/framework/validator/ip_validator_test.go b/mongodbatlas/framework/validator/ip_validator_test.go new file mode 100644 index 0000000000..57c6c3a362 --- /dev/null +++ b/mongodbatlas/framework/validator/ip_validator_test.go @@ -0,0 +1,66 @@ +package validator + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestValidIP(t *testing.T) { + tests := []struct { + name string + ip string + wantErr bool + }{ + { + name: "Valid IP v4", + ip: "192.0.2.1", + wantErr: false, + }, + { + name: "Valid IP v6", + ip: "2001:db8::68", + wantErr: false, + }, + { + name: "Valid IP v6", + ip: "::ffff:192.0.2.1", + wantErr: false, + }, + { + name: "invalid IP", + ip: "12312321", + wantErr: true, + }, + { + name: "empty", + ip: "", + wantErr: true, + }, + } + for _, tt := range tests { + val := tt.ip + wantErr := tt.wantErr + cidrValidator := IPValidator{} + + validatorRequest := validator.StringRequest{ + ConfigValue: types.StringValue(val), + } + + validatorResponse := validator.StringResponse{ + Diagnostics: diag.Diagnostics{}, + } + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + cidrValidator.ValidateString(context.Background(), validatorRequest, &validatorResponse) + + if validatorResponse.Diagnostics.HasError() && !wantErr { + t.Errorf("URL() error = %v, wantErr %v", validatorResponse.Diagnostics.Errors(), wantErr) + } + }) + } +} diff --git a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go index e9e583a82d..3c07d6c0c3 100644 --- a/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go +++ b/mongodbatlas/fw_data_source_mongodbatlas_project_ip_access_list.go @@ -78,6 +78,7 @@ func (d *ProjectIPAccessListDS) Schema(ctx context.Context, req datasource.Schem Optional: true, Computed: true, Validators: []validator.String{ + cstmvalidator.ValidIP(), stringvalidator.ConflictsWith(path.Expressions{ path.MatchRelative().AtParent().AtName("aws_security_group"), path.MatchRelative().AtParent().AtName("cidr_block"),