Skip to content

Commit

Permalink
Add deletion protection to resource_compute_instance (#1205)
Browse files Browse the repository at this point in the history
  • Loading branch information
nickjacques authored and nat-henderson committed Mar 16, 2018
1 parent ea0b05d commit 46a3d2f
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 13 deletions.
50 changes: 37 additions & 13 deletions google/resource_compute_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,12 @@ func resourceComputeInstance() *schema.Resource {
Optional: true,
},

"deletion_protection": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},

"label_fingerprint": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -717,19 +723,20 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err

// Create the instance information
instance := &computeBeta.Instance{
CanIpForward: d.Get("can_ip_forward").(bool),
Description: d.Get("description").(string),
Disks: disks,
MachineType: machineType.SelfLink,
Metadata: metadata,
Name: d.Get("name").(string),
NetworkInterfaces: networkInterfaces,
Tags: resourceInstanceTags(d),
Labels: expandLabels(d),
ServiceAccounts: expandServiceAccounts(d.Get("service_account").([]interface{})),
GuestAccelerators: accels,
MinCpuPlatform: d.Get("min_cpu_platform").(string),
Scheduling: scheduling,
CanIpForward: d.Get("can_ip_forward").(bool),
Description: d.Get("description").(string),
Disks: disks,
MachineType: machineType.SelfLink,
Metadata: metadata,
Name: d.Get("name").(string),
NetworkInterfaces: networkInterfaces,
Tags: resourceInstanceTags(d),
Labels: expandLabels(d),
ServiceAccounts: expandServiceAccounts(d.Get("service_account").([]interface{})),
GuestAccelerators: accels,
MinCpuPlatform: d.Get("min_cpu_platform").(string),
Scheduling: scheduling,
DeletionProtection: d.Get("deletion_protection").(bool),
}

log.Printf("[INFO] Requesting instance creation")
Expand Down Expand Up @@ -898,6 +905,7 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
d.Set("guest_accelerator", flattenGuestAccelerators(instance.GuestAccelerators))
d.Set("cpu_platform", instance.CpuPlatform)
d.Set("min_cpu_platform", instance.MinCpuPlatform)
d.Set("deletion_protection", instance.DeletionProtection)
d.Set("self_link", ConvertSelfLinkToV1(instance.SelfLink))
d.Set("instance_id", fmt.Sprintf("%d", instance.Id))
d.Set("project", project)
Expand Down Expand Up @@ -1268,6 +1276,22 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
scopesChange = !oScopes.Equal(nScopes)
}

if d.HasChange("deletion_protection") {
nDeletionProtection := d.Get("deletion_protection").(bool)

op, err := config.clientCompute.Instances.SetDeletionProtection(project, zone, d.Id()).DeletionProtection(nDeletionProtection).Do()
if err != nil {
return fmt.Errorf("Error updating deletion protection flag: %s", err)
}

opErr := computeOperationWaitTime(config.clientCompute, op, project, "deletion protection to update", int(d.Timeout(schema.TimeoutUpdate).Minutes()))
if opErr != nil {
return opErr
}

d.SetPartial("deletion_protection")
}

// Attributes which can only be changed if the instance is stopped
if scopesChange || d.HasChange("service_account.0.email") || d.HasChange("machine_type") || d.HasChange("min_cpu_platform") {
if !d.Get("allow_stopping_for_update").(bool) {
Expand Down
130 changes: 130 additions & 0 deletions google/resource_compute_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ func TestAccComputeInstance_basic1(t *testing.T) {
testAccCheckComputeInstanceMetadata(&instance, "foo", "bar"),
testAccCheckComputeInstanceMetadata(&instance, "baz", "qux"),
testAccCheckComputeInstanceDisk(&instance, instanceName, true, true),
// by default, DeletionProtection is implicitly false. This should be false on any
// instance resource without an explicit deletion_protection = true declaration.
// Other tests check explicit true/false configs: TestAccComputeInstance_deletionProtectionExplicit[True | False]
testAccCheckComputeInstanceHasConfiguredDeletionProtection(&instance, false),
),
},
resource.TestStep{
Expand Down Expand Up @@ -951,6 +955,67 @@ func TestAccComputeInstance_minCpuPlatform(t *testing.T) {
})
}

