Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instance encrypted disk start #1658

Merged
merged 1 commit into from
Jan 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions google-beta/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-beta/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-beta/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