First set of changes to enable internal load balancing using beta apis

This commit is contained in:
Dana Hoffman 2016-11-18 09:08:26 -07:00
parent b84bad481b
commit 69ce33641d
11 changed files with 1552 additions and 27 deletions

View File

@ -7,6 +7,7 @@ import (
"time"
"github.com/hashicorp/terraform/helper/resource"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
)
@ -30,6 +31,15 @@ type ComputeOperationWaiter struct {
Zone string
}
type ComputeOperationWaiterBeta struct {
Service *computeBeta.Service
Op *computeBeta.Operation
Project string
Region string
Type ComputeOperationWaitType
Zone string
}
func (w *ComputeOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
var op *compute.Operation
@ -60,6 +70,36 @@ func (w *ComputeOperationWaiter) RefreshFunc() resource.StateRefreshFunc {
}
}
func (w *ComputeOperationWaiterBeta) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
var op *computeBeta.Operation
var err error
switch w.Type {
case ComputeOperationWaitGlobal:
op, err = w.Service.GlobalOperations.Get(
w.Project, w.Op.Name).Do()
case ComputeOperationWaitRegion:
op, err = w.Service.RegionOperations.Get(
w.Project, w.Region, w.Op.Name).Do()
case ComputeOperationWaitZone:
op, err = w.Service.ZoneOperations.Get(
w.Project, w.Zone, w.Op.Name).Do()
default:
return nil, "bad-type", fmt.Errorf(
"Invalid wait type: %#v", w.Type)
}
if err != nil {
return nil, "", err
}
log.Printf("[DEBUG] Got %q when asking for operation %q", op.Status, w.Op.Name)
return op, op.Status, nil
}
}
func (w *ComputeOperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING", "RUNNING"},
@ -68,9 +108,18 @@ func (w *ComputeOperationWaiter) Conf() *resource.StateChangeConf {
}
}
func (w *ComputeOperationWaiterBeta) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING", "RUNNING"},
Target: []string{"DONE"},
Refresh: w.RefreshFunc(),
}
}
// ComputeOperationError wraps compute.OperationError and implements the
// error interface so it can be returned.
type ComputeOperationError compute.OperationError
type ComputeOperationErrorBeta computeBeta.OperationError
func (e ComputeOperationError) Error() string {
var buf bytes.Buffer
@ -82,6 +131,16 @@ func (e ComputeOperationError) Error() string {
return buf.String()
}
func (e ComputeOperationErrorBeta) Error() string {
var buf bytes.Buffer
for _, err := range e.Errors {
buf.WriteString(err.Message + "\n")
}
return buf.String()
}
func computeOperationWaitGlobal(config *Config, op *compute.Operation, project string, activity string) error {
return computeOperationWaitGlobalTime(config, op, project, activity, 4)
}
@ -111,6 +170,31 @@ func computeOperationWaitGlobalTime(config *Config, op *compute.Operation, proje
return nil
}
func computeOperationWaitGlobalBeta(config *Config, op *computeBeta.Operation, project string, activity string) error {
w := &ComputeOperationWaiterBeta{
Service: config.clientComputeBeta,
Op: op,
Project: project,
Type: ComputeOperationWaitGlobal,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = 4 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
op = opRaw.(*computeBeta.Operation)
if op.Error != nil {
return ComputeOperationErrorBeta(*op.Error)
}
return nil
}
func computeOperationWaitRegion(config *Config, op *compute.Operation, project string, region, activity string) error {
w := &ComputeOperationWaiter{
Service: config.clientCompute,
@ -137,6 +221,32 @@ func computeOperationWaitRegion(config *Config, op *compute.Operation, project s
return nil
}
func computeOperationWaitRegionBeta(config *Config, op *computeBeta.Operation, project string, region, activity string) error {
w := &ComputeOperationWaiterBeta{
Service: config.clientComputeBeta,
Op: op,
Project: project,
Type: ComputeOperationWaitRegion,
Region: region,
}
state := w.Conf()
state.Delay = 10 * time.Second
state.Timeout = 4 * time.Minute
state.MinTimeout = 2 * time.Second
opRaw, err := state.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for %s: %s", activity, err)
}
op = opRaw.(*computeBeta.Operation)
if op.Error != nil {
return ComputeOperationErrorBeta(*op.Error)
}
return nil
}
func computeOperationWaitZone(config *Config, op *compute.Operation, project string, zone, activity string) error {
return computeOperationWaitZoneTime(config, op, project, zone, 4, activity)
}

View File

@ -14,6 +14,7 @@ import (
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/jwt"
"google.golang.org/api/cloudresourcemanager/v1"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
"google.golang.org/api/container/v1"
"google.golang.org/api/dns/v1"
@ -31,6 +32,7 @@ type Config struct {
Region string
clientCompute *compute.Service
clientComputeBeta *computeBeta.Service
clientContainer *container.Service
clientDns *dns.Service
clientPubsub *pubsub.Service
@ -102,6 +104,13 @@ func (c *Config) loadAndValidate() error {
}
c.clientCompute.UserAgent = userAgent
log.Printf("[INFO] Instantiating GCE beta client...")
c.clientComputeBeta, err = computeBeta.New(client)
if err != nil {
return err
}
c.clientComputeBeta.UserAgent = userAgent
log.Printf("[INFO] Instantiating GKE client...")
c.clientContainer, err = container.New(client)
if err != nil {

View File

@ -69,6 +69,7 @@ func Provider() terraform.ResourceProvider {
"google_compute_forwarding_rule": resourceComputeForwardingRule(),
"google_compute_global_address": resourceComputeGlobalAddress(),
"google_compute_global_forwarding_rule": resourceComputeGlobalForwardingRule(),
"google_compute_health_check": resourceComputeHealthCheck(),
"google_compute_http_health_check": resourceComputeHttpHealthCheck(),
"google_compute_https_health_check": resourceComputeHttpsHealthCheck(),
"google_compute_image": resourceComputeImage(),
@ -78,6 +79,7 @@ func Provider() terraform.ResourceProvider {
"google_compute_instance_template": resourceComputeInstanceTemplate(),
"google_compute_network": resourceComputeNetwork(),
"google_compute_project_metadata": resourceComputeProjectMetadata(),
"google_compute_region_backend_service": resourceComputeRegionBackendService(),
"google_compute_route": resourceComputeRoute(),
"google_compute_ssl_certificate": resourceComputeSslCertificate(),
"google_compute_subnetwork": resourceComputeSubnetwork(),

View File

@ -4,11 +4,12 @@ import (
"bytes"
"fmt"
"log"
"os"
"regexp"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1"
"google.golang.org/api/compute/v0.beta"
"google.golang.org/api/googleapi"
)
@ -99,6 +100,12 @@ func resourceComputeBackendService() *schema.Resource {
Computed: true,
},
"load_balancing_scheme": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"port_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
@ -185,13 +192,22 @@ func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{
service.EnableCDN = v.(bool)
}
if v, ok := d.GetOk("load_balancing_scheme"); ok {
service.LoadBalancingScheme = v.(string)
}
if v, ok := d.GetOk("region"); ok {
service.Region = v.(string)
}
project, err := getProject(d, config)
if err != nil {
return err
}
fmt.Fprintf(os.Stderr, "[DEBUG] Creating new Backend Service: %#v", service)
log.Printf("[DEBUG] Creating new Backend Service: %#v", service)
op, err := config.clientCompute.BackendServices.Insert(
op, err := config.clientComputeBeta.BackendServices.Insert(
project, &service).Do()
if err != nil {
return fmt.Errorf("Error creating backend service: %s", err)
@ -201,7 +217,7 @@ func resourceComputeBackendServiceCreate(d *schema.ResourceData, meta interface{
d.SetId(service.Name)
err = computeOperationWaitGlobal(config, op, project, "Creating Backend Service")
err = computeOperationWaitGlobalBeta(config, op, project, "Creating Backend Service")
if err != nil {
return err
}
@ -217,7 +233,7 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{})
return err
}
service, err := config.clientCompute.BackendServices.Get(
service, err := config.clientComputeBeta.BackendServices.Get(
project, d.Id()).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
@ -238,6 +254,7 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{})
d.Set("session_affinity", service.SessionAffinity)
d.Set("timeout_sec", service.TimeoutSec)
d.Set("fingerprint", service.Fingerprint)
d.Set("load_balancing_scheme", service.LoadBalancingScheme)
d.Set("self_link", service.SelfLink)
d.Set("backend", flattenBackends(service.Backends))
@ -287,12 +304,16 @@ func resourceComputeBackendServiceUpdate(d *schema.ResourceData, meta interface{
service.SessionAffinity = d.Get("session_affinity").(string)
}
if v, ok := d.GetOk("load_balancing_scheme"); ok {
service.LoadBalancingScheme = v.(string)
}
if d.HasChange("enable_cdn") {
service.EnableCDN = d.Get("enable_cdn").(bool)
}
log.Printf("[DEBUG] Updating existing Backend Service %q: %#v", d.Id(), service)
op, err := config.clientCompute.BackendServices.Update(
op, err := config.clientComputeBeta.BackendServices.Update(
project, d.Id(), &service).Do()
if err != nil {
return fmt.Errorf("Error updating backend service: %s", err)
@ -300,7 +321,7 @@ func resourceComputeBackendServiceUpdate(d *schema.ResourceData, meta interface{
d.SetId(service.Name)
err = computeOperationWaitGlobal(config, op, project, "Updating Backend Service")
err = computeOperationWaitGlobalBeta(config, op, project, "Updating Backend Service")
if err != nil {
return err
}
@ -317,13 +338,13 @@ func resourceComputeBackendServiceDelete(d *schema.ResourceData, meta interface{
}
log.Printf("[DEBUG] Deleting backend service %s", d.Id())
op, err := config.clientCompute.BackendServices.Delete(
op, err := config.clientComputeBeta.BackendServices.Delete(
project, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting backend service: %s", err)
}
err = computeOperationWaitGlobal(config, op, project, "Deleting Backend Service")
err = computeOperationWaitGlobalBeta(config, op, project, "Deleting Backend Service")
if err != nil {
return err
}

View File

@ -7,7 +7,7 @@ import (
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"google.golang.org/api/compute/v1"
"google.golang.org/api/compute/v0.beta"
)
func TestAccComputeBackendService_basic(t *testing.T) {
@ -122,7 +122,7 @@ func testAccCheckComputeBackendServiceDestroy(s *terraform.State) error {
continue
}
_, err := config.clientCompute.BackendServices.Get(
_, err := config.clientComputeBeta.BackendServices.Get(
config.Project, rs.Primary.ID).Do()
if err == nil {
return fmt.Errorf("Backend service still exists")
@ -145,7 +145,7 @@ func testAccCheckComputeBackendServiceExists(n string, svc *compute.BackendServi
config := testAccProvider.Meta().(*Config)
found, err := config.clientCompute.BackendServices.Get(
found, err := config.clientComputeBeta.BackendServices.Get(
config.Project, rs.Primary.ID).Do()
if err != nil {
return err
@ -221,11 +221,39 @@ func TestAccComputeBackendService_withSessionAffinity(t *testing.T) {
}
}
func TestAccComputeBackendService_withInternalLoadBalancing(t *testing.T) {
serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
var svc compute.BackendService
// config := testAccProvider.Meta().(*Config)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeBackendServiceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeBackendService_withInternalLoadBalancing(
serviceName, checkName, "us-central1"),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeBackendServiceExists(
"google_compute_backend_service.foobar", &svc),
),
},
},
})
if svc.LoadBalancingScheme != "INTERNAL" {
t.Errorf("Expected LoadBalancingScheme == INTERNAL, got %q", svc.EnableCDN)
}
}
func testAccComputeBackendService_basic(serviceName, checkName string) string {
return fmt.Sprintf(`
resource "google_compute_backend_service" "foobar" {
name = "%s"
health_checks = ["${google_compute_http_health_check.zero.self_link}"]
health_checks = ["${google_compute_http_health_check.zero.name}"]
}
resource "google_compute_http_health_check" "zero" {
@ -254,6 +282,25 @@ resource "google_compute_http_health_check" "zero" {
`, serviceName, checkName)
}
func testAccComputeBackendService_withInternalLoadBalancing(serviceName, checkName, region string) string {
return fmt.Sprintf(`
resource "google_compute_backend_service" "foobar" {
name = "%s"
health_checks = ["${google_compute_http_health_check.zero.self_link}"]
load_balancing_scheme = "INTERNAL"
region = "%s"
}
resource "google_compute_http_health_check" "zero" {
name = "%s"
request_path = "/"
check_interval_sec = 1
timeout_sec = 1
}
`, serviceName, region, checkName)
}
func testAccComputeBackendService_basicModified(serviceName, checkOne, checkTwo string) string {
return fmt.Sprintf(`
resource "google_compute_backend_service" "foobar" {

View File

@ -5,7 +5,7 @@ import (
"log"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1"
"google.golang.org/api/compute/v0.beta"
"google.golang.org/api/googleapi"
)
@ -28,10 +28,16 @@ func resourceComputeForwardingRule() *schema.Resource {
"target": &schema.Schema{
Type: schema.TypeString,
Required: true,
Optional: true,
ForceNew: false,
},
"backend_service": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
@ -52,6 +58,19 @@ func resourceComputeForwardingRule() *schema.Resource {
Computed: true,
},
"load_balancing_scheme": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "EXTERNAL",
},
"network": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"port_range": &schema.Schema{
Type: schema.TypeString,
Optional: true,
@ -76,6 +95,12 @@ func resourceComputeForwardingRule() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"subnetwork": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
@ -94,16 +119,20 @@ func resourceComputeForwardingRuleCreate(d *schema.ResourceData, meta interface{
}
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),
BackendService: d.Get("backend_service").(string),
IPAddress: d.Get("ip_address").(string),
IPProtocol: d.Get("ip_protocol").(string),
Description: d.Get("description").(string),
LoadBalancingScheme: d.Get("load_balancing_scheme").(string),
Name: d.Get("name").(string),
Network: d.Get("network").(string),
PortRange: d.Get("port_range").(string),
Subnetwork: d.Get("subnetwork").(string),
Target: d.Get("target").(string),
}
log.Printf("[DEBUG] ForwardingRule insert request: %#v", frule)
op, err := config.clientCompute.ForwardingRules.Insert(
op, err := config.clientComputeBeta.ForwardingRules.Insert(
project, region, frule).Do()
if err != nil {
return fmt.Errorf("Error creating ForwardingRule: %s", err)
@ -112,7 +141,7 @@ func resourceComputeForwardingRuleCreate(d *schema.ResourceData, meta interface{
// It probably maybe worked, so store the ID now
d.SetId(frule.Name)
err = computeOperationWaitRegion(config, op, project, region, "Creating Fowarding Rule")
err = computeOperationWaitRegionBeta(config, op, project, region, "Creating Fowarding Rule")
if err != nil {
return err
}
@ -138,13 +167,13 @@ func resourceComputeForwardingRuleUpdate(d *schema.ResourceData, meta interface{
if d.HasChange("target") {
target_name := d.Get("target").(string)
target_ref := &compute.TargetReference{Target: target_name}
op, err := config.clientCompute.ForwardingRules.SetTarget(
op, err := config.clientComputeBeta.ForwardingRules.SetTarget(
project, region, d.Id(), target_ref).Do()
if err != nil {
return fmt.Errorf("Error updating target: %s", err)
}
err = computeOperationWaitRegion(config, op, project, region, "Updating Forwarding Rule")
err = computeOperationWaitRegionBeta(config, op, project, region, "Updating Forwarding Rule")
if err != nil {
return err
}
@ -170,7 +199,7 @@ func resourceComputeForwardingRuleRead(d *schema.ResourceData, meta interface{})
return err
}
frule, err := config.clientCompute.ForwardingRules.Get(
frule, err := config.clientComputeBeta.ForwardingRules.Get(
project, region, d.Id()).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
@ -186,10 +215,14 @@ func resourceComputeForwardingRuleRead(d *schema.ResourceData, meta interface{})
d.Set("name", frule.Name)
d.Set("target", frule.Target)
d.Set("backend_service", frule.BackendService)
d.Set("description", frule.Description)
d.Set("load_balancing_scheme", frule.LoadBalancingScheme)
d.Set("network", frule.Network)
d.Set("port_range", frule.PortRange)
d.Set("project", project)
d.Set("region", region)
d.Set("subnetwork", frule.Subnetwork)
d.Set("ip_address", frule.IPAddress)
d.Set("ip_protocol", frule.IPProtocol)
d.Set("self_link", frule.SelfLink)
@ -211,13 +244,13 @@ func resourceComputeForwardingRuleDelete(d *schema.ResourceData, meta interface{
// Delete the ForwardingRule
log.Printf("[DEBUG] ForwardingRule delete request")
op, err := config.clientCompute.ForwardingRules.Delete(
op, err := config.clientComputeBeta.ForwardingRules.Delete(
project, region, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting ForwardingRule: %s", err)
}
err = computeOperationWaitRegion(config, op, project, region, "Deleting Forwarding Rule")
err = computeOperationWaitRegionBeta(config, op, project, region, "Deleting Forwarding Rule")
if err != nil {
return err
}

View File

@ -50,6 +50,27 @@ func TestAccComputeForwardingRule_ip(t *testing.T) {
})
}
func TestAccComputeForwardingRule_internalLoadBalancing(t *testing.T) {
serviceName := fmt.Sprintf("tf-%s", acctest.RandString(10))
checkName := fmt.Sprintf("tf-%s", acctest.RandString(10))
ruleName := fmt.Sprintf("tf-%s", acctest.RandString(10))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeForwardingRuleDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeForwardingRule_internalLoadBalancing(serviceName, checkName, ruleName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeForwardingRuleExists(
"google_compute_forwarding_rule.foobar"),
),
},
},
})
}
func testAccCheckComputeForwardingRuleDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
@ -132,3 +153,30 @@ resource "google_compute_forwarding_rule" "foobar" {
}
`, addrName, poolName, ruleName)
}
func testAccComputeForwardingRule_internalLoadBalancing(serviceName, checkName, ruleName string) string {
return fmt.Sprintf(`
resource "google_compute_region_backend_service" "foobar-bs" {
name = "%s"
description = "Resource created for Terraform acceptance testing"
health_checks = ["${google_compute_health_check.zero.self_link}"]
load_balancing_scheme = "INTERNAL"
}
resource "google_compute_health_check" "zero" {
name = "%s"
description = "Resource created for Terraform acceptance testing"
check_interval_sec = 1
timeout_sec = 1
tcp_health_check {
port = "80"
}
}
resource "google_compute_forwarding_rule" "foobar" {
description = "Resource created for Terraform acceptance testing"
name = "%s"
load_balancing_scheme = "INTERNAL"
backend_service = "${google_compute_region_backend_service.foobar-bs.self_link}"
}
`, serviceName, checkName, ruleName)
}

View File

@ -0,0 +1,310 @@
package google
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
)
func resourceComputeHealthCheck() *schema.Resource {
return &schema.Resource{
Create: resourceComputeHealthCheckCreate,
Read: resourceComputeHealthCheckRead,
Delete: resourceComputeHealthCheckDelete,
Update: resourceComputeHealthCheckUpdate,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"check_interval_sec": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 5,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"healthy_threshold": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 2,
},
"type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "TCP",
},
"tcp_health_check": &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"port": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 80,
},
"port_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"proxy_header": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "NONE",
},
"request": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"response": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
},
},
"project": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
"self_link": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"timeout_sec": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 5,
},
"unhealthy_threshold": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 2,
},
},
}
}
func resourceComputeHealthCheckCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
// Build the parameter
hchk := &compute.HealthCheck{
Name: d.Get("name").(string),
}
// Optional things
if v, ok := d.GetOk("description"); ok {
hchk.Description = v.(string)
}
if v, ok := d.GetOk("check_interval_sec"); ok {
hchk.CheckIntervalSec = int64(v.(int))
}
if v, ok := d.GetOk("healthy_threshold"); ok {
hchk.HealthyThreshold = int64(v.(int))
}
if v, ok := d.GetOk("timeout_sec"); ok {
hchk.TimeoutSec = int64(v.(int))
}
if v, ok := d.GetOk("unhealthy_threshold"); ok {
hchk.UnhealthyThreshold = int64(v.(int))
}
if v, ok := d.GetOk("type"); ok {
hchk.Type = v.(string)
}
if v, ok := d.GetOk("tcp_health_check"); ok {
// check that type is tcp?
tcpcheck := v.([]interface{})[0].(map[string]interface{})
tcpHealthCheck := &compute.TCPHealthCheck{}
if val, ok := tcpcheck["port"]; ok {
tcpHealthCheck.Port = int64(val.(int))
}
if val, ok := tcpcheck["port_name"]; ok {
tcpHealthCheck.PortName = val.(string)
}
if val, ok := tcpcheck["proxy_header"]; ok {
tcpHealthCheck.ProxyHeader = val.(string)
}
if val, ok := tcpcheck["request"]; ok {
tcpHealthCheck.Request = val.(string)
}
if val, ok := tcpcheck["response"]; ok {
tcpHealthCheck.Response = val.(string)
}
hchk.TcpHealthCheck = tcpHealthCheck
}
log.Printf("[DEBUG] HealthCheck insert request: %#v", hchk)
op, err := config.clientCompute.HealthChecks.Insert(
project, hchk).Do()
if err != nil {
return fmt.Errorf("Error creating HealthCheck: %s", err)
}
// It probably maybe worked, so store the ID now
d.SetId(hchk.Name)
err = computeOperationWaitGlobal(config, op, project, "Creating Health Check")
if err != nil {
return err
}
return resourceComputeHealthCheckRead(d, meta)
}
func resourceComputeHealthCheckUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
// Build the parameter
hchk := &compute.HealthCheck{
Name: d.Get("name").(string),
}
// Optional things
if v, ok := d.GetOk("description"); ok {
hchk.Description = v.(string)
}
if v, ok := d.GetOk("check_interval_sec"); ok {
hchk.CheckIntervalSec = int64(v.(int))
}
if v, ok := d.GetOk("healthy_threshold"); ok {
hchk.HealthyThreshold = int64(v.(int))
}
if v, ok := d.GetOk("timeout_sec"); ok {
hchk.TimeoutSec = int64(v.(int))
}
if v, ok := d.GetOk("unhealthy_threshold"); ok {
hchk.UnhealthyThreshold = int64(v.(int))
}
if v, ok := d.GetOk("type"); ok {
hchk.Type = v.(string)
}
if v, ok := d.GetOk("tcp_health_check"); ok {
// check that type is tcp?
tcpcheck := v.([]interface{})[0].(map[string]interface{})
var tcpHealthCheck *compute.TCPHealthCheck
if val, ok := tcpcheck["port"]; ok {
tcpHealthCheck.Port = int64(val.(int))
}
if val, ok := tcpcheck["port_name"]; ok {
tcpHealthCheck.PortName = val.(string)
}
if val, ok := tcpcheck["proxy_header"]; ok {
tcpHealthCheck.ProxyHeader = val.(string)
}
if val, ok := tcpcheck["request"]; ok {
tcpHealthCheck.Request = val.(string)
}
if val, ok := tcpcheck["response"]; ok {
tcpHealthCheck.Response = val.(string)
}
hchk.TcpHealthCheck = tcpHealthCheck
}
log.Printf("[DEBUG] HealthCheck patch request: %#v", hchk)
op, err := config.clientCompute.HealthChecks.Patch(
project, hchk.Name, hchk).Do()
if err != nil {
return fmt.Errorf("Error patching HealthCheck: %s", err)
}
// It probably maybe worked, so store the ID now
d.SetId(hchk.Name)
err = computeOperationWaitGlobal(config, op, project, "Updating Health Check")
if err != nil {
return err
}
return resourceComputeHealthCheckRead(d, meta)
}
func resourceComputeHealthCheckRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
hchk, err := config.clientCompute.HealthChecks.Get(
project, d.Id()).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
// The resource doesn't exist anymore
log.Printf("[WARN] Removing Health Check %q because it's gone", d.Get("name").(string))
d.SetId("")
return nil
}
return fmt.Errorf("Error reading HealthCheck: %s", err)
}
d.Set("check_interval_sec", hchk.CheckIntervalSec)
d.Set("healthy_threshold", hchk.HealthyThreshold)
d.Set("timeout_sec", hchk.TimeoutSec)
d.Set("unhealthy_threshold", hchk.UnhealthyThreshold)
d.Set("type", hchk.Type)
d.Set("tcp_health_check", hchk.TcpHealthCheck)
d.Set("self_link", hchk.SelfLink)
d.Set("name", hchk.Name)
d.Set("description", hchk.Description)
d.Set("project", project)
return nil
}
func resourceComputeHealthCheckDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
// Delete the HealthCheck
op, err := config.clientCompute.HealthChecks.Delete(
project, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting HealthCheck: %s", err)
}
err = computeOperationWaitGlobal(config, op, project, "Deleting Health Check")
if err != nil {
return err
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,156 @@
package google
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"google.golang.org/api/compute/v1"
)
func TestAccComputeHealthCheck_basic(t *testing.T) {
var healthCheck compute.HealthCheck
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeHealthCheckDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeHealthCheck_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeHealthCheckExists(
"google_compute_health_check.foobar", &healthCheck),
testAccCheckComputeHealthCheckThresholds(
3, 3, &healthCheck),
),
},
},
})
}
func TestAccComputeHealthCheck_update(t *testing.T) {
var healthCheck compute.HealthCheck
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeHealthCheckDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeHealthCheck_update1,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeHealthCheckExists(
"google_compute_health_check.foobar", &healthCheck),
testAccCheckComputeHealthCheckThresholds(
2, 2, &healthCheck),
),
},
resource.TestStep{
Config: testAccComputeHealthCheck_update2,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeHealthCheckExists(
"google_compute_health_check.foobar", &healthCheck),
testAccCheckComputeHealthCheckThresholds(
10, 10, &healthCheck),
),
},
},
})
}
func testAccCheckComputeHealthCheckDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_health_check" {
continue
}
_, err := config.clientCompute.HealthChecks.Get(
config.Project, rs.Primary.ID).Do()
if err == nil {
return fmt.Errorf("HealthCheck still exists")
}
}
return nil
}
func testAccCheckComputeHealthCheckExists(n string, healthCheck *compute.HealthCheck) 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.HealthChecks.Get(
config.Project, rs.Primary.ID).Do()
if err != nil {
return err
}
if found.Name != rs.Primary.ID {
return fmt.Errorf("HealthCheck not found")
}
*healthCheck = *found
return nil
}
}
func testAccCheckComputeHealthCheckThresholds(healthy, unhealthy int64, healthCheck *compute.HealthCheck) resource.TestCheckFunc {
return func(s *terraform.State) error {
if healthCheck.HealthyThreshold != healthy {
return fmt.Errorf("HealthyThreshold doesn't match: expected %d, got %d", healthy, healthCheck.HealthyThreshold)
}
if healthCheck.UnhealthyThreshold != unhealthy {
return fmt.Errorf("UnhealthyThreshold doesn't match: expected %d, got %d", unhealthy, healthCheck.UnhealthyThreshold)
}
return nil
}
}
var testAccComputeHealthCheck_basic = fmt.Sprintf(`
resource "google_compute_health_check" "foobar" {
check_interval_sec = 3
description = "Resource created for Terraform acceptance testing"
healthy_threshold = 3
name = "health-test-%s"
timeout_sec = 2
unhealthy_threshold = 3
tcp_health_check {
port = "80"
}
}
`, acctest.RandString(10))
var testAccComputeHealthCheck_update1 = fmt.Sprintf(`
resource "google_compute_health_check" "foobar" {
name = "Health-test-%s"
description = "Resource created for Terraform acceptance testing"
request_path = "/not_default"
}
`, acctest.RandString(10))
/* Change description, restore request_path to default, and change
* thresholds from defaults */
var testAccComputeHealthCheck_update2 = fmt.Sprintf(`
resource "google_compute_health_check" "foobar" {
name = "Health-test-%s"
description = "Resource updated for Terraform acceptance testing"
healthy_threshold = 10
unhealthy_threshold = 10
}
`, acctest.RandString(10))

