diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index cb43a5e05fab..30c16b973efd 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -594,10 +594,34 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e } if d.HasChange("metadata") { - var metadataOpts servers.MetadataOpts - metadataOpts = make(servers.MetadataOpts) - newMetadata := d.Get("metadata").(map[string]interface{}) - for k, v := range newMetadata { + oldMetadata, newMetadata := d.GetChange("metadata") + var metadataToDelete []string + + // Determine if any metadata keys were removed from the configuration. + // Then request those keys to be deleted. + for oldKey, _ := range oldMetadata.(map[string]interface{}) { + var found bool + for newKey, _ := range newMetadata.(map[string]interface{}) { + if oldKey == newKey { + found = true + } + } + + if !found { + metadataToDelete = append(metadataToDelete, oldKey) + } + } + + for _, key := range metadataToDelete { + err := servers.DeleteMetadatum(computeClient, d.Id(), key).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting metadata (%s) from server (%s): %s", key, d.Id(), err) + } + } + + // Update existing metadata and add any new metadata. + metadataOpts := make(servers.MetadataOpts) + for k, v := range newMetadata.(map[string]interface{}) { metadataOpts[k] = v.(string) } diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go index 01a00e3971ad..3665ff99deb9 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go @@ -591,6 +591,35 @@ func TestAccComputeV2Instance_stopBeforeDestroy(t *testing.T) { }) } +func TestAccComputeV2Instance_metadataRemove(t *testing.T) { + var instance servers.Server + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeV2InstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeV2Instance_metadataRemove_1, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2InstanceExists("openstack_compute_instance_v2.instance_1", &instance), + testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"), + testAccCheckComputeV2InstanceMetadata(&instance, "abc", "def"), + ), + }, + resource.TestStep{ + Config: testAccComputeV2Instance_metadataRemove_2, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2InstanceExists("openstack_compute_instance_v2.instance_1", &instance), + testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"), + testAccCheckComputeV2InstanceMetadata(&instance, "ghi", "jkl"), + testAccCheckComputeV2InstanceNoMetadataKey(&instance, "abc"), + ), + }, + }, + }) +} + func testAccCheckComputeV2InstanceDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) computeClient, err := config.computeV2Client(OS_REGION_NAME) @@ -687,6 +716,23 @@ func testAccCheckComputeV2InstanceMetadata( } } +func testAccCheckComputeV2InstanceNoMetadataKey( + instance *servers.Server, k string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.Metadata == nil { + return nil + } + + for key, _ := range instance.Metadata { + if k == key { + return fmt.Errorf("Metadata found: %s", k) + } + } + + return nil + } +} + func testAccCheckComputeV2InstanceVolumeAttachment( instance *servers.Server, volume *volumes.Volume) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -1445,3 +1491,25 @@ resource "openstack_compute_instance_v2" "instance_1" { stop_before_destroy = true } ` + +const testAccComputeV2Instance_metadataRemove_1 = ` +resource "openstack_compute_instance_v2" "instance_1" { + name = "instance_1" + security_groups = ["default"] + metadata { + foo = "bar" + abc = "def" + } +} +` + +const testAccComputeV2Instance_metadataRemove_2 = ` +resource "openstack_compute_instance_v2" "instance_1" { + name = "instance_1" + security_groups = ["default"] + metadata { + foo = "bar" + ghi = "jkl" + } +} +`