Skip to content

Commit 6a4819a

Browse files
INTMDB-302: Ensure we handle new flow for project deletion well (#688)
* Before deleting a project, wait for any currently deleting clusters to finish deleting * First pass at adding a test which ensures project is deleted properly when it has an advanced_cluster * Removed clusterCount assertion and put in custom resource for advanced_cluster * Fix naming on AdvancedClusters response value Co-authored-by: Andrea Angiolillo <[email protected]> * Added concept of project dependents and added comments, using ErrorResponse.As * Fixed case where there are no dependencies * Reduced timeout to 30 minutes * Using else instead of otherwise in comment * Fixed formatting in project_test * Removed a state no longer in use Co-authored-by: Andrea Angiolillo <[email protected]>
1 parent db20884 commit 6a4819a

File tree

3 files changed

+127
-2
lines changed

3 files changed

+127
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ website/node_modules
2121
*~
2222
.*.swp
2323
.idea
24+
.vscode
2425
*.iml
2526
*.test
2627
*.iml

mongodbatlas/resource_mongodbatlas_project.go

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import (
55
"errors"
66
"log"
77
"net/http"
8+
"time"
89

910
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
1012
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1113
"github.com/mwielbut/pointy"
1214
matlas "go.mongodb.org/atlas/mongodbatlas"
@@ -99,6 +101,11 @@ func resourceMongoDBAtlasProject() *schema.Resource {
99101
}
100102
}
101103

104+
// Resources that need to be cleaned up before a project can be deleted
105+
type AtlastProjectDependents struct {
106+
AdvancedClusters *matlas.AdvancedClustersResponse
107+
}
108+
102109
func resourceMongoDBAtlasProjectCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
103110
// Get client connection.
104111
conn := meta.(*MongoDBClient).Atlas
@@ -292,14 +299,67 @@ func resourceMongoDBAtlasProjectDelete(ctx context.Context, d *schema.ResourceDa
292299
conn := meta.(*MongoDBClient).Atlas
293300
projectID := d.Id()
294301

295-
_, err := conn.Projects.Delete(ctx, projectID)
302+
stateConf := &resource.StateChangeConf{
303+
Pending: []string{"DELETING", "RETRY"},
304+
Target: []string{"IDLE"},
305+
Refresh: resourceProjectDependentsDeletingRefreshFunc(ctx, projectID, conn),
306+
Timeout: 30 * time.Minute,
307+
MinTimeout: 30 * time.Second,
308+
Delay: 0,
309+
}
310+
311+
_, err := stateConf.WaitForStateContext(ctx)
312+
313+
if err != nil {
314+
log.Printf("[ERROR] could not determine MongoDB project %s dependents status: %s", projectID, err.Error())
315+
}
316+
317+
_, err = conn.Projects.Delete(ctx, projectID)
318+
296319
if err != nil {
297320
return diag.Errorf(errorProjectDelete, projectID, err)
298321
}
299322

300323
return nil
301324
}
302325

326+
/*
327+
This assumes the project CRUD outcome will be the same for any non-zero number of dependents
328+
329+
If all dependents are deleting, wait to try and delete
330+
Else consider the aggregate dependents idle.
331+
332+
If we get a defined error response, return that right away
333+
Else retry
334+
*/
335+
func resourceProjectDependentsDeletingRefreshFunc(ctx context.Context, projectID string, client *matlas.Client) resource.StateRefreshFunc {
336+
return func() (interface{}, string, error) {
337+
var target *matlas.ErrorResponse
338+
clusters, _, err := client.AdvancedClusters.List(ctx, projectID, nil)
339+
dependents := AtlastProjectDependents{AdvancedClusters: clusters}
340+
341+
if errors.As(err, &target) {
342+
return nil, "", err
343+
} else if err != nil {
344+
return nil, "RETRY", nil
345+
}
346+
347+
if dependents.AdvancedClusters.TotalCount == 0 {
348+
return dependents, "IDLE", nil
349+
}
350+
351+
for _, v := range dependents.AdvancedClusters.Results {
352+
if v.StateName != "DELETING" {
353+
return dependents, "IDLE", nil
354+
}
355+
}
356+
357+
log.Printf("[DEBUG] status for MongoDB project %s dependents: %s", projectID, "DELETING")
358+
359+
return dependents, "DELETING", nil
360+
}
361+
}
362+
303363
func expandTeamsSet(teams *schema.Set) []*matlas.ProjectTeam {
304364
res := make([]*matlas.ProjectTeam, teams.Len())
305365

mongodbatlas/resource_mongodbatlas_project_test.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,37 @@ func testAccCheckMongoDBAtlasProjectExists(resourceName string, project *matlas.
293293
}
294294
}
295295

296+
func TestAccResourceMongoDBAtlasProject_CreateWithAdvancedCluster(t *testing.T) {
297+
var (
298+
project matlas.Project
299+
cluster matlas.AdvancedCluster
300+
clusterResourceName = "mongodbatlas_advanced_cluster.test"
301+
resourceName = "mongodbatlas_project.test"
302+
clusterName = fmt.Sprintf("testacc-project-%s", acctest.RandString(10))
303+
projectName = fmt.Sprintf("testacc-project-%s", acctest.RandString(10))
304+
orgID = os.Getenv("MONGODB_ATLAS_ORG_ID")
305+
projectOwnerID = os.Getenv("MONGODB_ATLAS_PROJECT_OWNER_ID")
306+
)
307+
308+
resource.ParallelTest(t, resource.TestCase{
309+
PreCheck: func() { testAccPreCheck(t) },
310+
ProviderFactories: testAccProviderFactories,
311+
CheckDestroy: testAccCheckMongoDBAtlasProjectDestroy,
312+
Steps: []resource.TestStep{
313+
{
314+
Config: testAccMongoDBAtlasProjectConfigWithAdvancedCluster(projectName, orgID, projectOwnerID, clusterName),
315+
Check: resource.ComposeTestCheckFunc(
316+
testAccCheckMongoDBAtlasProjectExists(resourceName, &project),
317+
testAccCheckMongoDBAtlasAdvancedClusterExists(clusterResourceName, &cluster),
318+
testAccCheckMongoDBAtlasProjectAttributes(&project, projectName),
319+
resource.TestCheckResourceAttr(resourceName, "name", projectName),
320+
resource.TestCheckResourceAttr(resourceName, "org_id", orgID),
321+
),
322+
},
323+
},
324+
})
325+
}
326+
296327
func testAccCheckMongoDBAtlasProjectAttributes(project *matlas.Project, projectName string) resource.TestCheckFunc {
297328
return func(s *terraform.State) error {
298329
if project.Name != projectName {
@@ -391,8 +422,41 @@ func testAccMongoDBAtlasProjectConfigWithFalseDefaultSettings(projectName, orgID
391422
resource "mongodbatlas_project" "test" {
392423
name = "%[1]s"
393424
org_id = "%[2]s"
394-
project_owner_id = "%[3]s"
425+
project_owner_id = "%[3]s"
395426
with_default_alerts_settings = false
396427
}
397428
`, projectName, orgID, projectOwnerID)
398429
}
430+
431+
func testAccMongoDBAtlasProjectConfigWithAdvancedCluster(projectName, orgID, projectOwnerID, clusterName string) string {
432+
return fmt.Sprintf(`
433+
resource "mongodbatlas_project" "test" {
434+
name = %[1]q
435+
org_id = %[2]q
436+
project_owner_id = %[3]q
437+
with_default_alerts_settings = false
438+
}
439+
440+
resource "mongodbatlas_advanced_cluster" "test" {
441+
project_id = mongodbatlas_project.test.id
442+
name = %[4]q
443+
cluster_type = "REPLICASET"
444+
445+
replication_specs {
446+
region_configs {
447+
electable_specs {
448+
instance_size = "M10"
449+
node_count = 3
450+
}
451+
analytics_specs {
452+
instance_size = "M10"
453+
node_count = 1
454+
}
455+
provider_name = "AWS"
456+
priority = 7
457+
region_name = "US_EAST_1"
458+
}
459+
}
460+
}
461+
`, projectName, orgID, projectOwnerID, clusterName)
462+
}

0 commit comments

Comments
 (0)