Multiple gcp improvements and new resources

This commit is contained in:
Dave Cunningham 2014-11-20 12:40:17 -05:00 committed by Paul Hinze
parent 6947ba2518
commit 6046647f38
15 changed files with 1394 additions and 8 deletions

2
.gitignore vendored
View File

@ -15,3 +15,5 @@ website/node_modules
*.tfstate
*.log
*.bak
*~
.*.swp

View File

@ -32,9 +32,12 @@ func Provider() terraform.ResourceProvider {
"google_compute_address": resourceComputeAddress(),
"google_compute_disk": resourceComputeDisk(),
"google_compute_firewall": resourceComputeFirewall(),
"google_compute_forwarding_rule": resourceComputeForwardingRule(),
"google_compute_http_health_check": resourceComputeHttpHealthCheck(),
"google_compute_instance": resourceComputeInstance(),
"google_compute_network": resourceComputeNetwork(),
"google_compute_route": resourceComputeRoute(),
"google_compute_target_pool": resourceComputeTargetPool(),
},
ConfigureFunc: providerConfigure,

View File

@ -27,6 +27,12 @@ func resourceComputeAddress() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"self_link": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
@ -90,6 +96,7 @@ func resourceComputeAddressRead(d *schema.ResourceData, meta interface{}) error
}
d.Set("address", addr.Address)
d.Set("self_link", addr.SelfLink)
return nil
}
@ -98,6 +105,7 @@ func resourceComputeAddressDelete(d *schema.ResourceData, meta interface{}) erro
config := meta.(*Config)
// Delete the address
log.Printf("[DEBUG] address delete request")
op, err := config.clientCompute.Addresses.Delete(
config.Project, config.Region, d.Id()).Do()
if err != nil {

View File

@ -0,0 +1,219 @@
package google
import (
"fmt"
"log"
"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/schema"
)
func resourceComputeForwardingRule() *schema.Resource {
return &schema.Resource{
Create: resourceComputeForwardingRuleCreate,
Read: resourceComputeForwardingRuleRead,
Delete: resourceComputeForwardingRuleDelete,
Update: resourceComputeForwardingRuleUpdate,
Schema: map[string]*schema.Schema{
"ip_address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
"ip_protocol": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"port_range": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"self_link": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"target": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: false,
},
},
}
}
func resourceComputeForwardingRuleCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
frule := &compute.ForwardingRule{
IPAddress: d.Get("ip_address").(string),
IPProtocol: d.Get("ip_protocol").(string),
Description: d.Get("description").(string),
Name: d.Get("name").(string),
PortRange: d.Get("port_range").(string),
Target: d.Get("target").(string),
}
log.Printf("[DEBUG] ForwardingRule insert request: %#v", frule)
op, err := config.clientCompute.ForwardingRules.Insert(
config.Project, config.Region, frule).Do()
if err != nil {
return fmt.Errorf("Error creating ForwardingRule: %s", err)
}
// It probably maybe worked, so store the ID now
d.SetId(frule.Name)
// Wait for the operation to complete
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Region: config.Region,
Project: config.Project,
Type: OperationWaitRegion,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for ForwardingRule 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 resourceComputeForwardingRuleRead(d, meta)
}
func resourceComputeForwardingRuleUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
d.Partial(true)
if d.HasChange("target") {
target_name := d.Get("target").(string)
target_ref := &compute.TargetReference{Target: target_name}
op, err := config.clientCompute.ForwardingRules.SetTarget(
config.Project, config.Region, d.Id(), target_ref).Do()
if err != nil {
return fmt.Errorf("Error updating target: %s", err)
}
// Wait for the operation to complete
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Region: config.Region,
Project: config.Project,
Type: OperationWaitRegion,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for ForwardingRule to update target: %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)
}
d.SetPartial("target")
}
d.Partial(false)
return resourceComputeForwardingRuleRead(d, meta)
}
func resourceComputeForwardingRuleRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
frule, err := config.clientCompute.ForwardingRules.Get(
config.Project, config.Region, 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 ForwardingRule: %s", err)
}
d.Set("ip_address", frule.IPAddress)
d.Set("ip_protocol", frule.IPProtocol)
d.Set("self_link", frule.SelfLink)
return nil
}
func resourceComputeForwardingRuleDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
// Delete the ForwardingRule
log.Printf("[DEBUG] ForwardingRule delete request")
op, err := config.clientCompute.ForwardingRules.Delete(
config.Project, config.Region, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting ForwardingRule: %s", err)
}
// Wait for the operation to complete
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Region: config.Region,
Project: config.Project,
Type: OperationWaitRegion,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for ForwardingRule to delete: %s", err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,125 @@
package google
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccComputeForwardingRule_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeForwardingRuleDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeForwardingRule_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeForwardingRuleExists(
"google_compute_forwarding_rule.foobar"),
),
},
},
})
}
func TestAccComputeForwardingRule_ip(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeForwardingRuleDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeForwardingRule_ip,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeForwardingRuleExists(
"google_compute_forwarding_rule.foobar"),
),
},
},
})
}
func testAccCheckComputeForwardingRuleDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_forwarding_rule" {
continue
}
_, err := config.clientCompute.ForwardingRules.Get(
config.Project, config.Region, rs.Primary.ID).Do()
if err == nil {
return fmt.Errorf("ForwardingRule still exists")
}
}
return nil
}
func testAccCheckComputeForwardingRuleExists(n string) 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.ForwardingRules.Get(
config.Project, config.Region, rs.Primary.ID).Do()
if err != nil {
return err
}
if found.Name != rs.Primary.ID {
return fmt.Errorf("ForwardingRule not found")
}
return nil
}
}
const testAccComputeForwardingRule_basic = `
resource "google_compute_target_pool" "foobar-tp" {
description = "Resource created for Terraform acceptance testing"
instances = ["us-central1-a/foo", "us-central1-b/bar"]
name = "terraform-test"
}
resource "google_compute_forwarding_rule" "foobar" {
description = "Resource created for Terraform acceptance testing"
ip_protocol = "UDP"
name = "terraform-test"
port_range = "80-81"
target = "${google_compute_target_pool.foobar-tp.self_link}"
}
`
const testAccComputeForwardingRule_ip = `
resource "google_compute_address" "foo" {
name = "foo"
}
resource "google_compute_target_pool" "foobar-tp" {
description = "Resource created for Terraform acceptance testing"
instances = ["us-central1-a/foo", "us-central1-b/bar"]
name = "terraform-test"
}
resource "google_compute_forwarding_rule" "foobar" {
description = "Resource created for Terraform acceptance testing"
ip_address = "${google_compute_address.foo.address}"
ip_protocol = "TCP"
name = "terraform-test"
port_range = "80-81"
target = "${google_compute_target_pool.foobar-tp.self_link}"
}
`

