Skip to content

Commit 2697cb8

Browse files
Provide support for deleting pending caches present on bucket, This w… (#13527) (#9737)
[upstream:fdd172e3ddfed3f384d598048c2e6549899bd07b] Signed-off-by: Modular Magician <[email protected]>
1 parent 81d3af3 commit 2697cb8

File tree

2 files changed

+115
-20
lines changed

2 files changed

+115
-20
lines changed

Diff for: google-beta/services/storage/resource_storage_anywhere_cache_test.go

+6-16
Original file line numberDiff line numberDiff line change
@@ -55,42 +55,32 @@ func TestAccStorageAnywhereCache_update(t *testing.T) {
5555
func testAccStorageAnywhereCache_full(context map[string]interface{}) string {
5656
return acctest.Nprintf(`
5757
resource "google_storage_bucket" "bucket" {
58-
name = "tf-test-bucket-name%{random_suffix}"
59-
location = "US"
60-
}
61-
62-
resource "time_sleep" "destroy_wait_5000_seconds" {
63-
depends_on = [google_storage_bucket.bucket]
64-
destroy_duration = "5000s"
58+
name = "tf-test-bucket-name%{random_suffix}"
59+
location = "US"
60+
force_destroy = "true"
6561
}
6662
6763
resource "google_storage_anywhere_cache" "cache" {
6864
bucket = google_storage_bucket.bucket.name
6965
zone = "us-central1-f"
7066
ttl = "3601s"
71-
depends_on = [time_sleep.destroy_wait_5000_seconds]
7267
}
7368
`, context)
7469
}
7570

7671
func testAccStorageAnywhereCache_update(context map[string]interface{}) string {
7772
return acctest.Nprintf(`
7873
resource "google_storage_bucket" "bucket" {
79-
name = "tf-test-bucket-name%{random_suffix}"
80-
location = "US"
81-
}
82-
83-
resource "time_sleep" "destroy_wait_5000_seconds" {
84-
depends_on = [google_storage_bucket.bucket]
85-
destroy_duration = "5000s"
74+
name = "tf-test-bucket-name%{random_suffix}"
75+
location = "US"
76+
force_destroy = "true"
8677
}
8778
8879
resource "google_storage_anywhere_cache" "cache" {
8980
bucket = google_storage_bucket.bucket.name
9081
zone = "us-central1-f"
9182
admission_policy = "admit-on-second-miss"
9283
ttl = "3620s"
93-
depends_on = [time_sleep.destroy_wait_5000_seconds]
9484
}
9585
`, context)
9686
}

Diff for: google-beta/services/storage/resource_storage_bucket.go

+109-4
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func ResourceStorageBucket() *schema.Resource {
103103
Type: schema.TypeBool,
104104
Optional: true,
105105
Default: false,
106-
Description: `When deleting a bucket, this boolean option will delete all contained objects. If you try to delete a bucket that contains objects, Terraform will fail that run.`,
106+
Description: `When deleting a bucket, this boolean option will delete all contained objects, or anywhereCaches (if any). If you try to delete a bucket that contains objects or anywhereCaches, Terraform will fail that run, deleting anywhereCaches may take 80 minutes to complete.`,
107107
},
108108

109109
"labels": {
@@ -592,6 +592,98 @@ func labelKeyValidator(val interface{}, key string) (warns []string, errs []erro
592592
return
593593
}
594594

595+
func getAnywhereCacheListResult(config *transport_tpg.Config, bucket string) ([]interface{}, error) {
596+
// Define the cache list URL
597+
cacheListUrl := fmt.Sprintf("https://storage.googleapis.com/storage/v1/b/%s/anywhereCaches/", bucket)
598+
599+
// Send request to get resource list
600+
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
601+
Config: config,
602+
Method: "GET",
603+
Project: config.Project,
604+
RawURL: cacheListUrl,
605+
UserAgent: config.UserAgent,
606+
})
607+
if err != nil {
608+
return nil, err
609+
}
610+
611+
resourceList, ok := res["items"]
612+
if !ok {
613+
return nil, nil // No cache exists, return nil list and no error
614+
}
615+
616+
rl, ok := resourceList.([]interface{})
617+
if !ok {
618+
return nil, fmt.Errorf("unexpected type for resource list: %T", resourceList)
619+
}
620+
621+
return rl, nil
622+
}
623+
624+
func deleteAnywhereCacheIfAny(config *transport_tpg.Config, bucket string) error {
625+
// Get the initial list of Anywhere Caches
626+
cacheList, err := getAnywhereCacheListResult(config, bucket)
627+
if err != nil {
628+
return err
629+
}
630+
631+
// If no cache exists initially, return early
632+
if len(cacheList) == 0 {
633+
return nil
634+
}
635+
636+
// Iterate over each object in the resource list
637+
for _, item := range cacheList {
638+
// Ensure the item is a map
639+
obj, ok := item.(map[string]interface{})
640+
if !ok {
641+
return fmt.Errorf("unexpected type for resource list item: %T", item)
642+
}
643+
644+
// Check the state of the object
645+
state, ok := obj["state"].(string)
646+
if !ok {
647+
continue // If state is not a string, skip this item
648+
}
649+
if !strings.EqualFold(state, "running") && !strings.EqualFold(state, "paused") {
650+
continue
651+
}
652+
653+
// Disable the cache if state is running or paused
654+
anywhereCacheId, ok := obj["anywhereCacheId"].(string)
655+
if !ok {
656+
return fmt.Errorf("missing or invalid anywhereCacheId: %v", obj)
657+
}
658+
disableUrl := fmt.Sprintf("https://storage.googleapis.com/storage/v1/b/%s/anywhereCaches/%s/disable", bucket, anywhereCacheId)
659+
_, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
660+
Config: config,
661+
Method: "POST",
662+
Project: config.Project,
663+
RawURL: disableUrl,
664+
UserAgent: config.UserAgent,
665+
})
666+
if err != nil {
667+
return err
668+
}
669+
}
670+
time.Sleep(80 * time.Minute) // It takes around 70 minutes of time for cache to finally delete post it disable time.
671+
672+
// Post this time, we check again!
673+
// Get the list of Anywhere Caches after the sleep
674+
cacheList, err = getAnywhereCacheListResult(config, bucket)
675+
if err != nil {
676+
return err
677+
}
678+
679+
// Check if the cache list is now empty
680+
if len(cacheList) == 0 {
681+
return nil
682+
}
683+
684+
return fmt.Errorf("Error while deleting the cache: caches still exists post 80mins of their disable time")
685+
}
686+
595687
func resourceDataplexLabelDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
596688
if strings.HasPrefix(k, resourceDataplexGoogleProvidedLabelPrefix) && new == "" {
597689
return true
@@ -994,8 +1086,13 @@ func resourceStorageBucketDelete(d *schema.ResourceData, meta interface{}) error
9941086
break
9951087
}
9961088

