2015-02-10 11:29:27 +01:00
|
|
|
package google
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-11-13 21:36:03 +01:00
|
|
|
"log"
|
2016-08-03 22:58:35 +02:00
|
|
|
"strings"
|
2015-02-10 11:29:27 +01:00
|
|
|
|
2016-05-11 23:54:47 +02:00
|
|
|
"github.com/hashicorp/terraform/helper/resource"
|
2015-02-10 11:29:27 +01:00
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
2015-03-18 18:10:39 +01:00
|
|
|
"google.golang.org/api/compute/v1"
|
|
|
|
"google.golang.org/api/googleapi"
|
2015-02-10 11:29:27 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func resourceComputeInstanceTemplate() *schema.Resource {
|
|
|
|
return &schema.Resource{
|
|
|
|
Create: resourceComputeInstanceTemplateCreate,
|
|
|
|
Read: resourceComputeInstanceTemplateRead,
|
|
|
|
Delete: resourceComputeInstanceTemplateDelete,
|
2016-08-03 22:58:35 +02:00
|
|
|
Importer: &schema.ResourceImporter{
|
|
|
|
State: schema.ImportStatePassthrough,
|
|
|
|
},
|
2015-02-10 11:29:27 +01:00
|
|
|
|
|
|
|
Schema: map[string]*schema.Schema{
|
2016-05-11 23:54:47 +02:00
|
|
|
"name": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
ConflictsWith: []string{"name_prefix"},
|
|
|
|
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
|
|
|
// https://cloud.google.com/compute/docs/reference/latest/instanceTemplates#resource
|
|
|
|
value := v.(string)
|
|
|
|
if len(value) > 63 {
|
|
|
|
errors = append(errors, fmt.Errorf(
|
|
|
|
"%q cannot be longer than 63 characters", k))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
"name_prefix": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
|
|
|
// https://cloud.google.com/compute/docs/reference/latest/instanceTemplates#resource
|
|
|
|
// uuid is 26 characters, limit the prefix to 37.
|
|
|
|
value := v.(string)
|
|
|
|
if len(value) > 37 {
|
|
|
|
errors = append(errors, fmt.Errorf(
|
|
|
|
"%q cannot be longer than 37 characters, name is limited to 63", k))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
},
|
|
|
|
},
|
2015-02-10 11:29:27 +01:00
|
|
|
"disk": &schema.Schema{
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"auto_delete": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
2015-03-18 18:50:03 +01:00
|
|
|
Default: true,
|
2015-02-10 11:29:27 +01:00
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"boot": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
2016-08-03 22:58:35 +02:00
|
|
|
Computed: true,
|
2015-02-10 11:29:27 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
"device_name": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"disk_name": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
2015-02-10 11:49:20 +01:00
|
|
|
Optional: true,
|
2015-02-10 11:29:27 +01:00
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"disk_size_gb": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"disk_type": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
2016-08-03 22:58:35 +02:00
|
|
|
Computed: true,
|
2015-02-10 11:29:27 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
"source_image": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"interface": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
2016-08-03 22:58:35 +02:00
|
|
|
Computed: true,
|
2015-02-10 11:29:27 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
"mode": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
2016-08-03 22:58:35 +02:00
|
|
|
Computed: true,
|
2015-02-10 11:29:27 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
"source": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"type": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
2016-08-03 22:58:35 +02:00
|
|
|
Computed: true,
|
2015-02-10 11:29:27 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2016-04-10 23:34:15 +02:00
|
|
|
"machine_type": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"automatic_restart": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: true,
|
|
|
|
ForceNew: true,
|
|
|
|
Deprecated: "Please use `scheduling.automatic_restart` instead",
|
|
|
|
},
|
|
|
|
|
|
|
|
"can_ip_forward": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: false,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"description": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"instance_description": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
2015-02-10 11:29:27 +01:00
|
|
|
"metadata": &schema.Schema{
|
2015-05-06 19:33:35 +02:00
|
|
|
Type: schema.TypeMap,
|
2015-02-10 11:29:27 +01:00
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
2016-04-10 23:34:15 +02:00
|
|
|
"metadata_fingerprint": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
|
2015-02-16 18:04:56 +01:00
|
|
|
"network_interface": &schema.Schema{
|
2015-02-10 11:29:27 +01:00
|
|
|
Type: schema.TypeList,
|
2015-02-16 18:04:56 +01:00
|
|
|
Optional: true,
|
2015-02-10 11:29:27 +01:00
|
|
|
ForceNew: true,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
2015-02-16 18:04:56 +01:00
|
|
|
"network": &schema.Schema{
|
2015-02-10 11:29:27 +01:00
|
|
|
Type: schema.TypeString,
|
2016-02-17 12:09:46 +01:00
|
|
|
Optional: true,
|
2015-02-16 18:04:56 +01:00
|
|
|
ForceNew: true,
|
2016-08-03 22:58:35 +02:00
|
|
|
Computed: true,
|
2015-02-10 11:29:27 +01:00
|
|
|
},
|
|
|
|
|
2016-02-15 04:17:55 +01:00
|
|
|
"subnetwork": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
2015-02-16 18:04:56 +01:00
|
|
|
"access_config": &schema.Schema{
|
|
|
|
Type: schema.TypeList,
|
2015-02-10 11:29:27 +01:00
|
|
|
Optional: true,
|
2015-02-16 18:04:56 +01:00
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"nat_ip": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-02-10 11:29:27 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
"on_host_maintenance": &schema.Schema{
|
2015-10-30 22:27:12 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
2015-10-28 16:33:49 +01:00
|
|
|
Deprecated: "Please use `scheduling.on_host_maintenance` instead",
|
|
|
|
},
|
|
|
|
|
2016-04-10 23:34:15 +02:00
|
|
|
"project": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
2016-08-03 22:58:35 +02:00
|
|
|
Computed: true,
|
2016-04-10 23:34:15 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
"region": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
2015-10-28 16:33:49 +01:00
|
|
|
"scheduling": &schema.Schema{
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
2016-08-03 22:58:35 +02:00
|
|
|
Computed: true,
|
2015-10-28 16:33:49 +01:00
|
|
|
ForceNew: true,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"preemptible": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
2016-08-03 22:58:35 +02:00
|
|
|
Default: false,
|
2015-10-28 16:33:49 +01:00
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"automatic_restart": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"on_host_maintenance": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
2016-08-03 22:58:35 +02:00
|
|
|
Computed: true,
|
2015-10-28 16:33:49 +01:00
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-02-10 11:29:27 +01:00
|
|
|
},
|
|
|
|
|
2016-04-10 23:34:15 +02:00
|
|
|
"self_link": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
|
2015-02-10 11:29:27 +01:00
|
|
|
"service_account": &schema.Schema{
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"email": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"scopes": &schema.Schema{
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
Elem: &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
StateFunc: func(v interface{}) string {
|
|
|
|
return canonicalizeServiceScope(v.(string))
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
"tags": &schema.Schema{
|
|
|
|
Type: schema.TypeSet,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
2016-02-08 00:51:26 +01:00
|
|
|
Set: schema.HashString,
|
2015-02-10 11:29:27 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
"tags_fingerprint": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-17 02:14:38 +02:00
|
|
|
func buildDisks(d *schema.ResourceData, meta interface{}) ([]*compute.AttachedDisk, error) {
|
|
|
|
config := meta.(*Config)
|
|
|
|
|
2015-02-10 11:29:27 +01:00
|
|
|
disksCount := d.Get("disk.#").(int)
|
|
|
|
|
|
|
|
disks := make([]*compute.AttachedDisk, 0, disksCount)
|
|
|
|
for i := 0; i < disksCount; i++ {
|
|
|
|
prefix := fmt.Sprintf("disk.%d", i)
|
|
|
|
|
|
|
|
// Build the disk
|
|
|
|
var disk compute.AttachedDisk
|
|
|
|
disk.Type = "PERSISTENT"
|
|
|
|
disk.Mode = "READ_WRITE"
|
|
|
|
disk.Interface = "SCSI"
|
|
|
|
disk.Boot = i == 0
|
2015-03-18 18:50:03 +01:00
|
|
|
disk.AutoDelete = d.Get(prefix + ".auto_delete").(bool)
|
2015-02-10 11:29:27 +01:00
|
|
|
|
|
|
|
if v, ok := d.GetOk(prefix + ".boot"); ok {
|
|
|
|
disk.Boot = v.(bool)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk(prefix + ".device_name"); ok {
|
|
|
|
disk.DeviceName = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk(prefix + ".source"); ok {
|
|
|
|
disk.Source = v.(string)
|
|
|
|
} else {
|
|
|
|
disk.InitializeParams = &compute.AttachedDiskInitializeParams{}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk(prefix + ".disk_name"); ok {
|
|
|
|
disk.InitializeParams.DiskName = v.(string)
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk(prefix + ".disk_size_gb"); ok {
|
2015-08-19 08:57:04 +02:00
|
|
|
disk.InitializeParams.DiskSizeGb = int64(v.(int))
|
2015-02-10 11:29:27 +01:00
|
|
|
}
|
|
|
|
disk.InitializeParams.DiskType = "pd-standard"
|
|
|
|
if v, ok := d.GetOk(prefix + ".disk_type"); ok {
|
|
|
|
disk.InitializeParams.DiskType = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk(prefix + ".source_image"); ok {
|
2015-05-17 02:14:38 +02:00
|
|
|
imageName := v.(string)
|
|
|
|
imageUrl, err := resolveImage(config, imageName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"Error resolving image name '%s': %s",
|
|
|
|
imageName, err)
|
|
|
|
}
|
|
|
|
disk.InitializeParams.SourceImage = imageUrl
|
2015-02-10 11:29:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk(prefix + ".interface"); ok {
|
|
|
|
disk.Interface = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk(prefix + ".mode"); ok {
|
|
|
|
disk.Mode = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk(prefix + ".type"); ok {
|
|
|
|
disk.Type = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
disks = append(disks, &disk)
|
|
|
|
}
|
|
|
|
|
2015-05-17 02:14:38 +02:00
|
|
|
return disks, nil
|
2015-02-10 11:29:27 +01:00
|
|
|
}
|
|
|
|
|
2016-04-10 18:32:19 +02:00
|
|
|
func buildNetworks(d *schema.ResourceData, meta interface{}) ([]*compute.NetworkInterface, error) {
|
2015-02-10 11:29:27 +01:00
|
|
|
// Build up the list of networks
|
2016-02-17 12:09:46 +01:00
|
|
|
config := meta.(*Config)
|
|
|
|
|
2016-04-10 18:59:57 +02:00
|
|
|
project, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-02-16 18:04:56 +01:00
|
|
|
networksCount := d.Get("network_interface.#").(int)
|
|
|
|
networkInterfaces := make([]*compute.NetworkInterface, 0, networksCount)
|
2015-02-10 11:29:27 +01:00
|
|
|
for i := 0; i < networksCount; i++ {
|
2015-02-16 18:04:56 +01:00
|
|
|
prefix := fmt.Sprintf("network_interface.%d", i)
|
2015-02-10 11:29:27 +01:00
|
|
|
|
2016-02-17 12:09:46 +01:00
|
|
|
var networkName, subnetworkName string
|
2015-02-16 18:04:56 +01:00
|
|
|
if v, ok := d.GetOk(prefix + ".network"); ok {
|
2016-02-17 12:09:46 +01:00
|
|
|
networkName = v.(string)
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk(prefix + ".subnetwork"); ok {
|
|
|
|
subnetworkName = v.(string)
|
2015-02-10 11:29:27 +01:00
|
|
|
}
|
|
|
|
|
2016-02-17 12:09:46 +01:00
|
|
|
if networkName == "" && subnetworkName == "" {
|
2016-04-10 18:32:19 +02:00
|
|
|
return nil, fmt.Errorf("network or subnetwork must be provided")
|
2016-02-17 12:09:46 +01:00
|
|
|
}
|
|
|
|
if networkName != "" && subnetworkName != "" {
|
2016-04-10 18:32:19 +02:00
|
|
|
return nil, fmt.Errorf("network or subnetwork must not both be provided")
|
2016-02-17 12:09:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var networkLink, subnetworkLink string
|
|
|
|
if networkName != "" {
|
|
|
|
network, err := config.clientCompute.Networks.Get(
|
2016-04-10 18:32:19 +02:00
|
|
|
project, networkName).Do()
|
2016-02-17 12:09:46 +01:00
|
|
|
if err != nil {
|
2016-04-10 18:32:19 +02:00
|
|
|
return nil, fmt.Errorf("Error referencing network '%s': %s",
|
|
|
|
networkName, err)
|
2016-02-17 12:09:46 +01:00
|
|
|
}
|
|
|
|
networkLink = network.SelfLink
|
|
|
|
} else {
|
|
|
|
// lookup subnetwork link using region and subnetwork name
|
2016-04-10 18:59:57 +02:00
|
|
|
region, err := getRegion(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2016-02-17 12:09:46 +01:00
|
|
|
}
|
|
|
|
subnetwork, err := config.clientCompute.Subnetworks.Get(
|
2016-04-10 18:32:19 +02:00
|
|
|
project, region, subnetworkName).Do()
|
2016-02-17 12:09:46 +01:00
|
|
|
if err != nil {
|
2016-04-10 18:32:19 +02:00
|
|
|
return nil, fmt.Errorf(
|
2016-02-17 12:09:46 +01:00
|
|
|
"Error referencing subnetwork '%s' in region '%s': %s",
|
2016-04-10 18:32:19 +02:00
|
|
|
subnetworkName, region, err)
|
2016-02-17 12:09:46 +01:00
|
|
|
}
|
|
|
|
subnetworkLink = subnetwork.SelfLink
|
|
|
|
}
|
2016-02-15 04:17:55 +01:00
|
|
|
|
2015-02-16 18:04:56 +01:00
|
|
|
// Build the networkInterface
|
2015-02-10 11:29:27 +01:00
|
|
|
var iface compute.NetworkInterface
|
2016-02-17 12:09:46 +01:00
|
|
|
iface.Network = networkLink
|
2016-02-15 04:17:55 +01:00
|
|
|
iface.Subnetwork = subnetworkLink
|
2015-02-16 18:04:56 +01:00
|
|
|
|
|
|
|
accessConfigsCount := d.Get(prefix + ".access_config.#").(int)
|
|
|
|
iface.AccessConfigs = make([]*compute.AccessConfig, accessConfigsCount)
|
|
|
|
for j := 0; j < accessConfigsCount; j++ {
|
|
|
|
acPrefix := fmt.Sprintf("%s.access_config.%d", prefix, j)
|
|
|
|
iface.AccessConfigs[j] = &compute.AccessConfig{
|
2015-02-10 11:29:27 +01:00
|
|
|
Type: "ONE_TO_ONE_NAT",
|
2015-02-16 18:04:56 +01:00
|
|
|
NatIP: d.Get(acPrefix + ".nat_ip").(string),
|
|
|
|
}
|
2015-02-10 11:29:27 +01:00
|
|
|
}
|
|
|
|
|
2015-02-16 18:04:56 +01:00
|
|
|
networkInterfaces = append(networkInterfaces, &iface)
|
2015-02-10 11:29:27 +01:00
|
|
|
}
|
2016-04-10 18:32:19 +02:00
|
|
|
return networkInterfaces, nil
|
2015-02-10 11:29:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
config := meta.(*Config)
|
|
|
|
|
2016-04-10 18:59:57 +02:00
|
|
|
project, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-02-10 11:29:27 +01:00
|
|
|
instanceProperties := &compute.InstanceProperties{}
|
|
|
|
|
|
|
|
instanceProperties.CanIpForward = d.Get("can_ip_forward").(bool)
|
|
|
|
instanceProperties.Description = d.Get("instance_description").(string)
|
|
|
|
instanceProperties.MachineType = d.Get("machine_type").(string)
|
2015-05-17 02:14:38 +02:00
|
|
|
disks, err := buildDisks(d, meta)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
instanceProperties.Disks = disks
|
2015-07-02 03:24:34 +02:00
|
|
|
metadata, err := resourceInstanceMetadata(d)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
instanceProperties.Metadata = metadata
|
2016-04-10 18:32:19 +02:00
|
|
|
networks, err := buildNetworks(d, meta)
|
2015-02-10 11:29:27 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
instanceProperties.NetworkInterfaces = networks
|
|
|
|
|
2015-10-28 16:33:49 +01:00
|
|
|
instanceProperties.Scheduling = &compute.Scheduling{}
|
2015-02-10 11:29:27 +01:00
|
|
|
instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE"
|
2015-10-28 16:33:49 +01:00
|
|
|
|
2016-08-03 22:58:35 +02:00
|
|
|
// Depreciated fields
|
2015-10-28 16:33:49 +01:00
|
|
|
if v, ok := d.GetOk("automatic_restart"); ok {
|
|
|
|
instanceProperties.Scheduling.AutomaticRestart = v.(bool)
|
|
|
|
}
|
|
|
|
|
2015-02-10 11:29:27 +01:00
|
|
|
if v, ok := d.GetOk("on_host_maintenance"); ok {
|
|
|
|
instanceProperties.Scheduling.OnHostMaintenance = v.(string)
|
|
|
|
}
|
|
|
|
|
2016-01-21 15:37:10 +01:00
|
|
|
forceSendFieldsScheduling := make([]string, 0, 3)
|
|
|
|
var hasSendMaintenance bool
|
|
|
|
hasSendMaintenance = false
|
2015-10-28 16:33:49 +01:00
|
|
|
if v, ok := d.GetOk("scheduling"); ok {
|
|
|
|
_schedulings := v.([]interface{})
|
|
|
|
if len(_schedulings) > 1 {
|
|
|
|
return fmt.Errorf("Error, at most one `scheduling` block can be defined")
|
|
|
|
}
|
|
|
|
_scheduling := _schedulings[0].(map[string]interface{})
|
|
|
|
|
|
|
|
if vp, okp := _scheduling["automatic_restart"]; okp {
|
|
|
|
instanceProperties.Scheduling.AutomaticRestart = vp.(bool)
|
2016-01-21 15:37:10 +01:00
|
|
|
forceSendFieldsScheduling = append(forceSendFieldsScheduling, "AutomaticRestart")
|
2015-10-28 16:33:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if vp, okp := _scheduling["on_host_maintenance"]; okp {
|
|
|
|
instanceProperties.Scheduling.OnHostMaintenance = vp.(string)
|
2016-01-21 15:37:10 +01:00
|
|
|
forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
|
|
|
|
hasSendMaintenance = true
|
2015-10-28 16:33:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if vp, okp := _scheduling["preemptible"]; okp {
|
|
|
|
instanceProperties.Scheduling.Preemptible = vp.(bool)
|
2016-01-21 15:37:10 +01:00
|
|
|
forceSendFieldsScheduling = append(forceSendFieldsScheduling, "Preemptible")
|
|
|
|
if vp.(bool) && !hasSendMaintenance {
|
|
|
|
instanceProperties.Scheduling.OnHostMaintenance = "TERMINATE"
|
|
|
|
forceSendFieldsScheduling = append(forceSendFieldsScheduling, "OnHostMaintenance")
|
|
|
|
}
|
2015-10-28 16:33:49 +01:00
|
|
|
}
|
|
|
|
}
|
2016-01-21 15:37:10 +01:00
|
|
|
instanceProperties.Scheduling.ForceSendFields = forceSendFieldsScheduling
|
2015-10-28 16:33:49 +01:00
|
|
|
|
2015-02-10 11:29:27 +01:00
|
|
|
serviceAccountsCount := d.Get("service_account.#").(int)
|
|
|
|
serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount)
|
|
|
|
for i := 0; i < serviceAccountsCount; i++ {
|
|
|
|
prefix := fmt.Sprintf("service_account.%d", i)
|
|
|
|
|
|
|
|
scopesCount := d.Get(prefix + ".scopes.#").(int)
|
|
|
|
scopes := make([]string, 0, scopesCount)
|
|
|
|
for j := 0; j < scopesCount; j++ {
|
|
|
|
scope := d.Get(fmt.Sprintf(prefix+".scopes.%d", j)).(string)
|
|
|
|
scopes = append(scopes, canonicalizeServiceScope(scope))
|
|
|
|
}
|
|
|
|
|
|
|
|
serviceAccount := &compute.ServiceAccount{
|
|
|
|
Email: "default",
|
|
|
|
Scopes: scopes,
|
|
|
|
}
|
|
|
|
|
|
|
|
serviceAccounts = append(serviceAccounts, serviceAccount)
|
|
|
|
}
|
|
|
|
instanceProperties.ServiceAccounts = serviceAccounts
|
|
|
|
|
|
|
|
instanceProperties.Tags = resourceInstanceTags(d)
|
|
|
|
|
2016-05-11 23:54:47 +02:00
|
|
|
var itName string
|
|
|
|
if v, ok := d.GetOk("name"); ok {
|
|
|
|
itName = v.(string)
|
|
|
|
} else if v, ok := d.GetOk("name_prefix"); ok {
|
|
|
|
itName = resource.PrefixedUniqueId(v.(string))
|
|
|
|
} else {
|
|
|
|
itName = resource.UniqueId()
|
|
|
|
}
|
2015-02-10 11:29:27 +01:00
|
|
|
instanceTemplate := compute.InstanceTemplate{
|
|
|
|
Description: d.Get("description").(string),
|
|
|
|
Properties: instanceProperties,
|
2016-05-11 23:54:47 +02:00
|
|
|
Name: itName,
|
2015-02-10 11:29:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
op, err := config.clientCompute.InstanceTemplates.Insert(
|
2016-04-10 18:59:57 +02:00
|
|
|
project, &instanceTemplate).Do()
|
2015-02-10 11:29:27 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error creating instance: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the ID now
|
|
|
|
d.SetId(instanceTemplate.Name)
|
|
|
|
|
2016-06-06 19:35:13 +02:00
|
|
|
err = computeOperationWaitGlobal(config, op, project, "Creating Instance Template")
|
2015-02-10 11:29:27 +01:00
|
|
|
if err != nil {
|
2015-09-24 22:30:12 +02:00
|
|
|
return err
|
2015-02-10 11:29:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return resourceComputeInstanceTemplateRead(d, meta)
|
|
|
|
}
|
|
|
|
|
2016-08-19 19:55:30 +02:00
|
|
|
func flattenDisks(disks []*compute.AttachedDisk, d *schema.ResourceData) []map[string]interface{} {
|
2016-08-03 22:58:35 +02:00
|
|
|
result := make([]map[string]interface{}, 0, len(disks))
|
2016-08-19 19:55:30 +02:00
|
|
|
for i, disk := range disks {
|
2016-08-03 22:58:35 +02:00
|
|
|
diskMap := make(map[string]interface{})
|
|
|
|
if disk.InitializeParams != nil {
|
2016-08-19 19:55:30 +02:00
|
|
|
var source_img = fmt.Sprintf("disk.%d.source_image", i)
|
|
|
|
if d.Get(source_img) == nil || d.Get(source_img) == "" {
|
|
|
|
sourceImageUrl := strings.Split(disk.InitializeParams.SourceImage, "/")
|
|
|
|
diskMap["source_image"] = sourceImageUrl[len(sourceImageUrl)-1]
|
|
|
|
} else {
|
|
|
|
diskMap["source_image"] = d.Get(source_img)
|
|
|
|
}
|
2016-08-03 22:58:35 +02:00
|
|
|
diskMap["disk_type"] = disk.InitializeParams.DiskType
|
|
|
|
diskMap["disk_name"] = disk.InitializeParams.DiskName
|
|
|
|
diskMap["disk_size_gb"] = disk.InitializeParams.DiskSizeGb
|
|
|
|
}
|
|
|
|
diskMap["auto_delete"] = disk.AutoDelete
|
|
|
|
diskMap["boot"] = disk.Boot
|
|
|
|
diskMap["device_name"] = disk.DeviceName
|
|
|
|
diskMap["interface"] = disk.Interface
|
|
|
|
diskMap["source"] = disk.Source
|
|
|
|
diskMap["mode"] = disk.Mode
|
|
|
|
diskMap["type"] = disk.Type
|
|
|
|
result = append(result, diskMap)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func flattenNetworkInterfaces(networkInterfaces []*compute.NetworkInterface) ([]map[string]interface{}, string) {
|
|
|
|
result := make([]map[string]interface{}, 0, len(networkInterfaces))
|
|
|
|
region := ""
|
|
|
|
for _, networkInterface := range networkInterfaces {
|
|
|
|
networkInterfaceMap := make(map[string]interface{})
|
|
|
|
if networkInterface.Network != "" {
|
|
|
|
networkUrl := strings.Split(networkInterface.Network, "/")
|
|
|
|
networkInterfaceMap["network"] = networkUrl[len(networkUrl)-1]
|
|
|
|
}
|
|
|
|
if networkInterface.Subnetwork != "" {
|
|
|
|
subnetworkUrl := strings.Split(networkInterface.Subnetwork, "/")
|
|
|
|
networkInterfaceMap["subnetwork"] = subnetworkUrl[len(subnetworkUrl)-1]
|
|
|
|
region = subnetworkUrl[len(subnetworkUrl)-3]
|
|
|
|
}
|
|
|
|
|
|
|
|
if networkInterface.AccessConfigs != nil {
|
|
|
|
accessConfigsMap := make([]map[string]interface{}, 0, len(networkInterface.AccessConfigs))
|
|
|
|
for _, accessConfig := range networkInterface.AccessConfigs {
|
|
|
|
accessConfigMap := make(map[string]interface{})
|
|
|
|
accessConfigMap["nat_ip"] = accessConfig.NatIP
|
|
|
|
|
|
|
|
accessConfigsMap = append(accessConfigsMap, accessConfigMap)
|
|
|
|
}
|
|
|
|
networkInterfaceMap["access_config"] = accessConfigsMap
|
|
|
|
}
|
|
|
|
result = append(result, networkInterfaceMap)
|
|
|
|
}
|
|
|
|
return result, region
|
|
|
|
}
|
|
|
|
|
|
|
|
func flattenScheduling(scheduling *compute.Scheduling) ([]map[string]interface{}, bool) {
|
|
|
|
result := make([]map[string]interface{}, 0, 1)
|
|
|
|
schedulingMap := make(map[string]interface{})
|
|
|
|
schedulingMap["automatic_restart"] = scheduling.AutomaticRestart
|
|
|
|
schedulingMap["on_host_maintenance"] = scheduling.OnHostMaintenance
|
|
|
|
schedulingMap["preemptible"] = scheduling.Preemptible
|
|
|
|
result = append(result, schedulingMap)
|
|
|
|
return result, scheduling.AutomaticRestart
|
|
|
|
}
|
|
|
|
|
|
|
|
func flattenServiceAccounts(serviceAccounts []*compute.ServiceAccount) []map[string]interface{} {
|
|
|
|
result := make([]map[string]interface{}, 0, len(serviceAccounts))
|
|
|
|
for _, serviceAccount := range serviceAccounts {
|
|
|
|
serviceAccountMap := make(map[string]interface{})
|
|
|
|
serviceAccountMap["email"] = serviceAccount.Email
|
|
|
|
serviceAccountMap["scopes"] = serviceAccount.Scopes
|
|
|
|
|
|
|
|
result = append(result, serviceAccountMap)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func flattenMetadata(metadata *compute.Metadata) map[string]string {
|
|
|
|
metadataMap := make(map[string]string)
|
|
|
|
for _, item := range metadata.Items {
|
|
|
|
metadataMap[item.Key] = *item.Value
|
|
|
|
}
|
|
|
|
return metadataMap
|
|
|
|
}
|
|
|
|
|
2015-02-10 11:29:27 +01:00
|
|
|
func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
config := meta.(*Config)
|
2016-04-10 18:59:57 +02:00
|
|
|
project, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-02-10 11:29:27 +01:00
|
|
|
instanceTemplate, err := config.clientCompute.InstanceTemplates.Get(
|
2016-04-10 18:59:57 +02:00
|
|
|
project, d.Id()).Do()
|
2015-02-10 11:29:27 +01:00
|
|
|
if err != nil {
|
|
|
|
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
|
2015-11-13 21:36:03 +01:00
|
|
|
log.Printf("[WARN] Removing Instance Template %q because it's gone", d.Get("name").(string))
|
2015-02-10 11:29:27 +01:00
|
|
|
// The resource doesn't exist anymore
|
|
|
|
d.SetId("")
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Errorf("Error reading instance template: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the metadata fingerprint if there is one.
|
|
|
|
if instanceTemplate.Properties.Metadata != nil {
|
|
|
|
d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the tags fingerprint if there is one.
|
|
|
|
if instanceTemplate.Properties.Tags != nil {
|
|
|
|
d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint)
|
|
|
|
}
|
|
|
|
d.Set("self_link", instanceTemplate.SelfLink)
|
2016-05-11 23:54:47 +02:00
|
|
|
d.Set("name", instanceTemplate.Name)
|
2016-08-03 22:58:35 +02:00
|
|
|
if instanceTemplate.Properties.Disks != nil {
|
2016-08-19 19:55:30 +02:00
|
|
|
d.Set("disk", flattenDisks(instanceTemplate.Properties.Disks, d))
|
2016-08-03 22:58:35 +02:00
|
|
|
}
|
|
|
|
d.Set("description", instanceTemplate.Description)
|
|
|
|
d.Set("machine_type", instanceTemplate.Properties.MachineType)
|
|
|
|
d.Set("can_ip_forward", instanceTemplate.Properties.CanIpForward)
|
|
|
|
if instanceTemplate.Properties.Metadata != nil {
|
|
|
|
d.Set("metadata", flattenMetadata(instanceTemplate.Properties.Metadata))
|
|
|
|
}
|
|
|
|
d.Set("instance_description", instanceTemplate.Properties.Description)
|
|
|
|
d.Set("project", project)
|
|
|
|
if instanceTemplate.Properties.NetworkInterfaces != nil {
|
|
|
|
networkInterfaces, region := flattenNetworkInterfaces(instanceTemplate.Properties.NetworkInterfaces)
|
|
|
|
d.Set("network_interface", networkInterfaces)
|
|
|
|
// region is where to look up the subnetwork if there is one attached to the instance template
|
|
|
|
if region != "" {
|
|
|
|
d.Set("region", region)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if instanceTemplate.Properties.Scheduling != nil {
|
|
|
|
scheduling, autoRestart := flattenScheduling(instanceTemplate.Properties.Scheduling)
|
|
|
|
d.Set("scheduling", scheduling)
|
|
|
|
d.Set("automatic_restart", autoRestart)
|
|
|
|
}
|
|
|
|
if instanceTemplate.Properties.Tags != nil {
|
|
|
|
d.Set("tags", instanceTemplate.Properties.Tags.Items)
|
|
|
|
}
|
|
|
|
if instanceTemplate.Properties.ServiceAccounts != nil {
|
|
|
|
d.Set("service_account", flattenServiceAccounts(instanceTemplate.Properties.ServiceAccounts))
|
|
|
|
}
|
2015-02-10 11:29:27 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
config := meta.(*Config)
|
|
|
|
|
2016-04-10 18:59:57 +02:00
|
|
|
project, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-02-10 11:29:27 +01:00
|
|
|
op, err := config.clientCompute.InstanceTemplates.Delete(
|
2016-04-10 18:59:57 +02:00
|
|
|
project, d.Id()).Do()
|
2015-02-10 11:29:27 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error deleting instance template: %s", err)
|
|
|
|
}
|
|
|
|
|
2016-06-06 19:35:13 +02:00
|
|
|
err = computeOperationWaitGlobal(config, op, project, "Deleting Instance Template")
|
2015-02-10 11:29:27 +01:00
|
|
|
if err != nil {
|
2015-09-24 22:30:12 +02:00
|
|
|
return err
|
2015-02-10 11:29:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
d.SetId("")
|
|
|
|
return nil
|
|
|
|
}
|