View File

@ -0,0 +1,260 @@
package google
import (
"fmt"
"log"
"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/schema"
)
func resourceComputeHttpHealthCheck() *schema.Resource {
return &schema.Resource{
Create: resourceComputeHttpHealthCheckCreate,
Read: resourceComputeHttpHealthCheckRead,
Delete: resourceComputeHttpHealthCheckDelete,
Update: resourceComputeHttpHealthCheckUpdate,
Schema: map[string]*schema.Schema{
"check_interval_sec": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: false,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"healthy_threshold": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: false,
},
"host": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"port": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: false,
},
"request_path": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"self_link": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"timeout_sec": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: false,
},
"unhealthy_threshold": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: false,
},
},
}
}
func resourceComputeHttpHealthCheckCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
// Build the parameter
hchk := &compute.HttpHealthCheck{
Description: d.Get("description").(string),
Host: d.Get("host").(string),
Name: d.Get("name").(string),
RequestPath: d.Get("request_path").(string),
}
if d.Get("check_interval_sec") != nil {
hchk.CheckIntervalSec = int64(d.Get("check_interval_sec").(int))
}
if d.Get("health_threshold") != nil {
hchk.HealthyThreshold = int64(d.Get("healthy_threshold").(int))
}
if d.Get("port") != nil {
hchk.Port = int64(d.Get("port").(int))
}
if d.Get("timeout") != nil {
hchk.TimeoutSec = int64(d.Get("timeout_sec").(int))
}
if d.Get("unhealthy_threshold") != nil {
hchk.UnhealthyThreshold = int64(d.Get("unhealthy_threshold").(int))
}
log.Printf("[DEBUG] HttpHealthCheck insert request: %#v", hchk)
op, err := config.clientCompute.HttpHealthChecks.Insert(
config.Project, hchk).Do()
if err != nil {
return fmt.Errorf("Error creating HttpHealthCheck: %s", err)
}
// It probably maybe worked, so store the ID now
d.SetId(hchk.Name)
// Wait for the operation to complete
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for HttpHealthCheck 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 resourceComputeHttpHealthCheckRead(d, meta)
}
func resourceComputeHttpHealthCheckUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
// Build the parameter
hchk := &compute.HttpHealthCheck{
Description: d.Get("description").(string),
Host: d.Get("host").(string),
Name: d.Get("name").(string),
RequestPath: d.Get("request_path").(string),
}
if d.Get("check_interval_sec") != nil {
hchk.CheckIntervalSec = int64(d.Get("check_interval_sec").(int))
}
if d.Get("health_threshold") != nil {
hchk.HealthyThreshold = int64(d.Get("healthy_threshold").(int))
}
if d.Get("port") != nil {
hchk.Port = int64(d.Get("port").(int))
}
if d.Get("timeout") != nil {
hchk.TimeoutSec = int64(d.Get("timeout_sec").(int))
}
if d.Get("unhealthy_threshold") != nil {
hchk.UnhealthyThreshold = int64(d.Get("unhealthy_threshold").(int))
}
log.Printf("[DEBUG] HttpHealthCheck patch request: %#v", hchk)
op, err := config.clientCompute.HttpHealthChecks.Patch(
config.Project, hchk.Name, hchk).Do()
if err != nil {
return fmt.Errorf("Error patching HttpHealthCheck: %s", err)
}
// It probably maybe worked, so store the ID now
d.SetId(hchk.Name)
// Wait for the operation to complete
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for HttpHealthCheck to patch: %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 resourceComputeHttpHealthCheckRead(d, meta)
}
func resourceComputeHttpHealthCheckRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
hchk, err := config.clientCompute.HttpHealthChecks.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 HttpHealthCheck: %s", err)
}
d.Set("self_link", hchk.SelfLink)
return nil
}
func resourceComputeHttpHealthCheckDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
// Delete the HttpHealthCheck
op, err := config.clientCompute.HttpHealthChecks.Delete(
config.Project, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting HttpHealthCheck: %s", err)
}
// Wait for the operation to complete
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Project: config.Project,
Type: OperationWaitGlobal,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for HttpHealthCheck to delete: %s", err)
}
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,85 @@
package google
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccComputeHttpHealthCheck_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeHttpHealthCheckDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeHttpHealthCheck_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeHttpHealthCheckExists(
"google_compute_http_health_check.foobar"),
),
},
},
})
}
func testAccCheckComputeHttpHealthCheckDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_http_health_check" {
continue
}
_, err := config.clientCompute.HttpHealthChecks.Get(
config.Project, rs.Primary.ID).Do()
if err == nil {
return fmt.Errorf("HttpHealthCheck still exists")
}
}
return nil
}
func testAccCheckComputeHttpHealthCheckExists(n string) 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.HttpHealthChecks.Get(
config.Project, rs.Primary.ID).Do()
if err != nil {
return err
}
if found.Name != rs.Primary.ID {
return fmt.Errorf("HttpHealthCheck not found")
}
return nil
}
}
const testAccComputeHttpHealthCheck_basic = `
resource "google_compute_http_health_check" "foobar" {
check_interval_sec = 1
description = "Resource created for Terraform acceptance testing"
healthy_threshold = 3
host = "foobar"
name = "terraform-test"
port = "80"
request_path = "/health_check"
timeout_sec = 2
unhealthy_threshold = 3
}
`

