github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/builtin/providers/google/resource_compute_instance_template.go (about) 1 package google 2 3 import ( 4 "fmt" 5 "time" 6 7 "code.google.com/p/google-api-go-client/compute/v1" 8 "code.google.com/p/google-api-go-client/googleapi" 9 "github.com/hashicorp/terraform/helper/hashcode" 10 "github.com/hashicorp/terraform/helper/schema" 11 ) 12 13 func resourceComputeInstanceTemplate() *schema.Resource { 14 return &schema.Resource{ 15 Create: resourceComputeInstanceTemplateCreate, 16 Read: resourceComputeInstanceTemplateRead, 17 Delete: resourceComputeInstanceTemplateDelete, 18 19 Schema: map[string]*schema.Schema{ 20 "name": &schema.Schema{ 21 Type: schema.TypeString, 22 Required: true, 23 ForceNew: true, 24 }, 25 26 "description": &schema.Schema{ 27 Type: schema.TypeString, 28 Optional: true, 29 ForceNew: true, 30 }, 31 32 "can_ip_forward": &schema.Schema{ 33 Type: schema.TypeBool, 34 Optional: true, 35 Default: false, 36 ForceNew: true, 37 }, 38 39 "instance_description": &schema.Schema{ 40 Type: schema.TypeString, 41 Optional: true, 42 ForceNew: true, 43 }, 44 45 "machine_type": &schema.Schema{ 46 Type: schema.TypeString, 47 Required: true, 48 ForceNew: true, 49 }, 50 51 // TODO: Constraint either source or other disk params 52 "disk": &schema.Schema{ 53 Type: schema.TypeList, 54 Required: true, 55 ForceNew: true, 56 Elem: &schema.Resource{ 57 Schema: map[string]*schema.Schema{ 58 "auto_delete": &schema.Schema{ 59 Type: schema.TypeBool, 60 Optional: true, 61 ForceNew: true, 62 }, 63 64 "boot": &schema.Schema{ 65 Type: schema.TypeBool, 66 Optional: true, 67 ForceNew: true, 68 }, 69 70 "device_name": &schema.Schema{ 71 Type: schema.TypeString, 72 Optional: true, 73 ForceNew: true, 74 }, 75 76 "disk_name": &schema.Schema{ 77 Type: schema.TypeString, 78 Optional: true, 79 ForceNew: true, 80 }, 81 82 "disk_size_gb": &schema.Schema{ 83 Type: schema.TypeInt, 84 Optional: true, 85 ForceNew: true, 86 }, 87 88 "disk_type": &schema.Schema{ 89 Type: schema.TypeString, 90 Optional: true, 91 ForceNew: true, 92 }, 93 94 "source_image": &schema.Schema{ 95 Type: schema.TypeString, 96 Optional: true, 97 ForceNew: true, 98 }, 99 100 "interface": &schema.Schema{ 101 Type: schema.TypeString, 102 Optional: true, 103 ForceNew: true, 104 }, 105 106 "mode": &schema.Schema{ 107 Type: schema.TypeString, 108 Optional: true, 109 ForceNew: true, 110 }, 111 112 "source": &schema.Schema{ 113 Type: schema.TypeString, 114 Optional: true, 115 ForceNew: true, 116 }, 117 118 "type": &schema.Schema{ 119 Type: schema.TypeString, 120 Optional: true, 121 ForceNew: true, 122 }, 123 }, 124 }, 125 }, 126 127 "metadata": &schema.Schema{ 128 Type: schema.TypeList, 129 Optional: true, 130 ForceNew: true, 131 Elem: &schema.Schema{ 132 Type: schema.TypeMap, 133 }, 134 }, 135 136 "network": &schema.Schema{ 137 Type: schema.TypeList, 138 Required: true, 139 ForceNew: true, 140 Elem: &schema.Resource{ 141 Schema: map[string]*schema.Schema{ 142 "source": &schema.Schema{ 143 Type: schema.TypeString, 144 ForceNew: true, 145 Required: true, 146 }, 147 148 "address": &schema.Schema{ 149 Type: schema.TypeString, 150 ForceNew: true, 151 Optional: true, 152 }, 153 }, 154 }, 155 }, 156 157 "automatic_restart": &schema.Schema{ 158 Type: schema.TypeBool, 159 Optional: true, 160 Default: true, 161 ForceNew: true, 162 }, 163 164 "on_host_maintenance": &schema.Schema{ 165 Type: schema.TypeString, 166 Optional: true, 167 ForceNew: true, 168 }, 169 170 "service_account": &schema.Schema{ 171 Type: schema.TypeList, 172 Optional: true, 173 ForceNew: true, 174 Elem: &schema.Resource{ 175 Schema: map[string]*schema.Schema{ 176 "email": &schema.Schema{ 177 Type: schema.TypeString, 178 Computed: true, 179 ForceNew: true, 180 }, 181 182 "scopes": &schema.Schema{ 183 Type: schema.TypeList, 184 Required: true, 185 ForceNew: true, 186 Elem: &schema.Schema{ 187 Type: schema.TypeString, 188 StateFunc: func(v interface{}) string { 189 return canonicalizeServiceScope(v.(string)) 190 }, 191 }, 192 }, 193 }, 194 }, 195 }, 196 197 "tags": &schema.Schema{ 198 Type: schema.TypeSet, 199 Optional: true, 200 ForceNew: true, 201 Elem: &schema.Schema{Type: schema.TypeString}, 202 Set: func(v interface{}) int { 203 return hashcode.String(v.(string)) 204 }, 205 }, 206 207 "metadata_fingerprint": &schema.Schema{ 208 Type: schema.TypeString, 209 Computed: true, 210 }, 211 212 "tags_fingerprint": &schema.Schema{ 213 Type: schema.TypeString, 214 Computed: true, 215 }, 216 217 "self_link": &schema.Schema{ 218 Type: schema.TypeString, 219 Computed: true, 220 }, 221 }, 222 } 223 } 224 225 func buildDisks(d *schema.ResourceData, meta interface{}) []*compute.AttachedDisk { 226 disksCount := d.Get("disk.#").(int) 227 228 disks := make([]*compute.AttachedDisk, 0, disksCount) 229 for i := 0; i < disksCount; i++ { 230 prefix := fmt.Sprintf("disk.%d", i) 231 232 // Build the disk 233 var disk compute.AttachedDisk 234 disk.Type = "PERSISTENT" 235 disk.Mode = "READ_WRITE" 236 disk.Interface = "SCSI" 237 disk.Boot = i == 0 238 disk.AutoDelete = true 239 240 if v, ok := d.GetOk(prefix + ".auto_delete"); ok { 241 disk.AutoDelete = v.(bool) 242 } 243 244 if v, ok := d.GetOk(prefix + ".boot"); ok { 245 disk.Boot = v.(bool) 246 } 247 248 if v, ok := d.GetOk(prefix + ".device_name"); ok { 249 disk.DeviceName = v.(string) 250 } 251 252 if v, ok := d.GetOk(prefix + ".source"); ok { 253 disk.Source = v.(string) 254 } else { 255 disk.InitializeParams = &compute.AttachedDiskInitializeParams{} 256 257 if v, ok := d.GetOk(prefix + ".disk_name"); ok { 258 disk.InitializeParams.DiskName = v.(string) 259 } 260 if v, ok := d.GetOk(prefix + ".disk_size_gb"); ok { 261 disk.InitializeParams.DiskSizeGb = v.(int64) 262 } 263 disk.InitializeParams.DiskType = "pd-standard" 264 if v, ok := d.GetOk(prefix + ".disk_type"); ok { 265 disk.InitializeParams.DiskType = v.(string) 266 } 267 268 if v, ok := d.GetOk(prefix + ".source_image"); ok { 269 disk.InitializeParams.SourceImage = v.(string) 270 } 271 } 272 273 if v, ok := d.GetOk(prefix + ".interface"); ok { 274 disk.Interface = v.(string) 275 } 276 277 if v, ok := d.GetOk(prefix + ".mode"); ok { 278 disk.Mode = v.(string) 279 } 280 281 if v, ok := d.GetOk(prefix + ".type"); ok { 282 disk.Type = v.(string) 283 } 284 285 disks = append(disks, &disk) 286 } 287 288 return disks 289 } 290 291 func buildNetworks(d *schema.ResourceData, meta interface{}) (error, []*compute.NetworkInterface) { 292 // Build up the list of networks 293 networksCount := d.Get("network.#").(int) 294 networks := make([]*compute.NetworkInterface, 0, networksCount) 295 for i := 0; i < networksCount; i++ { 296 prefix := fmt.Sprintf("network.%d", i) 297 298 source := "global/networks/default" 299 if v, ok := d.GetOk(prefix + ".source"); ok { 300 if v.(string) != "default" { 301 source = v.(string) 302 } 303 } 304 305 // Build the interface 306 var iface compute.NetworkInterface 307 iface.AccessConfigs = []*compute.AccessConfig{ 308 &compute.AccessConfig{ 309 Type: "ONE_TO_ONE_NAT", 310 NatIP: d.Get(prefix + ".address").(string), 311 }, 312 } 313 iface.Network = source 314 315 networks = append(networks, &iface) 316 } 317 return nil, networks 318 } 319 320 func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error { 321 config := meta.(*Config) 322 323 instanceProperties := &compute.InstanceProperties{} 324 325 instanceProperties.CanIpForward = d.Get("can_ip_forward").(bool) 326 instanceProperties.Description = d.Get("instance_description").(string) 327 instanceProperties.MachineType = d.Get("machine_type").(string) 328 instanceProperties.Disks = buildDisks(d, meta) 329 instanceProperties.Metadata = resourceInstanceMetadata(d) 330 err, networks := buildNetworks(d, meta) 331 if err != nil { 332 return err 333 } 334 instanceProperties.NetworkInterfaces = networks 335 336 instanceProperties.Scheduling = &compute.Scheduling{ 337 AutomaticRestart: d.Get("automatic_restart").(bool), 338 } 339 instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE" 340 if v, ok := d.GetOk("on_host_maintenance"); ok { 341 instanceProperties.Scheduling.OnHostMaintenance = v.(string) 342 } 343 344 serviceAccountsCount := d.Get("service_account.#").(int) 345 serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount) 346 for i := 0; i < serviceAccountsCount; i++ { 347 prefix := fmt.Sprintf("service_account.%d", i) 348 349 scopesCount := d.Get(prefix + ".scopes.#").(int) 350 scopes := make([]string, 0, scopesCount) 351 for j := 0; j < scopesCount; j++ { 352 scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string) 353 scopes = append(scopes, canonicalizeServiceScope(scope)) 354 } 355 356 serviceAccount := &compute.ServiceAccount{ 357 Email: "default", 358 Scopes: scopes, 359 } 360 361 serviceAccounts = append(serviceAccounts, serviceAccount) 362 } 363 instanceProperties.ServiceAccounts = serviceAccounts 364 365 instanceProperties.Tags = resourceInstanceTags(d) 366 367 instanceTemplate := compute.InstanceTemplate{ 368 Description: d.Get("description").(string), 369 Properties: instanceProperties, 370 Name: d.Get("name").(string), 371 } 372 373 op, err := config.clientCompute.InstanceTemplates.Insert( 374 config.Project, &instanceTemplate).Do() 375 if err != nil { 376 return fmt.Errorf("Error creating instance: %s", err) 377 } 378 379 // Store the ID now 380 d.SetId(instanceTemplate.Name) 381 382 // Wait for the operation to complete 383 w := &OperationWaiter{ 384 Service: config.clientCompute, 385 Op: op, 386 Project: config.Project, 387 Type: OperationWaitGlobal, 388 } 389 state := w.Conf() 390 state.Delay = 10 * time.Second 391 state.Timeout = 10 * time.Minute 392 state.MinTimeout = 2 * time.Second 393 opRaw, err := state.WaitForState() 394 if err != nil { 395 return fmt.Errorf("Error waiting for instance template to create: %s", err) 396 } 397 op = opRaw.(*compute.Operation) 398 if op.Error != nil { 399 // The resource didn't actually create 400 d.SetId("") 401 402 // Return the error 403 return OperationError(*op.Error) 404 } 405 406 return resourceComputeInstanceTemplateRead(d, meta) 407 } 408 409 func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error { 410 config := meta.(*Config) 411 412 instanceTemplate, err := config.clientCompute.InstanceTemplates.Get( 413 config.Project, d.Id()).Do() 414 if err != nil { 415 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 416 // The resource doesn't exist anymore 417 d.SetId("") 418 419 return nil 420 } 421 422 return fmt.Errorf("Error reading instance template: %s", err) 423 } 424 425 // Set the metadata fingerprint if there is one. 426 if instanceTemplate.Properties.Metadata != nil { 427 d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint) 428 } 429 430 // Set the tags fingerprint if there is one. 431 if instanceTemplate.Properties.Tags != nil { 432 d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint) 433 } 434 d.Set("self_link", instanceTemplate.SelfLink) 435 436 return nil 437 } 438 439 func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error { 440 config := meta.(*Config) 441 442 op, err := config.clientCompute.InstanceTemplates.Delete( 443 config.Project, d.Id()).Do() 444 if err != nil { 445 return fmt.Errorf("Error deleting instance template: %s", err) 446 } 447 448 // Wait for the operation to complete 449 w := &OperationWaiter{ 450 Service: config.clientCompute, 451 Op: op, 452 Project: config.Project, 453 Type: OperationWaitGlobal, 454 } 455 state := w.Conf() 456 state.Delay = 5 * time.Second 457 state.Timeout = 5 * time.Minute 458 state.MinTimeout = 2 * time.Second 459 opRaw, err := state.WaitForState() 460 if err != nil { 461 return fmt.Errorf("Error waiting for instance template to delete: %s", err) 462 } 463 op = opRaw.(*compute.Operation) 464 if op.Error != nil { 465 // Return the error 466 return OperationError(*op.Error) 467 } 468 469 d.SetId("") 470 return nil 471 }