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