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

Make google_compute_project_metadata authoritative. #2205

Merged
merged 1 commit into from
Oct 10, 2018
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
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