providers/google: compute_instance

This commit is contained in:
Mitchell Hashimoto 2014-08-25 14:57:17 -07:00
parent 043a7d516e
commit 5bf258809f
3 changed files with 364 additions and 2 deletions

View File

@ -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()
}

View File

@ -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
} }

View File

@ -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"
}
}`