github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/google/resource_compute_instance_template.go (about) 1 package google 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform/helper/hashcode" 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: func(v interface{}) int { 237 return hashcode.String(v.(string)) 238 }, 239 }, 240 241 "metadata_fingerprint": &schema.Schema{ 242 Type: schema.TypeString, 243 Computed: true, 244 }, 245 246 "tags_fingerprint": &schema.Schema{ 247 Type: schema.TypeString, 248 Computed: true, 249 }, 250 251 "self_link": &schema.Schema{ 252 Type: schema.TypeString, 253 Computed: true, 254 }, 255 }, 256 } 257 } 258 259 func buildDisks(d *schema.ResourceData, meta interface{}) ([]*compute.AttachedDisk, error) { 260 config := meta.(*Config) 261 262 disksCount := d.Get("disk.#").(int) 263 264 disks := make([]*compute.AttachedDisk, 0, disksCount) 265 for i := 0; i < disksCount; i++ { 266 prefix := fmt.Sprintf("disk.%d", i) 267 268 // Build the disk 269 var disk compute.AttachedDisk 270 disk.Type = "PERSISTENT" 271 disk.Mode = "READ_WRITE" 272 disk.Interface = "SCSI" 273 disk.Boot = i == 0 274 disk.AutoDelete = d.Get(prefix + ".auto_delete").(bool) 275 276 if v, ok := d.GetOk(prefix + ".boot"); ok { 277 disk.Boot = v.(bool) 278 } 279 280 if v, ok := d.GetOk(prefix + ".device_name"); ok { 281 disk.DeviceName = v.(string) 282 } 283 284 if v, ok := d.GetOk(prefix + ".source"); ok { 285 disk.Source = v.(string) 286 } else { 287 disk.InitializeParams = &compute.AttachedDiskInitializeParams{} 288 289 if v, ok := d.GetOk(prefix + ".disk_name"); ok { 290 disk.InitializeParams.DiskName = v.(string) 291 } 292 if v, ok := d.GetOk(prefix + ".disk_size_gb"); ok { 293 disk.InitializeParams.DiskSizeGb = int64(v.(int)) 294 } 295 disk.InitializeParams.DiskType = "pd-standard" 296 if v, ok := d.GetOk(prefix + ".disk_type"); ok { 297 disk.InitializeParams.DiskType = v.(string) 298 } 299 300 if v, ok := d.GetOk(prefix + ".source_image"); ok { 301 imageName := v.(string) 302 imageUrl, err := resolveImage(config, imageName) 303 if err != nil { 304 return nil, fmt.Errorf( 305 "Error resolving image name '%s': %s", 306 imageName, err) 307 } 308 disk.InitializeParams.SourceImage = imageUrl 309 } 310 } 311 312 if v, ok := d.GetOk(prefix + ".interface"); ok { 313 disk.Interface = v.(string) 314 } 315 316 if v, ok := d.GetOk(prefix + ".mode"); ok { 317 disk.Mode = v.(string) 318 } 319 320 if v, ok := d.GetOk(prefix + ".type"); ok { 321 disk.Type = v.(string) 322 } 323 324 disks = append(disks, &disk) 325 } 326 327 return disks, nil 328 } 329 330 func buildNetworks(d *schema.ResourceData, meta interface{}) (error, []*compute.NetworkInterface) { 331 // Build up the list of networks 332 networksCount := d.Get("network_interface.#").(int) 333 networkInterfaces := make([]*compute.NetworkInterface, 0, networksCount) 334 for i := 0; i < networksCount; i++ { 335 prefix := fmt.Sprintf("network_interface.%d", i) 336 337 source := "global/networks/" 338 if v, ok := d.GetOk(prefix + ".network"); ok { 339 source += v.(string) 340 } 341 342 // Build the networkInterface 343 var iface compute.NetworkInterface 344 iface.Network = source 345 346 accessConfigsCount := d.Get(prefix + ".access_config.#").(int) 347 iface.AccessConfigs = make([]*compute.AccessConfig, accessConfigsCount) 348 for j := 0; j < accessConfigsCount; j++ { 349 acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j) 350 iface.AccessConfigs[j] = &compute.AccessConfig{ 351 Type: "ONE_TO_ONE_NAT", 352 NatIP: d.Get(acPrefix + ".nat_ip").(string), 353 } 354 } 355 356 networkInterfaces = append(networkInterfaces, &iface) 357 } 358 return nil, networkInterfaces 359 } 360 361 func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error { 362 config := meta.(*Config) 363 364 instanceProperties := &compute.InstanceProperties{} 365 366 instanceProperties.CanIpForward = d.Get("can_ip_forward").(bool) 367 instanceProperties.Description = d.Get("instance_description").(string) 368 instanceProperties.MachineType = d.Get("machine_type").(string) 369 disks, err := buildDisks(d, meta) 370 if err != nil { 371 return err 372 } 373 instanceProperties.Disks = disks 374 metadata, err := resourceInstanceMetadata(d) 375 if err != nil { 376 return err 377 } 378 instanceProperties.Metadata = metadata 379 err, networks := buildNetworks(d, meta) 380 if err != nil { 381 return err 382 } 383 instanceProperties.NetworkInterfaces = networks 384 385 instanceProperties.Scheduling = &compute.Scheduling{} 386 instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE" 387 388 if v, ok := d.GetOk("automatic_restart"); ok { 389 instanceProperties.Scheduling.AutomaticRestart = v.(bool) 390 } 391 392 if v, ok := d.GetOk("on_host_maintenance"); ok { 393 instanceProperties.Scheduling.OnHostMaintenance = v.(string) 394 } 395 396 if v, ok := d.GetOk("scheduling"); ok { 397 _schedulings := v.([]interface{}) 398 if len(_schedulings) > 1 { 399 return fmt.Errorf("Error, at most one `scheduling` block can be defined") 400 } 401 _scheduling := _schedulings[0].(map[string]interface{}) 402 403 if vp, okp := _scheduling["automatic_restart"]; okp { 404 instanceProperties.Scheduling.AutomaticRestart = vp.(bool) 405 } 406 407 if vp, okp := _scheduling["on_host_maintenance"]; okp { 408 instanceProperties.Scheduling.OnHostMaintenance = vp.(string) 409 } 410 411 if vp, okp := _scheduling["preemptible"]; okp { 412 instanceProperties.Scheduling.Preemptible = vp.(bool) 413 } 414 } 415 416 serviceAccountsCount := d.Get("service_account.#").(int) 417 serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount) 418 for i := 0; i < serviceAccountsCount; i++ { 419 prefix := fmt.Sprintf("service_account.%d", i) 420 421 scopesCount := d.Get(prefix + ".scopes.#").(int) 422 scopes := make([]string, 0, scopesCount) 423 for j := 0; j < scopesCount; j++ { 424 scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string) 425 scopes = append(scopes, canonicalizeServiceScope(scope)) 426 } 427 428 serviceAccount := &compute.ServiceAccount{ 429 Email: "default", 430 Scopes: scopes, 431 } 432 433 serviceAccounts = append(serviceAccounts, serviceAccount) 434 } 435 instanceProperties.ServiceAccounts = serviceAccounts 436 437 instanceProperties.Tags = resourceInstanceTags(d) 438 439 instanceTemplate := compute.InstanceTemplate{ 440 Description: d.Get("description").(string), 441 Properties: instanceProperties, 442 Name: d.Get("name").(string), 443 } 444 445 op, err := config.clientCompute.InstanceTemplates.Insert( 446 config.Project, &instanceTemplate).Do() 447 if err != nil { 448 return fmt.Errorf("Error creating instance: %s", err) 449 } 450 451 // Store the ID now 452 d.SetId(instanceTemplate.Name) 453 454 err = computeOperationWaitGlobal(config, op, "Creating Instance Template") 455 if err != nil { 456 return err 457 } 458 459 return resourceComputeInstanceTemplateRead(d, meta) 460 } 461 462 func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error { 463 config := meta.(*Config) 464 465 instanceTemplate, err := config.clientCompute.InstanceTemplates.Get( 466 config.Project, d.Id()).Do() 467 if err != nil { 468 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 469 // The resource doesn't exist anymore 470 d.SetId("") 471 472 return nil 473 } 474 475 return fmt.Errorf("Error reading instance template: %s", err) 476 } 477 478 // Set the metadata fingerprint if there is one. 479 if instanceTemplate.Properties.Metadata != nil { 480 d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint) 481 } 482 483 // Set the tags fingerprint if there is one. 484 if instanceTemplate.Properties.Tags != nil { 485 d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint) 486 } 487 d.Set("self_link", instanceTemplate.SelfLink) 488 489 return nil 490 } 491 492 func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error { 493 config := meta.(*Config) 494 495 op, err := config.clientCompute.InstanceTemplates.Delete( 496 config.Project, d.Id()).Do() 497 if err != nil { 498 return fmt.Errorf("Error deleting instance template: %s", err) 499 } 500 501 err = computeOperationWaitGlobal(config, op, "Deleting Instance Template") 502 if err != nil { 503 return err 504 } 505 506 d.SetId("") 507 return nil 508 }