Skip to content

Commit cf9a131

Browse files
karolgorcc2thorn
authored andcommitted
Add a functionality to create a snapshot before a disk is deleted (GoogleCloudPlatform#12947)
Co-authored-by: Cameron Thornton <[email protected]>
1 parent 28570c7 commit cf9a131

File tree

5 files changed

+281
-0
lines changed

5 files changed

+281
-0
lines changed

mmv1/products/compute/Disk.yaml

+12
Original file line numberDiff line numberDiff line change
@@ -567,3 +567,15 @@ properties:
567567
default_from_api: true
568568
update_url: 'projects/{{project}}/zones/{{zone}}/disks/{{name}}?paths=accessMode'
569569
update_verb: 'PATCH'
570+
virtual_fields:
571+
- name: 'create_snapshot_before_destroy'
572+
type: Boolean
573+
default_value: false
574+
description: |
575+
If set to true, a snapshot of the disk will be created before it is destroyed.
576+
If your disk is encrypted with customer managed encryption keys these will be reused for the snapshot creation.
577+
The name of the snapshot by default will be `{{disk-name}}-YYYYMMDD-HHmm`
578+
- name: 'create_snapshot_before_destroy_prefix'
579+
type: String
580+
description: |
581+
This will set a custom name prefix for the snapshot that's created when the disk is deleted.

mmv1/products/compute/RegionDisk.yaml

+19
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ properties:
133133
Specifies a 256-bit customer-supplied encryption key, encoded in
134134
RFC 4648 base64 to either encrypt or decrypt this resource.
135135
sensitive: true
136+
- name: 'rsaEncryptedKey'
137+
type: String
138+
description: |
139+
Specifies an RFC 4648 base64 encoded, RSA-wrapped 2048-bit
140+
customer-supplied encryption key to either encrypt or decrypt
141+
this resource. You can provide either the rawKey or the rsaEncryptedKey.
142+
sensitive: true
136143
- name: 'sha256'
137144
type: String
138145
description: |
@@ -363,3 +370,15 @@ properties:
363370
description: 'An applicable license URI'
364371
resource: 'License'
365372
imports: 'selfLink'
373+
virtual_fields:
374+
- name: 'create_snapshot_before_destroy'
375+
type: Boolean
376+
default_value: false
377+
description: |
378+
If set to true, a snapshot of the disk will be created before it is destroyed.
379+
If your disk is encrypted with customer managed encryption keys these will be reused for the snapshot creation.
380+
The name of the snapshot by default will be `{{disk-name}}-YYYYMMDD-HHmm`
381+
- name: 'create_snapshot_before_destroy_prefix'
382+
type: String
383+
description: |
384+
This will set a custom name prefix for the snapshot that's created when the disk is deleted.

mmv1/templates/terraform/pre_delete/detach_disk.tmpl

+47
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,53 @@ if err != nil {
1515
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("ComputeDisk %q", d.Id()))
1616
}
1717

18+
// if the create_snapshot_before_destroy is set to true then create a snapshot before deleting the disk
19+
if d.Get("create_snapshot_before_destroy").(bool) {
20+
instanceName := d.Get("name").(string)
21+
nameOrigin := "disk"
22+
if d.Get("create_snapshot_before_destroy_prefix").(string) != "" {
23+
instanceName = d.Get("create_snapshot_before_destroy_prefix").(string)
24+
nameOrigin = "create_snapshot_before_destroy_prefix"
25+
}
26+
27+
if len(instanceName) > 48 {
28+
return fmt.Errorf(`Your %s name is too long to perform this action. The max is 48 characters. Please use "create_snapshot_before_destroy_prefix" to set a custom name for the snapshot.`, nameOrigin)
29+
}
30+
31+
snapshotObj := &compute.Snapshot{
32+
Name: fmt.Sprintf("%s-%s", instanceName, time.Now().Format("20060102-150405")),
33+
SourceDisk: d.Get("self_link").(string),
34+
}
35+
36+
//Handling encryption
37+
if d.Get("disk_encryption_key.0.raw_key").(string) != "" {
38+
snapshotObj.SourceDiskEncryptionKey = &compute.CustomerEncryptionKey{
39+
RawKey: d.Get("disk_encryption_key.0.raw_key").(string),
40+
}
41+
snapshotObj.SnapshotEncryptionKey = &compute.CustomerEncryptionKey{
42+
RawKey: d.Get("disk_encryption_key.0.raw_key").(string),
43+
}
44+
}
45+
46+
if d.Get("disk_encryption_key.0.rsa_encrypted_key").(string) != "" {
47+
snapshotObj.SourceDiskEncryptionKey = &compute.CustomerEncryptionKey{
48+
RsaEncryptedKey: d.Get("disk_encryption_key.0.rsa_encrypted_key").(string),
49+
}
50+
snapshotObj.SnapshotEncryptionKey = &compute.CustomerEncryptionKey{
51+
RsaEncryptedKey: d.Get("disk_encryption_key.0.rsa_encrypted_key").(string),
52+
}
53+
}
54+
55+
snapshot, err := config.NewComputeClient(userAgent).Snapshots.Insert(project, snapshotObj).Do()
56+
if err != nil {
57+
return fmt.Errorf("Error creating snapshot: %s", err)
58+
}
59+
err = ComputeOperationWaitTime(config, snapshot, project, "Creating Snapshot", userAgent, d.Timeout(schema.TimeoutCreate))
60+
if err != nil {
61+
return err
62+
}
63+
}
64+
1865
// if disks are attached to instances, they must be detached before the disk can be deleted
1966
if v, ok := readRes["users"].([]interface{}); ok {
2067
type detachArgs struct{ project, zone, instance, deviceName string }

mmv1/third_party/terraform/services/compute/resource_compute_disk_test.go.tmpl

+110
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"testing"
7+
"time"
78

89
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
910
"github.com/hashicorp/terraform-plugin-testing/terraform"
@@ -896,6 +897,26 @@ func testAccCheckEncryptionKey(t *testing.T, n string, disk *compute.Disk) resou
896897
}
897898
}
898899