View File

@ -0,0 +1,443 @@
package google
import (
"bytes"
"fmt"
"log"
"os"
"regexp"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v0.beta"
"google.golang.org/api/googleapi"
)
func resourceComputeRegionBackendService() *schema.Resource {
return &schema.Resource{
Create: resourceComputeRegionBackendServiceCreate,
Read: resourceComputeRegionBackendServiceRead,
Update: resourceComputeRegionBackendServiceUpdate,
Delete: resourceComputeRegionBackendServiceDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
re := `^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$`
if !regexp.MustCompile(re).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q (%q) doesn't match regexp %q", k, value, re))
}
return
},
},
"health_checks": &schema.Schema{
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Required: true,
Set: schema.HashString,
},
"backend": &schema.Schema{
Type: schema.TypeSet,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"group": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"balancing_mode": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "UTILIZATION",
},
"capacity_scaler": &schema.Schema{
Type: schema.TypeFloat,
Optional: true,
Default: 1,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"max_rate": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"max_rate_per_instance": &schema.Schema{
Type: schema.TypeFloat,
Optional: true,
},
"max_utilization": &schema.Schema{
Type: schema.TypeFloat,
Optional: true,
Default: 0.8,
},
},
},
Optional: true,
Set: resourceGoogleComputeRegionBackendServiceBackendHash,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"enable_cdn": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"fingerprint": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"load_balancing_scheme": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"port_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"project": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"protocol": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"region": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"self_link": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"timeout_sec": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
},
}
}
func resourceComputeRegionBackendServiceCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
hc := d.Get("health_checks").(*schema.Set).List()
healthChecks := make([]string, 0, len(hc))
for _, v := range hc {
healthChecks = append(healthChecks, v.(string))
}
service := compute.BackendService{
Name: d.Get("name").(string),
HealthChecks: healthChecks,
}
if v, ok := d.GetOk("backend"); ok {
service.Backends = expandBackends(v.(*schema.Set).List())
}
if v, ok := d.GetOk("description"); ok {
service.Description = v.(string)
}
if v, ok := d.GetOk("port_name"); ok {
service.PortName = v.(string)
}
if v, ok := d.GetOk("protocol"); ok {
service.Protocol = v.(string)
}
if v, ok := d.GetOk("timeout_sec"); ok {
service.TimeoutSec = int64(v.(int))
}
if v, ok := d.GetOk("enable_cdn"); ok {
service.EnableCDN = v.(bool)
}
if v, ok := d.GetOk("load_balancing_scheme"); ok {
service.LoadBalancingScheme = v.(string)
}
project, err := getProject(d, config)
if err != nil {
return err
}
region, err := getRegion(d, config)
if err != nil {
return err
}
fmt.Fprintf(os.Stderr, "[DEBUG] Creating new Region Backend Service: %#v", service) // DO NOT SUBMIT
log.Printf("[DEBUG] Creating new Region Backend Service: %#v", service)
op, err := config.clientComputeBeta.RegionBackendServices.Insert(
project, region, &service).Do()
if err != nil {
return fmt.Errorf("Error creating backend service: %s", err)
}
log.Printf("[DEBUG] Waiting for new backend service, operation: %#v", op)
d.SetId(service.Name)
err = computeOperationWaitGlobalBeta(config, op, project, "Creating Backend Service")
if err != nil {
return err
}
return resourceComputeRegionBackendServiceRead(d, meta)
}
func resourceComputeRegionBackendServiceRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
region, err := getRegion(d, config)
if err != nil {
return err
}
service, err := config.clientComputeBeta.RegionBackendServices.Get(
project, region, d.Id()).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
// The resource doesn't exist anymore
log.Printf("[WARN] Removing Backend Service %q because it's gone", d.Get("name").(string))
d.SetId("")
return nil
}
return fmt.Errorf("Error reading service: %s", err)
}
d.Set("description", service.Description)
d.Set("enable_cdn", service.EnableCDN)
d.Set("port_name", service.PortName)
d.Set("protocol", service.Protocol)
d.Set("timeout_sec", service.TimeoutSec)
d.Set("fingerprint", service.Fingerprint)
d.Set("load_balancing_scheme", service.LoadBalancingScheme)
d.Set("self_link", service.SelfLink)
d.Set("backend", flattenBackends(service.Backends))
d.Set("health_checks", service.HealthChecks)
return nil
}
func resourceComputeRegionBackendServiceUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
region, err := getRegion(d, config)
if err != nil {
return err
}
hc := d.Get("health_checks").(*schema.Set).List()
healthChecks := make([]string, 0, len(hc))
for _, v := range hc {
healthChecks = append(healthChecks, v.(string))
}
service := compute.BackendService{
Name: d.Get("name").(string),
Fingerprint: d.Get("fingerprint").(string),
HealthChecks: healthChecks,
}
// Optional things
if v, ok := d.GetOk("backend"); ok {
service.Backends = expandBackends(v.(*schema.Set).List())
}
if v, ok := d.GetOk("description"); ok {
service.Description = v.(string)
}
if v, ok := d.GetOk("port_name"); ok {
service.PortName = v.(string)
}
if v, ok := d.GetOk("protocol"); ok {
service.Protocol = v.(string)
}
if v, ok := d.GetOk("timeout_sec"); ok {
service.TimeoutSec = int64(v.(int))
}
if v, ok := d.GetOk("load_balancing_scheme"); ok {
service.LoadBalancingScheme = v.(string)
}
if d.HasChange("enable_cdn") {
service.EnableCDN = d.Get("enable_cdn").(bool)
}
log.Printf("[DEBUG] Updating existing Backend Service %q: %#v", d.Id(), service)
op, err := config.clientComputeBeta.RegionBackendServices.Update(
project, region, d.Id(), &service).Do()
if err != nil {
return fmt.Errorf("Error updating backend service: %s", err)
}
d.SetId(service.Name)
err = computeOperationWaitGlobalBeta(config, op, project, "Updating Backend Service")
if err != nil {
return err
}
return resourceComputeRegionBackendServiceRead(d, meta)
}
func resourceComputeRegionBackendServiceDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
region, err := getRegion(d, config)
if err != nil {
return err
}
log.Printf("[DEBUG] Deleting backend service %s", d.Id())
op, err := config.clientComputeBeta.RegionBackendServices.Delete(
project, region, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting backend service: %s", err)
}
err = computeOperationWaitGlobalBeta(config, op, project, "Deleting Backend Service")
if err != nil {
return err
}
d.SetId("")
return nil
}
// func expandBackends(configured []interface{}) []*compute.Backend {
// backends := make([]*compute.Backend, 0, len(configured))
// for _, raw := range configured {
// data := raw.(map[string]interface{})
// b := compute.Backend{
// Group: data["group"].(string),
// }
// if v, ok := data["balancing_mode"]; ok {
// b.BalancingMode = v.(string)
// }
// if v, ok := data["capacity_scaler"]; ok {
// b.CapacityScaler = v.(float64)
// }
// if v, ok := data["description"]; ok {
// b.Description = v.(string)
// }
// if v, ok := data["max_rate"]; ok {
// b.MaxRate = int64(v.(int))
// }
// if v, ok := data["max_rate_per_instance"]; ok {
// b.MaxRatePerInstance = v.(float64)
// }
// if v, ok := data["max_utilization"]; ok {
// b.MaxUtilization = v.(float64)
// }
// backends = append(backends, &b)
// }
// return backends
// }
// func flattenBackends(backends []*compute.Backend) []map[string]interface{} {
// result := make([]map[string]interface{}, 0, len(backends))
// for _, b := range backends {
// data := make(map[string]interface{})
// data["balancing_mode"] = b.BalancingMode
// data["capacity_scaler"] = b.CapacityScaler
// data["description"] = b.Description
// data["group"] = b.Group
// data["max_rate"] = b.MaxRate
// data["max_rate_per_instance"] = b.MaxRatePerInstance
// data["max_utilization"] = b.MaxUtilization
// result = append(result, data)
// }
// return result
// }
func resourceGoogleComputeRegionBackendServiceBackendHash(v interface{}) int {
if v == nil {
return 0
}
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%s-", m["group"].(string)))
if v, ok := m["balancing_mode"]; ok {
buf.WriteString(fmt.Sprintf("%s-", v.(string)))
}
if v, ok := m["capacity_scaler"]; ok {
buf.WriteString(fmt.Sprintf("%f-", v.(float64)))
}
if v, ok := m["description"]; ok {
buf.WriteString(fmt.Sprintf("%s-", v.(string)))
}
if v, ok := m["max_rate"]; ok {
buf.WriteString(fmt.Sprintf("%d-", int64(v.(int))))
}
if v, ok := m["max_rate_per_instance"]; ok {
buf.WriteString(fmt.Sprintf("%f-", v.(float64)))
}
if v, ok := m["max_rate_per_instance"]; ok {
buf.WriteString(fmt.Sprintf("%f-", v.(float64)))
}
return hashcode.String(buf.String())
}

