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 }