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