Skip to content

Commit

Permalink
Instance encrypted disk start (#5436)
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <[email protected]>

Co-authored-by: Sam Levenick <[email protected]>
  • Loading branch information
modular-magician and slevenick committed Jan 21, 2020
1 parent 93dc892 commit 9861323
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 2 deletions.
29 changes: 29 additions & 0 deletions google/compute_instance_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package google

import (
"fmt"
"reflect"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
Expand Down Expand Up @@ -347,3 +348,31 @@ func flattenEnableDisplay(displayDevice *computeBeta.DisplayDevice) interface{}

return displayDevice.EnableDisplay
}

// Terraform doesn't correctly calculate changes on schema.Set, so we do it manually
// https://github.com/hashicorp/terraform-plugin-sdk/issues/98
func schedulingHasChange(d *schema.ResourceData) bool {
if !d.HasChange("scheduling") {
// This doesn't work correctly, which is why this method exists
// But it is here for posterity
return false
}
o, n := d.GetChange("scheduling")
oScheduling := o.([]interface{})[0].(map[string]interface{})
newScheduling := n.([]interface{})[0].(map[string]interface{})
originalNa := oScheduling["node_affinities"].(*schema.Set)
newNa := newScheduling["node_affinities"].(*schema.Set)
if oScheduling["automatic_restart"] != newScheduling["automatic_restart"] {
return true
}

if oScheduling["preemptible"] != newScheduling["preemptible"] {
return true
}

if oScheduling["on_host_maintenance"] != newScheduling["on_host_maintenance"] {
return true
}

return reflect.DeepEqual(newNa, originalNa)
}
28 changes: 26 additions & 2 deletions google/resource_compute_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,10 @@ func resourceComputeInstance() *schema.Resource {
Optional: true,
Computed: true,
Elem: &schema.Resource{
// !!! IMPORTANT !!!
// We have a custom diff function for the scheduling block due to issues with Terraform's
// diff on schema.Set. If changes are made to this block, they must be reflected in that
// method. See schedulingHasChange in compute_instance_helpers.go
Schema: map[string]*schema.Schema{
"on_host_maintenance": {
Type: schema.TypeString,
Expand Down Expand Up @@ -1051,7 +1055,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
d.SetPartial("labels")
}

if d.HasChange("scheduling") {
if schedulingHasChange(d) {
scheduling, err := expandScheduling(d.Get("scheduling"))
if err != nil {
return fmt.Errorf("Error creating request data to update scheduling: %s", err)
Expand Down Expand Up @@ -1399,7 +1403,27 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
d.SetPartial("enable_display")
}

op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do()
// Retrieve instance from config to pull encryption keys if necessary
instanceFromConfig, err := expandComputeInstance(project, d, config)
if err != nil {
return err
}

var encrypted []*compute.CustomerEncryptionKeyProtectedDisk
for _, disk := range instanceFromConfig.Disks {
if disk.DiskEncryptionKey != nil {
key := compute.CustomerEncryptionKey{RawKey: disk.DiskEncryptionKey.RawKey, KmsKeyName: disk.DiskEncryptionKey.KmsKeyName}
eDisk := compute.CustomerEncryptionKeyProtectedDisk{Source: disk.Source, DiskEncryptionKey: &key}
encrypted = append(encrypted, &eDisk)
}
}

if len(encrypted) > 0 {
request := compute.InstancesStartWithEncryptionKeyRequest{Disks: encrypted}
op, err = config.clientCompute.Instances.StartWithEncryptionKey(project, zone, instance.Name, &request).Do()
} else {
op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do()
}
if err != nil {
return errwrap.Wrapf("Error starting instance: {{err}}", err)
}
Expand Down
149 changes: 149 additions & 0 deletions google/resource_compute_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,45 @@ func TestAccComputeInstance_diskEncryption(t *testing.T) {
})
}

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

var instance compute.Instance
var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10))
bootEncryptionKey := "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0="
bootEncryptionKeyHash := "esTuF7d4eatX4cnc4JsiEiaI+Rff78JgPhA/v1zxX9E="
diskNameToEncryptionKey := map[string]*compute.CustomerEncryptionKey{
fmt.Sprintf("instance-testd-%s", acctest.RandString(10)): {
RawKey: "Ym9vdDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=",
Sha256: "awJ7p57H+uVZ9axhJjl1D3lfC2MgA/wnt/z88Ltfvss=",
},
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccComputeInstance_disks_encryption_restart(bootEncryptionKey, diskNameToEncryptionKey, instanceName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(
"google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceDiskEncryptionKey("google_compute_instance.foobar", &instance, bootEncryptionKeyHash, diskNameToEncryptionKey),
),
},
{
Config: testAccComputeInstance_disks_encryption_restartUpdate(bootEncryptionKey, diskNameToEncryptionKey, instanceName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(
"google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceDiskEncryptionKey("google_compute_instance.foobar", &instance, bootEncryptionKeyHash, diskNameToEncryptionKey),
),
},
},
})
}

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

