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