View File

@ -0,0 +1,346 @@
package google
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"google.golang.org/api/compute/v0.beta"
)
func TestAccComputeRegionBackendService_basic(t *testing.T) {
serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
extraCheckName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
var svc compute.BackendService
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeRegionBackendServiceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRegionBackendService_basic(serviceName, checkName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeRegionBackendServiceExists(
"google_compute_region_backend_service.foobar", &svc),
),
},
resource.TestStep{
Config: testAccComputeRegionBackendService_basicModified(
serviceName, checkName, extraCheckName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeRegionBackendServiceExists(
"google_compute_region_backend_service.foobar", &svc),
),
},
},
})
}
func TestAccComputeRegionBackendService_withBackend(t *testing.T) {
serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
igName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
itName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
var svc compute.BackendService
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeRegionBackendServiceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRegionBackendService_withBackend(
serviceName, igName, itName, checkName, 10),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeRegionBackendServiceExists(
"google_compute_region_backend_service.lipsum", &svc),
),
},
},
})
if svc.TimeoutSec != 10 {
t.Errorf("Expected TimeoutSec == 10, got %d", svc.TimeoutSec)
}
if svc.Protocol != "HTTP" {
t.Errorf("Expected Protocol to be HTTP, got %q", svc.Protocol)
}
if len(svc.Backends) != 1 {
t.Errorf("Expected 1 backend, got %d", len(svc.Backends))
}
}
func TestAccComputeRegionBackendService_withBackendAndUpdate(t *testing.T) {
serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
igName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
itName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
var svc compute.BackendService
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeRegionBackendServiceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRegionBackendService_withBackend(
serviceName, igName, itName, checkName, 10),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeRegionBackendServiceExists(
"google_compute_region_backend_service.lipsum", &svc),
),
},
resource.TestStep{
Config: testAccComputeRegionBackendService_withBackend(
serviceName, igName, itName, checkName, 20),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeRegionBackendServiceExists(
"google_compute_region_backend_service.lipsum", &svc),
),
},
},
})
if svc.TimeoutSec != 20 {
t.Errorf("Expected TimeoutSec == 20, got %d", svc.TimeoutSec)
}
if svc.Protocol != "HTTP" {
t.Errorf("Expected Protocol to be HTTP, got %q", svc.Protocol)
}
if len(svc.Backends) != 1 {
t.Errorf("Expected 1 backend, got %d", len(svc.Backends))
}
}
func testAccCheckComputeRegionBackendServiceDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_region_backend_service" {
continue
}
_, err := config.clientComputeBeta.RegionBackendServices.Get(
config.Project, config.Region, rs.Primary.ID).Do()
if err == nil {
return fmt.Errorf("Backend service still exists")
}
}
return nil
}
func testAccCheckComputeRegionBackendServiceExists(n string, svc *compute.BackendService) 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.clientComputeBeta.RegionBackendServices.Get(
config.Project, config.Region, rs.Primary.ID).Do()
if err != nil {
return err
}
if found.Name != rs.Primary.ID {
return fmt.Errorf("Backend service not found")
}
*svc = *found
return nil
}
}
func TestAccComputeRegionBackendService_withCDNEnabled(t *testing.T) {
serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
var svc compute.BackendService
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeRegionBackendServiceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRegionBackendService_withCDNEnabled(
serviceName, checkName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeRegionBackendServiceExists(
"google_compute_region_backend_service.foobar", &svc),
),
},
},
})
if svc.EnableCDN != true {
t.Errorf("Expected EnableCDN == true, got %t", svc.EnableCDN)
}
}
func TestAccComputeRegionBackendService_withInternalLoadBalancing(t *testing.T) {
serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
var svc compute.BackendService
// config := testAccProvider.Meta().(*Config)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeRegionBackendServiceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeRegionBackendService_withInternalLoadBalancing(
serviceName, checkName, "us-central1"),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeRegionBackendServiceExists(
"google_compute_region_backend_service.foobar", &svc),
),
},
},
})
if svc.LoadBalancingScheme != "INTERNAL" {
t.Errorf("Expected LoadBalancingScheme == INTERNAL, got %q", svc.EnableCDN)
}
}
func testAccComputeRegionBackendService_basic(serviceName, checkName string) string {
return fmt.Sprintf(`
resource "google_compute_region_backend_service" "foobar" {
name = "%s"
health_checks = ["${google_compute_health_check.zero.self_link}"]
load_balancing_scheme = "INTERNAL"
}
resource "google_compute_health_check" "zero" {
name = "%s"
check_interval_sec = 1
timeout_sec = 1
tcp_health_check {
port = "80"
}
}
`, serviceName, checkName)
}
func testAccComputeRegionBackendService_withCDNEnabled(serviceName, checkName string) string {
return fmt.Sprintf(`
resource "google_compute_region_backend_service" "foobar" {
name = "%s"
health_checks = ["${google_compute_http_health_check.zero.self_link}"]
enable_cdn = true
}
resource "google_compute_http_health_check" "zero" {
name = "%s"
request_path = "/"
check_interval_sec = 1
timeout_sec = 1
}
`, serviceName, checkName)
}
func testAccComputeRegionBackendService_withInternalLoadBalancing(serviceName, checkName, region string) string {
return fmt.Sprintf(`
resource "google_compute_region_backend_service" "foobar" {
name = "%s"
health_checks = ["${google_compute_health_check.zero.self_link}"]
load_balancing_scheme = "INTERNAL"
}
resource "google_compute_health_check" "zero" {
name = "%s"
check_interval_sec = 1
timeout_sec = 1
tcp_health_check {
port = "80"
}
}
`, serviceName, region, checkName)
}
func testAccComputeRegionBackendService_basicModified(serviceName, checkOne, checkTwo string) string {
return fmt.Sprintf(`
resource "google_compute_region_backend_service" "foobar" {
name = "%s"
health_checks = ["${google_compute_http_health_check.one.self_link}"]
}
resource "google_compute_http_health_check" "zero" {
name = "%s"
request_path = "/"
check_interval_sec = 1
timeout_sec = 1
}
resource "google_compute_http_health_check" "one" {
name = "%s"
request_path = "/one"
check_interval_sec = 30
timeout_sec = 30
}
`, serviceName, checkOne, checkTwo)
}
func testAccComputeRegionBackendService_withBackend(
serviceName, igName, itName, checkName string, timeout int64) string {
return fmt.Sprintf(`
resource "google_compute_region_backend_service" "lipsum" {
name = "%s"
description = "Hello World 1234"
port_name = "http"
protocol = "HTTP"
timeout_sec = %v
backend {
group = "${google_compute_instance_group_manager.foobar.instance_group}"
}
health_checks = ["${google_compute_http_health_check.default.self_link}"]
}
resource "google_compute_instance_group_manager" "foobar" {
name = "%s"
instance_template = "${google_compute_instance_template.foobar.self_link}"
base_instance_name = "foobar"
zone = "us-central1-f"
target_size = 1
}
resource "google_compute_instance_template" "foobar" {
name = "%s"
machine_type = "n1-standard-1"
network_interface {
network = "default"
}
disk {
source_image = "debian-8-jessie-v20160803"
auto_delete = true
boot = true
}
}
resource "google_compute_http_health_check" "default" {
name = "%s"
request_path = "/"
check_interval_sec = 1
timeout_sec = 1
}
`, serviceName, timeout, igName, itName, checkName)
}