github.com/biggiemac/terraform@v0.6.12-0.20160217180759-34b7665af0d6/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 Required: true, 141 ForceNew: true, 142 }, 143 144 "access_config": &schema.Schema{ 145 Type: schema.TypeList, 146 Optional: true, 147 Elem: &schema.Resource{ 148 Schema: map[string]*schema.Schema{ 149 "nat_ip": &schema.Schema{ 150 Type: schema.TypeString, 151 Computed: true, 152 Optional: true, 153 }, 154 }, 155 }, 156 }, 157 }, 158 }, 159 }, 160 161 "automatic_restart": &schema.Schema{ 162 Type: schema.TypeBool, 163 Optional: true, 164 Default: true, 165 ForceNew: true, 166 Deprecated: "Please use `scheduling.automatic_restart` instead", 167 }, 168 169 "on_host_maintenance": &schema.Schema{ 170 Type: schema.TypeString, 171 Optional: true, 172 ForceNew: true, 173 Deprecated: "Please use `scheduling.on_host_maintenance` instead", 174 }, 175 176 "scheduling": &schema.Schema{ 177 Type: schema.TypeList, 178 Optional: true, 179 ForceNew: true, 180 Elem: &schema.Resource{ 181 Schema: map[string]*schema.Schema{ 182 "preemptible": &schema.Schema{ 183 Type: schema.TypeBool, 184 Optional: true, 185 ForceNew: true, 186 }, 187 188 "automatic_restart": &schema.Schema{ 189 Type: schema.TypeBool, 190 Optional: true, 191 Default: true, 192 ForceNew: true, 193 }, 194 195 "on_host_maintenance": &schema.Schema{ 196 Type: schema.TypeString, 197 Optional: true, 198 ForceNew: true, 199 }, 200 }, 201 }, 202 }, 203 204 "service_account": &schema.Schema{ 205 Type: schema.TypeList, 206 Optional: true, 207 ForceNew: true, 208 Elem: &schema.Resource{ 209 Schema: map[string]*schema.Schema{ 210 "email": &schema.Schema{ 211 Type: schema.TypeString, 212 Computed: true, 213 ForceNew: true, 214 }, 215 216 "scopes": &schema.Schema{ 217 Type: schema.TypeList, 218 Required: true, 219 ForceNew: true, 220 Elem: &schema.Schema{ 221 Type: schema.TypeString, 222 StateFunc: func(v interface{}) string { 223 return canonicalizeServiceScope(v.(string)) 224 }, 225 }, 226 }, 227 }, 228 }, 229 }, 230 231 "tags": &schema.Schema{ 232 Type: schema.TypeSet, 233 Optional: true, 234 ForceNew: true, 235 Elem: &schema.Schema{Type: schema.TypeString}, 236 Set: schema.HashString, 237 }, 238 239 "metadata_fingerprint": &schema.Schema{ 240 Type: schema.TypeString, 241 Computed: true, 242 }, 243 244 "tags_fingerprint": &schema.Schema{ 245 Type: schema.TypeString, 246 Computed: true, 247 }, 248 249 "self_link": &schema.Schema{ 250 Type: schema.TypeString, 251 Computed: true, 252 }, 253 }, 254 } 255 } 256 257 func buildDisks(d *schema.ResourceData, meta interface{}) ([]*compute.AttachedDisk, error) { 258 config := meta.(*Config) 259 260 disksCount := d.Get("disk.#").(int) 261 262 disks := make([]*compute.AttachedDisk, 0, disksCount) 263 for i := 0; i < disksCount; i++ { 264 prefix := fmt.Sprintf("disk.%d", i) 265 266 // Build the disk 267 var disk compute.AttachedDisk 268 disk.Type = "PERSISTENT" 269 disk.Mode = "READ_WRITE" 270 disk.Interface = "SCSI" 271 disk.Boot = i == 0 272 disk.AutoDelete = d.Get(prefix + ".auto_delete").(bool) 273 274 if v, ok := d.GetOk(prefix + ".boot"); ok { 275 disk.Boot = v.(bool) 276 } 277 278 if v, ok := d.GetOk(prefix + ".device_name"); ok { 279 disk.DeviceName = v.(string) 280 } 281 282 if v, ok := d.GetOk(prefix + ".source"); ok { 283 disk.Source = v.(string) 284 } else { 285 disk.InitializeParams = &compute.AttachedDiskInitializeParams{} 286 287 if v, ok := d.GetOk(prefix + ".disk_name"); ok { 288 disk.InitializeParams.DiskName = v.(string) 289 } 290 if v, ok := d.GetOk(prefix + ".disk_size_gb"); ok { 291 disk.InitializeParams.DiskSizeGb = int64(v.(int)) 292 } 293 disk.InitializeParams.DiskType = "pd-standard" 294 if v, ok := d.GetOk(prefix + ".disk_type"); ok { 295 disk.InitializeParams.DiskType = v.(string) 296 } 297 298 if v, ok := d.GetOk(prefix + ".source_image"); ok { 299 imageName := v.(string) 300 imageUrl, err := resolveImage(config, imageName) 301 if err != nil { 302 return nil, fmt.Errorf( 303 "Error resolving image name '%s': %s", 304 imageName, err) 305 } 306 disk.InitializeParams.SourceImage = imageUrl 307 } 308 } 309 310 if v, ok := d.GetOk(prefix + ".interface"); ok { 311 disk.Interface = v.(string) 312 } 313 314 if v, ok := d.GetOk(prefix + ".mode"); ok { 315 disk.Mode = v.(string) 316 } 317 318 if v, ok := d.GetOk(prefix + ".type"); ok { 319 disk.Type = v.(string) 320 } 321 322 disks = append(disks, &disk) 323 } 324 325 return disks, nil 326 } 327 328 func buildNetworks(d *schema.ResourceData, meta interface{}) (error, []*compute.NetworkInterface) { 329 // Build up the list of networks 330 networksCount := d.Get("network_interface.#").(int) 331 networkInterfaces := make([]*compute.NetworkInterface, 0, networksCount) 332 for i := 0; i < networksCount; i++ { 333 prefix := fmt.Sprintf("network_interface.%d", i) 334 335 source := "global/networks/" 336 if v, ok := d.GetOk(prefix + ".network"); ok { 337 source += v.(string) 338 } 339 340 // Build the networkInterface 341 var iface compute.NetworkInterface 342 iface.Network = source 343 344 accessConfigsCount := d.Get(prefix + ".access_config.#").(int) 345 iface.AccessConfigs = make([]*compute.AccessConfig, accessConfigsCount) 346 for j := 0; j < accessConfigsCount; j++ { 347 acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j) 348 iface.AccessConfigs[j] = &compute.AccessConfig{ 349 Type: "ONE_TO_ONE_NAT", 350 NatIP: d.Get(acPrefix + ".nat_ip").(string), 351 } 352 } 353 354 networkInterfaces = append(networkInterfaces, &iface) 355 } 356 return nil, networkInterfaces 357 } 358 359 func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error { 360 config := meta.(*Config) 361 362 instanceProperties := &compute.InstanceProperties{} 363 364 instanceProperties.CanIpForward = d.Get("can_ip_forward").(bool) 365 instanceProperties.Description = d.Get("instance_description").(string) 366 instanceProperties.MachineType = d.Get("machine_type").(string) 367 disks, err := buildDisks(d, meta) 368 if err != nil { 369 return err 370 } 371 instanceProperties.Disks = disks 372 metadata, err := resourceInstanceMetadata(d) 373 if err != nil { 374 return err 375 } 376 instanceProperties.Metadata = metadata 377 err, networks := buildNetworks(d, meta) 378 if err != nil { 379 return err 380 } 381 instanceProperties.NetworkInterfaces = networks 382 383 instanceProperties.Scheduling = &compute.Scheduling{} 384 instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE" 385 386 if v, ok := d.GetOk("automatic_restart"); ok { 387 instanceProperties.Scheduling.AutomaticRestart = v.(bool) 388 } 389 390 if v, ok := d.GetOk("on_host_maintenance"); ok { 391 instanceProperties.Scheduling.OnHostMaintenance = v.(string) 392 } 393 394 if v, ok := d.GetOk("scheduling"); ok { 395 _schedulings := v.([]interface{}) 396 if len(_schedulings) > 1 { 397 return fmt.Errorf("Error, at most one `scheduling` block can be defined") 398 } 399 _scheduling := _schedulings[0].(map[string]interface{}) 400 401 if vp, okp := _scheduling["automatic_restart"]; okp { 402 instanceProperties.Scheduling.AutomaticRestart = vp.(bool) 403 } 404 405 if vp, okp := _scheduling["on_host_maintenance"]; okp { 406 instanceProperties.Scheduling.OnHostMaintenance = vp.(string) 407 } 408 409 if vp, okp := _scheduling["preemptible"]; okp { 410 instanceProperties.Scheduling.Preemptible = vp.(bool) 411 } 412 } 413 414 serviceAccountsCount := d.Get("service_account.#").(int) 415 serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount) 416 for i := 0; i < serviceAccountsCount; i++ { 417 prefix := fmt.Sprintf("service_account.%d", i) 418 419 scopesCount := d.Get(prefix + ".scopes.#").(int) 420 scopes := make([]string, 0, scopesCount) 421 for j := 0; j < scopesCount; j++ { 422 scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string) 423 scopes = append(scopes, canonicalizeServiceScope(scope)) 424 } 425 426 serviceAccount := &compute.ServiceAccount{ 427 Email: "default", 428 Scopes: scopes, 429 } 430 431 serviceAccounts = append(serviceAccounts, serviceAccount) 432 } 433 instanceProperties.ServiceAccounts = serviceAccounts 434 435 instanceProperties.Tags = resourceInstanceTags(d) 436 437 instanceTemplate := compute.InstanceTemplate{ 438 Description: d.Get("description").(string), 439 Properties: instanceProperties, 440 Name: d.Get("name").(string), 441 } 442 443 op, err := config.clientCompute.InstanceTemplates.Insert( 444 config.Project, &instanceTemplate).Do() 445 if err != nil { 446 return fmt.Errorf("Error creating instance: %s", err) 447 } 448 449 // Store the ID now 450 d.SetId(instanceTemplate.Name) 451 452 err = computeOperationWaitGlobal(config, op, "Creating Instance Template") 453 if err != nil { 454 return err 455 } 456 457 return resourceComputeInstanceTemplateRead(d, meta) 458 } 459 460 func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error { 461 config := meta.(*Config) 462 463 instanceTemplate, err := config.clientCompute.InstanceTemplates.Get( 464 config.Project, d.Id()).Do() 465 if err != nil { 466 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 467 log.Printf("[WARN] Removing Instance Template %q because it's gone", d.Get("name").(string)) 468 // The resource doesn't exist anymore 469 d.SetId("") 470 471 return nil 472 } 473 474 return fmt.Errorf("Error reading instance template: %s", err) 475 } 476 477 // Set the metadata fingerprint if there is one. 478 if instanceTemplate.Properties.Metadata != nil { 479 d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint) 480 } 481 482 // Set the tags fingerprint if there is one. 483 if instanceTemplate.Properties.Tags != nil { 484 d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint) 485 } 486 d.Set("self_link", instanceTemplate.SelfLink) 487 488 return nil 489 } 490 491 func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error { 492 config := meta.(*Config) 493 494 op, err := config.clientCompute.InstanceTemplates.Delete( 495 config.Project, d.Id()).Do() 496 if err != nil { 497 return fmt.Errorf("Error deleting instance template: %s", err) 498 } 499 500 err = computeOperationWaitGlobal(config, op, "Deleting Instance Template") 501 if err != nil { 502 return err 503 } 504 505 d.SetId("") 506 return nil 507 }