mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-10-04 17:51:11 +00:00
Add support for setting labels to compute_image (#339)
This commit is contained in:
parent
b52f09d491
commit
227ea5660f
@ -3,6 +3,7 @@ package google
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ func TestAccComputeImage_importFromRawDisk(t *testing.T) {
|
|||||||
CheckDestroy: testAccCheckComputeImageDestroy,
|
CheckDestroy: testAccCheckComputeImageDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccComputeImage_basic,
|
Config: testAccComputeImage_basic("image-test-" + acctest.RandString(10)),
|
||||||
},
|
},
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
ResourceName: "google_compute_image.foobar",
|
ResourceName: "google_compute_image.foobar",
|
||||||
|
@ -90,6 +90,18 @@ func resourceComputeImage() *schema.Resource {
|
|||||||
Optional: true,
|
Optional: true,
|
||||||
Default: computeImageCreateTimeoutDefault,
|
Default: computeImageCreateTimeoutDefault,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"labels": &schema.Schema{
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
|
||||||
|
"label_fingerprint": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,6 +146,10 @@ func resourceComputeImageCreate(d *schema.ResourceData, meta interface{}) error
|
|||||||
image.RawDisk = imageRawDisk
|
image.RawDisk = imageRawDisk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := d.GetOk("labels"); ok {
|
||||||
|
image.Labels = expandLabels(d)
|
||||||
|
}
|
||||||
|
|
||||||
// Read create timeout
|
// Read create timeout
|
||||||
var createTimeout int
|
var createTimeout int
|
||||||
if v, ok := d.GetOk("create_timeout"); ok {
|
if v, ok := d.GetOk("create_timeout"); ok {
|
||||||
@ -186,13 +202,53 @@ func resourceComputeImageRead(d *schema.ResourceData, meta interface{}) error {
|
|||||||
d.Set("description", image.Description)
|
d.Set("description", image.Description)
|
||||||
d.Set("family", image.Family)
|
d.Set("family", image.Family)
|
||||||
d.Set("self_link", image.SelfLink)
|
d.Set("self_link", image.SelfLink)
|
||||||
|
d.Set("labels", image.Labels)
|
||||||
|
d.Set("label_fingerprint", image.LabelFingerprint)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceComputeImageUpdate(d *schema.ResourceData, meta interface{}) error {
|
func resourceComputeImageUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
// Pass-through for updates to Terraform-specific `create_timeout` field.
|
config := meta.(*Config)
|
||||||
// The Google Cloud Image resource doesn't support update.
|
|
||||||
|
project, err := getProject(d, config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Technically we are only updating one attribute, but setting d.Partial here makes it easier to add updates later
|
||||||
|
d.Partial(true)
|
||||||
|
|
||||||
|
if d.HasChange("labels") {
|
||||||
|
labels := expandLabels(d)
|
||||||
|
labelFingerprint := d.Get("label_fingerprint").(string)
|
||||||
|
setLabelsRequest := compute.GlobalSetLabelsRequest{
|
||||||
|
LabelFingerprint: labelFingerprint,
|
||||||
|
Labels: labels,
|
||||||
|
ForceSendFields: []string{"Labels"},
|
||||||
|
}
|
||||||
|
|
||||||
|
op, err := config.clientCompute.Images.SetLabels(project, d.Id(), &setLabelsRequest).Do()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetPartial("labels")
|
||||||
|
|
||||||
|
err = computeOperationWaitTime(config, op, project, "Setting labels", 4)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Perform a read to see the new label_fingerprint value
|
||||||
|
image, err := config.clientCompute.Images.Get(project, d.Id()).Do()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.Set("label_fingerprint", image.LabelFingerprint)
|
||||||
|
d.SetPartial("label_fingerprint")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Partial(false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,50 @@ func TestAccComputeImage_basic(t *testing.T) {
|
|||||||
CheckDestroy: testAccCheckComputeImageDestroy,
|
CheckDestroy: testAccCheckComputeImageDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccComputeImage_basic,
|
Config: testAccComputeImage_basic("image-test-" + acctest.RandString(10)),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckComputeImageExists(
|
testAccCheckComputeImageExists(
|
||||||
"google_compute_image.foobar", &image),
|
"google_compute_image.foobar", &image),
|
||||||
testAccCheckComputeImageDescription(&image, "description-test"),
|
testAccCheckComputeImageDescription(&image, "description-test"),
|
||||||
testAccCheckComputeImageFamily(&image, "family-test"),
|
testAccCheckComputeImageFamily(&image, "family-test"),
|
||||||
|
testAccCheckComputeImageContainsLabel(&image, "my-label", "my-label-value"),
|
||||||
|
testAccCheckComputeImageContainsLabel(&image, "empty-label", ""),
|
||||||
|
testAccCheckComputeImageHasComputedFingerprint(&image, "google_compute_image.foobar"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccComputeImage_update(t *testing.T) {
|
||||||
|
var image compute.Image
|
||||||
|
|
||||||
|
name := "image-test-" + acctest.RandString(10)
|
||||||
|
// Only labels supports an update
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeImageDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeImage_basic(name),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeImageExists(
|
||||||
|
"google_compute_image.foobar", &image),
|
||||||
|
testAccCheckComputeImageContainsLabel(&image, "my-label", "my-label-value"),
|
||||||
|
testAccCheckComputeImageContainsLabel(&image, "empty-label", ""),
|
||||||
|
testAccCheckComputeImageHasComputedFingerprint(&image, "google_compute_image.foobar"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeImage_update(name),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeImageExists(
|
||||||
|
"google_compute_image.foobar", &image),
|
||||||
|
testAccCheckComputeImageDoesNotContainLabel(&image, "my-label"),
|
||||||
|
testAccCheckComputeImageContainsLabel(&image, "empty-label", "oh-look-theres-a-label-now"),
|
||||||
|
testAccCheckComputeImageContainsLabel(&image, "new-field", "only-shows-up-when-updated"),
|
||||||
|
testAccCheckComputeImageHasComputedFingerprint(&image, "google_compute_image.foobar"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -116,6 +154,51 @@ func testAccCheckComputeImageFamily(image *compute.Image, family string) resourc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeImageContainsLabel(image *compute.Image, key string, value string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
v, ok := image.Labels[key]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Expected label with key '%s' not found", key)
|
||||||
|
}
|
||||||
|
if v != value {
|
||||||
|
return fmt.Errorf("Incorrect label value for key '%s': expected '%s' but found '%s'", key, value, v)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeImageDoesNotContainLabel(image *compute.Image, key string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
if v, ok := image.Labels[key]; ok {
|
||||||
|
return fmt.Errorf("Expected no label for key '%s' but found one with value '%s'", key, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeImageHasComputedFingerprint(image *compute.Image, resource string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
// First ensure we actually have a fingerprint
|
||||||
|
if image.LabelFingerprint == "" {
|
||||||
|
return fmt.Errorf("No fingerprint set in API read result")
|
||||||
|
}
|
||||||
|
|
||||||
|
state := s.RootModule().Resources[resource]
|
||||||
|
if state == nil {
|
||||||
|
return fmt.Errorf("Unable to find resource named %s in resources", resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
storedFingerprint := state.Primary.Attributes["label_fingerprint"]
|
||||||
|
if storedFingerprint != image.LabelFingerprint {
|
||||||
|
return fmt.Errorf("Stored fingerprint doesn't match fingerprint found on server; stored '%s', server '%s'",
|
||||||
|
storedFingerprint, image.LabelFingerprint)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckComputeImageHasSourceDisk(image *compute.Image) resource.TestCheckFunc {
|
func testAccCheckComputeImageHasSourceDisk(image *compute.Image) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
if image.SourceType == "" {
|
if image.SourceType == "" {
|
||||||
@ -125,16 +208,39 @@ func testAccCheckComputeImageHasSourceDisk(image *compute.Image) resource.TestCh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var testAccComputeImage_basic = fmt.Sprintf(`
|
func testAccComputeImage_basic(name string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
resource "google_compute_image" "foobar" {
|
resource "google_compute_image" "foobar" {
|
||||||
name = "image-test-%s"
|
name = "%s"
|
||||||
description = "description-test"
|
description = "description-test"
|
||||||
family = "family-test"
|
family = "family-test"
|
||||||
raw_disk {
|
raw_disk {
|
||||||
source = "https://storage.googleapis.com/bosh-cpi-artifacts/bosh-stemcell-3262.4-google-kvm-ubuntu-trusty-go_agent-raw.tar.gz"
|
source = "https://storage.googleapis.com/bosh-cpi-artifacts/bosh-stemcell-3262.4-google-kvm-ubuntu-trusty-go_agent-raw.tar.gz"
|
||||||
}
|
}
|
||||||
create_timeout = 5
|
create_timeout = 5
|
||||||
}`, acctest.RandString(10))
|
labels = {
|
||||||
|
my-label = "my-label-value"
|
||||||
|
empty-label = ""
|
||||||
|
}
|
||||||
|
}`, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccComputeImage_update(name string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "google_compute_image" "foobar" {
|
||||||
|
name = "%s"
|
||||||
|
description = "description-test"
|
||||||
|
family = "family-test"
|
||||||
|
raw_disk {
|
||||||
|
source = "https://storage.googleapis.com/bosh-cpi-artifacts/bosh-stemcell-3262.4-google-kvm-ubuntu-trusty-go_agent-raw.tar.gz"
|
||||||
|
}
|
||||||
|
create_timeout = 5
|
||||||
|
labels = {
|
||||||
|
empty-label = "oh-look-theres-a-label-now"
|
||||||
|
new-field = "only-shows-up-when-updated"
|
||||||
|
}
|
||||||
|
}`, name)
|
||||||
|
}
|
||||||
|
|
||||||
var testAccComputeImage_basedondisk = fmt.Sprintf(`
|
var testAccComputeImage_basedondisk = fmt.Sprintf(`
|
||||||
resource "google_compute_disk" "foobar" {
|
resource "google_compute_disk" "foobar" {
|
||||||
|
@ -806,7 +806,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
|
|||||||
Name: d.Get("name").(string),
|
Name: d.Get("name").(string),
|
||||||
NetworkInterfaces: networkInterfaces,
|
NetworkInterfaces: networkInterfaces,
|
||||||
Tags: resourceInstanceTags(d),
|
Tags: resourceInstanceTags(d),
|
||||||
Labels: resourceInstanceLabels(d),
|
Labels: expandLabels(d),
|
||||||
ServiceAccounts: serviceAccounts,
|
ServiceAccounts: serviceAccounts,
|
||||||
Scheduling: scheduling,
|
Scheduling: scheduling,
|
||||||
}
|
}
|
||||||
@ -1144,7 +1144,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("labels") {
|
if d.HasChange("labels") {
|
||||||
labels := resourceInstanceLabels(d)
|
labels := expandLabels(d)
|
||||||
labelFingerprint := d.Get("label_fingerprint").(string)
|
labelFingerprint := d.Get("label_fingerprint").(string)
|
||||||
req := compute.InstancesSetLabelsRequest{Labels: labels, LabelFingerprint: labelFingerprint}
|
req := compute.InstancesSetLabelsRequest{Labels: labels, LabelFingerprint: labelFingerprint}
|
||||||
|
|
||||||
@ -1307,17 +1307,6 @@ func resourceInstanceMetadata(d *schema.ResourceData) (*compute.Metadata, error)
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceInstanceLabels(d *schema.ResourceData) map[string]string {
|
|
||||||
labels := map[string]string{}
|
|
||||||
if v, ok := d.GetOk("labels"); ok {
|
|
||||||
labelMap := v.(map[string]interface{})
|
|
||||||
for k, v := range labelMap {
|
|
||||||
labels[k] = v.(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceInstanceTags(d *schema.ResourceData) *compute.Tags {
|
func resourceInstanceTags(d *schema.ResourceData) *compute.Tags {
|
||||||
// Calculate the tags
|
// Calculate the tags
|
||||||
var tags *compute.Tags
|
var tags *compute.Tags
|
||||||
|
@ -229,6 +229,23 @@ func linkDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// expandLabels pulls the value of "labels" out of a schema.ResourceData as a map[string]string.
|
||||||
|
func expandLabels(d *schema.ResourceData) map[string]string {
|
||||||
|
return expandStringMap(d, "labels")
|
||||||
|
}
|
||||||
|
|
||||||
|
// expandStringMap pulls the value of key out of a schema.ResourceData as a map[string]string.
|
||||||
|
func expandStringMap(d *schema.ResourceData, key string) map[string]string {
|
||||||
|
mp := map[string]string{}
|
||||||
|
if v, ok := d.GetOk(key); ok {
|
||||||
|
labelMap := v.(map[string]interface{})
|
||||||
|
for k, v := range labelMap {
|
||||||
|
mp[k] = v.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mp
|
||||||
|
}
|
||||||
|
|
||||||
func convertStringArr(ifaceArr []interface{}) []string {
|
func convertStringArr(ifaceArr []interface{}) []string {
|
||||||
var arr []string
|
var arr []string
|
||||||
for _, v := range ifaceArr {
|
for _, v := range ifaceArr {
|
||||||
|
@ -77,6 +77,8 @@ The `raw_disk` block supports:
|
|||||||
|
|
||||||
* `family` - (Optional) The name of the image family to which this image belongs.
|
* `family` - (Optional) The name of the image family to which this image belongs.
|
||||||
|
|
||||||
|
* `labels` - (Optional) A set of key/value label pairs to assign to the image.
|
||||||
|
|
||||||
## Attributes Reference
|
## Attributes Reference
|
||||||
|
|
||||||
In addition to the arguments listed above, the following computed attributes are
|
In addition to the arguments listed above, the following computed attributes are
|
||||||
@ -84,6 +86,8 @@ exported:
|
|||||||
|
|
||||||
* `self_link` - The URI of the created resource.
|
* `self_link` - The URI of the created resource.
|
||||||
|
|
||||||
|
* `label_fingerprint` - The fingerprint of the assigned labels.
|
||||||
|
|
||||||
## Import
|
## Import
|
||||||
|
|
||||||
VM image can be imported using the `name`, e.g.
|
VM image can be imported using the `name`, e.g.
|
||||||
|
Loading…
Reference in New Issue
Block a user