View File

@ -109,6 +109,30 @@ func resourceComputeInstance() *schema.Resource {
},
},
"service_accounts": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"email": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"scopes": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
"can_ip_forward": &schema.Schema{
Type: schema.TypeBool,
Optional: true,

View File

@ -0,0 +1,404 @@
package google
import (
"fmt"
"log"
"strings"
"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/schema"
)
func resourceComputeTargetPool() *schema.Resource {
return &schema.Resource{
Create: resourceComputeTargetPoolCreate,
Read: resourceComputeTargetPoolRead,
Delete: resourceComputeTargetPoolDelete,
Update: resourceComputeTargetPoolUpdate,
Schema: map[string]*schema.Schema{
"backup_pool": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"failover_ratio": &schema.Schema{
Type: schema.TypeFloat,
Optional: true,
ForceNew: true,
},
"health_checks": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
},
"instances": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"self_link": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"session_affinity": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func convertStringArr(ifaceArr []interface{}) []string {
arr := make([]string, len(ifaceArr))
for i, v := range ifaceArr {
arr[i] = v.(string)
}
return arr
}
func waitOp(config *Config, op *compute.Operation,
resource string, action string) (*compute.Operation, error) {
w := &OperationWaiter{
Service: config.clientCompute,
Op: op,
Region: config.Region,
Project: config.Project,
Type: OperationWaitRegion,
}
state := w.Conf()
state.Timeout = 2 * time.Minute
state.MinTimeout = 1 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return nil, fmt.Errorf("Error waiting for %s to %s: %s", resource, action, err)
}
return opRaw.(*compute.Operation), nil
}
// Healthchecks need to exist before being referred to from the target pool.
func convertHealthChecks(config *Config, names []string) ([]string, error) {
urls := make([]string, len(names))
for i, name := range names {
// Look up the healthcheck
res, err := config.clientCompute.HttpHealthChecks.Get(config.Project, name).Do()
if err != nil {
return nil, fmt.Errorf("Error reading HealthCheck: %s", err)
}
urls[i] = res.SelfLink
}
return urls, nil
}
// Instances do not need to exist yet, so we simply generate URLs.
// Instances can be full URLS or zone/name
func convertInstances(config *Config, names []string) ([]string, error) {
urls := make([]string, len(names))
for i, name := range names {
if strings.HasPrefix(name, "https://www.googleapis.com/compute/v1/") {
urls[i] = name
} else {
splitName := strings.Split(name, "/")
if len(splitName) != 2 {
return nil, fmt.Errorf("Invalid instance name, require URL or zone/name: %s", name)
} else {
urls[i] = fmt.Sprintf(
"https://www.googleapis.com/compute/v1/projects/%s/zones/%s/instances/%s",
config.Project, splitName[0], splitName[1])
}
}
}
return urls, nil
}
func resourceComputeTargetPoolCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
hchkUrls, err := convertHealthChecks(
config, convertStringArr(d.Get("health_checks").([]interface{})))
if err != nil {
return err
}
instanceUrls, err := convertInstances(
config, convertStringArr(d.Get("instances").([]interface{})))
if err != nil {
return err
}
// Build the parameter
tpool := &compute.TargetPool{
BackupPool: d.Get("backup_pool").(string),
Description: d.Get("description").(string),
HealthChecks: hchkUrls,
Instances: instanceUrls,
Name: d.Get("name").(string),
SessionAffinity: d.Get("session_affinity").(string),
}
if d.Get("failover_ratio") != nil {
tpool.FailoverRatio = d.Get("failover_ratio").(float64)
}
log.Printf("[DEBUG] TargetPool insert request: %#v", tpool)
op, err := config.clientCompute.TargetPools.Insert(
config.Project, config.Region, tpool).Do()
if err != nil {
return fmt.Errorf("Error creating TargetPool: %s", err)
}
// It probably maybe worked, so store the ID now
d.SetId(tpool.Name)
op, err = waitOp(config, op, "TargetPool", "create")
if err != nil {
return err
}
if op.Error != nil {
// The resource didn't actually create
d.SetId("")
// Return the error
return OperationError(*op.Error)
}
return resourceComputeTargetPoolRead(d, meta)
}
func calcAddRemove(from []string, to []string) ([]string, []string) {
add := make([]string, 0)
remove := make([]string, 0)
for _, u := range to {
found := false
for _, v := range from {
if u == v {
found = true
break
}
}
if !found {
add = append(add, u)
}
}
for _, u := range from {
found := false
for _, v := range to {
if u == v {
found = true
break
}
}
if !found {
remove = append(remove, u)
}
}
return add, remove
}
func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
d.Partial(true)
if d.HasChange("health_checks") {
from_, to_ := d.GetChange("health_checks")
from := convertStringArr(from_.([]interface{}))
to := convertStringArr(to_.([]interface{}))
fromUrls, err := convertHealthChecks(config, from)
if err != nil {
return err
}
toUrls, err := convertHealthChecks(config, to)
if err != nil {
return err
}
add, remove := calcAddRemove(fromUrls, toUrls)
removeReq := &compute.TargetPoolsRemoveHealthCheckRequest{
HealthChecks: make([]*compute.HealthCheckReference, len(remove)),
}
for i, v := range remove {
removeReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v}
}
op, err := config.clientCompute.TargetPools.RemoveHealthCheck(
config.Project, config.Region, d.Id(), removeReq).Do()
if err != nil {
return fmt.Errorf("Error updating health_check: %s", err)
}
op, err = waitOp(config, op, "TargetPool", "removing HealthChecks")
if err != nil {
return err
}
if op.Error != nil {
return OperationError(*op.Error)
}
addReq := &compute.TargetPoolsAddHealthCheckRequest{
HealthChecks: make([]*compute.HealthCheckReference, len(add)),
}
for i, v := range add {
addReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v}
}
op, err = config.clientCompute.TargetPools.AddHealthCheck(
config.Project, config.Region, d.Id(), addReq).Do()
if err != nil {
return fmt.Errorf("Error updating health_check: %s", err)
}
op, err = waitOp(config, op, "TargetPool", "adding HealthChecks")
if err != nil {
return err
}
if op.Error != nil {
return OperationError(*op.Error)
}
d.SetPartial("health_checks")
}
if d.HasChange("instances") {
from_, to_ := d.GetChange("instances")
from := convertStringArr(from_.([]interface{}))
to := convertStringArr(to_.([]interface{}))
fromUrls, err := convertInstances(config, from)
if err != nil {
return err
}
toUrls, err := convertInstances(config, to)
if err != nil {
return err
}
add, remove := calcAddRemove(fromUrls, toUrls)
addReq := &compute.TargetPoolsAddInstanceRequest{
Instances: make([]*compute.InstanceReference, len(add)),
}
for i, v := range add {
addReq.Instances[i] = &compute.InstanceReference{Instance: v}
}
op, err := config.clientCompute.TargetPools.AddInstance(
config.Project, config.Region, d.Id(), addReq).Do()
if err != nil {
return fmt.Errorf("Error updating instances: %s", err)
}
op, err = waitOp(config, op, "TargetPool", "adding instances")
if err != nil {
return err
}
if op.Error != nil {
return OperationError(*op.Error)
}
removeReq := &compute.TargetPoolsRemoveInstanceRequest{
Instances: make([]*compute.InstanceReference, len(remove)),
}
for i, v := range remove {
removeReq.Instances[i] = &compute.InstanceReference{Instance: v}
}
op, err = config.clientCompute.TargetPools.RemoveInstance(
config.Project, config.Region, d.Id(), removeReq).Do()
if err != nil {
return fmt.Errorf("Error updating instances: %s", err)
}
op, err = waitOp(config, op, "TargetPool", "removing instances")
if err != nil {
return err
}
if op.Error != nil {
return OperationError(*op.Error)
}
d.SetPartial("instances")
}
if d.HasChange("backup_pool") {
bpool_name := d.Get("backup_pool").(string)
tref := &compute.TargetReference{
Target: bpool_name,
}
op, err := config.clientCompute.TargetPools.SetBackup(
config.Project, config.Region, d.Id(), tref).Do()
if err != nil {
return fmt.Errorf("Error updating backup_pool: %s", err)
}
op, err = waitOp(config, op, "TargetPool", "updating backup_pool")
if err != nil {
return err
}
if op.Error != nil {
return OperationError(*op.Error)
}
d.SetPartial("backup_pool")
}
d.Partial(false)
return resourceComputeTargetPoolRead(d, meta)
}
func resourceComputeTargetPoolRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
tpool, err := config.clientCompute.TargetPools.Get(
config.Project, config.Region, 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 TargetPool: %s", err)
}
d.Set("self_link", tpool.SelfLink)
return nil
}
func resourceComputeTargetPoolDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
// Delete the TargetPool
op, err := config.clientCompute.TargetPools.Delete(
config.Project, config.Region, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting TargetPool: %s", err)
}
op, err = waitOp(config, op, "TargetPool", "delete")
if err != nil {
return err
}
if op.Error != nil {
return OperationError(*op.Error)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,80 @@
package google
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccComputeTargetPool_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeTargetPoolDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeTargetPool_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeTargetPoolExists(
"google_compute_target_pool.foobar"),
),
},
},
})
}
func testAccCheckComputeTargetPoolDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_target_pool" {
continue
}
_, err := config.clientCompute.TargetPools.Get(
config.Project, config.Region, rs.Primary.ID).Do()
if err == nil {
return fmt.Errorf("TargetPool still exists")
}
}
return nil
}
func testAccCheckComputeTargetPoolExists(n string) 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.TargetPools.Get(
config.Project, config.Region, rs.Primary.ID).Do()
if err != nil {
return err
}
if found.Name != rs.Primary.ID {
return fmt.Errorf("TargetPool not found")
}
return nil
}
}
const testAccComputeTargetPool_basic = `
resource "google_compute_target_pool" "foobar" {
description = "Resource created for Terraform acceptance testing"
instances = ["us-central1-a/foo", "us-central1-b/bar"]
name = "terraform-test"
session_affinity = "CLIENT_IP_PROTO"
}`

