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