diff --git a/provider.go b/provider.go index b63aa389..3cfc363d 100644 --- a/provider.go +++ b/provider.go @@ -43,6 +43,7 @@ func Provider() terraform.ResourceProvider { "google_compute_global_address": resourceComputeGlobalAddress(), "google_compute_global_forwarding_rule": resourceComputeGlobalForwardingRule(), "google_compute_http_health_check": resourceComputeHttpHealthCheck(), + "google_compute_https_health_check": resourceComputeHttpsHealthCheck(), "google_compute_instance": resourceComputeInstance(), "google_compute_instance_group_manager": resourceComputeInstanceGroupManager(), "google_compute_instance_template": resourceComputeInstanceTemplate(), diff --git a/resource_compute_https_health_check.go b/resource_compute_https_health_check.go new file mode 100644 index 00000000..32a8dfb3 --- /dev/null +++ b/resource_compute_https_health_check.go @@ -0,0 +1,227 @@ +package google + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "google.golang.org/api/compute/v1" + "google.golang.org/api/googleapi" +) + +func resourceComputeHttpsHealthCheck() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeHttpsHealthCheckCreate, + Read: resourceComputeHttpsHealthCheckRead, + Delete: resourceComputeHttpsHealthCheckDelete, + Update: resourceComputeHttpsHealthCheckUpdate, + + Schema: map[string]*schema.Schema{ + "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, + }, + + "host": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "port": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 443, + }, + + "request_path": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "/", + }, + + "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 resourceComputeHttpsHealthCheckCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Build the parameter + hchk := &compute.HttpsHealthCheck{ + Name: d.Get("name").(string), + } + // Optional things + if v, ok := d.GetOk("description"); ok { + hchk.Description = v.(string) + } + if v, ok := d.GetOk("host"); ok { + hchk.Host = v.(string) + } + if v, ok := d.GetOk("request_path"); ok { + hchk.RequestPath = 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("port"); ok { + hchk.Port = 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)) + } + + log.Printf("[DEBUG] HttpsHealthCheck insert request: %#v", hchk) + op, err := config.clientCompute.HttpsHealthChecks.Insert( + config.Project, hchk).Do() + if err != nil { + return fmt.Errorf("Error creating HttpsHealthCheck: %s", err) + } + + // It probably maybe worked, so store the ID now + d.SetId(hchk.Name) + + err = computeOperationWaitGlobal(config, op, "Creating Https Health Check") + if err != nil { + return err + } + + return resourceComputeHttpsHealthCheckRead(d, meta) +} + +func resourceComputeHttpsHealthCheckUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Build the parameter + hchk := &compute.HttpsHealthCheck{ + Name: d.Get("name").(string), + } + // Optional things + if v, ok := d.GetOk("description"); ok { + hchk.Description = v.(string) + } + if v, ok := d.GetOk("host"); ok { + hchk.Host = v.(string) + } + if v, ok := d.GetOk("request_path"); ok { + hchk.RequestPath = 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("port"); ok { + hchk.Port = 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)) + } + + log.Printf("[DEBUG] HttpsHealthCheck patch request: %#v", hchk) + op, err := config.clientCompute.HttpsHealthChecks.Patch( + config.Project, hchk.Name, hchk).Do() + if err != nil { + return fmt.Errorf("Error patching HttpsHealthCheck: %s", err) + } + + // It probably maybe worked, so store the ID now + d.SetId(hchk.Name) + + err = computeOperationWaitGlobal(config, op, "Updating Https Health Check") + if err != nil { + return err + } + + return resourceComputeHttpsHealthCheckRead(d, meta) +} + +func resourceComputeHttpsHealthCheckRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + hchk, err := config.clientCompute.HttpsHealthChecks.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 HttpsHealthCheck: %s", err) + } + + d.Set("host", hchk.Host) + d.Set("request_path", hchk.RequestPath) + d.Set("check_interval_sec", hchk.CheckIntervalSec) + d.Set("health_threshold", hchk.HealthyThreshold) + d.Set("port", hchk.Port) + d.Set("timeout_sec", hchk.TimeoutSec) + d.Set("unhealthy_threshold", hchk.UnhealthyThreshold) + d.Set("self_link", hchk.SelfLink) + + return nil +} + +func resourceComputeHttpsHealthCheckDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + // Delete the HttpsHealthCheck + op, err := config.clientCompute.HttpsHealthChecks.Delete( + config.Project, d.Id()).Do() + if err != nil { + return fmt.Errorf("Error deleting HttpsHealthCheck: %s", err) + } + + err = computeOperationWaitGlobal(config, op, "Deleting Https Health Check") + if err != nil { + return err + } + + d.SetId("") + return nil +} diff --git a/resource_compute_https_health_check_test.go b/resource_compute_https_health_check_test.go new file mode 100644 index 00000000..d263bfd8 --- /dev/null +++ b/resource_compute_https_health_check_test.go @@ -0,0 +1,171 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestAccComputeHttpsHealthCheck_basic(t *testing.T) { + var healthCheck compute.HttpsHealthCheck + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeHttpsHealthCheckDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeHttpsHealthCheck_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeHttpsHealthCheckExists( + "google_compute_https_health_check.foobar", &healthCheck), + testAccCheckComputeHttpsHealthCheckRequestPath( + "/health_check", &healthCheck), + testAccCheckComputeHttpsHealthCheckThresholds( + 3, 3, &healthCheck), + ), + }, + }, + }) +} + +func TestAccComputeHttpsHealthCheck_update(t *testing.T) { + var healthCheck compute.HttpsHealthCheck + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeHttpsHealthCheckDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeHttpsHealthCheck_update1, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeHttpsHealthCheckExists( + "google_compute_https_health_check.foobar", &healthCheck), + testAccCheckComputeHttpsHealthCheckRequestPath( + "/not_default", &healthCheck), + testAccCheckComputeHttpsHealthCheckThresholds( + 2, 2, &healthCheck), + ), + }, + resource.TestStep{ + Config: testAccComputeHttpsHealthCheck_update2, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeHttpsHealthCheckExists( + "google_compute_https_health_check.foobar", &healthCheck), + testAccCheckComputeHttpsHealthCheckRequestPath( + "/", &healthCheck), + testAccCheckComputeHttpsHealthCheckThresholds( + 10, 10, &healthCheck), + ), + }, + }, + }) +} + +func testAccCheckComputeHttpsHealthCheckDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_https_health_check" { + continue + } + + _, err := config.clientCompute.HttpsHealthChecks.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("HttpsHealthCheck still exists") + } + } + + return nil +} + +func testAccCheckComputeHttpsHealthCheckExists(n string, healthCheck *compute.HttpsHealthCheck) 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.HttpsHealthChecks.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("HttpsHealthCheck not found") + } + + *healthCheck = *found + + return nil + } +} + +func testAccCheckComputeHttpsHealthCheckRequestPath(path string, healthCheck *compute.HttpsHealthCheck) resource.TestCheckFunc { + return func(s *terraform.State) error { + if healthCheck.RequestPath != path { + return fmt.Errorf("RequestPath doesn't match: expected %s, got %s", path, healthCheck.RequestPath) + } + + return nil + } +} + +func testAccCheckComputeHttpsHealthCheckThresholds(healthy, unhealthy int64, healthCheck *compute.HttpsHealthCheck) 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 + } +} + +const testAccComputeHttpsHealthCheck_basic = ` +resource "google_compute_https_health_check" "foobar" { + check_interval_sec = 3 + 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 +} +` + +const testAccComputeHttpsHealthCheck_update1 = ` +resource "google_compute_https_health_check" "foobar" { + name = "terraform-test" + description = "Resource created for Terraform acceptance testing" + request_path = "/not_default" +} +` + +/* Change description, restore request_path to default, and change +* thresholds from defaults */ +const testAccComputeHttpsHealthCheck_update2 = ` +resource "google_compute_https_health_check" "foobar" { + name = "terraform-test" + description = "Resource updated for Terraform acceptance testing" + healthy_threshold = 10 + unhealthy_threshold = 10 +} +`