Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide support for deleting pending caches present on bucket, This w… #13527

Merged
merged 4 commits into from
Apr 8, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,15 @@ func TestAccStorageAnywhereCache_update(t *testing.T) {
func testAccStorageAnywhereCache_full(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_storage_bucket" "bucket" {
name = "tf-test-bucket-name%{random_suffix}"
location = "US"
}

resource "time_sleep" "destroy_wait_5000_seconds" {
depends_on = [google_storage_bucket.bucket]
destroy_duration = "5000s"
name = "tf-test-bucket-name%{random_suffix}"
location = "US"
force_destroy = "true"
}

resource "google_storage_anywhere_cache" "cache" {
bucket = google_storage_bucket.bucket.name
zone = "us-central1-f"
ttl = "3601s"
depends_on = [time_sleep.destroy_wait_5000_seconds]
}
`, context)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func ResourceStorageBucket() *schema.Resource {
Type: schema.TypeBool,
Optional: true,
Default: false,
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.`,
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.`,
},

"labels": {
Expand Down Expand Up @@ -590,6 +590,98 @@ func labelKeyValidator(val interface{}, key string) (warns []string, errs []erro
return
}

func getAnywhereCacheListResult(config *transport_tpg.Config, bucket string) ([]interface{}, error) {
// Define the cache list URL
cacheListUrl := fmt.Sprintf("https://storage.googleapis.com/storage/v1/b/%s/anywhereCaches/", bucket)

// Send request to get resource list
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "GET",
Project: config.Project,
RawURL: cacheListUrl,
UserAgent: config.UserAgent,
})
if err != nil {
return nil, err
}

resourceList, ok := res["items"]
if !ok {
return nil, nil // No cache exists, return nil list and no error
}

rl, ok := resourceList.([]interface{})
if !ok {
return nil, fmt.Errorf("unexpected type for resource list: %T", resourceList)
}

return rl, nil
}

func deleteAnywhereCacheIfAny(config *transport_tpg.Config, bucket string) error {
// Get the initial list of Anywhere Caches
cacheList, err := getAnywhereCacheListResult(config, bucket)
if err != nil {
return err
}

// If no cache exists initially, return early
if len(cacheList) == 0 {
return nil
}

// Iterate over each object in the resource list
for _, item := range cacheList {
// Ensure the item is a map
obj, ok := item.(map[string]interface{})
if !ok {
return fmt.Errorf("unexpected type for resource list item: %T", item)
}

// Check the state of the object
state, ok := obj["state"].(string)
if !ok {
continue // If state is not a string, skip this item
}
if state != "running" && state != "paused" {
continue
}

// Disable the cache if state is running or paused
anywhereCacheId, ok := obj["anywhereCacheId"].(string)
if !ok {
return fmt.Errorf("missing or invalid anywhereCacheId: %v", obj)
}
disableUrl := fmt.Sprintf("https://storage.googleapis.com/storage/v1/b/%s/anywhereCaches/%s/disable", bucket, anywhereCacheId)
_, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "POST",
Project: config.Project,
RawURL: disableUrl,
UserAgent: config.UserAgent,
})
if err != nil {
return err
}
}
time.Sleep(80 * time.Minute) // It takes around 70 minutes of time for cache to finally delete post it disable time.

// Post this time, we check again!
// Get the list of Anywhere Caches after the sleep
cacheList, err = getAnywhereCacheListResult(config, bucket)
if err != nil {
return err
}

// Check if the cache list is now empty
if len(cacheList) == 0 {
return nil
}

return fmt.Errorf("error while deleting the cache!")
}

func resourceDataplexLabelDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
if strings.HasPrefix(k, resourceDataplexGoogleProvidedLabelPrefix) && new == "" {
return true
Expand Down Expand Up @@ -992,8 +1084,13 @@ func resourceStorageBucketDelete(d *schema.ResourceData, meta interface{}) error
break
}

if len(res.Items) == 0 {
break // 0 items, bucket empty
cacheList, cacheListErr := getAnywhereCacheListResult(config, bucket)
if cacheListErr != nil {
return cacheListErr
}

if len(res.Items) == 0 && len(cacheList) == 0 {
break // 0 items and no caches, bucket empty
}

if d.Get("retention_policy.0.is_locked").(bool) {
Expand All @@ -1011,10 +1108,11 @@ func resourceStorageBucketDelete(d *schema.ResourceData, meta interface{}) error
}

if !d.Get("force_destroy").(bool) {
deleteErr := fmt.Errorf("Error trying to delete bucket %s containing objects without `force_destroy` set to true", bucket)
deleteErr := fmt.Errorf("Error trying to delete bucket %s without `force_destroy` set to true", bucket)
log.Printf("Error! %s : %s\n\n", bucket, deleteErr)
return deleteErr
}

// GCS requires that a bucket be empty (have no objects or object
// versions) before it can be deleted.
log.Printf("[DEBUG] GCS Bucket attempting to forceDestroy\n\n")
Expand Down Expand Up @@ -1046,6 +1144,12 @@ func resourceStorageBucketDelete(d *schema.ResourceData, meta interface{}) error
})
}

// While we wait our objects to be deleted in background, delete the cache if any!
err = deleteAnywhereCacheIfAny(config, bucket)
if err != nil {
return fmt.Errorf("Error deleting the caches on the bucket %s : %v", bucket, err)
}

// Wait for everything to finish.
wp.StopWait()
}
Expand Down
Loading