Merge branch 'master' into oauth2
This commit is contained in:
commit
66ad26f19a
|
@ -72,7 +72,13 @@ If you're working on a feature of a provider and want to verify it is functionin
|
||||||
To run the acceptance tests, invoke `make testacc`:
|
To run the acceptance tests, invoke `make testacc`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ make testacc TEST=./builtin/providers/aws TESTARGS='-run=VPC'
|
$ make testacc TEST=./builtin/providers/aws TESTARGS='-run=Vpc'
|
||||||
|
go generate ./...
|
||||||
|
TF_ACC=1 go test ./builtin/providers/aws -v -run=Vpc -timeout 45m
|
||||||
|
=== RUN TestAccVpc_basic
|
||||||
|
2015/02/10 14:11:17 [INFO] Test: Using us-west-2 as test region
|
||||||
|
[...]
|
||||||
|
[...]
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ cd /opt/go/src && ./all.bash
|
||||||
mkdir -p /opt/gopath
|
mkdir -p /opt/gopath
|
||||||
cat <<EOF >/etc/profile.d/gopath.sh
|
cat <<EOF >/etc/profile.d/gopath.sh
|
||||||
export GOPATH="/opt/gopath"
|
export GOPATH="/opt/gopath"
|
||||||
export PATH="/opt/go/bin:\$GOPATH/bin:\$PATH"
|
export PATH="/opt/go/bin:$GOPATH/bin:\$PATH"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Make sure the GOPATH is usable by vagrant
|
# Make sure the GOPATH is usable by vagrant
|
||||||
|
|
|
@ -1,44 +1,75 @@
|
||||||
package google
|
package google
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.google.com/p/google-api-go-client/compute/v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// readImage finds the image with the given name.
|
// If the given name is a URL, return it.
|
||||||
func readImage(c *Config, name string) (*compute.Image, error) {
|
// If it is of the form project/name, use that URL.
|
||||||
// First, always try ourselves first.
|
// If it is of the form name then look in the configured project and then hosted image projects.
|
||||||
image, err := c.clientCompute.Images.Get(c.Project, name).Do()
|
func resolveImage(c *Config, name string) (string, error) {
|
||||||
if err == nil && image != nil && image.SelfLink != "" {
|
|
||||||
return image, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a map of names to the project name where a public image is
|
|
||||||
// hosted. GCE doesn't have an API to simply look up an image without
|
|
||||||
// a project so we do this jank thing.
|
|
||||||
imageMap := map[string]string{
|
|
||||||
"centos": "centos-cloud",
|
|
||||||
"coreos": "coreos-cloud",
|
|
||||||
"debian": "debian-cloud",
|
|
||||||
"opensuse": "opensuse-cloud",
|
|
||||||
"rhel": "rhel-cloud",
|
|
||||||
"sles": "suse-cloud",
|
|
||||||
"ubuntu": "ubuntu-os-cloud",
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we match a lookup for an alternate project, then try that next.
|
if strings.HasPrefix(name, "https://www.googleapis.com/compute/v1/") {
|
||||||
// If not, we return the error.
|
return name, nil
|
||||||
var project string
|
|
||||||
for k, v := range imageMap {
|
} else {
|
||||||
if strings.Contains(name, k) {
|
splitName := strings.Split(name, "/")
|
||||||
project = v
|
if len(splitName) == 1 {
|
||||||
break
|
|
||||||
|
// Must infer the project name:
|
||||||
|
|
||||||
|
// First, try the configured project.
|
||||||
|
image, err := c.clientCompute.Images.Get(c.Project, name).Do()
|
||||||
|
if err == nil {
|
||||||
|
return image.SelfLink, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we match a lookup for an alternate project, then try that next.
|
||||||
|
// If not, we return the original error.
|
||||||
|
|
||||||
|
// If the image name contains the left hand side, we use the project from the right hand
|
||||||
|
// side.
|
||||||
|
imageMap := map[string]string{
|
||||||
|
"centos": "centos-cloud",
|
||||||
|
"coreos": "coreos-cloud",
|
||||||
|
"debian": "debian-cloud",
|
||||||
|
"opensuse": "opensuse-cloud",
|
||||||
|
"rhel": "rhel-cloud",
|
||||||
|
"sles": "suse-cloud",
|
||||||
|
"ubuntu": "ubuntu-os-cloud",
|
||||||
|
"windows": "windows-cloud",
|
||||||
|
}
|
||||||
|
var project string
|
||||||
|
for k, v := range imageMap {
|
||||||
|
if strings.Contains(name, k) {
|
||||||
|
project = v
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if project == "" {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// There was a match, but the image still may not exist, so check it:
|
||||||
|
image, err = c.clientCompute.Images.Get(project, name).Do()
|
||||||
|
if err == nil {
|
||||||
|
return image.SelfLink, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
|
||||||
|
} else if len(splitName) == 2 {
|
||||||
|
image, err := c.clientCompute.Images.Get(splitName[0], splitName[1]).Do()
|
||||||
|
if err == nil {
|
||||||
|
return image.SelfLink, nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("Invalid image name, require URL, project/name, or just name: %s", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if project == "" {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.clientCompute.Images.Get(project, name).Do()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
"google_compute_forwarding_rule": resourceComputeForwardingRule(),
|
"google_compute_forwarding_rule": resourceComputeForwardingRule(),
|
||||||
"google_compute_http_health_check": resourceComputeHttpHealthCheck(),
|
"google_compute_http_health_check": resourceComputeHttpHealthCheck(),
|
||||||
"google_compute_instance": resourceComputeInstance(),
|
"google_compute_instance": resourceComputeInstance(),
|
||||||
|
"google_compute_instance_template": resourceComputeInstanceTemplate(),
|
||||||
"google_compute_network": resourceComputeNetwork(),
|
"google_compute_network": resourceComputeNetwork(),
|
||||||
"google_compute_route": resourceComputeRoute(),
|
"google_compute_route": resourceComputeRoute(),
|
||||||
"google_compute_target_pool": resourceComputeTargetPool(),
|
"google_compute_target_pool": resourceComputeTargetPool(),
|
||||||
|
|
|
@ -46,6 +46,11 @@ func resourceComputeDisk() *schema.Resource {
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"self_link": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,15 +75,15 @@ func resourceComputeDiskCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
// If we were given a source image, load that.
|
// If we were given a source image, load that.
|
||||||
if v, ok := d.GetOk("image"); ok {
|
if v, ok := d.GetOk("image"); ok {
|
||||||
log.Printf("[DEBUG] Loading image: %s", v.(string))
|
log.Printf("[DEBUG] Resolving image name: %s", v.(string))
|
||||||
image, err := readImage(config, v.(string))
|
imageUrl, err := resolveImage(config, v.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"Error loading image '%s': %s",
|
"Error resolving image name '%s': %s",
|
||||||
v.(string), err)
|
v.(string), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
disk.SourceImage = image.SelfLink
|
disk.SourceImage = imageUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, ok := d.GetOk("type"); ok {
|
if v, ok := d.GetOk("type"); ok {
|
||||||
|
@ -132,7 +137,7 @@ func resourceComputeDiskCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
func resourceComputeDiskRead(d *schema.ResourceData, meta interface{}) error {
|
func resourceComputeDiskRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
|
|
||||||
_, err := config.clientCompute.Disks.Get(
|
disk, err := config.clientCompute.Disks.Get(
|
||||||
config.Project, d.Get("zone").(string), d.Id()).Do()
|
config.Project, d.Get("zone").(string), d.Id()).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
|
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
|
||||||
|
@ -145,6 +150,8 @@ func resourceComputeDiskRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
return fmt.Errorf("Error reading disk: %s", err)
|
return fmt.Errorf("Error reading disk: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.Set("self_link", disk.SelfLink)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,11 @@ func resourceComputeFirewall() *schema.Resource {
|
||||||
return hashcode.String(v.(string))
|
return hashcode.String(v.(string))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"self_link": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +164,7 @@ func resourceComputeFirewallCreate(d *schema.ResourceData, meta interface{}) err
|
||||||
func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error {
|
func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
|
|
||||||
_, err := config.clientCompute.Firewalls.Get(
|
firewall, err := config.clientCompute.Firewalls.Get(
|
||||||
config.Project, d.Id()).Do()
|
config.Project, d.Id()).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
|
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
|
||||||
|
@ -172,6 +177,8 @@ func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error
|
||||||
return fmt.Errorf("Error reading firewall: %s", err)
|
return fmt.Errorf("Error reading firewall: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.Set("self_link", firewall.SelfLink)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -304,15 +304,17 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
|
||||||
// Load up the image for this disk if specified
|
// Load up the image for this disk if specified
|
||||||
if v, ok := d.GetOk(prefix + ".image"); ok {
|
if v, ok := d.GetOk(prefix + ".image"); ok {
|
||||||
imageName := v.(string)
|
imageName := v.(string)
|
||||||
image, err := readImage(config, imageName)
|
|
||||||
|
|
||||||
|
imageUrl, err := resolveImage(config, imageName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"Error loading image '%s': %s",
|
"Error resolving image name '%s': %s",
|
||||||
imageName, err)
|
imageName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
disk.InitializeParams = &compute.AttachedDiskInitializeParams{
|
disk.InitializeParams = &compute.AttachedDiskInitializeParams{
|
||||||
SourceImage: image.SelfLink,
|
SourceImage: imageUrl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,471 @@
|
||||||
|
package google
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.google.com/p/google-api-go-client/compute/v1"
|
||||||
|
"code.google.com/p/google-api-go-client/googleapi"
|
||||||
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceComputeInstanceTemplate() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceComputeInstanceTemplateCreate,
|
||||||
|
Read: resourceComputeInstanceTemplateRead,
|
||||||
|
Delete: resourceComputeInstanceTemplateDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"description": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"can_ip_forward": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"instance_description": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"machine_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// TODO: Constraint either source or other disk params
|
||||||
|
"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,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"boot": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"device_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"disk_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
|
||||||
|
"source_image": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"interface": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"mode": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"source": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"metadata": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Schema{
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"network": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"source": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ForceNew: true,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"address": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ForceNew: true,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"automatic_restart": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"on_host_maintenance": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"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},
|
||||||
|
Set: func(v interface{}) int {
|
||||||
|
return hashcode.String(v.(string))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"metadata_fingerprint": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"tags_fingerprint": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"self_link": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildDisks(d *schema.ResourceData, meta interface{}) []*compute.AttachedDisk {
|
||||||
|
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
|
||||||
|
disk.AutoDelete = true
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(prefix + ".auto_delete"); ok {
|
||||||
|
disk.AutoDelete = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
disk.InitializeParams.DiskSizeGb = v.(int64)
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
disk.InitializeParams.SourceImage = v.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return disks
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildNetworks(d *schema.ResourceData, meta interface{}) (error, []*compute.NetworkInterface) {
|
||||||
|
// Build up the list of networks
|
||||||
|
networksCount := d.Get("network.#").(int)
|
||||||
|
networks := make([]*compute.NetworkInterface, 0, networksCount)
|
||||||
|
for i := 0; i < networksCount; i++ {
|
||||||
|
prefix := fmt.Sprintf("network.%d", i)
|
||||||
|
|
||||||
|
source := "global/networks/default"
|
||||||
|
if v, ok := d.GetOk(prefix + ".source"); ok {
|
||||||
|
if v.(string) != "default" {
|
||||||
|
source = v.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the interface
|
||||||
|
var iface compute.NetworkInterface
|
||||||
|
iface.AccessConfigs = []*compute.AccessConfig{
|
||||||
|
&compute.AccessConfig{
|
||||||
|
Type: "ONE_TO_ONE_NAT",
|
||||||
|
NatIP: d.Get(prefix + ".address").(string),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
iface.Network = source
|
||||||
|
|
||||||
|
networks = append(networks, &iface)
|
||||||
|
}
|
||||||
|
return nil, networks
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
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)
|
||||||
|
instanceProperties.Disks = buildDisks(d, meta)
|
||||||
|
instanceProperties.Metadata = resourceInstanceMetadata(d)
|
||||||
|
err, networks := buildNetworks(d, meta)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
instanceProperties.NetworkInterfaces = networks
|
||||||
|
|
||||||
|
instanceProperties.Scheduling = &compute.Scheduling{
|
||||||
|
AutomaticRestart: d.Get("automatic_restart").(bool),
|
||||||
|
}
|
||||||
|
instanceProperties.Scheduling.OnHostMaintenance = "MIGRATE"
|
||||||
|
if v, ok := d.GetOk("on_host_maintenance"); ok {
|
||||||
|
instanceProperties.Scheduling.OnHostMaintenance = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
instanceTemplate := compute.InstanceTemplate{
|
||||||
|
Description: d.Get("description").(string),
|
||||||
|
Properties: instanceProperties,
|
||||||
|
Name: d.Get("name").(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
op, err := config.clientCompute.InstanceTemplates.Insert(
|
||||||
|
config.Project, &instanceTemplate).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating instance: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the ID now
|
||||||
|
d.SetId(instanceTemplate.Name)
|
||||||
|
|
||||||
|
// Wait for the operation to complete
|
||||||
|
w := &OperationWaiter{
|
||||||
|
Service: config.clientCompute,
|
||||||
|
Op: op,
|
||||||
|
Project: config.Project,
|
||||||
|
Type: OperationWaitGlobal,
|
||||||
|
}
|
||||||
|
state := w.Conf()
|
||||||
|
state.Delay = 10 * time.Second
|
||||||
|
state.Timeout = 10 * time.Minute
|
||||||
|
state.MinTimeout = 2 * time.Second
|
||||||
|
opRaw, err := state.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for instance template to create: %s", err)
|
||||||
|
}
|
||||||
|
op = opRaw.(*compute.Operation)
|
||||||
|
if op.Error != nil {
|
||||||
|
// The resource didn't actually create
|
||||||
|
d.SetId("")
|
||||||
|
|
||||||
|
// Return the error
|
||||||
|
return OperationError(*op.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceComputeInstanceTemplateRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
instanceTemplate, err := config.clientCompute.InstanceTemplates.Get(
|
||||||
|
config.Project, d.Id()).Do()
|
||||||
|
if err != nil {
|
||||||
|
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceComputeInstanceTemplateDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
op, err := config.clientCompute.InstanceTemplates.Delete(
|
||||||
|
config.Project, d.Id()).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error deleting instance template: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the operation to complete
|
||||||
|
w := &OperationWaiter{
|
||||||
|
Service: config.clientCompute,
|
||||||
|
Op: op,
|
||||||
|
Project: config.Project,
|
||||||
|
Type: OperationWaitGlobal,
|
||||||
|
}
|
||||||
|
state := w.Conf()
|
||||||
|
state.Delay = 5 * time.Second
|
||||||
|
state.Timeout = 5 * time.Minute
|
||||||
|
state.MinTimeout = 2 * time.Second
|
||||||
|
opRaw, err := state.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for instance template to delete: %s", err)
|
||||||
|
}
|
||||||
|
op = opRaw.(*compute.Operation)
|
||||||
|
if op.Error != nil {
|
||||||
|
// Return the error
|
||||||
|
return OperationError(*op.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,278 @@
|
||||||
|
package google
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.google.com/p/google-api-go-client/compute/v1"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccComputeInstanceTemplate_basic(t *testing.T) {
|
||||||
|
var instanceTemplate compute.InstanceTemplate
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeInstanceTemplateDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeInstanceTemplate_basic,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeInstanceTemplateExists(
|
||||||
|
"google_compute_instance_template.foobar", &instanceTemplate),
|
||||||
|
testAccCheckComputeInstanceTemplateTag(&instanceTemplate, "foo"),
|
||||||
|
testAccCheckComputeInstanceTemplateMetadata(&instanceTemplate, "foo", "bar"),
|
||||||
|
testAccCheckComputeInstanceTemplateDisk(&instanceTemplate, "debian-7-wheezy-v20140814", true, true),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccComputeInstanceTemplate_IP(t *testing.T) {
|
||||||
|
var instanceTemplate compute.InstanceTemplate
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeInstanceTemplateDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeInstanceTemplate_ip,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeInstanceTemplateExists(
|
||||||
|
"google_compute_instance_template.foobar", &instanceTemplate),
|
||||||
|
testAccCheckComputeInstanceTemplateNetwork(&instanceTemplate),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccComputeInstanceTemplate_disks(t *testing.T) {
|
||||||
|
var instanceTemplate compute.InstanceTemplate
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeInstanceTemplateDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeInstanceTemplate_disks,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeInstanceTemplateExists(
|
||||||
|
"google_compute_instance_template.foobar", &instanceTemplate),
|
||||||
|
testAccCheckComputeInstanceTemplateDisk(&instanceTemplate, "debian-7-wheezy-v20140814", true, true),
|
||||||
|
testAccCheckComputeInstanceTemplateDisk(&instanceTemplate, "foo_existing_disk", false, false),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeInstanceTemplateDestroy(s *terraform.State) error {
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "google_compute_instance_template" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := config.clientCompute.InstanceTemplates.Get(
|
||||||
|
config.Project, rs.Primary.ID).Do()
|
||||||
|
if err == nil {
|
||||||
|
return fmt.Errorf("Instance template still exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeInstanceTemplateExists(n string, instanceTemplate *compute.InstanceTemplate) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
|
||||||
|
found, err := config.clientCompute.InstanceTemplates.Get(
|
||||||
|
config.Project, rs.Primary.ID).Do()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if found.Name != rs.Primary.ID {
|
||||||
|
return fmt.Errorf("Instance template not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
*instanceTemplate = *found
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeInstanceTemplateMetadata(
|
||||||
|
instanceTemplate *compute.InstanceTemplate,
|
||||||
|
k string, v string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
if instanceTemplate.Properties.Metadata == nil {
|
||||||
|
return fmt.Errorf("no metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range instanceTemplate.Properties.Metadata.Items {
|
||||||
|
if k != item.Key {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v == item.Value {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("bad value for %s: %s", k, item.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("metadata not found: %s", k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeInstanceTemplateNetwork(instanceTemplate *compute.InstanceTemplate) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
for _, i := range instanceTemplate.Properties.NetworkInterfaces {
|
||||||
|
for _, c := range i.AccessConfigs {
|
||||||
|
if c.NatIP == "" {
|
||||||
|
return fmt.Errorf("no NAT IP")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeInstanceTemplateDisk(instanceTemplate *compute.InstanceTemplate, source string, delete bool, boot bool) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
if instanceTemplate.Properties.Disks == nil {
|
||||||
|
return fmt.Errorf("no disks")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, disk := range instanceTemplate.Properties.Disks {
|
||||||
|
if disk.InitializeParams == nil {
|
||||||
|
// Check disk source
|
||||||
|
if disk.Source == source {
|
||||||
|
if disk.AutoDelete == delete && disk.Boot == boot {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check source image
|
||||||
|
if disk.InitializeParams.SourceImage == source {
|
||||||
|
if disk.AutoDelete == delete && disk.Boot == boot {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Disk not found: %s", source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeInstanceTemplateTag(instanceTemplate *compute.InstanceTemplate, n string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
if instanceTemplate.Properties.Tags == nil {
|
||||||
|
return fmt.Errorf("no tags")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, k := range instanceTemplate.Properties.Tags.Items {
|
||||||
|
if k == n {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("tag not found: %s", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccComputeInstanceTemplate_basic = `
|
||||||
|
resource "google_compute_instance_template" "foobar" {
|
||||||
|
name = "terraform-test"
|
||||||
|
machine_type = "n1-standard-1"
|
||||||
|
can_ip_forward = false
|
||||||
|
tags = ["foo", "bar"]
|
||||||
|
|
||||||
|
disk {
|
||||||
|
source_image = "debian-7-wheezy-v20140814"
|
||||||
|
auto_delete = true
|
||||||
|
boot = true
|
||||||
|
}
|
||||||
|
|
||||||
|
network {
|
||||||
|
source = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
foo = "bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
service_account {
|
||||||
|
scopes = ["userinfo-email", "compute-ro", "storage-ro"]
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
const testAccComputeInstanceTemplate_ip = `
|
||||||
|
resource "google_compute_address" "foo" {
|
||||||
|
name = "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_compute_instance_template" "foobar" {
|
||||||
|
name = "terraform-test"
|
||||||
|
machine_type = "n1-standard-1"
|
||||||
|
tags = ["foo", "bar"]
|
||||||
|
|
||||||
|
disk {
|
||||||
|
source_image = "debian-7-wheezy-v20140814"
|
||||||
|
}
|
||||||
|
|
||||||
|
network {
|
||||||
|
source = "default"
|
||||||
|
address = "${google_compute_address.foo.address}"
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
foo = "bar"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
const testAccComputeInstanceTemplate_disks = `
|
||||||
|
resource "google_compute_instance_template" "foobar" {
|
||||||
|
name = "terraform-test"
|
||||||
|
machine_type = "n1-standard-1"
|
||||||
|
|
||||||
|
disk {
|
||||||
|
source_image = "debian-7-wheezy-v20140814"
|
||||||
|
auto_delete = true
|
||||||
|
boot = true
|
||||||
|
}
|
||||||
|
|
||||||
|
disk {
|
||||||
|
source = "foo_existing_disk"
|
||||||
|
auto_delete = false
|
||||||
|
boot = false
|
||||||
|
}
|
||||||
|
|
||||||
|
network {
|
||||||
|
source = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
foo = "bar"
|
||||||
|
}
|
||||||
|
}`
|
|
@ -54,6 +54,50 @@ func TestAccComputeInstance_basic(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccComputeInstance_basic2(t *testing.T) {
|
||||||
|
var instance compute.Instance
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeInstance_basic2,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeInstanceExists(
|
||||||
|
"google_compute_instance.foobar", &instance),
|
||||||
|
testAccCheckComputeInstanceTag(&instance, "foo"),
|
||||||
|
testAccCheckComputeInstanceMetadata(&instance, "foo", "bar"),
|
||||||
|
testAccCheckComputeInstanceDisk(&instance, "terraform-test", true, true),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccComputeInstance_basic3(t *testing.T) {
|
||||||
|
var instance compute.Instance
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeInstance_basic3,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeInstanceExists(
|
||||||
|
"google_compute_instance.foobar", &instance),
|
||||||
|
testAccCheckComputeInstanceTag(&instance, "foo"),
|
||||||
|
testAccCheckComputeInstanceMetadata(&instance, "foo", "bar"),
|
||||||
|
testAccCheckComputeInstanceDisk(&instance, "terraform-test", true, true),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccComputeInstance_IP(t *testing.T) {
|
func TestAccComputeInstance_IP(t *testing.T) {
|
||||||
var instance compute.Instance
|
var instance compute.Instance
|
||||||
|
|
||||||
|
@ -345,6 +389,49 @@ resource "google_compute_instance" "foobar" {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
|
const testAccComputeInstance_basic2 = `
|
||||||
|
resource "google_compute_instance" "foobar" {
|
||||||
|
name = "terraform-test"
|
||||||
|
machine_type = "n1-standard-1"
|
||||||
|
zone = "us-central1-a"
|
||||||
|
can_ip_forward = false
|
||||||
|
tags = ["foo", "bar"]
|
||||||
|
|
||||||
|
disk {
|
||||||
|
image = "debian-cloud/debian-7-wheezy-v20140814"
|
||||||
|
}
|
||||||
|
|
||||||
|
network_interface {
|
||||||
|
network = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
foo = "bar"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
const testAccComputeInstance_basic3 = `
|
||||||
|
resource "google_compute_instance" "foobar" {
|
||||||
|
name = "terraform-test"
|
||||||
|
machine_type = "n1-standard-1"
|
||||||
|
zone = "us-central1-a"
|
||||||
|
can_ip_forward = false
|
||||||
|
tags = ["foo", "bar"]
|
||||||
|
|
||||||
|
disk {
|
||||||
|
image = "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140814"
|
||||||
|
}
|
||||||
|
|
||||||
|
network_interface {
|
||||||
|
network = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
foo = "bar"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
// Update metadata, tags, and network_interface
|
// Update metadata, tags, and network_interface
|
||||||
const testAccComputeInstance_update = `
|
const testAccComputeInstance_update = `
|
||||||
resource "google_compute_instance" "foobar" {
|
resource "google_compute_instance" "foobar" {
|
||||||
|
|
|
@ -33,6 +33,11 @@ func resourceComputeNetwork() *schema.Resource {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"self_link": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +103,7 @@ func resourceComputeNetworkRead(d *schema.ResourceData, meta interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
d.Set("gateway_ipv4", network.GatewayIPv4)
|
d.Set("gateway_ipv4", network.GatewayIPv4)
|
||||||
|
d.Set("self_link", network.SelfLink)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,11 @@ func resourceComputeRoute() *schema.Resource {
|
||||||
return hashcode.String(v.(string))
|
return hashcode.String(v.(string))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"self_link": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,7 +188,7 @@ func resourceComputeRouteCreate(d *schema.ResourceData, meta interface{}) error
|
||||||
func resourceComputeRouteRead(d *schema.ResourceData, meta interface{}) error {
|
func resourceComputeRouteRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
|
|
||||||
_, err := config.clientCompute.Routes.Get(
|
route, err := config.clientCompute.Routes.Get(
|
||||||
config.Project, d.Id()).Do()
|
config.Project, d.Id()).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
|
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
|
||||||
|
@ -196,6 +201,8 @@ func resourceComputeRouteRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
return fmt.Errorf("Error reading route: %#v", err)
|
return fmt.Errorf("Error reading route: %#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.Set("self_link", route.SelfLink)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,15 +51,18 @@ resource "aws_elb" "bar" {
|
||||||
The following arguments are supported:
|
The following arguments are supported:
|
||||||
|
|
||||||
* `name` - (Required) The name of the ELB
|
* `name` - (Required) The name of the ELB
|
||||||
* `availability_zones` - (Optional) The AZ's to serve traffic in.
|
* `availability_zones` - (Required for an EC2-classic ELB) The AZ's to serve traffic in.
|
||||||
* `security_groups` - (Optional) A list of security group IDs to assign to the ELB.
|
* `security_groups` - (Optional) A list of security group IDs to assign to the ELB.
|
||||||
* `subnets` - (Optional) A list of subnets to attach to the ELB.
|
* `subnets` - (Required for a VPC ELB) A list of subnet IDs to attach to the ELB.
|
||||||
* `instances` - (Optional) A list of instance ids to place in the ELB pool.
|
* `instances` - (Optional) A list of instance ids to place in the ELB pool.
|
||||||
* `internal` - (Optional) If true, ELB will be an internal ELB.
|
* `internal` - (Optional) If true, ELB will be an internal ELB.
|
||||||
* `listener` - (Required) A list of listener blocks. Listeners documented below.
|
* `listener` - (Required) A list of listener blocks. Listeners documented below.
|
||||||
* `health_check` - (Optional) A health_check block. Health Check documented below.
|
* `health_check` - (Optional) A health_check block. Health Check documented below.
|
||||||
* `cross_zone_load_balancing` - (Optional) Enable cross-zone load balancing.
|
* `cross_zone_load_balancing` - (Optional) Enable cross-zone load balancing.
|
||||||
|
|
||||||
|
Exactly one of `availability_zones` or `subnets` must be specified: this
|
||||||
|
determines if the ELB exists in a VPC or in EC2-classic.
|
||||||
|
|
||||||
Listeners support the following:
|
Listeners support the following:
|
||||||
|
|
||||||
* `instance_port` - (Required) The port on the instance to route to
|
* `instance_port` - (Required) The port on the instance to route to
|
||||||
|
|
|
@ -30,7 +30,9 @@ The following arguments are supported:
|
||||||
|
|
||||||
* `zone` - (Required) The zone where this disk will be available.
|
* `zone` - (Required) The zone where this disk will be available.
|
||||||
|
|
||||||
* `image` - (Optional) The machine image to base this disk off of.
|
* `image` - (Optional) The image from which to initialize this disk. Either the full URL, a
|
||||||
|
contraction of the form "project/name", or just a name (in which case the current project is
|
||||||
|
used).
|
||||||
|
|
||||||
* `size` - (Optional) The size of the image in gigabytes. If not specified,
|
* `size` - (Optional) The size of the image in gigabytes. If not specified,
|
||||||
it will inherit the size of its base image.
|
it will inherit the size of its base image.
|
||||||
|
|
|
@ -84,8 +84,9 @@ The `disk` block supports:
|
||||||
* `disk` - (Required if image not set) The name of the disk (such as
|
* `disk` - (Required if image not set) The name of the disk (such as
|
||||||
those managed by `google_compute_disk`) to attach.
|
those managed by `google_compute_disk`) to attach.
|
||||||
|
|
||||||
* `image` - (Required if disk not set) The name of the image to base
|
* `image` - (Required if disk not set) The image from which to initialize this
|
||||||
this disk off of.
|
disk. Either the full URL, a contraction of the form "project/name", or just
|
||||||
|
a name (in which case the current project is used).
|
||||||
|
|
||||||
* `auto_delete` - (Optional) Whether or not the disk should be auto-deleted.
|
* `auto_delete` - (Optional) Whether or not the disk should be auto-deleted.
|
||||||
This defaults to true.
|
This defaults to true.
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
---
|
||||||
|
layout: "google"
|
||||||
|
page_title: "Google: google_compute_instance_template"
|
||||||
|
sidebar_current: "docs-google-resource-instance_template"
|
||||||
|
description: |-
|
||||||
|
Manages a VM instance template resource within GCE.
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
# google\_compute\_instance\_template
|
||||||
|
|
||||||
|
Manages a VM instance template resource within GCE. For more information see
|
||||||
|
[the official documentation](https://cloud.google.com/compute/docs/instance-templates)
|
||||||
|
and
|
||||||
|
[API](https://cloud.google.com/compute/docs/reference/latest/instanceTemplates).
|
||||||
|
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "google_compute_instance_template" "foobar" {
|
||||||
|
name = "terraform-test"
|
||||||
|
description = "template description"
|
||||||
|
instance_description = "description assigned to instances"
|
||||||
|
machine_type = "n1-standard-1"
|
||||||
|
can_ip_forward = false
|
||||||
|
automatic_restart = true
|
||||||
|
on_host_maintenance = "MIGRATE"
|
||||||
|
tags = ["foo", "bar"]
|
||||||
|
|
||||||
|
# Create a new boot disk from an image
|
||||||
|
disk {
|
||||||
|
source_image = "debian-7-wheezy-v20140814"
|
||||||
|
auto_delete = true
|
||||||
|
boot = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use an existing disk resource
|
||||||
|
disk {
|
||||||
|
source = "foo_existing_disk"
|
||||||
|
auto_delete = false
|
||||||
|
boot = false
|
||||||
|
}
|
||||||
|
|
||||||
|
network {
|
||||||
|
source = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
foo = "bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
service_account {
|
||||||
|
scopes = ["userinfo-email", "compute-ro", "storage-ro"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
Note that changing any field for this resource forces a new resource to be created.
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `name` - (Required) A unique name for the resource, required by GCE.
|
||||||
|
|
||||||
|
* `description` - (Optional) A brief description of this resource.
|
||||||
|
|
||||||
|
* `can_ip_forward` - (Optional) Whether to allow sending and receiving of
|
||||||
|
packets with non-matching source or destination IPs.
|
||||||
|
This defaults to false.
|
||||||
|
|
||||||
|
* `instance_description` - (Optional) A brief description to use for instances
|
||||||
|
created from this template.
|
||||||
|
|
||||||
|
* `machine_type` - (Required) The machine type to create.
|
||||||
|
|
||||||
|
* `disk` - (Required) Disks to attach to instances created from this
|
||||||
|
template. This can be specified multiple times for multiple disks.
|
||||||
|
Structure is documented below.
|
||||||
|
|
||||||
|
* `metadata` - (Optional) Metadata key/value pairs to make available from
|
||||||
|
within instances created from this template.
|
||||||
|
|
||||||
|
* `network` - (Required) Networks to attach to instances created from this template.
|
||||||
|
This can be specified multiple times for multiple networks. Structure is
|
||||||
|
documented below.
|
||||||
|
|
||||||
|
* `automatic_restart` - (Optional) Specifies whether the instance should be
|
||||||
|
automatically restarted if it is terminated by Compute Engine (not
|
||||||
|
terminated by a user).
|
||||||
|
This defaults to true.
|
||||||
|
|
||||||
|
* `on_host_maintenance` - (Optional) Defines the maintenance behavior for this
|
||||||
|
instance.
|
||||||
|
|
||||||
|
* `service_account` - (Optional) Service account to attach to the instance.
|
||||||
|
|
||||||
|
* `tags` - (Optional) Tags to attach to the instance.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The `disk` block supports:
|
||||||
|
|
||||||
|
* `auto_delete` - (Optional) Whether or not the disk should be auto-deleted.
|
||||||
|
This defaults to true.
|
||||||
|
|
||||||
|
* `boot` - (Optional) Indicates that this is a boot disk.
|
||||||
|
|
||||||
|
* `device_name` - (Optional) A unique device name that is reflected into
|
||||||
|
the /dev/ tree of a Linux operating system running within the instance.
|
||||||
|
If not specified, the server chooses a default device name to apply to
|
||||||
|
this disk.
|
||||||
|
|
||||||
|
* `disk_name` - (Optional) Name of the disk. When not provided, this defaults
|
||||||
|
to the name of the instance.
|
||||||
|
|
||||||
|
* `source_image` - (Required if source not set) The name of the image to base
|
||||||
|
this disk off of.
|
||||||
|
|
||||||
|
* `interface` - (Optional) Specifies the disk interface to use for attaching
|
||||||
|
this disk.
|
||||||
|
|
||||||
|
* `mode` - (Optional) The mode in which to attach this disk, either READ_WRITE
|
||||||
|
or READ_ONLY. If you are attaching or creating a boot disk, this must
|
||||||
|
read-write mode.
|
||||||
|
|
||||||
|
* `source` - (Required if source_image not set) The name of the disk (such as
|
||||||
|
those managed by `google_compute_disk`) to attach.
|
||||||
|
|
||||||
|
* `type` - (Optional) The GCE disk type.
|
||||||
|
|
||||||
|
The `network` block supports:
|
||||||
|
|
||||||
|
* `source` - (Required) The name of the network to attach this interface to.
|
||||||
|
|
||||||
|
* `address` - (Optional) The IP address of a reserved IP address to assign
|
||||||
|
to this interface.
|
||||||
|
|
||||||
|
The `service_account` block supports:
|
||||||
|
|
||||||
|
* `scopes` - (Required) A list of service scopes. Both OAuth2 URLs and gcloud
|
||||||
|
short names are supported.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `self_link` - The URL of the created resource.
|
Loading…
Reference in New Issue