Skip to content

Commit da7fca3

Browse files
Add force_destroy to spanner instance (#4644) (#3227)
Co-authored-by: upodroid <[email protected]> Signed-off-by: Modular Magician <[email protected]> Co-authored-by: upodroid <[email protected]>
1 parent 681cbdc commit da7fca3

File tree

5 files changed

+76
-125
lines changed

5 files changed

+76
-125
lines changed

.changelog/4644.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
spanner: added `force_destroy` to `google_spanner_instance` to delete instances that have backups enabled.
3+
```

google-beta/resource_dataflow_flex_template_job_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88

99
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
1010
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
11-
compute "google.golang.org/api/compute/v1"
11+
"google.golang.org/api/compute/v1"
1212
)
1313

1414
func TestAccDataflowFlexTemplateJob_basic(t *testing.T) {

google-beta/resource_spanner_instance.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
3065
func 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

google-beta/resource_spanner_instance_sweeper_test.go

Lines changed: 0 additions & 124 deletions
This file was deleted.

website/docs/r/spanner_instance.html.markdown

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ The following arguments are supported:
113113
* `project` - (Optional) The ID of the project in which the resource belongs.
114114
If it is not provided, the provider project is used.
115115

116+
* `force_destroy` - (Optional) When deleting a spanner instance, this boolean option will delete all backups of this instance.
117+
This must be set to true if you created a backup manually in the console.
118+
116119

117120
## Attributes Reference
118121

0 commit comments

Comments
 (0)