Expand Down Expand Up @@ -2289,6 +2328,8 @@ resource "google_compute_instance" "foobar" {
metadata = {
foo = "bar"
}
allow_stopping_for_update = true
}
`, diskNames[0], diskNameToEncryptionKey[diskNames[0]].RawKey,
diskNames[1], diskNameToEncryptionKey[diskNames[1]].RawKey,
Expand All @@ -2298,6 +2339,114 @@ resource "google_compute_instance" "foobar" {
diskNameToEncryptionKey[diskNames[0]].RawKey, diskNameToEncryptionKey[diskNames[1]].RawKey, diskNameToEncryptionKey[diskNames[2]].RawKey)
}

func testAccComputeInstance_disks_encryption_restart(bootEncryptionKey string, diskNameToEncryptionKey map[string]*compute.CustomerEncryptionKey, instance string) string {
diskNames := []string{}
for k := range diskNameToEncryptionKey {
diskNames = append(diskNames, k)
}
return fmt.Sprintf(`
data "google_compute_image" "my_image" {
family = "debian-9"
project = "debian-cloud"
}
resource "google_compute_disk" "foobar" {
name = "%s"
size = 10
type = "pd-ssd"
zone = "us-central1-a"
disk_encryption_key {
raw_key = "%s"
}
}
resource "google_compute_instance" "foobar" {
name = "%s"
machine_type = "n1-standard-1"
zone = "us-central1-a"
boot_disk {
initialize_params {
image = data.google_compute_image.my_image.self_link
}
disk_encryption_key_raw = "%s"
}
attached_disk {
source = google_compute_disk.foobar.self_link
disk_encryption_key_raw = "%s"
}
network_interface {
network = "default"
}
metadata = {
foo = "bar"
}
allow_stopping_for_update = true
}
`, diskNames[0], diskNameToEncryptionKey[diskNames[0]].RawKey,
instance, bootEncryptionKey,
diskNameToEncryptionKey[diskNames[0]].RawKey)
}

func testAccComputeInstance_disks_encryption_restartUpdate(bootEncryptionKey string, diskNameToEncryptionKey map[string]*compute.CustomerEncryptionKey, instance string) string {
diskNames := []string{}
for k := range diskNameToEncryptionKey {
diskNames = append(diskNames, k)
}
return fmt.Sprintf(`
data "google_compute_image" "my_image" {
family = "debian-9"
project = "debian-cloud"
}
resource "google_compute_disk" "foobar" {
name = "%s"
size = 10
type = "pd-ssd"
zone = "us-central1-a"
disk_encryption_key {
raw_key = "%s"
}
}
resource "google_compute_instance" "foobar" {
name = "%s"
machine_type = "n1-standard-2"
zone = "us-central1-a"
boot_disk {
initialize_params {
image = data.google_compute_image.my_image.self_link
}
disk_encryption_key_raw = "%s"
}
attached_disk {
source = google_compute_disk.foobar.self_link
disk_encryption_key_raw = "%s"
}
network_interface {
network = "default"
}
metadata = {
foo = "bar"
}
allow_stopping_for_update = true
}
`, diskNames[0], diskNameToEncryptionKey[diskNames[0]].RawKey,
instance, bootEncryptionKey,
diskNameToEncryptionKey[diskNames[0]].RawKey)
}

func testAccComputeInstance_disks_kms(pid string, bootEncryptionKey string, diskNameToEncryptionKey map[string]*compute.CustomerEncryptionKey, instance string) string {
diskNames := []string{}
for k := range diskNameToEncryptionKey {
Expand Down

0 comments on commit 9861323

Please sign in to comment.