Make google_compute_project_metadata authoritative. (#2205)

This commit is contained in:
Riley Karson 2018-10-10 15:13:25 -07:00 committed by Nathan McKinley
parent 50fe1b6c83
commit a9b6e3f5e1
3 changed files with 49 additions and 140 deletions

View File

@ -111,18 +111,15 @@ func BetaMetadataUpdate(oldMDMap map[string]interface{}, newMDMap map[string]int
}
}
// expandComputeMetadata transforms a map representing computing metadata into a list of compute.MetadataItems suitable
// for the GCP client.
func expandComputeMetadata(m map[string]string) []*compute.MetadataItems {
func expandComputeMetadata(m map[string]interface{}) []*compute.MetadataItems {
metadata := make([]*compute.MetadataItems, len(m))
idx := 0
for key, value := range m {
// Make a copy of value as we need a ptr type; if we directly use 'value' then all items will reference the same
// memory address
vtmp := value
metadata[idx] = &compute.MetadataItems{Key: key, Value: &vtmp}
idx++
// Append new metadata to existing metadata
for key, val := range m {
v := val.(string)
metadata = append(metadata, &compute.MetadataItems{
Key: key,
Value: &v,
})
}
return metadata
@ -140,8 +137,8 @@ func flattenMetadataBeta(metadata *computeBeta.Metadata) map[string]string {
// compute.metadata rather than computeBeta.metadata as an argument. It should
// be removed in favour of flattenMetadataBeta if/when all resources using it get
// beta support.
func flattenMetadata(metadata *compute.Metadata) map[string]string {
metadataMap := make(map[string]string)
func flattenMetadata(metadata *compute.Metadata) map[string]interface{} {
metadataMap := make(map[string]interface{})
for _, item := range metadata.Items {
metadataMap[item.Key] = *item.Value
}

View File

@ -10,9 +10,9 @@ import (
func resourceComputeProjectMetadata() *schema.Resource {
return &schema.Resource{
Create: resourceComputeProjectMetadataCreate,
Create: resourceComputeProjectMetadataCreateOrUpdate,
Read: resourceComputeProjectMetadataRead,
Update: resourceComputeProjectMetadataUpdate,
Update: resourceComputeProjectMetadataCreateOrUpdate,
Delete: resourceComputeProjectMetadataDelete,
SchemaVersion: 0,
@ -34,7 +34,7 @@ func resourceComputeProjectMetadata() *schema.Resource {
}
}
func resourceComputeProjectMetadataCreate(d *schema.ResourceData, meta interface{}) error {
func resourceComputeProjectMetadataCreateOrUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
projectID, err := getProject(d, config)
@ -42,47 +42,13 @@ func resourceComputeProjectMetadataCreate(d *schema.ResourceData, meta interface
return err
}
createMD := func() error {
// Load project service
log.Printf("[DEBUG] Loading project service: %s", projectID)
project, err := config.clientCompute.Projects.Get(projectID).Do()
if err != nil {
return fmt.Errorf("Error loading project '%s': %s", projectID, err)
}
md := project.CommonInstanceMetadata
newMDMap := d.Get("metadata").(map[string]interface{})
// Ensure that we aren't overwriting entries that already exist
for _, kv := range md.Items {
if _, ok := newMDMap[kv.Key]; ok {
return fmt.Errorf("Error, key '%s' already exists in project '%s'", kv.Key, projectID)
}
}
// Append new metadata to existing metadata
for key, val := range newMDMap {
v := val.(string)
md.Items = append(md.Items, &compute.MetadataItems{
Key: key,
Value: &v,
})
}
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()
if err != nil {
return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
}
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
return computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
md := &compute.Metadata{
Items: expandComputeMetadata(d.Get("metadata").(map[string]interface{})),
}
err = MetadataRetryWrapper(createMD)
err = resourceComputeProjectMetadataSet(projectID, config, md)
if err != nil {
return err
return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
}
return resourceComputeProjectMetadataRead(d, meta)
@ -103,70 +69,13 @@ func resourceComputeProjectMetadataRead(d *schema.ResourceData, meta interface{}
return handleNotFoundError(err, d, fmt.Sprintf("Project metadata for project %q", projectID))
}
md := flattenMetadata(project.CommonInstanceMetadata)
existingMetadata := d.Get("metadata").(map[string]interface{})
// Remove all keys not explicitly mentioned in the terraform config
for k := range md {
if _, ok := existingMetadata[k]; !ok {
delete(md, k)
}
}
if err = d.Set("metadata", md); err != nil {
err = d.Set("metadata", flattenMetadata(project.CommonInstanceMetadata))
if err != nil {
return fmt.Errorf("Error setting metadata: %s", err)
}
d.Set("project", projectID)
d.SetId("common_metadata")
return nil
}
func resourceComputeProjectMetadataUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
projectID, err := getProject(d, config)
if err != nil {
return err
}
if d.HasChange("metadata") {
o, n := d.GetChange("metadata")
updateMD := func() error {
// Load project service
log.Printf("[DEBUG] Loading project service: %s", projectID)
project, err := config.clientCompute.Projects.Get(projectID).Do()
if err != nil {
return fmt.Errorf("Error loading project '%s': %s", projectID, err)
}
md := project.CommonInstanceMetadata
MetadataUpdate(o.(map[string]interface{}), n.(map[string]interface{}), md)
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()
if err != nil {
return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
}
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
// Optimistic locking requires the fingerprint received to match
// the fingerprint we send the server, if there is a mismatch then we
// are working on old data, and must retry
return computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
}
err := MetadataRetryWrapper(updateMD)
if err != nil {
return err
}
return resourceComputeProjectMetadataRead(d, meta)
}
return nil
}
@ -178,30 +87,33 @@ func resourceComputeProjectMetadataDelete(d *schema.ResourceData, meta interface
return err
}
// Load project service
log.Printf("[DEBUG] Loading project service: %s", projectID)
project, err := config.clientCompute.Projects.Get(projectID).Do()
md := &compute.Metadata{}
err = resourceComputeProjectMetadataSet(projectID, config, md)
if err != nil {
return fmt.Errorf("Error loading project '%s': %s", projectID, err)
}
md := project.CommonInstanceMetadata
// Remove all items
md.Items = nil
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()
if err != nil {
return fmt.Errorf("Error removing metadata from project %s: %s", projectID, err)
}
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
err = computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
if err != nil {
return err
return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
}
return resourceComputeProjectMetadataRead(d, meta)
}
func resourceComputeProjectMetadataSet(projectID string, config *Config, md *compute.Metadata) error {
createMD := func() error {
log.Printf("[DEBUG] Loading project service: %s", projectID)
project, err := config.clientCompute.Projects.Get(projectID).Do()
if err != nil {
return fmt.Errorf("Error loading project '%s': %s", projectID, err)
}
md.Fingerprint = project.CommonInstanceMetadata.Fingerprint
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()
if err != nil {
return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
}
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
return computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
}
err := MetadataRetryWrapper(createMD)
return err
}

View File

@ -8,13 +8,14 @@ description: |-
# google\_compute\_project\_metadata
Manages metadata common to all instances for a project in GCE. For more information see
Authoritatively manages metadata common to all instances for a project in GCE. For more information see
[the official documentation](https://cloud.google.com/compute/docs/storing-retrieving-metadata)
and
[API](https://cloud.google.com/compute/docs/reference/latest/projects/setCommonInstanceMetadata).
~> **Note:** If you want to manage only single key/value pairs within the project metadata
rather than the entire set, then use
~> **Note:** This resource manages all project-level metadata including project-level ssh keys.
Keys unset in config but set on the server will be removed. If you want to manage only single
key/value pairs within the project metadata rather than the entire set, then use
[google_compute_project_metadata_item](compute_project_metadata_item.html).
## Example Usage
@ -33,8 +34,7 @@ resource "google_compute_project_metadata" "default" {
The following arguments are supported:
* `metadata` - (Required) A series of key value pairs. Changing this resource
updates the GCE state.
* `metadata` - (Required) A series of key value pairs.
- - -