func TestAccComputeInstance_deletionProtectionExplicitFalse(t *testing.T) {
t.Parallel()

var instance compute.Instance
var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeInstanceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeInstance_basic_deletionProtectionFalse(instanceName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(
"google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceHasConfiguredDeletionProtection(&instance, false),
),
},
},
})
}

func TestAccComputeInstance_deletionProtectionExplicitTrueAndUpdateFalse(t *testing.T) {
t.Parallel()

var instance compute.Instance
var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeInstanceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeInstance_basic_deletionProtectionTrue(instanceName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(
"google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceHasConfiguredDeletionProtection(&instance, true),
),
},
resource.TestStep{
ResourceName: "google_compute_instance.foobar",
ImportState: true,
ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName),
ImportStateVerifyIgnore: []string{"create_timeout"},
},
// Update deletion_protection to false, otherwise the test harness can't delete the instance
resource.TestStep{
Config: testAccComputeInstance_basic_deletionProtectionFalse(instanceName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(
"google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceHasConfiguredDeletionProtection(&instance, false),
),
},
},
})
}

func TestAccComputeInstance_primaryAliasIpRange(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -1466,6 +1531,16 @@ func testAccCheckComputeInstanceHasAssignedIP(s *terraform.State) error {
return nil
}

func testAccCheckComputeInstanceHasConfiguredDeletionProtection(instance *compute.Instance, configuredDeletionProtection bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
if instance.DeletionProtection != configuredDeletionProtection {
return fmt.Errorf("Wrong deletion protection flag: expected %t, got %t", configuredDeletionProtection, instance.DeletionProtection)
}

return nil
}
}

func testAccComputeInstance_basic(instance string) string {
return fmt.Sprintf(`
resource "google_compute_instance" "foobar" {
Expand All @@ -1474,6 +1549,7 @@ resource "google_compute_instance" "foobar" {
zone = "us-central1-a"
can_ip_forward = false
tags = ["foo", "bar"]
//deletion_protection = false is implicit in this config due to default value
boot_disk {
initialize_params{
Expand Down Expand Up @@ -1609,6 +1685,60 @@ resource "google_compute_instance" "foobar" {
`, instance)
}

func testAccComputeInstance_basic_deletionProtectionFalse(instance string) string {
return fmt.Sprintf(`
resource "google_compute_instance" "foobar" {
name = "%s"
machine_type = "n1-standard-1"
zone = "us-central1-a"
can_ip_forward = false
tags = ["foo", "bar"]
deletion_protection = false
boot_disk {
initialize_params{
image = "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20160803"
}
}
network_interface {
network = "default"
}
metadata {
foo = "bar"
}
}
`, instance)
}

func testAccComputeInstance_basic_deletionProtectionTrue(instance string) string {
return fmt.Sprintf(`
resource "google_compute_instance" "foobar" {
name = "%s"
machine_type = "n1-standard-1"
zone = "us-central1-a"
can_ip_forward = false
tags = ["foo", "bar"]
deletion_protection = true
boot_disk {
initialize_params{
image = "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20160803"
}
}
network_interface {
network = "default"
}
metadata {
foo = "bar"
}
}
`, instance)
}

// Update zone to ForceNew, and change metadata k/v entirely
// Generates diff mismatch
func testAccComputeInstance_forceNewAndChangeMetadata(instance string) string {
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/compute_instance.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ The following arguments are supported:

* `description` - (Optional) A brief description of this resource.

* `deletion_protection` - (Optional) Enable deletion protection on this instance. Defaults to false.

* `guest_accelerator` - (Optional) List of the type and count of accelerator cards attached to the instance. Structure documented below.

* `labels` - (Optional) A set of key/value label pairs to assign to the instance.
Expand Down

0 comments on commit 46a3d2f

Please sign in to comment.