Skip to content

Commit

Permalink
Make google_compute_project_metadata authoritative. (#2205)
Browse files Browse the repository at this point in the history
  • Loading branch information
rileykarson authored Oct 10, 2018
1 parent c77ce97 commit 456bde2
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 136 deletions.
23 changes: 10 additions & 13 deletions google/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down
148 changes: 30 additions & 118 deletions google/resource_compute_project_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -34,55 +34,21 @@ 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)
if err != nil {
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)
Expand All @@ -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
}

Expand All @@ -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)
return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
}

md := project.CommonInstanceMetadata

// Remove all items
md.Items = nil

op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()
return resourceComputeProjectMetadataRead(d, meta)
}

if err != nil {
return fmt.Errorf("Error removing metadata from project %s: %s", projectID, err)
}
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)
}

log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
md.Fingerprint = project.CommonInstanceMetadata.Fingerprint
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()
if err != nil {
return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
}

err = computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
if err != nil {
return err
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
return computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
}

return resourceComputeProjectMetadataRead(d, meta)
err := MetadataRetryWrapper(createMD)
return err
}
10 changes: 5 additions & 5 deletions website/docs/r/compute_project_metadata.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.

- - -

Expand Down

0 comments on commit 456bde2

Please sign in to comment.