@@ -27,6 +27,41 @@ import (
2727 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2828)
2929
30+ func deleteSpannerBackups (d * schema.ResourceData , config * Config , res map [string ]interface {}, userAgent string , billingProject string ) error {
31+ var v interface {}
32+ var ok bool
33+
34+ v , ok = res ["backups" ]
35+ if ! ok || v == nil {
36+ return nil
37+ }
38+
39+ // Iterate over the list and delete each backup.
40+ for _ , itemRaw := range v .([]interface {}) {
41+ if itemRaw == nil {
42+ continue
43+ }
44+ item := itemRaw .(map [string ]interface {})
45+
46+ backupName := item ["name" ].(string )
47+
48+ log .Printf ("[DEBUG] Found backups for resource %q: %#v)" , d .Id (), item )
49+
50+ path := "{{SpannerBasePath}}" + backupName
51+
52+ url , err := replaceVars (d , config , path )
53+ if err != nil {
54+ return err
55+ }
56+
57+ _ , err = sendRequest (config , "DELETE" , billingProject , url , userAgent , nil )
58+ if err != nil {
59+ return err
60+ }
61+ }
62+ return nil
63+ }
64+
3065func resourceSpannerInstance () * schema.Resource {
3166 return & schema.Resource {
3267 Create : resourceSpannerInstanceCreate ,
@@ -94,6 +129,11 @@ Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.`,
94129 Computed : true ,
95130 Description : `Instance status: 'CREATING' or 'READY'.` ,
96131 },
132+ "force_destroy" : {
133+ Type : schema .TypeBool ,
134+ Optional : true ,
135+ Default : false ,
136+ },
97137 "project" : {
98138 Type : schema .TypeString ,
99139 Optional : true ,
@@ -263,6 +303,12 @@ func resourceSpannerInstanceRead(d *schema.ResourceData, meta interface{}) error
263303 return nil
264304 }
265305
306+ // Explicitly set virtual fields to default values if unset
307+ if _ , ok := d .GetOkExists ("force_destroy" ); ! ok {
308+ if err := d .Set ("force_destroy" , false ); err != nil {
309+ return fmt .Errorf ("Error setting force_destroy: %s" , err )
310+ }
311+ }
266312 if err := d .Set ("project" , project ); err != nil {
267313 return fmt .Errorf ("Error reading Instance: %s" , err )
268314 }
@@ -381,6 +427,24 @@ func resourceSpannerInstanceDelete(d *schema.ResourceData, meta interface{}) err
381427 }
382428
383429 var obj map [string ]interface {}
430+
431+ if d .Get ("force_destroy" ).(bool ) {
432+ backupsUrl , err := replaceVars (d , config , "{{SpannerBasePath}}projects/{{project}}/instances/{{name}}/backups" )
433+ if err != nil {
434+ return err
435+ }
436+
437+ resp , err := sendRequest (config , "GET" , billingProject , backupsUrl , userAgent , nil )
438+ if err != nil {
439+ // API returns 200 if no backups exist but the instance still exists, hence the error check.
440+ return handleNotFoundError (err , d , fmt .Sprintf ("SpannerInstance %q" , d .Id ()))
441+ }
442+
443+ err = deleteSpannerBackups (d , config , resp , billingProject , userAgent )
444+ if err != nil {
445+ return err
446+ }
447+ }
384448 log .Printf ("[DEBUG] Deleting Instance %q" , d .Id ())
385449
386450 // err == nil indicates that the billing_project value was found
@@ -414,6 +478,11 @@ func resourceSpannerInstanceImport(d *schema.ResourceData, meta interface{}) ([]
414478 }
415479 d .SetId (id )
416480
481+ // Explicitly set virtual fields to default values on import
482+ if err := d .Set ("force_destroy" , false ); err != nil {
483+ return nil , fmt .Errorf ("Error setting force_destroy: %s" , err )
484+ }
485+
417486 return []* schema.ResourceData {d }, nil
418487}
419488
0 commit comments