Skip to content

Commit

Permalink
chore: Upgrades privatelink_endpoint_serverless resource to auto-ge…
Browse files Browse the repository at this point in the history
…nerated SDK (#1908)

* rename methods

* migrate to new SDK

* add migration test

* fix evalOrder: may want to evaluate p.GetStatus() before the return statement

* add privatelink endpoint serverless to migration tests change detection

* typo

* remove to avoid conflicts

* change mig test name to match regex

* test mig

* Revert "test mig"

This reverts commit 47c9019.

* re-add comment for 404/400 case

* rename mig test
  • Loading branch information
oarbusi committed Feb 6, 2024
1 parent e519c3c commit 3acfe45
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 61 deletions.
Expand Up @@ -8,7 +8,7 @@ import (
"strings"
"time"

matlas "go.mongodb.org/atlas/mongodbatlas"
"go.mongodb.org/atlas-sdk/v20231115005/admin"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
Expand All @@ -27,11 +27,11 @@ const (

func Resource() *schema.Resource {
return &schema.Resource{
CreateContext: resourceMongoDBAtlasPrivateLinkEndpointServerlessCreate,
ReadContext: resourceMongoDBAtlasPrivateLinkEndpointServerlessRead,
DeleteContext: resourceMongoDBAtlasPrivateLinkEndpointServerlessDelete,
CreateContext: resourceCreate,
ReadContext: resourceRead,
DeleteContext: resourceDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceMongoDBAtlasPrivateLinkEndpointServerlessImportState,
StateContext: resourceImport,
},
Schema: map[string]*schema.Schema{
"project_id": {
Expand Down Expand Up @@ -74,57 +74,53 @@ func Resource() *schema.Resource {
}
}

func resourceMongoDBAtlasPrivateLinkEndpointServerlessCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
// Get client connection.
conn := meta.(*config.MongoDBClient).Atlas
func resourceCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
connV2 := meta.(*config.MongoDBClient).AtlasV2
projectID := d.Get("project_id").(string)
instanceName := d.Get("instance_name").(string)

privateLinkRequest := &matlas.ServerlessPrivateEndpointConnection{
Comment: "create",
serverlessTenantCreateRequest := &admin.ServerlessTenantCreateRequest{
Comment: conversion.StringPtr("create"),
}

endPoint, _, err := conn.ServerlessPrivateEndpoints.Create(ctx, projectID, instanceName, privateLinkRequest)
endPoint, _, err := connV2.ServerlessPrivateEndpointsApi.CreateServerlessPrivateEndpoint(ctx, projectID, instanceName, serverlessTenantCreateRequest).Execute()
if err != nil {
return diag.Errorf(privatelinkendpointserviceserverless.ErrorServerlessServiceEndpointAdd, privateLinkRequest.CloudProviderEndpointID, err)
return diag.Errorf(privatelinkendpointserviceserverless.ErrorServerlessServiceEndpointAdd, endPoint.GetCloudProviderEndpointId(), err)
}

stateConf := &retry.StateChangeConf{
Pending: []string{"RESERVATION_REQUESTED", "INITIATING", "DELETING"},
Target: []string{"RESERVED", "FAILED", "DELETED", "AVAILABLE"},
Refresh: resourcePrivateLinkEndpointServerlessRefreshFunc(ctx, conn, projectID, instanceName, endPoint.ID),
Refresh: resourceRefreshFunc(ctx, connV2, projectID, instanceName, endPoint.GetId()),
Timeout: d.Timeout(schema.TimeoutCreate),
MinTimeout: 5 * time.Second,
Delay: 5 * time.Second,
}
// RESERVATION_REQUESTED, RESERVED, INITIATING, AVAILABLE, FAILED, DELETING.
// Wait, catching any errors

_, err = stateConf.WaitForStateContext(ctx)
if err != nil {
return diag.FromErr(fmt.Errorf(errorServerlessEndpointAdd, err, endPoint.ID))
return diag.FromErr(fmt.Errorf(errorServerlessEndpointAdd, err, endPoint.GetId()))
}

d.SetId(conversion.EncodeStateID(map[string]string{
"project_id": projectID,
"instance_name": instanceName,
"endpoint_id": endPoint.ID,
"endpoint_id": endPoint.GetId(),
}))

return resourceMongoDBAtlasPrivateLinkEndpointServerlessRead(ctx, d, meta)
return resourceRead(ctx, d, meta)
}

func resourceMongoDBAtlasPrivateLinkEndpointServerlessRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
// Get client connection.
conn := meta.(*config.MongoDBClient).Atlas
func resourceRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
connV2 := meta.(*config.MongoDBClient).AtlasV2
ids := conversion.DecodeStateID(d.Id())
projectID := ids["project_id"]
instanceName := ids["instance_name"]
endpointID := ids["endpoint_id"]

privateLinkResponse, _, err := conn.ServerlessPrivateEndpoints.Get(ctx, projectID, instanceName, endpointID)
privateLinkResponse, _, err := connV2.ServerlessPrivateEndpointsApi.GetServerlessPrivateEndpoint(ctx, projectID, instanceName, endpointID).Execute()
if err != nil {
// case 404/ 400
// deleted in the backend case
// case 404/400: deleted in the backend case
if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "400") {
d.SetId("")
return nil
Expand All @@ -133,41 +129,39 @@ func resourceMongoDBAtlasPrivateLinkEndpointServerlessRead(ctx context.Context,
return diag.Errorf("error getting Serverless private link endpoint information: %s", err)
}

if err := d.Set("endpoint_id", privateLinkResponse.ID); err != nil {
if err := d.Set("endpoint_id", privateLinkResponse.GetId()); err != nil {
return diag.Errorf("error setting `endpoint_id` for endpoint_id (%s): %s", d.Id(), err)
}

if err := d.Set("instance_name", instanceName); err != nil {
return diag.Errorf("error setting `instance Name` for endpoint_id (%s): %s", d.Id(), err)
}

if err := d.Set("endpoint_service_name", privateLinkResponse.EndpointServiceName); err != nil {
if err := d.Set("endpoint_service_name", privateLinkResponse.GetEndpointServiceName()); err != nil {
return diag.Errorf("error setting `endpoint_service_name Name` for endpoint_id (%s): %s", d.Id(), err)
}

if err := d.Set("private_link_service_resource_id", privateLinkResponse.PrivateLinkServiceResourceID); err != nil {
if err := d.Set("private_link_service_resource_id", privateLinkResponse.GetPrivateLinkServiceResourceId()); err != nil {
return diag.Errorf("error setting `private_link_service_resource_id Name` for endpoint_id (%s): %s", d.Id(), err)
}

if err := d.Set("status", privateLinkResponse.Status); err != nil {
if err := d.Set("status", privateLinkResponse.GetStatus()); err != nil {
return diag.FromErr(fmt.Errorf(privatelinkendpoint.ErrorPrivateLinkEndpointsSetting, "status", d.Id(), err))
}

return nil
}

func resourceMongoDBAtlasPrivateLinkEndpointServerlessDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
// Get client connection.
conn := meta.(*config.MongoDBClient).Atlas
func resourceDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
connV2 := meta.(*config.MongoDBClient).AtlasV2
ids := conversion.DecodeStateID(d.Id())
projectID := ids["project_id"]
instanceName := ids["instance_name"]
endpointID := ids["endpoint_id"]

_, _, err := conn.ServerlessPrivateEndpoints.Get(ctx, projectID, instanceName, endpointID)
_, _, err := connV2.ServerlessPrivateEndpointsApi.GetServerlessPrivateEndpoint(ctx, projectID, instanceName, endpointID).Execute()
if err != nil {
// case 404
// deleted in the backend case
// case 404/400: deleted in the backend case
if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "400") {
d.SetId("")
return nil
Expand All @@ -176,15 +170,15 @@ func resourceMongoDBAtlasPrivateLinkEndpointServerlessDelete(ctx context.Context
return diag.Errorf("error getting Serverless private link endpoint information: %s", err)
}

_, err = conn.ServerlessPrivateEndpoints.Delete(ctx, projectID, instanceName, endpointID)
_, _, err = connV2.ServerlessPrivateEndpointsApi.DeleteServerlessPrivateEndpoint(ctx, projectID, instanceName, endpointID).Execute()
if err != nil {
return diag.Errorf("error deleting serverless private link endpoint(%s): %s", endpointID, err)
}

stateConf := &retry.StateChangeConf{
Pending: []string{"DELETING"},
Target: []string{"DELETED", "FAILED"},
Refresh: resourcePrivateLinkEndpointServerlessRefreshFunc(ctx, conn, projectID, instanceName, endpointID),
Refresh: resourceRefreshFunc(ctx, connV2, projectID, instanceName, endpointID),
Timeout: d.Timeout(schema.TimeoutDelete),
MinTimeout: 5 * time.Second,
Delay: 5 * time.Second,
Expand All @@ -198,8 +192,8 @@ func resourceMongoDBAtlasPrivateLinkEndpointServerlessDelete(ctx context.Context
return nil
}

func resourceMongoDBAtlasPrivateLinkEndpointServerlessImportState(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
conn := meta.(*config.MongoDBClient).Atlas
func resourceImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
connV2 := meta.(*config.MongoDBClient).AtlasV2

parts := strings.SplitN(d.Id(), "--", 3)
if len(parts) != 3 {
Expand All @@ -210,7 +204,7 @@ func resourceMongoDBAtlasPrivateLinkEndpointServerlessImportState(ctx context.Co
instanceName := parts[1]
endpointID := parts[2]

privateLinkResponse, _, err := conn.ServerlessPrivateEndpoints.Get(ctx, projectID, instanceName, endpointID)
privateLinkResponse, _, err := connV2.ServerlessPrivateEndpointsApi.GetServerlessPrivateEndpoint(ctx, projectID, instanceName, endpointID).Execute()
if err != nil {
return nil, fmt.Errorf("couldn't import serverless private link endpoint (%s) in projectID (%s) , error: %s", endpointID, projectID, err)
}
Expand All @@ -226,11 +220,11 @@ func resourceMongoDBAtlasPrivateLinkEndpointServerlessImportState(ctx context.Co
log.Printf("[WARN] Error setting instance_name for (%s): %s", endpointID, err)
}

if err := d.Set("endpoint_service_name", privateLinkResponse.EndpointServiceName); err != nil {
if err := d.Set("endpoint_service_name", privateLinkResponse.GetEndpointServiceName()); err != nil {
log.Printf("[WARN] Error setting endpoint_service_name for (%s): %s", endpointID, err)
}

if privateLinkResponse.PrivateLinkServiceResourceID != "" {
if privateLinkResponse.GetPrivateLinkServiceResourceId() != "" {
if err := d.Set("provider_name", "AZURE"); err != nil {
log.Printf("[WARN] Error setting provider_name for (%s): %s", endpointID, err)
}
Expand All @@ -249,21 +243,22 @@ func resourceMongoDBAtlasPrivateLinkEndpointServerlessImportState(ctx context.Co
return []*schema.ResourceData{d}, nil
}

func resourcePrivateLinkEndpointServerlessRefreshFunc(ctx context.Context, client *matlas.Client, projectID, instanceName, privateLinkID string) retry.StateRefreshFunc {
func resourceRefreshFunc(ctx context.Context, client *admin.APIClient, projectID, instanceName, privateLinkID string) retry.StateRefreshFunc {
return func() (any, string, error) {
p, resp, err := client.ServerlessPrivateEndpoints.Get(ctx, projectID, instanceName, privateLinkID)
p, resp, err := client.ServerlessPrivateEndpointsApi.GetServerlessPrivateEndpoint(ctx, projectID, instanceName, privateLinkID).Execute()
if err != nil {
if resp.Response.StatusCode == 404 || resp.Response.StatusCode == 400 {
if resp.StatusCode == 404 || resp.StatusCode == 400 {
return "", "DELETED", nil
}

return nil, "REJECTED", err
}

if p.Status != "WAITING_FOR_USER" {
return "", p.Status, nil
status := p.GetStatus()

if status != "WAITING_FOR_USER" {
return "", status, nil
}

return p, p.Status, nil
return p, status, nil
}
}
@@ -0,0 +1,46 @@
package privatelinkendpointserverless_test

import (
"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"
"github.com/mongodb/terraform-provider-mongodbatlas/internal/testutil/acc"
"github.com/mongodb/terraform-provider-mongodbatlas/internal/testutil/mig"
)

func TestAccMigrationServerlessPrivateLinkEndpoint_basic(t *testing.T) {
var (
resourceName = "mongodbatlas_privatelink_endpoint_serverless.test"
orgID = os.Getenv("MONGODB_ATLAS_ORG_ID")
projectName = acctest.RandomWithPrefix("test-acc-serverless")
instanceName = "serverlessplink"
)

resource.Test(t, resource.TestCase{
PreCheck: func() { mig.PreCheckBasic(t) },
CheckDestroy: checkDestroy,
Steps: []resource.TestStep{
{
ExternalProviders: mig.ExternalProviders(),
Config: configBasic(orgID, projectName, instanceName, true),
Check: resource.ComposeTestCheckFunc(
checkExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "instance_name", instanceName),
),
},
{
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
Config: configBasic(orgID, projectName, instanceName, true),
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
acc.DebugPlan(),
plancheck.ExpectEmptyPlan(),
},
},
},
},
})
}
Expand Up @@ -24,12 +24,12 @@ func TestAccServerlessPrivateLinkEndpoint_basic(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acc.PreCheckBasic(t) },
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
CheckDestroy: testAccCheckMongoDBAtlasPrivateLinkEndpointServerlessDestroy,
CheckDestroy: checkDestroy,
Steps: []resource.TestStep{
{
Config: testAccMongoDBAtlasPrivateLinkEndpointServerlessConfig(orgID, projectName, instanceName, true),
Config: configBasic(orgID, projectName, instanceName, true),
Check: resource.ComposeTestCheckFunc(
testAccCheckMongoDBAtlasPrivateLinkEndpointServerlessExists(resourceName),
checkExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "instance_name", instanceName),
),
},
Expand All @@ -47,18 +47,18 @@ func TestAccServerlessPrivateLinkEndpoint_importBasic(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acc.PreCheckBasic(t) },
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
CheckDestroy: testAccCheckMongoDBAtlasPrivateLinkEndpointServerlessDestroy,
CheckDestroy: checkDestroy,
Steps: []resource.TestStep{
{
Config: testAccMongoDBAtlasPrivateLinkEndpointServerlessConfig(orgID, projectName, instanceName, true),
Config: configBasic(orgID, projectName, instanceName, true),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "instance_name", instanceName),
),
},
{
Config: testAccMongoDBAtlasPrivateLinkEndpointServerlessConfig(orgID, projectName, instanceName, false),
Config: configBasic(orgID, projectName, instanceName, false),
ResourceName: resourceName,
ImportStateIdFunc: testAccCheckMongoDBAtlasPrivateLinkEndpointServerlessImportStateIDFunc(resourceName),
ImportStateIdFunc: importStateIDFuncBasic(resourceName),
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"connection_strings_private_endpoint_srv"},
Expand All @@ -67,21 +67,21 @@ func TestAccServerlessPrivateLinkEndpoint_importBasic(t *testing.T) {
})
}

func testAccCheckMongoDBAtlasPrivateLinkEndpointServerlessDestroy(state *terraform.State) error {
func checkDestroy(state *terraform.State) error {
for _, rs := range state.RootModule().Resources {
if rs.Type != "mongodbatlas_privatelink_endpoint_serverless" {
continue
}
ids := conversion.DecodeStateID(rs.Primary.ID)
privateLink, _, err := acc.Conn().ServerlessPrivateEndpoints.Get(context.Background(), ids["project_id"], ids["instance_name"], ids["endpoint_id"])
privateLink, _, err := acc.ConnV2().ServerlessPrivateEndpointsApi.GetServerlessPrivateEndpoint(context.Background(), ids["project_id"], ids["instance_name"], ids["endpoint_id"]).Execute()
if err == nil && privateLink != nil {
return fmt.Errorf("endpoint_id (%s) still exists", ids["endpoint_id"])
}
}
return nil
}

func testAccMongoDBAtlasPrivateLinkEndpointServerlessConfig(orgID, projectName, instanceName string, ignoreConnectionStrings bool) string {
func configBasic(orgID, projectName, instanceName string, ignoreConnectionStrings bool) string {
return fmt.Sprintf(`
resource "mongodbatlas_privatelink_endpoint_serverless" "test" {
Expand All @@ -94,7 +94,7 @@ func testAccMongoDBAtlasPrivateLinkEndpointServerlessConfig(orgID, projectName,
`, acc.ConfigServerlessInstanceBasic(orgID, projectName, instanceName, ignoreConnectionStrings))
}

func testAccCheckMongoDBAtlasPrivateLinkEndpointServerlessExists(resourceName string) resource.TestCheckFunc {
func checkExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
Expand All @@ -104,15 +104,15 @@ func testAccCheckMongoDBAtlasPrivateLinkEndpointServerlessExists(resourceName st
return fmt.Errorf("no ID is set")
}
ids := conversion.DecodeStateID(rs.Primary.ID)
_, _, err := acc.Conn().ServerlessPrivateEndpoints.Get(context.Background(), ids["project_id"], ids["instance_name"], ids["endpoint_id"])
_, _, err := acc.ConnV2().ServerlessPrivateEndpointsApi.GetServerlessPrivateEndpoint(context.Background(), ids["project_id"], ids["instance_name"], ids["endpoint_id"]).Execute()
if err == nil {
return nil
}
return fmt.Errorf("endpoint_id (%s) does not exist", ids["endpoint_id"])
}
}

func testAccCheckMongoDBAtlasPrivateLinkEndpointServerlessImportStateIDFunc(resourceName string) resource.ImportStateIdFunc {
func importStateIDFuncBasic(resourceName string) resource.ImportStateIdFunc {
return func(s *terraform.State) (string, error) {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
Expand Down

0 comments on commit 3acfe45

Please sign in to comment.