github.com/markdia/terraform@v0.5.1-0.20150508012022-f1ae920aa970/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 // TODO: Constraint either source or other disk params 52 "disk": &schema.Schema{ 53 Type: schema.TypeList, 54 Required: true, 55 ForceNew: true, 56 Elem: &schema.Resource{ 57 Schema: map[string]*schema.Schema{ 58 "auto_delete": &schema.Schema{ 59 Type: schema.TypeBool, 60 Optional: true, 61 Default: true, 62 ForceNew: true, 63 }, 64 65 "boot": &schema.Schema{ 66 Type: schema.TypeBool, 67 Optional: true, 68 ForceNew: true, 69 }, 70 71 "device_name": &schema.Schema{ 72 Type: schema.TypeString, 73 Optional: true, 74 ForceNew: true, 75 }, 76 77 "disk_name": &schema.Schema{ 78 Type: schema.TypeString, 79 Optional: true, 80 ForceNew: true, 81 }, 82 83 "disk_size_gb": &schema.Schema{ 84 Type: schema.TypeInt, 85 Optional: true, 86 ForceNew: true, 87 }, 88 89 "disk_type": &schema.Schema{ 90 Type: schema.TypeString, 91 Optional: true, 92 ForceNew: true, 93 }, 94 95 "source_image": &schema.Schema{ 96 Type: schema.TypeString, 97 Optional: true, 98 ForceNew: true, 99 }, 100 101 "interface": &schema.Schema{ 102 Type: schema.TypeString, 103 Optional: true, 104 ForceNew: true, 105 }, 106 107 "mode": &schema.Schema{ 108 Type: schema.TypeString, 109 Optional: true, 110 ForceNew: true, 111 }, 112 113 "source": &schema.Schema{ 114 Type: schema.TypeString, 115 Optional: true, 116 ForceNew: true, 117 }, 118 119 "type": &schema.Schema{ 120 Type: schema.TypeString, 121 Optional: true, 122 ForceNew: true, 123 }, 124 }, 125 }, 126 }, 127 128 "metadata": &schema.Schema{ 129 Type: schema.TypeMap, 130 Optional: true, 131 ForceNew: true, 132 }, 133 134 "network": &schema.Schema{ 135 Type: schema.TypeList, 136 Required: true, 137 ForceNew: true, 138 Elem: &schema.Resource{ 139 Schema: map[string]*schema.Schema{ 140 "source": &schema.Schema{ 141 Type: schema.TypeString, 142 ForceNew: true, 143 Required: true, 144 }, 145 146 "address": &schema.Schema{ 147 Type: schema.TypeString, 148 ForceNew: true, 149 Optional: true, 150 }, 151 }, 152 }, 153 }, 154 155 "automatic_restart": &schema.Schema{ 156 Type: schema.TypeBool, 157 Optional: true, 158 Default: true, 159 ForceNew: true, 160 }, 161 162 "on_host_maintenance": &schema.Schema{ 163 Type: schema.TypeString, 164 Optional: true, 165 ForceNew: true, 166 }, 167 168 "service_account": &schema.Schema{ 169 Type: schema.TypeList, 170 Optional: true, 171 ForceNew: true, 172 Elem: &schema.Resource{ 173 Schema: map[string]*schema.Schema{ 174 "email": &schema.Schema{ 175 Type: schema.TypeString, 176 Computed: true, 177 ForceNew: true, 178 }, 179 180 "scopes": &schema.Schema{ 181 Type: schema.TypeList, 182 Required: true, 183 ForceNew: true, 184 Elem: &schema.Schema{ 185 Type: schema.TypeString, 186 StateFunc: func(v interface{}) string { 187 return canonicalizeServiceScope(v.(string)) 188 }, 189 }, 190 }, 191 }, 192 }, 193 }, 194 195 "tags": &schema.Schema{ 196 Type: schema.TypeSet, 197 Optional: true, 198 ForceNew: true, 199 Elem: &schema.Schema{Type: schema.TypeString}, 200 Set: func(v interface{}) int { 201 return hashcode.String(v.(string)) 202 }, 203 }, 204 205 "metadata_fingerprint": &schema.Schema{ 206 Type: schema.TypeString, 207 Computed: true, 208 }, 209 210 "tags_fingerprint": &schema.Schema{ 211 Type: schema.TypeString, 212 Computed: true, 213 }, 214 215 "self_link": &schema.Schema{ 216 Type: schema.TypeString, 217 Computed: true, 218 }, 219 }, 220 } 221 } 222 223 func buildDisks(d *schema.ResourceData, meta interface{}) []*compute.AttachedDisk { 224 disksCount := d.Get("disk.#").(int) 225 226 disks := make([]*compute.AttachedDisk, 0, disksCount) 227 for i := 0; i < disksCount; i++ { 228 prefix := fmt.Sprintf("disk.%d", i) 229 230 // Build the disk 231 var disk compute.AttachedDisk 232 disk.Type = "PERSISTENT" 233 disk.Mode = "READ_WRITE" 234 disk.Interface = "SCSI" 235 disk.Boot = i == 0 236 disk.AutoDelete = d.Get(prefix + ".auto_delete").(bool) 237 238 if v, ok := d.GetOk(prefix + ".boot"); ok { 239 disk.Boot = v.(bool) 240 } 241 242 if v, ok := d.GetOk(prefix + ".device_name"); ok { 243 disk.DeviceName = v.(string) 244 } 245 246 if v, ok := d.GetOk(prefix + ".source"); ok { 247 disk.Source = v.(string) 248 } else { 249 disk.InitializeParams = &compute.AttachedDiskInitializeParams{} 250 251 if v, ok := d.GetOk(prefix + ".disk_name"); ok { 252 disk.InitializeParams.DiskName = v.(string) 253 } 254 if v, ok := d.GetOk(prefix + ".disk_size_gb"); ok { 255 disk.InitializeParams.DiskSizeGb = v.(int64) 256 } 257 disk.InitializeParams.DiskType = "pd-standard" 258 if v, ok := d.GetOk(prefix + ".disk_type"); ok { 259 disk.InitializeParams.DiskType = v.(string) 260 } 261 262 if v, ok := d.GetOk(prefix + ".source_image"); ok { 263 disk.InitializeParams.SourceImage = v.(string) 264 } 265 } 266 267 if v, ok := d.GetOk(prefix + ".interface"); ok { 268 disk.Interface = v.(string) 269 } 270 271 if v, ok := d.GetOk(prefix + ".mode"); ok { 272 disk.Mode = v.(string) 273 } 274 275 if v, ok := d.GetOk(prefix + ".type"); ok { 276 disk.Type = v.(string) 277 } 278 279 disks = append(disks, &disk) 280 } 281 282 return disks 283 } 284 285 func buildNetworks(d *schema.ResourceData, meta interface{}) (error, []*compute.NetworkInterface) { 286 // Build up the list of networks 287 networksCount := d.Get("network.#").(int) 288 networks := make([]*compute.NetworkInterface, 0, networksCount) 289 for i := 0; i < networksCount; i++ { 290 prefix := fmt.Sprintf("network.%d", i) 291 292 source := "global/networks/default" 293 if v, ok := d.GetOk(prefix + ".source"); ok { 294 if v.(string) != "default" { 295 source = v.(string) 296 } 297 } 298 299 // Build the interface 300 var iface compute.NetworkInterface 301 iface.AccessConfigs = []*compute.AccessConfig{ 302 &compute.AccessConfig{ 303 Type: "ONE_TO_ONE_NAT", 304 NatIP: d.Get(prefix + ".address").(string), 305 }, 306 } 307 iface.Network = source 308 309 networks = append(networks, &iface) 310 } 311 return nil, networks 312 } 313 314 func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error { 315 config := meta.(*Config) 316 317 instanceProperties := &compute.InstanceProperties{} 318 319 instanceProperties.CanIpForward = d.Get("can_ip_forward").(bool) 320 instanceProperties.Description = d.Get("instance_description").(string) 321 instanceProperties.MachineType = d.Get("machine_type").(string) 322 instanceProperties.Disks = buildDisks(d, meta) 323 instanceProperties.Metadata = resourceInstanceMetadata(d) 324 err, networks := buildNetworks(d, meta) 325 if err != nil { 326 return err 327 } 328 instanceProperties.NetworkInterfaces = networks 329 330 instanceProperties.Scheduling = &compute.Scheduling{ 331 AutomaticRestart: d.Get("automatic_restart").(bool), 332 } 333 instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE" 334 if v, ok := d.GetOk("on_host_maintenance"); ok { 335 instanceProperties.Scheduling.OnHostMaintenance = v.(string) 336 } 337 338 serviceAccountsCount := d.Get("service_account.#").(int) 339 serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount) 340 for i := 0; i < serviceAccountsCount; i++ { 341 prefix := fmt.Sprintf("service_account.%d", i) 342 343 scopesCount := d.Get(prefix + ".scopes.#").(int) 344 scopes := make([]string, 0, scopesCount) 345 for j := 0; j < scopesCount; j++ { 346 scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string) 347 scopes = append(scopes, canonicalizeServiceScope(scope)) 348 } 349 350 serviceAccount := &compute.ServiceAccount{ 351 Email: "default", 352 Scopes: scopes, 353 } 354 355 serviceAccounts = append(serviceAccounts, serviceAccount) 356 } 357 instanceProperties.ServiceAccounts = serviceAccounts 358 359 instanceProperties.Tags = resourceInstanceTags(d) 360 361 instanceTemplate := compute.InstanceTemplate{ 362 Description: d.Get("description").(string), 363 Properties: instanceProperties, 364 Name: d.Get("name").(string), 365 } 366 367 op, err := config.clientCompute.InstanceTemplates.Insert( 368 config.Project, &instanceTemplate).Do() 369 if err != nil { 370 return fmt.Errorf("Error creating instance: %s", err) 371 } 372 373 // Store the ID now 374 d.SetId(instanceTemplate.Name) 375 376 // Wait for the operation to complete 377 w := &OperationWaiter{ 378 Service: config.clientCompute, 379 Op: op, 380 Project: config.Project, 381 Type: OperationWaitGlobal, 382 } 383 state := w.Conf() 384 state.Delay = 10 * time.Second 385 state.Timeout = 10 * time.Minute 386 state.MinTimeout = 2 * time.Second 387 opRaw, err := state.WaitForState() 388 if err != nil { 389 return fmt.Errorf("Error waiting for instance template to create: %s", err) 390 } 391 op = opRaw.(*compute.Operation) 392 if op.Error != nil { 393 // The resource didn't actually create 394 d.SetId("") 395 396 // Return the error 397 return OperationError(*op.Error) 398 } 399 400 return resourceComputeInstanceTemplateRead(d, meta) 401 } 402 403 func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error { 404 config := meta.(*Config) 405 406 instanceTemplate, err := config.clientCompute.InstanceTemplates.Get( 407 config.Project, d.Id()).Do() 408 if err != nil { 409 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 410 // The resource doesn't exist anymore 411 d.SetId("") 412 413 return nil 414 } 415 416 return fmt.Errorf("Error reading instance template: %s", err) 417 } 418 419 // Set the metadata fingerprint if there is one. 420 if instanceTemplate.Properties.Metadata != nil { 421 d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint) 422 } 423 424 // Set the tags fingerprint if there is one. 425 if instanceTemplate.Properties.Tags != nil { 426 d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint) 427 } 428 d.Set("self_link", instanceTemplate.SelfLink) 429 430 return nil 431 } 432 433 func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error { 434 config := meta.(*Config) 435 436 op, err := config.clientCompute.InstanceTemplates.Delete( 437 config.Project, d.Id()).Do() 438 if err != nil { 439 return fmt.Errorf("Error deleting instance template: %s", err) 440 } 441 442 // Wait for the operation to complete 443 w := &OperationWaiter{ 444 Service: config.clientCompute, 445 Op: op, 446 Project: config.Project, 447 Type: OperationWaitGlobal, 448 } 449 state := w.Conf() 450 state.Delay = 5 * time.Second 451 state.Timeout = 5 * time.Minute 452 state.MinTimeout = 2 * time.Second 453 opRaw, err := state.WaitForState() 454 if err != nil { 455 return fmt.Errorf("Error waiting for instance template to delete: %s", err) 456 } 457 op = opRaw.(*compute.Operation) 458 if op.Error != nil { 459 // Return the error 460 return OperationError(*op.Error) 461 } 462 463 d.SetId("") 464 return nil 465 }