Merge pull request #10453 from danawillow/google-ilb

providers/google: Add support for Internal Load Balancing
This commit is contained in:
Paddy 2016-12-12 09:47:20 -08:00 committed by GitHub
commit 1c7fb083c9
8 changed files with 1480 additions and 16 deletions

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

@ -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,12 +58,33 @@ 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,
Computed: true,
},
"port_range": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"ports": &schema.Schema{
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Set: schema.HashString,
},
"project": &schema.Schema{
Type: schema.TypeString,
Optional: true,
@ -76,6 +103,13 @@ func resourceComputeForwardingRule() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"subnetwork": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
},
}
}
@ -93,13 +127,24 @@ func resourceComputeForwardingRuleCreate(d *schema.ResourceData, meta interface{
return err
}
ps := d.Get("ports").(*schema.Set).List()
ports := make([]string, 0, len(ps))
for _, v := range ps {
ports = append(ports, v.(string))
}
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),
Ports: ports,
Subnetwork: d.Get("subnetwork").(string),
Target: d.Get("target").(string),
}
log.Printf("[DEBUG] ForwardingRule insert request: %#v", frule)
@ -186,10 +231,15 @@ 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("ports", frule.Ports)
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)

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,31 @@ 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}"]
region = "us-central1"
}
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}"
ports = ["80"]
}
`, serviceName, checkName, ruleName)
}

View File

@ -0,0 +1,494 @@
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,
},
"tcp_health_check": &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"ssl_health_check", "http_health_check", "https_health_check"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"port": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 80,
},
"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,
},
},
},
},
"ssl_health_check": &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"tcp_health_check", "http_health_check", "https_health_check"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"port": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 443,
},
"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,
},
},
},
},
"http_health_check": &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"tcp_health_check", "ssl_health_check", "https_health_check"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"host": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: 80,
},
"port": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"proxy_header": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "NONE",
},
"request_path": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "/",
},
},
},
},
"https_health_check": &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"tcp_health_check", "ssl_health_check", "http_health_check"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"host": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: 443,
},
"port": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"proxy_header": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "NONE",
},
"request_path": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "/",
},
},
},
},
"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("tcp_health_check"); ok {
hchk.Type = "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["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
}
if v, ok := d.GetOk("ssl_health_check"); ok {
hchk.Type = "SSL"
sslcheck := v.([]interface{})[0].(map[string]interface{})
sslHealthCheck := &compute.SSLHealthCheck{}
if val, ok := sslcheck["port"]; ok {
sslHealthCheck.Port = int64(val.(int))
}
if val, ok := sslcheck["proxy_header"]; ok {
sslHealthCheck.ProxyHeader = val.(string)
}
if val, ok := sslcheck["request"]; ok {
sslHealthCheck.Request = val.(string)
}
if val, ok := sslcheck["response"]; ok {
sslHealthCheck.Response = val.(string)
}
hchk.SslHealthCheck = sslHealthCheck
}
if v, ok := d.GetOk("http_health_check"); ok {
hchk.Type = "HTTP"
httpcheck := v.([]interface{})[0].(map[string]interface{})
httpHealthCheck := &compute.HTTPHealthCheck{}
if val, ok := httpcheck["host"]; ok {
httpHealthCheck.Host = val.(string)
}
if val, ok := httpcheck["port"]; ok {
httpHealthCheck.Port = int64(val.(int))
}
if val, ok := httpcheck["proxy_header"]; ok {
httpHealthCheck.ProxyHeader = val.(string)
}
if val, ok := httpcheck["request_path"]; ok {
httpHealthCheck.RequestPath = val.(string)
}
hchk.HttpHealthCheck = httpHealthCheck
}
if v, ok := d.GetOk("https_health_check"); ok {
hchk.Type = "HTTPS"
httpscheck := v.([]interface{})[0].(map[string]interface{})
httpsHealthCheck := &compute.HTTPSHealthCheck{}
if val, ok := httpscheck["host"]; ok {
httpsHealthCheck.Host = val.(string)
}
if val, ok := httpscheck["port"]; ok {
httpsHealthCheck.Port = int64(val.(int))
}
if val, ok := httpscheck["proxy_header"]; ok {
httpsHealthCheck.ProxyHeader = val.(string)
}
if val, ok := httpscheck["request_path"]; ok {
httpsHealthCheck.RequestPath = val.(string)
}
hchk.HttpsHealthCheck = httpsHealthCheck
}
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("tcp_health_check"); ok {
hchk.Type = "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["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
}
if v, ok := d.GetOk("ssl_health_check"); ok {
hchk.Type = "SSL"
sslcheck := v.([]interface{})[0].(map[string]interface{})
sslHealthCheck := &compute.SSLHealthCheck{}
if val, ok := sslcheck["port"]; ok {
sslHealthCheck.Port = int64(val.(int))
}
if val, ok := sslcheck["proxy_header"]; ok {
sslHealthCheck.ProxyHeader = val.(string)
}
if val, ok := sslcheck["request"]; ok {
sslHealthCheck.Request = val.(string)
}
if val, ok := sslcheck["response"]; ok {
sslHealthCheck.Response = val.(string)
}
hchk.SslHealthCheck = sslHealthCheck
}
if v, ok := d.GetOk("http_health_check"); ok {
hchk.Type = "HTTP"
httpcheck := v.([]interface{})[0].(map[string]interface{})
httpHealthCheck := &compute.HTTPHealthCheck{}
if val, ok := httpcheck["host"]; ok {
httpHealthCheck.Host = val.(string)
}
if val, ok := httpcheck["port"]; ok {
httpHealthCheck.Port = int64(val.(int))
}
if val, ok := httpcheck["proxy_header"]; ok {
httpHealthCheck.ProxyHeader = val.(string)
}
if val, ok := httpcheck["request_path"]; ok {
httpHealthCheck.RequestPath = val.(string)
}
hchk.HttpHealthCheck = httpHealthCheck
}
if v, ok := d.GetOk("https_health_check"); ok {
hchk.Type = "HTTPS"
httpscheck := v.([]interface{})[0].(map[string]interface{})
httpsHealthCheck := &compute.HTTPSHealthCheck{}
if val, ok := httpscheck["host"]; ok {
httpsHealthCheck.Host = val.(string)
}
if val, ok := httpscheck["port"]; ok {
httpsHealthCheck.Port = int64(val.(int))
}
if val, ok := httpscheck["proxy_header"]; ok {
httpsHealthCheck.ProxyHeader = val.(string)
}
if val, ok := httpscheck["request_path"]; ok {
httpsHealthCheck.RequestPath = val.(string)
}
hchk.HttpsHealthCheck = httpsHealthCheck
}
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("tcp_health_check", hchk.TcpHealthCheck)
d.Set("ssl_health_check", hchk.SslHealthCheck)
d.Set("http_health_check", hchk.HttpHealthCheck)
d.Set("https_health_check", hchk.HttpsHealthCheck)
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,308 @@
package google
import (
"fmt"
"regexp"
"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_tcp(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_tcp,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeHealthCheckExists(
"google_compute_health_check.foobar", &healthCheck),
testAccCheckComputeHealthCheckThresholds(
3, 3, &healthCheck),
testAccCheckComputeHealthCheckTcpPort(80, &healthCheck),
),
},
},
})
}
func TestAccComputeHealthCheck_tcp_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_tcp,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeHealthCheckExists(
"google_compute_health_check.foobar", &healthCheck),
testAccCheckComputeHealthCheckThresholds(
3, 3, &healthCheck),
testAccCheckComputeHealthCheckTcpPort(80, &healthCheck),
),
},
resource.TestStep{
Config: testAccComputeHealthCheck_tcp_update,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeHealthCheckExists(
"google_compute_health_check.foobar", &healthCheck),
testAccCheckComputeHealthCheckThresholds(
10, 10, &healthCheck),
testAccCheckComputeHealthCheckTcpPort(8080, &healthCheck),
),
},
},
})
}
func TestAccComputeHealthCheck_ssl(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_ssl,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeHealthCheckExists(
"google_compute_health_check.foobar", &healthCheck),
testAccCheckComputeHealthCheckThresholds(
3, 3, &healthCheck),
),
},
},
})
}
func TestAccComputeHealthCheck_http(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_http,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeHealthCheckExists(
"google_compute_health_check.foobar", &healthCheck),
testAccCheckComputeHealthCheckThresholds(
3, 3, &healthCheck),
),
},
},
})
}
func TestAccComputeHealthCheck_https(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_https,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeHealthCheckExists(
"google_compute_health_check.foobar", &healthCheck),
testAccCheckComputeHealthCheckThresholds(
3, 3, &healthCheck),
),
},
},
})
}
func TestAccComputeHealthCheck_tcpAndSsl_shouldFail(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeHealthCheckDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeHealthCheck_tcpAndSsl_shouldFail,
ExpectError: regexp.MustCompile("conflicts with tcp_health_check"),
},
},
})
}
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 %s still exists", rs.Primary.ID)
}
}
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 testAccCheckErrorCreating(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
_, ok := s.RootModule().Resources[n]
if ok {
return fmt.Errorf("HealthCheck %s created successfully with bad config", n)
}
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
}
}
func testAccCheckComputeHealthCheckTcpPort(port int64, healthCheck *compute.HealthCheck) resource.TestCheckFunc {
return func(s *terraform.State) error {
if healthCheck.TcpHealthCheck.Port != port {
return fmt.Errorf("Port doesn't match: expected %v, got %v", port, healthCheck.TcpHealthCheck.Port)
}
return nil
}
}
var testAccComputeHealthCheck_tcp = 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 {
}
}
`, acctest.RandString(10))
var testAccComputeHealthCheck_tcp_update = fmt.Sprintf(`
resource "google_compute_health_check" "foobar" {
check_interval_sec = 3
description = "Resource updated for Terraform acceptance testing"
healthy_threshold = 10
name = "health-test-%s"
timeout_sec = 2
unhealthy_threshold = 10
tcp_health_check {
port = "8080"
}
}
`, acctest.RandString(10))
var testAccComputeHealthCheck_ssl = 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
ssl_health_check {
port = "443"
}
}
`, acctest.RandString(10))
var testAccComputeHealthCheck_http = 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
http_health_check {
port = "80"
}
}
`, acctest.RandString(10))
var testAccComputeHealthCheck_https = 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
https_health_check {
port = "443"
}
}
`, acctest.RandString(10))
var testAccComputeHealthCheck_tcpAndSsl_shouldFail = 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 {
}
ssl_health_check {
}
}
`, acctest.RandString(10))

