providers/google: compute_instance
This commit is contained in:
parent
043a7d516e
commit
5bf258809f
|
@ -0,0 +1,43 @@
|
||||||
|
package google
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.google.com/p/google-api-go-client/compute/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// readImage finds the image with the given name.
|
||||||
|
func readImage(c *Config, name string) (*compute.Image, error) {
|
||||||
|
// First, always try ourselves first.
|
||||||
|
image, err := c.clientCompute.Images.Get(c.Project, name).Do()
|
||||||
|
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",
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we match a lookup for an alternate project, then try that next.
|
||||||
|
// If not, we return the error.
|
||||||
|
var project string
|
||||||
|
for k, v := range imageMap {
|
||||||
|
if strings.Contains(name, k) {
|
||||||
|
project = v
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if project == "" {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.clientCompute.Images.Get(project, name).Do()
|
||||||
|
}
|
|
@ -1,17 +1,245 @@
|
||||||
package google
|
package google
|
||||||
|
|
||||||
import(
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.google.com/p/google-api-go-client/compute/v1"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceComputeInstance() *schema.Resource {
|
func resourceComputeInstance() *schema.Resource {
|
||||||
return &schema.Resource{
|
return &schema.Resource{
|
||||||
Create: resourceComputeInstanceCreate,
|
Create: resourceComputeInstanceCreate,
|
||||||
|
Read: resourceComputeInstanceRead,
|
||||||
|
Delete: resourceComputeInstanceDelete,
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{},
|
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,
|
||||||
|
},
|
||||||
|
|
||||||
|
"machine_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"zone": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"disk": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"source": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"network": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"source": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
// Get the zone
|
||||||
|
log.Printf("[DEBUG] Loading zone: %s", d.Get("zone").(string))
|
||||||
|
zone, err := config.clientCompute.Zones.Get(
|
||||||
|
config.Project, d.Get("zone").(string)).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error loading zone '%s': %s", d.Get("zone").(string), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the machine type
|
||||||
|
log.Printf("[DEBUG] Loading machine type: %s", d.Get("machine_type").(string))
|
||||||
|
machineType, err := config.clientCompute.MachineTypes.Get(
|
||||||
|
config.Project, zone.Name, d.Get("machine_type").(string)).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error loading machine type: %s",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build up the list of disks
|
||||||
|
disksCount := d.Get("disk.#").(int)
|
||||||
|
disks := make([]*compute.AttachedDisk, 0, disksCount)
|
||||||
|
for i := 0; i < disksCount; i++ {
|
||||||
|
// Load up the image for this disk
|
||||||
|
imageName := d.Get(fmt.Sprintf("disk.%d.source", i)).(string)
|
||||||
|
image, err := readImage(config, imageName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error loading image '%s': %s",
|
||||||
|
imageName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the disk
|
||||||
|
var disk compute.AttachedDisk
|
||||||
|
disk.Type = "PERSISTENT"
|
||||||
|
disk.Mode = "READ_WRITE"
|
||||||
|
disk.Boot = i == 0
|
||||||
|
disk.AutoDelete = true
|
||||||
|
disk.InitializeParams = &compute.AttachedDiskInitializeParams{
|
||||||
|
SourceImage: image.SelfLink,
|
||||||
|
}
|
||||||
|
|
||||||
|
disks = append(disks, &disk)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build up the list of networks
|
||||||
|
networksCount := d.Get("network.#").(int)
|
||||||
|
networks := make([]*compute.NetworkInterface, 0, networksCount)
|
||||||
|
for i := 0; i < networksCount; i++ {
|
||||||
|
// Load up the name of this network
|
||||||
|
networkName := d.Get(fmt.Sprintf("network.%d.source", i)).(string)
|
||||||
|
network, err := config.clientCompute.Networks.Get(
|
||||||
|
config.Project, networkName).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error loading network '%s': %s",
|
||||||
|
networkName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the disk
|
||||||
|
var iface compute.NetworkInterface
|
||||||
|
iface.AccessConfigs = []*compute.AccessConfig{
|
||||||
|
&compute.AccessConfig{
|
||||||
|
Type: "ONE_TO_ONE_NAT",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
iface.Network = network.SelfLink
|
||||||
|
|
||||||
|
networks = append(networks, &iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the instance information
|
||||||
|
instance := compute.Instance{
|
||||||
|
Description: d.Get("description").(string),
|
||||||
|
Disks: disks,
|
||||||
|
MachineType: machineType.SelfLink,
|
||||||
|
/*
|
||||||
|
Metadata: &compute.Metadata{
|
||||||
|
Items: metadata,
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
Name: d.Get("name").(string),
|
||||||
|
NetworkInterfaces: networks,
|
||||||
|
/*
|
||||||
|
ServiceAccounts: []*compute.ServiceAccount{
|
||||||
|
&compute.ServiceAccount{
|
||||||
|
Email: "default",
|
||||||
|
Scopes: []string{
|
||||||
|
"https://www.googleapis.com/auth/userinfo.email",
|
||||||
|
"https://www.googleapis.com/auth/compute",
|
||||||
|
"https://www.googleapis.com/auth/devstorage.full_control",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Tags: &compute.Tags{
|
||||||
|
Items: c.Tags,
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[INFO] Requesting instance creation")
|
||||||
|
op, err := config.clientCompute.Instances.Insert(
|
||||||
|
config.Project, zone.Name, &instance).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating instance: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the ID now
|
||||||
|
d.SetId(instance.Name)
|
||||||
|
|
||||||
|
// Wait for the operation to complete
|
||||||
|
w := &OperationWaiter{
|
||||||
|
Service: config.clientCompute,
|
||||||
|
Op: op,
|
||||||
|
Project: config.Project,
|
||||||
|
Zone: zone.Name,
|
||||||
|
Type: OperationWaitZone,
|
||||||
|
}
|
||||||
|
state := w.Conf()
|
||||||
|
state.Delay = 10 * time.Second
|
||||||
|
state.Timeout = 10 * time.Minute
|
||||||
|
state.MinTimeout = 2 * time.Second
|
||||||
|
if _, err := state.WaitForState(); err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for instance to create: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceComputeInstanceRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
_, err := config.clientCompute.Instances.Get(
|
||||||
|
config.Project, d.Get("zone").(string), d.Id()).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error reading instance: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
op, err := config.clientCompute.Instances.Delete(
|
||||||
|
config.Project, d.Get("zone").(string), d.Id()).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error deleting instance: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the operation to complete
|
||||||
|
w := &OperationWaiter{
|
||||||
|
Service: config.clientCompute,
|
||||||
|
Op: op,
|
||||||
|
Project: config.Project,
|
||||||
|
Zone: d.Get("zone").(string),
|
||||||
|
Type: OperationWaitZone,
|
||||||
|
}
|
||||||
|
state := w.Conf()
|
||||||
|
state.Delay = 5 * time.Second
|
||||||
|
state.Timeout = 5 * time.Minute
|
||||||
|
state.MinTimeout = 2 * time.Second
|
||||||
|
if _, err := state.WaitForState(); err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for instance to create: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
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 TestAccComputeInstance_basic(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_basic,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeInstanceExists(
|
||||||
|
"google_compute_instance.foobar", &instance),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeInstanceDestroy(s *terraform.State) error {
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
|
||||||
|
for _, rs := range s.Resources {
|
||||||
|
if rs.Type != "google_compute_instance" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := config.clientCompute.Instances.Get(
|
||||||
|
config.Project, rs.Attributes["zone"], rs.ID).Do()
|
||||||
|
if err == nil {
|
||||||
|
return fmt.Errorf("Instance still exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeInstanceExists(n string, instance *compute.Instance) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
|
||||||
|
found, err := config.clientCompute.Instances.Get(
|
||||||
|
config.Project, rs.Attributes["zone"], rs.ID).Do()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if found.Name != rs.ID {
|
||||||
|
return fmt.Errorf("Instance not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
*instance = *found
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccComputeInstance_basic = `
|
||||||
|
resource "google_compute_instance" "foobar" {
|
||||||
|
name = "terraform-test"
|
||||||
|
machine_type = "n1-standard-1"
|
||||||
|
zone = "us-central1-a"
|
||||||
|
|
||||||
|
disk {
|
||||||
|
source = "debian-7-wheezy-v20140814"
|
||||||
|
}
|
||||||
|
|
||||||
|
network {
|
||||||
|
source = "default"
|
||||||
|
}
|
||||||
|
}`
|
Loading…
Reference in New Issue