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
|
||||
|
||||
import(
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/google-api-go-client/compute/v1"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func resourceComputeInstance() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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