View File

@ -0,0 +1,306 @@
package google
import (
"bytes"
"fmt"
"log"
"regexp"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1"
"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,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
},
Optional: true,
Set: resourceGoogleComputeRegionBackendServiceBackendHash,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"fingerprint": &schema.Schema{
Type: schema.TypeString,
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,
LoadBalancingScheme: "INTERNAL",
}
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("protocol"); ok {
service.Protocol = v.(string)
}
if v, ok := d.GetOk("timeout_sec"); ok {
service.TimeoutSec = int64(v.(int))
}
project, err := getProject(d, config)
if err != nil {
return err
}
region, err := getRegion(d, config)
if err != nil {
return err
}
log.Printf("[DEBUG] Creating new Region Backend Service: %#v", service)
op, err := config.clientCompute.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 = computeOperationWaitRegion(config, op, project, region, "Creating Region 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.clientCompute.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("protocol", service.Protocol)
d.Set("timeout_sec", service.TimeoutSec)
d.Set("fingerprint", service.Fingerprint)
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,
LoadBalancingScheme: "INTERNAL",
}
// 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("protocol"); ok {
service.Protocol = v.(string)
}
if v, ok := d.GetOk("timeout_sec"); ok {
service.TimeoutSec = int64(v.(int))
}
log.Printf("[DEBUG] Updating existing Backend Service %q: %#v", d.Id(), service)
op, err := config.clientCompute.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 = computeOperationWaitRegion(config, op, project, region, "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.clientCompute.RegionBackendServices.Delete(
project, region, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting backend service: %s", err)
}
err = computeOperationWaitRegion(config, op, project, region, "Deleting Backend Service")
if err != nil {
return err
}
d.SetId("")
return nil
}
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["description"]; ok {
buf.WriteString(fmt.Sprintf("%s-", v.(string)))
}
return hashcode.String(buf.String())
}

