github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/google/resource_compute_instance_template.go (about) 1 package google 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/terraform/helper/schema" 8 "google.golang.org/api/compute/v1" 9 "google.golang.org/api/googleapi" 10 ) 11 12 func resourceComputeInstanceTemplate() *schema.Resource { 13 return &schema.Resource{ 14 Create: resourceComputeInstanceTemplateCreate, 15 Read: resourceComputeInstanceTemplateRead, 16 Delete: resourceComputeInstanceTemplateDelete, 17 18 Schema: map[string]*schema.Schema{ 19 "disk": &schema.Schema{ 20 Type: schema.TypeList, 21 Required: true, 22 ForceNew: true, 23 Elem: &schema.Resource{ 24 Schema: map[string]*schema.Schema{ 25 "auto_delete": &schema.Schema{ 26 Type: schema.TypeBool, 27 Optional: true, 28 Default: true, 29 ForceNew: true, 30 }, 31 32 "boot": &schema.Schema{ 33 Type: schema.TypeBool, 34 Optional: true, 35 ForceNew: true, 36 }, 37 38 "device_name": &schema.Schema{ 39 Type: schema.TypeString, 40 Optional: true, 41 ForceNew: true, 42 }, 43 44 "disk_name": &schema.Schema{ 45 Type: schema.TypeString, 46 Optional: true, 47 ForceNew: true, 48 }, 49 50 "disk_size_gb": &schema.Schema{ 51 Type: schema.TypeInt, 52 Optional: true, 53 ForceNew: true, 54 }, 55 56 "disk_type": &schema.Schema{ 57 Type: schema.TypeString, 58 Optional: true, 59 ForceNew: true, 60 }, 61 62 "source_image": &schema.Schema{ 63 Type: schema.TypeString, 64 Optional: true, 65 ForceNew: true, 66 }, 67 68 "interface": &schema.Schema{ 69 Type: schema.TypeString, 70 Optional: true, 71 ForceNew: true, 72 }, 73 74 "mode": &schema.Schema{ 75 Type: schema.TypeString, 76 Optional: true, 77 ForceNew: true, 78 }, 79 80 "source": &schema.Schema{ 81 Type: schema.TypeString, 82 Optional: true, 83 ForceNew: true, 84 }, 85 86 "type": &schema.Schema{ 87 Type: schema.TypeString, 88 Optional: true, 89 ForceNew: true, 90 }, 91 }, 92 }, 93 }, 94 95 "machine_type": &schema.Schema{ 96 Type: schema.TypeString, 97 Required: true, 98 ForceNew: true, 99 }, 100 101 "name": &schema.Schema{ 102 Type: schema.TypeString, 103 Required: true, 104 ForceNew: true, 105 }, 106 107 "automatic_restart": &schema.Schema{ 108 Type: schema.TypeBool, 109 Optional: true, 110 Default: true, 111 ForceNew: true, 112 Deprecated: "Please use `scheduling.automatic_restart` instead", 113 }, 114 115 "can_ip_forward": &schema.Schema{ 116 Type: schema.TypeBool, 117 Optional: true, 118 Default: false, 119 ForceNew: true, 120 }, 121 122 "description": &schema.Schema{ 123 Type: schema.TypeString, 124 Optional: true, 125 ForceNew: true, 126 }, 127 128 "instance_description": &schema.Schema{ 129 Type: schema.TypeString, 130 Optional: true, 131 ForceNew: true, 132 }, 133 134 "metadata": &schema.Schema{ 135 Type: schema.TypeMap, 136 Optional: true, 137 ForceNew: true, 138 }, 139 140 "metadata_fingerprint": &schema.Schema{ 141 Type: schema.TypeString, 142 Computed: true, 143 }, 144 145 "network_interface": &schema.Schema{ 146 Type: schema.TypeList, 147 Optional: true, 148 ForceNew: true, 149 Elem: &schema.Resource{ 150 Schema: map[string]*schema.Schema{ 151 "network": &schema.Schema{ 152 Type: schema.TypeString, 153 Optional: true, 154 ForceNew: true, 155 }, 156 157 "subnetwork": &schema.Schema{ 158 Type: schema.TypeString, 159 Optional: true, 160 ForceNew: true, 161 }, 162 163 "access_config": &schema.Schema{ 164 Type: schema.TypeList, 165 Optional: true, 166 Elem: &schema.Resource{ 167 Schema: map[string]*schema.Schema{ 168 "nat_ip": &schema.Schema{ 169 Type: schema.TypeString, 170 Computed: true, 171 Optional: true, 172 }, 173 }, 174 }, 175 }, 176 }, 177 }, 178 }, 179 180 "on_host_maintenance": &schema.Schema{ 181 Type: schema.TypeString, 182 Optional: true, 183 ForceNew: true, 184 Deprecated: "Please use `scheduling.on_host_maintenance` instead", 185 }, 186 187 "project": &schema.Schema{ 188 Type: schema.TypeString, 189 Optional: true, 190 ForceNew: true, 191 }, 192 193 "region": &schema.Schema{ 194 Type: schema.TypeString, 195 Optional: true, 196 ForceNew: true, 197 }, 198 199 "scheduling": &schema.Schema{ 200 Type: schema.TypeList, 201 Optional: true, 202 ForceNew: true, 203 Elem: &schema.Resource{ 204 Schema: map[string]*schema.Schema{ 205 "preemptible": &schema.Schema{ 206 Type: schema.TypeBool, 207 Optional: true, 208 ForceNew: true, 209 }, 210 211 "automatic_restart": &schema.Schema{ 212 Type: schema.TypeBool, 213 Optional: true, 214 Default: true, 215 ForceNew: true, 216 }, 217 218 "on_host_maintenance": &schema.Schema{ 219 Type: schema.TypeString, 220 Optional: true, 221 ForceNew: true, 222 }, 223 }, 224 }, 225 }, 226 227 "self_link": &schema.Schema{ 228 Type: schema.TypeString, 229 Computed: true, 230 }, 231 232 "service_account": &schema.Schema{ 233 Type: schema.TypeList, 234 Optional: true, 235 ForceNew: true, 236 Elem: &schema.Resource{ 237 Schema: map[string]*schema.Schema{ 238 "email": &schema.Schema{ 239 Type: schema.TypeString, 240 Computed: true, 241 ForceNew: true, 242 }, 243 244 "scopes": &schema.Schema{ 245 Type: schema.TypeList, 246 Required: true, 247 ForceNew: true, 248 Elem: &schema.Schema{ 249 Type: schema.TypeString, 250 StateFunc: func(v interface{}) string { 251 return canonicalizeServiceScope(v.(string)) 252 }, 253 }, 254 }, 255 }, 256 }, 257 }, 258 259 "tags": &schema.Schema{ 260 Type: schema.TypeSet, 261 Optional: true, 262 ForceNew: true, 263 Elem: &schema.Schema{Type: schema.TypeString}, 264 Set: schema.HashString, 265 }, 266 267 "tags_fingerprint": &schema.Schema{ 268 Type: schema.TypeString, 269 Computed: true, 270 }, 271 }, 272 } 273 } 274 275 func buildDisks(d *schema.ResourceData, meta interface{}) ([]*compute.AttachedDisk, error) { 276 config := meta.(*Config) 277 278 disksCount := d.Get("disk.#").(int) 279 280 disks := make([]*compute.AttachedDisk, 0, disksCount) 281 for i := 0; i < disksCount; i++ { 282 prefix := fmt.Sprintf("disk.%d", i) 283 284 // Build the disk 285 var disk compute.AttachedDisk 286 disk.Type = "PERSISTENT" 287 disk.Mode = "READ_WRITE" 288 disk.Interface = "SCSI" 289 disk.Boot = i == 0 290 disk.AutoDelete = d.Get(prefix + ".auto_delete").(bool) 291 292 if v, ok := d.GetOk(prefix + ".boot"); ok { 293 disk.Boot = v.(bool) 294 } 295 296 if v, ok := d.GetOk(prefix + ".device_name"); ok { 297 disk.DeviceName = v.(string) 298 } 299 300 if v, ok := d.GetOk(prefix + ".source"); ok { 301 disk.Source = v.(string) 302 } else { 303 disk.InitializeParams = &compute.AttachedDiskInitializeParams{} 304 305 if v, ok := d.GetOk(prefix + ".disk_name"); ok { 306 disk.InitializeParams.DiskName = v.(string) 307 } 308 if v, ok := d.GetOk(prefix + ".disk_size_gb"); ok { 309 disk.InitializeParams.DiskSizeGb = int64(v.(int)) 310 } 311 disk.InitializeParams.DiskType = "pd-standard" 312 if v, ok := d.GetOk(prefix + ".disk_type"); ok { 313 disk.InitializeParams.DiskType = v.(string) 314 } 315 316 if v, ok := d.GetOk(prefix + ".source_image"); ok { 317 imageName := v.(string) 318 imageUrl, err := resolveImage(config, imageName) 319 if err != nil { 320 return nil, fmt.Errorf( 321 "Error resolving image name '%s': %s", 322 imageName, err) 323 } 324 disk.InitializeParams.SourceImage = imageUrl 325 } 326 } 327 328 if v, ok := d.GetOk(prefix + ".interface"); ok { 329 disk.Interface = v.(string) 330 } 331 332 if v, ok := d.GetOk(prefix + ".mode"); ok { 333 disk.Mode = v.(string) 334 } 335 336 if v, ok := d.GetOk(prefix + ".type"); ok { 337 disk.Type = v.(string) 338 } 339 340 disks = append(disks, &disk) 341 } 342 343 return disks, nil 344 } 345 346 func buildNetworks(d *schema.ResourceData, meta interface{}) ([]*compute.NetworkInterface, error) { 347 // Build up the list of networks 348 config := meta.(*Config) 349 350 project, err := getProject(d, config) 351 if err != nil { 352 return nil, err 353 } 354 355 networksCount := d.Get("network_interface.#").(int) 356 networkInterfaces := make([]*compute.NetworkInterface, 0, networksCount) 357 for i := 0; i < networksCount; i++ { 358 prefix := fmt.Sprintf("network_interface.%d", i) 359 360 var networkName, subnetworkName string 361 if v, ok := d.GetOk(prefix + ".network"); ok { 362 networkName = v.(string) 363 } 364 if v, ok := d.GetOk(prefix + ".subnetwork"); ok { 365 subnetworkName = v.(string) 366 } 367 368 if networkName == "" && subnetworkName == "" { 369 return nil, fmt.Errorf("network or subnetwork must be provided") 370 } 371 if networkName != "" && subnetworkName != "" { 372 return nil, fmt.Errorf("network or subnetwork must not both be provided") 373 } 374 375 var networkLink, subnetworkLink string 376 if networkName != "" { 377 network, err := config.clientCompute.Networks.Get( 378 project, networkName).Do() 379 if err != nil { 380 return nil, fmt.Errorf("Error referencing network '%s': %s", 381 networkName, err) 382 } 383 networkLink = network.SelfLink 384 } else { 385 // lookup subnetwork link using region and subnetwork name 386 region, err := getRegion(d, config) 387 if err != nil { 388 return nil, err 389 } 390 subnetwork, err := config.clientCompute.Subnetworks.Get( 391 project, region, subnetworkName).Do() 392 if err != nil { 393 return nil, fmt.Errorf( 394 "Error referencing subnetwork '%s' in region '%s': %s", 395 subnetworkName, region, err) 396 } 397 subnetworkLink = subnetwork.SelfLink 398 } 399 400 // Build the networkInterface 401 var iface compute.NetworkInterface 402 iface.Network = networkLink 403 iface.Subnetwork = subnetworkLink 404 405 accessConfigsCount := d.Get(prefix + ".access_config.#").(int) 406 iface.AccessConfigs = make([]*compute.AccessConfig, accessConfigsCount) 407 for j := 0; j < accessConfigsCount; j++ { 408 acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j) 409 iface.AccessConfigs[j] = &compute.AccessConfig{ 410 Type: "ONE_TO_ONE_NAT", 411 NatIP: d.Get(acPrefix + ".nat_ip").(string), 412 } 413 } 414 415 networkInterfaces = append(networkInterfaces, &iface) 416 } 417 return networkInterfaces, nil 418 } 419 420 func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error { 421 config := meta.(*Config) 422 423 project, err := getProject(d, config) 424 if err != nil { 425 return err 426 } 427 428 instanceProperties := &compute.InstanceProperties{} 429 430 instanceProperties.CanIpForward = d.Get("can_ip_forward").(bool) 431 instanceProperties.Description = d.Get("instance_description").(string) 432 instanceProperties.MachineType = d.Get("machine_type").(string) 433 disks, err := buildDisks(d, meta) 434 if err != nil { 435 return err 436 } 437 instanceProperties.Disks = disks 438 metadata, err := resourceInstanceMetadata(d) 439 if err != nil { 440 return err 441 } 442 instanceProperties.Metadata = metadata 443 networks, err := buildNetworks(d, meta) 444 if err != nil { 445 return err 446 } 447 instanceProperties.NetworkInterfaces = networks 448 449 instanceProperties.Scheduling = &compute.Scheduling{} 450 instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE" 451 452 if v, ok := d.GetOk("automatic_restart"); ok { 453 instanceProperties.Scheduling.AutomaticRestart = v.(bool) 454 } 455 456 if v, ok := d.GetOk("on_host_maintenance"); ok { 457 instanceProperties.Scheduling.OnHostMaintenance = v.(string) 458 } 459 460 forceSendFieldsScheduling := make([]string, 0, 3) 461 var hasSendMaintenance bool 462 hasSendMaintenance = false 463 if v, ok := d.GetOk("scheduling"); ok { 464 _schedulings := v.([]interface{}) 465 if len(_schedulings) > 1 { 466 return fmt.Errorf("Error, at most one `scheduling` block can be defined") 467 } 468 _scheduling := _schedulings[0].(map[string]interface{}) 469 470 if vp, okp := _scheduling["automatic_restart"]; okp { 471 instanceProperties.Scheduling.AutomaticRestart = vp.(bool) 472 forceSendFieldsScheduling = append(forceSendFieldsScheduling, "AutomaticRestart") 473 } 474 475 if vp, okp := _scheduling["on_host_maintenance"]; okp { 476 instanceProperties.Scheduling.OnHostMaintenance = vp.(string) 477 forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance") 478 hasSendMaintenance = true 479 } 480 481 if vp, okp := _scheduling["preemptible"]; okp { 482 instanceProperties.Scheduling.Preemptible = vp.(bool) 483 forceSendFieldsScheduling = append(forceSendFieldsScheduling, "Preemptible") 484 if vp.(bool) && !hasSendMaintenance { 485 instanceProperties.Scheduling.OnHostMaintenance = "TERMINATE" 486 forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance") 487 } 488 } 489 } 490 instanceProperties.Scheduling.ForceSendFields = forceSendFieldsScheduling 491 492 serviceAccountsCount := d.Get("service_account.#").(int) 493 serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount) 494 for i := 0; i < serviceAccountsCount; i++ { 495 prefix := fmt.Sprintf("service_account.%d", i) 496 497 scopesCount := d.Get(prefix + ".scopes.#").(int) 498 scopes := make([]string, 0, scopesCount) 499 for j := 0; j < scopesCount; j++ { 500 scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string) 501 scopes = append(scopes, canonicalizeServiceScope(scope)) 502 } 503 504 serviceAccount := &compute.ServiceAccount{ 505 Email: "default", 506 Scopes: scopes, 507 } 508 509 serviceAccounts = append(serviceAccounts, serviceAccount) 510 } 511 instanceProperties.ServiceAccounts = serviceAccounts 512 513 instanceProperties.Tags = resourceInstanceTags(d) 514 515 instanceTemplate := compute.InstanceTemplate{ 516 Description: d.Get("description").(string), 517 Properties: instanceProperties, 518 Name: d.Get("name").(string), 519 } 520 521 op, err := config.clientCompute.InstanceTemplates.Insert( 522 project, &instanceTemplate).Do() 523 if err != nil { 524 return fmt.Errorf("Error creating instance: %s", err) 525 } 526 527 // Store the ID now 528 d.SetId(instanceTemplate.Name) 529 530 err = computeOperationWaitGlobal(config, op, "Creating Instance Template") 531 if err != nil { 532 return err 533 } 534 535 return resourceComputeInstanceTemplateRead(d, meta) 536 } 537 538 func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error { 539 config := meta.(*Config) 540 541 project, err := getProject(d, config) 542 if err != nil { 543 return err 544 } 545 546 instanceTemplate, err := config.clientCompute.InstanceTemplates.Get( 547 project, d.Id()).Do() 548 if err != nil { 549 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 550 log.Printf("[WARN] Removing Instance Template %q because it's gone", d.Get("name").(string)) 551 // The resource doesn't exist anymore 552 d.SetId("") 553 554 return nil 555 } 556 557 return fmt.Errorf("Error reading instance template: %s", err) 558 } 559 560 // Set the metadata fingerprint if there is one. 561 if instanceTemplate.Properties.Metadata != nil { 562 d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint) 563 } 564 565 // Set the tags fingerprint if there is one. 566 if instanceTemplate.Properties.Tags != nil { 567 d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint) 568 } 569 d.Set("self_link", instanceTemplate.SelfLink) 570 571 return nil 572 } 573 574 func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error { 575 config := meta.(*Config) 576 577 project, err := getProject(d, config) 578 if err != nil { 579 return err 580 } 581 582 op, err := config.clientCompute.InstanceTemplates.Delete( 583 project, d.Id()).Do() 584 if err != nil { 585 return fmt.Errorf("Error deleting instance template: %s", err) 586 } 587 588 err = computeOperationWaitGlobal(config, op, "Deleting Instance Template") 589 if err != nil { 590 return err 591 } 592 593 d.SetId("") 594 return nil 595 }