github.com/hobbeswalsh/terraform@v0.3.7-0.20150619183303-ad17cf55a0fa/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 instanceProperties.Metadata = resourceInstanceMetadata(d) 335 err, networks := buildNetworks(d, meta) 336 if err != nil { 337 return err 338 } 339 instanceProperties.NetworkInterfaces = networks 340 341 instanceProperties.Scheduling = &compute.Scheduling{ 342 AutomaticRestart: d.Get("automatic_restart").(bool), 343 } 344 instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE" 345 if v, ok := d.GetOk("on_host_maintenance"); ok { 346 instanceProperties.Scheduling.OnHostMaintenance = v.(string) 347 } 348 349 serviceAccountsCount := d.Get("service_account.#").(int) 350 serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount) 351 for i := 0; i < serviceAccountsCount; i++ { 352 prefix := fmt.Sprintf("service_account.%d", i) 353 354 scopesCount := d.Get(prefix + ".scopes.#").(int) 355 scopes := make([]string, 0, scopesCount) 356 for j := 0; j < scopesCount; j++ { 357 scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string) 358 scopes = append(scopes, canonicalizeServiceScope(scope)) 359 } 360 361 serviceAccount := &compute.ServiceAccount{ 362 Email: "default", 363 Scopes: scopes, 364 } 365 366 serviceAccounts = append(serviceAccounts, serviceAccount) 367 } 368 instanceProperties.ServiceAccounts = serviceAccounts 369 370 instanceProperties.Tags = resourceInstanceTags(d) 371 372 instanceTemplate := compute.InstanceTemplate{ 373 Description: d.Get("description").(string), 374 Properties: instanceProperties, 375 Name: d.Get("name").(string), 376 } 377 378 op, err := config.clientCompute.InstanceTemplates.Insert( 379 config.Project, &instanceTemplate).Do() 380 if err != nil { 381 return fmt.Errorf("Error creating instance: %s", err) 382 } 383 384 // Store the ID now 385 d.SetId(instanceTemplate.Name) 386 387 // Wait for the operation to complete 388 w := &OperationWaiter{ 389 Service: config.clientCompute, 390 Op: op, 391 Project: config.Project, 392 Type: OperationWaitGlobal, 393 } 394 state := w.Conf() 395 state.Delay = 10 * time.Second 396 state.Timeout = 10 * time.Minute 397 state.MinTimeout = 2 * time.Second 398 opRaw, err := state.WaitForState() 399 if err != nil { 400 return fmt.Errorf("Error waiting for instance template to create: %s", err) 401 } 402 op = opRaw.(*compute.Operation) 403 if op.Error != nil { 404 // The resource didn't actually create 405 d.SetId("") 406 407 // Return the error 408 return OperationError(*op.Error) 409 } 410 411 return resourceComputeInstanceTemplateRead(d, meta) 412 } 413 414 func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error { 415 config := meta.(*Config) 416 417 instanceTemplate, err := config.clientCompute.InstanceTemplates.Get( 418 config.Project, d.Id()).Do() 419 if err != nil { 420 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 421 // The resource doesn't exist anymore 422 d.SetId("") 423 424 return nil 425 } 426 427 return fmt.Errorf("Error reading instance template: %s", err) 428 } 429 430 // Set the metadata fingerprint if there is one. 431 if instanceTemplate.Properties.Metadata != nil { 432 d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint) 433 } 434 435 // Set the tags fingerprint if there is one. 436 if instanceTemplate.Properties.Tags != nil { 437 d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint) 438 } 439 d.Set("self_link", instanceTemplate.SelfLink) 440 441 return nil 442 } 443 444 func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error { 445 config := meta.(*Config) 446 447 op, err := config.clientCompute.InstanceTemplates.Delete( 448 config.Project, d.Id()).Do() 449 if err != nil { 450 return fmt.Errorf("Error deleting instance template: %s", err) 451 } 452 453 // Wait for the operation to complete 454 w := &OperationWaiter{ 455 Service: config.clientCompute, 456 Op: op, 457 Project: config.Project, 458 Type: OperationWaitGlobal, 459 } 460 state := w.Conf() 461 state.Delay = 5 * time.Second 462 state.Timeout = 5 * time.Minute 463 state.MinTimeout = 2 * time.Second 464 opRaw, err := state.WaitForState() 465 if err != nil { 466 return fmt.Errorf("Error waiting for instance template to delete: %s", err) 467 } 468 op = opRaw.(*compute.Operation) 469 if op.Error != nil { 470 // Return the error 471 return OperationError(*op.Error) 472 } 473 474 d.SetId("") 475 return nil 476 }