997-
if len(res.Items) == 0 {
998-
break // 0 items, bucket empty
1089+
cacheList, cacheListErr := getAnywhereCacheListResult(config, bucket)
1090+
if cacheListErr != nil {
1091+
return cacheListErr
1092+
}
1093+
1094+
if len(res.Items) == 0 && len(cacheList) == 0 {
1095+
break // 0 items and no caches, bucket empty
9991096
}
10001097

10011098
if d.Get("retention_policy.0.is_locked").(bool) {
@@ -1013,10 +1110,11 @@ func resourceStorageBucketDelete(d *schema.ResourceData, meta interface{}) error
10131110
}
10141111

10151112
if !d.Get("force_destroy").(bool) {
1016-
deleteErr := fmt.Errorf("Error trying to delete bucket %s containing objects without `force_destroy` set to true", bucket)
1113+
deleteErr := fmt.Errorf("Error trying to delete bucket %s without `force_destroy` set to true", bucket)
10171114
log.Printf("Error! %s : %s\n\n", bucket, deleteErr)
10181115
return deleteErr
10191116
}
1117+
10201118
// GCS requires that a bucket be empty (have no objects or object
10211119
// versions) before it can be deleted.
10221120
log.Printf("[DEBUG] GCS Bucket attempting to forceDestroy\n\n")
@@ -1033,6 +1131,13 @@ func resourceStorageBucketDelete(d *schema.ResourceData, meta interface{}) error
10331131
// is it scheduled to be plumbed to individual providers.
10341132
wp := workerpool.New(runtime.NumCPU() - 1)
10351133

1134+
wp.Submit(func() {
1135+
err = deleteAnywhereCacheIfAny(config, bucket)
1136+
if err != nil {
1137+
deleteObjectError = fmt.Errorf("error deleting the caches on the bucket %s : %w", bucket, err)
1138+
}
1139+
})
1140+
10361141
for _, object := range res.Items {
10371142
log.Printf("[DEBUG] Found %s", object.Name)
10381143
object := object

0 commit comments

Comments
 (0)