View File

@ -0,0 +1,262 @@
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 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 != "TCP" {
t.Errorf("Expected Protocol to be TCP, 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 != "TCP" {
t.Errorf("Expected Protocol to be TCP, 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.clientCompute.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.clientCompute.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_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}"]
region = "us-central1"
}
resource "google_compute_health_check" "zero" {
name = "%s"
check_interval_sec = 1
timeout_sec = 1
tcp_health_check {
port = "80"
}
}
`, serviceName, 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_health_check.one.self_link}"]
region = "us-central1"
}
resource "google_compute_health_check" "zero" {
name = "%s"
check_interval_sec = 1
timeout_sec = 1
tcp_health_check {
}
}
resource "google_compute_health_check" "one" {
name = "%s"
check_interval_sec = 30
timeout_sec = 30
tcp_health_check {
}
}
`, 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"
protocol = "TCP"
region = "us-central1"
timeout_sec = %v
backend {
group = "${google_compute_instance_group_manager.foobar.instance_group}"
}
health_checks = ["${google_compute_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_health_check" "default" {
name = "%s"
check_interval_sec = 1
timeout_sec = 1
tcp_health_check {
}
}
`, serviceName, timeout, igName, itName, checkName)
}

View File

@ -150,15 +150,8 @@ func resourceStorageObjectAclRead(d *schema.ResourceData, meta interface{}) erro
}
for _, v := range res.Items {
role := ""
entity := ""
for key, val := range v.(map[string]interface{}) {
if key == "role" {
role = val.(string)
} else if key == "entity" {
entity = val.(string)
}
}
role := v.Role
entity := v.Entity
if _, in := re_local_map[entity]; in {
role_entity = append(role_entity, fmt.Sprintf("%s:%s", role, entity))
log.Printf("[DEBUG]: saving re %s-%s", role, entity)