mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-20 11:48:24 -06:00
Merge pull request #14651 from hashicorp/paddy_gcp_detach_deleted_disks
provider/google: detach disks before deleting them.
This commit is contained in:
commit
dd17c1a78c
@ -10,6 +10,14 @@ import (
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
const (
|
||||
computeDiskUserRegexString = "^(?:https://www.googleapis.com/compute/v1/projects/)?([-_a-zA-Z0-9]*)/zones/([-_a-zA-Z0-9]*)/instances/([-_a-zA-Z0-9]*)$"
|
||||
)
|
||||
|
||||
var (
|
||||
computeDiskUserRegex = regexp.MustCompile(computeDiskUserRegexString)
|
||||
)
|
||||
|
||||
func resourceComputeDisk() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceComputeDiskCreate,
|
||||
@ -75,6 +83,11 @@ func resourceComputeDisk() *schema.Resource {
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"users": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -186,6 +199,7 @@ func resourceComputeDiskRead(d *schema.ResourceData, meta interface{}) error {
|
||||
if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" {
|
||||
d.Set("disk_encryption_key_sha256", disk.DiskEncryptionKey.Sha256)
|
||||
}
|
||||
d.Set("users", disk.Users)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -198,6 +212,52 @@ func resourceComputeDiskDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// if disks are attached, they must be detached before the disk can be deleted
|
||||
if instances, ok := d.Get("users").([]interface{}); ok {
|
||||
type detachArgs struct{ project, zone, instance, deviceName string }
|
||||
var detachCalls []detachArgs
|
||||
self := d.Get("self_link").(string)
|
||||
for _, instance := range instances {
|
||||
if !computeDiskUserRegex.MatchString(instance.(string)) {
|
||||
return fmt.Errorf("Unknown user %q of disk %q", instance, self)
|
||||
}
|
||||
matches := computeDiskUserRegex.FindStringSubmatch(instance.(string))
|
||||
instanceProject := matches[1]
|
||||
instanceZone := matches[2]
|
||||
instanceName := matches[3]
|
||||
i, err := config.clientCompute.Instances.Get(instanceProject, instanceZone, instanceName).Do()
|
||||
if err != nil {
|
||||
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
|
||||
log.Printf("[WARN] instance %q not found, not bothering to detach disks", instance.(string))
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("Error retrieving instance %s: %s", instance.(string), err.Error())
|
||||
}
|
||||
for _, disk := range i.Disks {
|
||||
if disk.Source == self {
|
||||
detachCalls = append(detachCalls, detachArgs{
|
||||
project: project,
|
||||
zone: i.Zone,
|
||||
instance: i.Name,
|
||||
deviceName: disk.DeviceName,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, call := range detachCalls {
|
||||
op, err := config.clientCompute.Instances.DetachDisk(call.project, call.zone, call.instance, call.deviceName).Do()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error detaching disk %s from instance %s/%s/%s: %s", call.deviceName, call.project,
|
||||
call.zone, call.instance, err.Error())
|
||||
}
|
||||
err = computeOperationWaitZone(config, op, call.project, call.zone,
|
||||
fmt.Sprintf("Detaching disk from %s/%s/%s", call.project, call.zone, call.instance))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the disk
|
||||
op, err := config.clientCompute.Disks.Delete(
|
||||
project, d.Get("zone").(string), d.Id()).Do()
|
||||
|
@ -3,6 +3,7 @@ package google
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
@ -77,6 +78,40 @@ func TestAccComputeDisk_encryption(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccComputeDisk_deleteDetach(t *testing.T) {
|
||||
diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
|
||||
instanceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
|
||||
var disk compute.Disk
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckComputeDiskDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccComputeDisk_deleteDetach(instanceName, diskName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckComputeDiskExists(
|
||||
"google_compute_disk.foo", &disk),
|
||||
),
|
||||
},
|
||||
// this needs to be a second step so we refresh and see the instance
|
||||
// listed as attached to the disk; the instance is created after the
|
||||
// disk. and the disk's properties aren't refreshed unless there's
|
||||
// another step
|
||||
resource.TestStep{
|
||||
Config: testAccComputeDisk_deleteDetach(instanceName, diskName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckComputeDiskExists(
|
||||
"google_compute_disk.foo", &disk),
|
||||
testAccCheckComputeDiskInstances(
|
||||
"google_compute_disk.foo", &disk),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckComputeDiskDestroy(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
@ -144,6 +179,28 @@ func testAccCheckEncryptionKey(n string, disk *compute.Disk) resource.TestCheckF
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckComputeDiskInstances(n string, disk *compute.Disk) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
attr := rs.Primary.Attributes["users.#"]
|
||||
if strconv.Itoa(len(disk.Users)) != attr {
|
||||
return fmt.Errorf("Disk %s has mismatched users.\nTF State: %+v\nGCP State: %+v", n, rs.Primary.Attributes["users"], disk.Users)
|
||||
}
|
||||
|
||||
for pos, user := range disk.Users {
|
||||
if rs.Primary.Attributes["users."+strconv.Itoa(pos)] != user {
|
||||
return fmt.Errorf("Disk %s has mismatched users.\nTF State: %+v.\nGCP State: %+v",
|
||||
n, rs.Primary.Attributes["users"], disk.Users)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccComputeDisk_basic(diskName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "google_compute_disk" "foobar" {
|
||||
@ -191,3 +248,33 @@ resource "google_compute_disk" "foobar" {
|
||||
disk_encryption_key_raw = "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0="
|
||||
}`, diskName)
|
||||
}
|
||||
|
||||
func testAccComputeDisk_deleteDetach(instanceName, diskName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "google_compute_disk" "foo" {
|
||||
name = "%s"
|
||||
image = "debian-8"
|
||||
size = 50
|
||||
type = "pd-ssd"
|
||||
zone = "us-central1-a"
|
||||
}
|
||||
|
||||
resource "google_compute_instance" "bar" {
|
||||
name = "%s"
|
||||
machine_type = "n1-standard-1"
|
||||
zone = "us-central1-a"
|
||||
|
||||
disk {
|
||||
image = "debian-8"
|
||||
}
|
||||
|
||||
disk {
|
||||
disk = "${google_compute_disk.foo.name}"
|
||||
auto_delete = false
|
||||
}
|
||||
|
||||
network_interface {
|
||||
network = "default"
|
||||
}
|
||||
}`, diskName, instanceName)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user