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