github.com/anuaimi/terraform@v0.6.4-0.20150904235404-2bf9aec61da8/builtin/providers/google/resource_compute_project_metadata.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	//	"github.com/hashicorp/terraform/helper/hashcode"
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  	"google.golang.org/api/compute/v1"
    11  	//	"google.golang.org/api/googleapi"
    12  )
    13  
    14  func resourceComputeProjectMetadata() *schema.Resource {
    15  	return &schema.Resource{
    16  		Create: resourceComputeProjectMetadataCreate,
    17  		Read:   resourceComputeProjectMetadataRead,
    18  		Update: resourceComputeProjectMetadataUpdate,
    19  		Delete: resourceComputeProjectMetadataDelete,
    20  
    21  		SchemaVersion: 0,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"metadata": &schema.Schema{
    25  				Elem:     schema.TypeString,
    26  				Type:     schema.TypeMap,
    27  				Required: true,
    28  			},
    29  		},
    30  	}
    31  }
    32  
    33  const FINGERPRINT_RETRIES = 10
    34  const FINGERPRINT_FAIL = "Invalid fingerprint."
    35  
    36  func resourceOperationWaitGlobal(config *Config, op *compute.Operation, activity string) error {
    37  	w := &OperationWaiter{
    38  		Service: config.clientCompute,
    39  		Op:      op,
    40  		Project: config.Project,
    41  		Type:    OperationWaitGlobal,
    42  	}
    43  
    44  	state := w.Conf()
    45  	state.Timeout = 2 * time.Minute
    46  	state.MinTimeout = 1 * time.Second
    47  	opRaw, err := state.WaitForState()
    48  	if err != nil {
    49  		return fmt.Errorf("Error waiting for %s: %s", activity, err)
    50  	}
    51  
    52  	op = opRaw.(*compute.Operation)
    53  	if op.Error != nil {
    54  		return OperationError(*op.Error)
    55  	}
    56  
    57  	return nil
    58  }
    59  
    60  func resourceComputeProjectMetadataCreate(d *schema.ResourceData, meta interface{}) error {
    61  	attempt := 0
    62  
    63  	config := meta.(*Config)
    64  
    65  	for attempt < FINGERPRINT_RETRIES {
    66  		// Load project service
    67  		log.Printf("[DEBUG] Loading project service: %s", config.Project)
    68  		project, err := config.clientCompute.Projects.Get(config.Project).Do()
    69  		if err != nil {
    70  			return fmt.Errorf("Error loading project '%s': %s", config.Project, err)
    71  		}
    72  
    73  		md := project.CommonInstanceMetadata
    74  
    75  		newMDMap := d.Get("metadata").(map[string]interface{})
    76  		// Ensure that we aren't overwriting entries that already exist
    77  		for _, kv := range md.Items {
    78  			if _, ok := newMDMap[kv.Key]; ok {
    79  				return fmt.Errorf("Error, key '%s' already exists in project '%s'", kv.Key, config.Project)
    80  			}
    81  		}
    82  
    83  		// Append new metadata to existing metadata
    84  		for key, val := range newMDMap {
    85  			v := val.(string)
    86  			md.Items = append(md.Items, &compute.MetadataItems{
    87  				Key:   key,
    88  				Value: &v,
    89  			})
    90  		}
    91  
    92  		op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(config.Project, md).Do()
    93  
    94  		if err != nil {
    95  			return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
    96  		}
    97  
    98  		log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
    99  
   100  		// Optimistic locking requires the fingerprint recieved to match
   101  		// the fingerprint we send the server, if there is a mismatch then we
   102  		// are working on old data, and must retry
   103  		err = resourceOperationWaitGlobal(config, op, "SetCommonMetadata")
   104  		if err == nil {
   105  			return resourceComputeProjectMetadataRead(d, meta)
   106  		} else if err.Error() == FINGERPRINT_FAIL {
   107  			attempt++
   108  		} else {
   109  			return err
   110  		}
   111  	}
   112  
   113  	return fmt.Errorf("Error, unable to set metadata resource after %d attempts", attempt)
   114  }
   115  
   116  func resourceComputeProjectMetadataRead(d *schema.ResourceData, meta interface{}) error {
   117  	config := meta.(*Config)
   118  
   119  	// Load project service
   120  	log.Printf("[DEBUG] Loading project service: %s", config.Project)
   121  	project, err := config.clientCompute.Projects.Get(config.Project).Do()
   122  	if err != nil {
   123  		return fmt.Errorf("Error loading project '%s': %s", config.Project, err)
   124  	}
   125  
   126  	md := project.CommonInstanceMetadata
   127  
   128  	newMD := make(map[string]interface{})
   129  
   130  	for _, kv := range md.Items {
   131  		newMD[kv.Key] = kv.Value
   132  	}
   133  
   134  	if err = d.Set("metadata", newMD); err != nil {
   135  		return fmt.Errorf("Error setting metadata: %s", err)
   136  	}
   137  
   138  	d.SetId("common_metadata")
   139  
   140  	return nil
   141  }
   142  
   143  func resourceComputeProjectMetadataUpdate(d *schema.ResourceData, meta interface{}) error {
   144  	attempt := 0
   145  
   146  	config := meta.(*Config)
   147  
   148  	if d.HasChange("metadata") {
   149  		o, n := d.GetChange("metadata")
   150  		oMDMap, nMDMap := o.(map[string]interface{}), n.(map[string]interface{})
   151  
   152  		for attempt < FINGERPRINT_RETRIES {
   153  			// Load project service
   154  			log.Printf("[DEBUG] Loading project service: %s", config.Project)
   155  			project, err := config.clientCompute.Projects.Get(config.Project).Do()
   156  			if err != nil {
   157  				return fmt.Errorf("Error loading project '%s': %s", config.Project, err)
   158  			}
   159  
   160  			md := project.CommonInstanceMetadata
   161  
   162  			curMDMap := make(map[string]string)
   163  			// Load metadata on server into map
   164  			for _, kv := range md.Items {
   165  				// If the server state has a key that we had in our old
   166  				// state, but not in our new state, we should delete it
   167  				_, okOld := oMDMap[kv.Key]
   168  				_, okNew := nMDMap[kv.Key]
   169  				if okOld && !okNew {
   170  					continue
   171  				} else {
   172  					if kv.Value != nil {
   173  						curMDMap[kv.Key] = *kv.Value
   174  					}
   175  				}
   176  			}
   177  
   178  			// Insert new metadata into existing metadata (overwriting when needed)
   179  			for key, val := range nMDMap {
   180  				curMDMap[key] = val.(string)
   181  			}
   182  
   183  			// Reformat old metadata into a list
   184  			md.Items = nil
   185  			for key, val := range curMDMap {
   186  				md.Items = append(md.Items, &compute.MetadataItems{
   187  					Key:   key,
   188  					Value: &val,
   189  				})
   190  			}
   191  
   192  			op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(config.Project, md).Do()
   193  
   194  			if err != nil {
   195  				return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
   196  			}
   197  
   198  			log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
   199  
   200  			// Optimistic locking requires the fingerprint recieved to match
   201  			// the fingerprint we send the server, if there is a mismatch then we
   202  			// are working on old data, and must retry
   203  			err = resourceOperationWaitGlobal(config, op, "SetCommonMetadata")
   204  			if err == nil {
   205  				return resourceComputeProjectMetadataRead(d, meta)
   206  			} else if err.Error() == FINGERPRINT_FAIL {
   207  				attempt++
   208  			} else {
   209  				return err
   210  			}
   211  		}
   212  
   213  		return fmt.Errorf("Error, unable to set metadata resource after %d attempts", attempt)
   214  	}
   215  
   216  	return nil
   217  }
   218  
   219  func resourceComputeProjectMetadataDelete(d *schema.ResourceData, meta interface{}) error {
   220  	config := meta.(*Config)
   221  
   222  	// Load project service
   223  	log.Printf("[DEBUG] Loading project service: %s", config.Project)
   224  	project, err := config.clientCompute.Projects.Get(config.Project).Do()
   225  	if err != nil {
   226  		return fmt.Errorf("Error loading project '%s': %s", config.Project, err)
   227  	}
   228  
   229  	md := project.CommonInstanceMetadata
   230  
   231  	// Remove all items
   232  	md.Items = nil
   233  
   234  	op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(config.Project, md).Do()
   235  
   236  	log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
   237  
   238  	err = resourceOperationWaitGlobal(config, op, "SetCommonMetadata")
   239  	if err != nil {
   240  		return err
   241  	}
   242  
   243  	return resourceComputeProjectMetadataRead(d, meta)
   244  }