View File

@ -8,7 +8,10 @@ description: |-
# google\_compute\_address
Creates a static IP address resource for Google Compute Engine.
Creates a static IP address resource for Google Compute Engine. For more information see
[the official documentation](https://cloud.google.com/compute/docs/instances-and-network) and
[API](https://cloud.google.com/compute/docs/reference/latest/addresses).
## Example Usage
@ -31,3 +34,4 @@ The following attributes are exported:
* `name` - The name of the resource.
* `address` - The IP address that was allocated.
* `self_link` - The URI of the created resource.

View File

@ -0,0 +1,53 @@
---
layout: "google"
page_title: "Google: google_compute_forwarding_rule"
sidebar_current: "docs-google-resource-forwarding_rule"
description: |-
Manages a Target Pool within GCE.
---
# google\_compute\_forwarding\_rule
Manages a Forwarding Rule within GCE. This binds an ip and port range to a target pool. For more
information see [the official
documentation](https://cloud.google.com/compute/docs/load-balancing/network/forwarding-rules) and
[API](https://cloud.google.com/compute/docs/reference/latest/forwardingRules).
## Example Usage
```
resource "google_compute_forwarding_rule" "default" {
name = "test"
target = "${google_compute_target_pool.default.self_link}"
port_range = "80"
}
```
## Argument Reference
The following arguments are supported:
* `description` - (Optional) Textual description field.
* `ip_address` - (Optional) The static IP. (if not set, an ephemeral IP is
used).
* `ip_protocol` - (Optional) The IP protocol to route, one of "TCP" "UDP" "AH" "ESP" or "SCTP". (default "TCP").
* `name` - (Required) A unique name for the resource, required by GCE. Changing
this forces a new resource to be created.
* `port_range` - (Optional) A range e.g. "1024-2048" or a single port "1024"
(defaults to all ports!).
* `target` - URL of target pool.
## Attributes Reference
The following attributes are exported:
* `self_link` - The URL of the created resource.
* `ip_address` - The IP address that was chosen (or specified).

View File

@ -0,0 +1,57 @@
---
layout: "google"
page_title: "Google: google_compute_http_health_check"
sidebar_current: "docs-google-resource-http_health_check"
description: |-
Manages an HTTP Health Check within GCE.
---
# google\_compute\_http\_health\_check
Manages an HTTP health check within GCE. This is used to monitor instances
behind load balancers. Timeouts or HTTP errors cause the instance to be
removed from the pool. For more information, see [the official
documentation](https://cloud.google.com/compute/docs/load-balancing/health-checks)
and
[API](https://cloud.google.com/compute/docs/reference/latest/httpHealthChecks).
## Example Usage
```
resource "google_compute_http_health_check" "default" {
name = "test"
request_path = "/health_check"
check_interval_sec = 1
timeout_sec = 1
}
```
## Argument Reference
The following arguments are supported:
* `check_interval_sec` - (Optional) How often to poll each instance (default 5).
* `description` - (Optional) Textual description field.
* `healthy_threshold` - (Optional) Consecutive successes required (default 2).
* `host` - (Optional) HTTP host header field (default instance's public ip).
* `name` - (Required) A unique name for the resource, required by GCE.
Changing this forces a new resource to be created.
* `port` - (Optional) TCP port to connect to (default 80).
* `request_path` - (Optional) URL path to query (default /).
* `timeout_sec` - (Optional) How long before declaring failure (default 5).
* `unhealthy_threshold` - (Optional) Consecutive failures required (default 2).
## Attributes Reference
The following attributes are exported:
* `self_link` - The URL of the created resource.

View File

@ -8,7 +8,11 @@ description: |-
# google\_compute\_instance
Manages a VM instance resource within GCE.
Manages a VM instance resource within GCE. For more information see
[the official documentation](https://cloud.google.com/compute/docs/instances)
and
[API](https://cloud.google.com/compute/docs/reference/latest/instances).
## Example Usage

View File

@ -0,0 +1,58 @@
---
layout: "google"
page_title: "Google: google_compute_target_pool"
sidebar_current: "docs-google-resource-target_pool"
description: |-
Manages a Target Pool within GCE.
---
# google\_compute\_target\_pool
Manages a Target Pool within GCE. This is a collection of instances used as
target of a network load balancer (Forwarding Rule). For more information see
[the official
documentation](https://cloud.google.com/compute/docs/load-balancing/network/target-pools)
and [API](https://cloud.google.com/compute/docs/reference/latest/targetPools).
## Example Usage
```
resource "google_compute_target_pool" "default" {
name = "test"
instances = [ "us-central1-a/myinstance1", "us-central1-b/myinstance2" ]
health_checks = [ "${google_compute_http_health_check.default.name}" ]
}
```
## Argument Reference
The following arguments are supported:
* `backup_pool` - (Optional) URL to the backup target pool. Must also set
failover\_ratio.
* `description` - (Optional) Textual description field.
* `failover_ratio` - (Optional) Ratio (0 to 1) of failed nodes before using the
backup pool (which must also be set).
* `health_checks` - (Optional) List of zero or one healthcheck names.
* `instances` - (Optional) List of instances in the pool. They can be given as
URLs, or in the form of "zone/name". Note that the instances need not exist
at the time of target pool creation, so there is no need to use the Terraform
interpolators to create a dependency on the instances from the target pool.
* `name` - (Required) A unique name for the resource, required by GCE. Changing
this forces a new resource to be created.
* `session_affinity` - (Optional) How to distribute load. Options are "NONE" (no affinity). "CLIENT\_IP" (hash of the source/dest addresses / ports), and "CLIENT\_IP\_PROTO" also includes the protocol (default "NONE").
## Attributes Reference
The following attributes are exported:
* `self_link` - The URL of the created resource.