900+
func testAccCheckComputeDisk_removeBackupSnapshot(t *testing.T, parentDiskName string) resource.TestCheckFunc {
901+
return func(s *terraform.State) error {
902+
config := acctest.GoogleProviderConfig(t)
903+
snapshot, err := config.NewComputeClient(config.UserAgent).Snapshots.List(envvar.GetTestProjectFromEnv()).Filter(fmt.Sprintf("name eq %s.*", parentDiskName)).Do()
904+
if err != nil {
905+
return err
906+
}
907+
908+
if len(snapshot.Items) == 0 {
909+
return fmt.Errorf("No snapshot found")
910+
}
911+
912+
op, err := config.NewComputeClient(config.UserAgent).Snapshots.Delete(envvar.GetTestProjectFromEnv(), snapshot.Items[0].Name).Do()
913+
if err != nil {
914+
return err
915+
}
916+
return tpgcompute.ComputeOperationWaitTime(config, op, envvar.GetTestProjectFromEnv(), "Deleting Snapshot", config.UserAgent, 10*time.Minute)
917+
}
918+
}
919+
899920
func TestAccComputeDisk_cloneDisk(t *testing.T) {
900921
t.Parallel()
901922
pid := envvar.GetTestProjectFromEnv()
@@ -1070,6 +1091,52 @@ func TestAccComputeDisk_featuresUpdated(t *testing.T) {
10701091
})
10711092
}
10721093

1094+
func TestAccComputeDisk_createSnapshotBeforeDestroy(t *testing.T) {
1095+
acctest.SkipIfVcr(t) // Disk cleanup test check
1096+
t.Parallel()
1097+
1098+
var disk1 compute.Disk
1099+
var disk2 compute.Disk
1100+
var disk3 compute.Disk
1101+
context := map[string]interface{}{
1102+
"disk_name1": fmt.Sprintf("tf-test-disk-%s", acctest.RandString(t, 10)),
1103+
"disk_name2": fmt.Sprintf("test-%s", acctest.RandString(t, 44)), //this is over the snapshot character creation limit of 48
1104+
"disk_name3": fmt.Sprintf("tf-test-disk-%s", acctest.RandString(t, 10)),
1105+
"snapshot_prefix": fmt.Sprintf("tf-test-snapshot-%s", acctest.RandString(t, 10)),
1106+
"kms_key_self_link": acctest.BootstrapKMSKey(t).CryptoKey.Name,
1107+
"raw_key": "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=",
1108+
"rsa_encrypted_key": "ieCx/NcW06PcT7Ep1X6LUTc/hLvUDYyzSZPPVCVPTVEohpeHASqC8uw5TzyO9U+Fka9JFHz0mBibXUInrC/jEk014kCK/NPjYgEMOyssZ4ZINPKxlUh2zn1bV+MCaTICrdmuSBTWlUUiFoDD6PYznLwh8ZNdaheCeZ8ewEXgFQ8V+sDroLaN3Xs3MDTXQEMMoNUXMCZEIpg9Vtp9x2oeQ5lAbtt7bYAAHf5l+gJWw3sUfs0/Glw5fpdjT8Uggrr+RMZezGrltJEF293rvTIjWOEB3z5OHyHwQkvdrPDFcTqsLfh+8Hr8g+mf+7zVPEC8nEbqpdl3GPv3A7AwpFp7MA==",
1109+
}
1110+
1111+
acctest.VcrTest(t, resource.TestCase{
1112+
PreCheck: func() { acctest.AccTestPreCheck(t) },
1113+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
1114+
CheckDestroy: testAccCheckComputeDiskDestroyProducer(t),
1115+
Steps: []resource.TestStep{
1116+
{
1117+
Config: testAccComputeDisk_createSnapshotBeforeDestroy_init(context),
1118+
Check: resource.ComposeTestCheckFunc(
1119+
testAccCheckComputeDiskExists(
1120+
t, "google_compute_disk.raw-encrypted-name", envvar.GetTestProjectFromEnv(), &disk1),
1121+
testAccCheckComputeDiskExists(
1122+
t, "google_compute_disk.rsa-encrypted-prefix", envvar.GetTestProjectFromEnv(), &disk2),
1123+
testAccCheckComputeDiskExists(
1124+
t, "google_compute_disk.kms-encrypted-name", envvar.GetTestProjectFromEnv(), &disk3),
1125+
),
1126+
},
1127+
{
1128+
Config: testAccComputeDisk_createSnapshotBeforeDestroy_init(context),
1129+
Destroy: true,
1130+
Check: resource.ComposeTestCheckFunc(
1131+
testAccCheckComputeDisk_removeBackupSnapshot(t, context["disk_name1"].(string)),
1132+
testAccCheckComputeDisk_removeBackupSnapshot(t, context["snapshot_prefix"].(string)),
1133+
testAccCheckComputeDisk_removeBackupSnapshot(t, context["disk_name3"].(string)),
1134+
),
1135+
},
1136+
},
1137+
})
1138+
}
1139+
10731140

10741141
func testAccComputeDisk_basic(diskName string, diskType string) string {
10751142
return fmt.Sprintf(`
@@ -2017,6 +2084,49 @@ resource "google_compute_disk" "foobar" {
20172084
`, diskName, accessMode)
20182085
}
20192086

2087+
func testAccComputeDisk_createSnapshotBeforeDestroy_init(context map[string]interface{}) string {
2088+
return acctest.Nprintf(`
2089+
resource "google_compute_disk" "raw-encrypted-name" {
2090+
name = "%{disk_name1}"
2091+
type = "pd-ssd"
2092+
size = 10
2093+
zone = "us-central1-a"
2094+
2095+
disk_encryption_key {
2096+
raw_key = "%{raw_key}"
2097+
}
2098+
2099+
create_snapshot_before_destroy = true
2100+
}
2101+
2102+
resource "google_compute_disk" "rsa-encrypted-prefix" {
2103+
name = "%{disk_name2}"
2104+
type = "pd-ssd"
2105+
size = 10
2106+
zone = "us-central1-a"
2107+
2108+
disk_encryption_key {
2109+
rsa_encrypted_key = "%{rsa_encrypted_key}"
2110+
}
2111+
2112+
create_snapshot_before_destroy = true
2113+
create_snapshot_before_destroy_prefix = "%{snapshot_prefix}"
2114+
}
2115+
2116+
resource "google_compute_disk" "kms-encrypted-name" {
2117+
name = "%{disk_name3}"
2118+
type = "pd-ssd"
2119+
size = 10
2120+
zone = "us-central1-a"
2121+
2122+
disk_encryption_key {
2123+
kms_key_self_link = "%{kms_key_self_link}"
2124+
}
2125+
2126+
create_snapshot_before_destroy = true
2127+
}`, context)
2128+
}
2129+
20202130
func testAccComputeDisk_architecture(context map[string]interface{}) string {
20212131
return acctest.Nprintf(`
20222132
resource "google_compute_disk" "foobar" {

mmv1/third_party/terraform/services/compute/resource_compute_region_disk_test.go.tmpl

+93
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,51 @@ func TestAccComputeRegionDisk_featuresUpdated(t *testing.T) {
252252
})
253253
}
254254

255+
func TestAccComputeRegionDisk_createSnapshotBeforeDestroy(t *testing.T) {
256+
acctest.SkipIfVcr(t) // Disk cleanup test check
257+
t.Parallel()
258+
259+
var disk1 compute.Disk
260+
var disk2 compute.Disk
261+
var disk3 compute.Disk
262+
context := map[string]interface{}{
263+
"disk_name1": fmt.Sprintf("tf-test-disk-%s", acctest.RandString(t, 10)),
264+
"disk_name2": fmt.Sprintf("test-%s", acctest.RandString(t, 44)), //this is over the snapshot character creation limit of 48
265+
"disk_name3": fmt.Sprintf("tf-test-disk-%s", acctest.RandString(t, 10)),
266+
"snapshot_prefix": fmt.Sprintf("tf-test-snapshot-%s", acctest.RandString(t, 10)),
267+
"kms_key_self_link": acctest.BootstrapKMSKey(t).CryptoKey.Name,
268+
"raw_key": "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=",
269+
"rsa_encrypted_key": "ieCx/NcW06PcT7Ep1X6LUTc/hLvUDYyzSZPPVCVPTVEohpeHASqC8uw5TzyO9U+Fka9JFHz0mBibXUInrC/jEk014kCK/NPjYgEMOyssZ4ZINPKxlUh2zn1bV+MCaTICrdmuSBTWlUUiFoDD6PYznLwh8ZNdaheCeZ8ewEXgFQ8V+sDroLaN3Xs3MDTXQEMMoNUXMCZEIpg9Vtp9x2oeQ5lAbtt7bYAAHf5l+gJWw3sUfs0/Glw5fpdjT8Uggrr+RMZezGrltJEF293rvTIjWOEB3z5OHyHwQkvdrPDFcTqsLfh+8Hr8g+mf+7zVPEC8nEbqpdl3GPv3A7AwpFp7MA==",
270+
}
271+
272+
acctest.VcrTest(t, resource.TestCase{
273+
PreCheck: func() { acctest.AccTestPreCheck(t) },
274+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
275+
CheckDestroy: testAccCheckComputeRegionDiskDestroyProducer(t),
276+
Steps: []resource.TestStep{
277+
{
278+
Config: testAccComputeRegionDisk_createSnapshotBeforeDestroy_init(context),
279+
Check: resource.ComposeTestCheckFunc(
280+
testAccCheckComputeRegionDiskExists(
281+
t, "google_compute_region_disk.raw-encrypted-name", &disk1),
282+
testAccCheckComputeRegionDiskExists(
283+
t, "google_compute_region_disk.rsa-encrypted-prefix", &disk2),
284+
testAccCheckComputeRegionDiskExists(
285+
t, "google_compute_region_disk.kms-encrypted-name", &disk3),
286+
),
287+
},
288+
{
289+
Config: testAccComputeRegionDisk_createSnapshotBeforeDestroy_init(context),
290+
Destroy: true,
291+
Check: resource.ComposeTestCheckFunc(
292+
testAccCheckComputeDisk_removeBackupSnapshot(t, context["disk_name1"].(string)),
293+
testAccCheckComputeDisk_removeBackupSnapshot(t, context["snapshot_prefix"].(string)),
294+
testAccCheckComputeDisk_removeBackupSnapshot(t, context["disk_name3"].(string)),
295+
),
296+
},
297+
},
298+
})
299+
}
255300

256301
func testAccCheckComputeRegionDiskExists(t *testing.T, n string, disk *compute.Disk) resource.TestCheckFunc {
257302
return func(s *terraform.State) error {
@@ -564,3 +609,51 @@ resource "google_compute_region_disk" "regiondisk" {
564609
}
565610
`, diskName)
566611
}
612+
613+
func testAccComputeRegionDisk_createSnapshotBeforeDestroy_init(context map[string]interface{}) string {
614+
return acctest.Nprintf(`
615+
resource "google_compute_region_disk" "raw-encrypted-name" {
616+
name = "%{disk_name1}"
617+
type = "pd-ssd"
618+
size = 10
619+
region = "us-central1"
620+
replica_zones = ["us-central1-a", "us-central1-f"]
621+
622+
disk_encryption_key {
623+
raw_key = "%{raw_key}"
624+
}
625+
626+
create_snapshot_before_destroy = true
627+
}
628+
629+
resource "google_compute_region_disk" "rsa-encrypted-prefix" {
630+
name = "%{disk_name2}"
631+
type = "pd-ssd"
632+
size = 10
633+
region = "us-central1"
634+
replica_zones = ["us-central1-a", "us-central1-f"]
635+
636+
disk_encryption_key {
637+
rsa_encrypted_key = "%{rsa_encrypted_key}"
638+
}
639+
640+
create_snapshot_before_destroy = true
641+
create_snapshot_before_destroy_prefix = "%{snapshot_prefix}"
642+
}
643+
644+
resource "google_compute_region_disk" "kms-encrypted-name" {
645+
name = "%{disk_name3}"
646+
type = "pd-ssd"
647+
size = 10
648+
region = "us-central1"
649+
replica_zones = ["us-central1-a", "us-central1-f"]
650+
651+
disk_encryption_key {
652+
kms_key_name = "%{kms_key_self_link}"
653+
}
654+
655+
create_snapshot_before_destroy = true
656+
}
657+
658+
`, context)
659+
}

0 commit comments

Comments
 (0)