diff --git a/google/field_helpers.go b/google/field_helpers.go index 67d75887a20..1f533e6048c 100644 --- a/google/field_helpers.go +++ b/google/field_helpers.go @@ -18,6 +18,10 @@ func ParseNetworkFieldValue(network string, d TerraformResourceData, config *Con return parseGlobalFieldValue("networks", network, "project", d, config, true) } +func ParseSslCertificateFieldValue(sslCertificate string, d TerraformResourceData, config *Config) (*GlobalFieldValue, error) { + return parseGlobalFieldValue("sslCertificates", sslCertificate, "project", d, config, false) +} + // ------------------------------------------------------------ // Base helpers used to create helpers for specific fields. // ------------------------------------------------------------ diff --git a/google/import_compute_target_ssl_proxy_test.go b/google/import_compute_target_ssl_proxy_test.go new file mode 100644 index 00000000000..f7b762d38fa --- /dev/null +++ b/google/import_compute_target_ssl_proxy_test.go @@ -0,0 +1,31 @@ +package google + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccComputeTargetSslProxy_import(t *testing.T) { + target := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + cert := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + backend := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + hc := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeTargetSslProxyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeTargetSslProxy_basic1(target, cert, backend, hc), + }, + resource.TestStep{ + ResourceName: "google_compute_target_ssl_proxy.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/google/provider.go b/google/provider.go index d9ab460a513..e8943ec712e 100644 --- a/google/provider.go +++ b/google/provider.go @@ -102,6 +102,7 @@ func Provider() terraform.ResourceProvider { "google_compute_target_http_proxy": resourceComputeTargetHttpProxy(), "google_compute_target_https_proxy": resourceComputeTargetHttpsProxy(), "google_compute_target_tcp_proxy": resourceComputeTargetTcpProxy(), + "google_compute_target_ssl_proxy": resourceComputeTargetSslProxy(), "google_compute_target_pool": resourceComputeTargetPool(), "google_compute_url_map": resourceComputeUrlMap(), "google_compute_vpn_gateway": resourceComputeVpnGateway(), diff --git a/google/resource_compute_target_ssl_proxy.go b/google/resource_compute_target_ssl_proxy.go new file mode 100644 index 00000000000..57661434baf --- /dev/null +++ b/google/resource_compute_target_ssl_proxy.go @@ -0,0 +1,249 @@ +package google + +import ( + "fmt" + "log" + "strconv" + + "github.com/hashicorp/terraform/helper/schema" + "google.golang.org/api/compute/v1" +) + +func resourceComputeTargetSslProxy() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeTargetSslProxyCreate, + Read: resourceComputeTargetSslProxyRead, + Delete: resourceComputeTargetSslProxyDelete, + Update: resourceComputeTargetSslProxyUpdate, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "backend_service": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "ssl_certificates": &schema.Schema{ + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "proxy_header": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "NONE", + }, + + "project": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "proxy_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "self_link": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceComputeTargetSslProxyCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + sslCertificates, err := expandSslCertificates(d, config) + if err != nil { + return err + } + + proxy := &compute.TargetSslProxy{ + Name: d.Get("name").(string), + Service: d.Get("backend_service").(string), + ProxyHeader: d.Get("proxy_header").(string), + Description: d.Get("description").(string), + SslCertificates: sslCertificates, + } + + log.Printf("[DEBUG] TargetSslProxy insert request: %#v", proxy) + op, err := config.clientCompute.TargetSslProxies.Insert( + project, proxy).Do() + if err != nil { + return fmt.Errorf("Error creating TargetSslProxy: %s", err) + } + + err = computeOperationWait(config, op, project, "Creating Target Ssl Proxy") + if err != nil { + return err + } + + d.SetId(proxy.Name) + + return resourceComputeTargetSslProxyRead(d, meta) +} + +func resourceComputeTargetSslProxyUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + d.Partial(true) + + if d.HasChange("proxy_header") { + proxyHeader := d.Get("proxy_header").(string) + proxyHeaderPayload := &compute.TargetSslProxiesSetProxyHeaderRequest{ + ProxyHeader: proxyHeader, + } + op, err := config.clientCompute.TargetSslProxies.SetProxyHeader( + project, d.Id(), proxyHeaderPayload).Do() + if err != nil { + return fmt.Errorf("Error updating proxy_header: %s", err) + } + + err = computeOperationWait(config, op, project, "Updating Target SSL Proxy") + if err != nil { + return err + } + + d.SetPartial("proxy_header") + } + + if d.HasChange("backend_service") { + op, err := config.clientCompute.TargetSslProxies.SetBackendService(project, d.Id(), &compute.TargetSslProxiesSetBackendServiceRequest{ + Service: d.Get("backend_service").(string), + }).Do() + + if err != nil { + return fmt.Errorf("Error updating backend_service: %s", err) + } + + err = computeOperationWait(config, op, project, "Updating Target SSL Proxy") + if err != nil { + return err + } + + d.SetPartial("backend_service") + } + + if d.HasChange("ssl_certificates") { + sslCertificates, err := expandSslCertificates(d, config) + if err != nil { + return err + } + + op, err := config.clientCompute.TargetSslProxies.SetSslCertificates(project, d.Id(), &compute.TargetSslProxiesSetSslCertificatesRequest{ + SslCertificates: sslCertificates, + }).Do() + + if err != nil { + return fmt.Errorf("Error updating backend_service: %s", err) + } + + err = computeOperationWait(config, op, project, "Updating Target SSL Proxy") + if err != nil { + return err + } + + d.SetPartial("ssl_certificates") + } + + d.Partial(false) + + return resourceComputeTargetSslProxyRead(d, meta) +} + +func resourceComputeTargetSslProxyRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + proxy, err := config.clientCompute.TargetSslProxies.Get( + project, d.Id()).Do() + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("Target SSL Proxy %q", d.Get("name").(string))) + } + + d.Set("name", proxy.Name) + d.Set("description", proxy.Description) + d.Set("proxy_header", proxy.ProxyHeader) + d.Set("backend_service", proxy.Service) + d.Set("ssl_certificates", proxy.SslCertificates) + d.Set("self_link", proxy.SelfLink) + d.Set("proxy_id", strconv.FormatUint(proxy.Id, 10)) + + return nil +} + +func resourceComputeTargetSslProxyDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + op, err := config.clientCompute.TargetSslProxies.Delete( + project, d.Id()).Do() + if err != nil { + return fmt.Errorf("Error deleting TargetSslProxy: %s", err) + } + + err = computeOperationWait(config, op, project, "Deleting Target SSL Proxy") + if err != nil { + return err + } + + d.SetId("") + return nil +} + +func expandSslCertificates(d *schema.ResourceData, config *Config) ([]string, error) { + configured := d.Get("ssl_certificates").([]interface{}) + certs := make([]string, 0, len(configured)) + + for _, sslCertificate := range configured { + sslCertificateFieldValue, err := ParseSslCertificateFieldValue(sslCertificate.(string), d, config) + if err != nil { + return nil, fmt.Errorf("Invalid ssl certificate: %s", err) + } + + certs = append(certs, sslCertificateFieldValue.RelativeLink()) + } + + return certs, nil +} diff --git a/google/resource_compute_target_ssl_proxy_test.go b/google/resource_compute_target_ssl_proxy_test.go new file mode 100644 index 00000000000..cc668484b84 --- /dev/null +++ b/google/resource_compute_target_ssl_proxy_test.go @@ -0,0 +1,195 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeTargetSslProxy_basic(t *testing.T) { + target := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + cert := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + backend := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + hc := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeTargetSslProxyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeTargetSslProxy_basic1(target, cert, backend, hc), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetSslProxy( + "google_compute_target_ssl_proxy.foobar", "NONE", cert), + ), + }, + }, + }) +} + +func TestAccComputeTargetSslProxy_update(t *testing.T) { + target := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + cert1 := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + cert2 := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + backend1 := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + backend2 := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + hc := fmt.Sprintf("tssl-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeTargetSslProxyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeTargetSslProxy_basic1(target, cert1, backend1, hc), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetSslProxy( + "google_compute_target_ssl_proxy.foobar", "NONE", cert1), + ), + }, + resource.TestStep{ + Config: testAccComputeTargetSslProxy_basic2(target, cert1, cert2, backend1, backend2, hc), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetSslProxy( + "google_compute_target_ssl_proxy.foobar", "PROXY_V1", cert2), + ), + }, + }, + }) +} + +func testAccCheckComputeTargetSslProxyDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_target_ssl_proxy" { + continue + } + + _, err := config.clientCompute.TargetSslProxies.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("TargetSslProxy still exists") + } + } + + return nil +} + +func testAccCheckComputeTargetSslProxy(n, proxyHeader, sslCert string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + found, err := config.clientCompute.TargetSslProxies.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("TargetSslProxy not found") + } + + if found.ProxyHeader != proxyHeader { + return fmt.Errorf("Wrong proxy header. Expected '%s', got '%s'", proxyHeader, found.ProxyHeader) + } + + foundCertName := GetResourceNameFromSelfLink(found.SslCertificates[0]) + if foundCertName != sslCert { + return fmt.Errorf("Wrong ssl certificates. Expected '%s', got '%s'", sslCert, foundCertName) + } + + return nil + } +} + +func testAccComputeTargetSslProxy_basic1(target, sslCert, backend, hc string) string { + return fmt.Sprintf(` +resource "google_compute_target_ssl_proxy" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + backend_service = "${google_compute_backend_service.foo.self_link}" + ssl_certificates = ["${google_compute_ssl_certificate.foo.self_link}"] + proxy_header = "NONE" +} + +resource "google_compute_ssl_certificate" "foo" { + name = "%s" + private_key = "${file("test-fixtures/ssl_cert/test.key")}" + certificate = "${file("test-fixtures/ssl_cert/test.crt")}" +} + +resource "google_compute_backend_service" "foo" { + name = "%s" + protocol = "SSL" + health_checks = ["${google_compute_health_check.zero.self_link}"] +} + +resource "google_compute_health_check" "zero" { + name = "%s" + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "443" + } +} +`, target, sslCert, backend, hc) +} + +func testAccComputeTargetSslProxy_basic2(target, sslCert1, sslCert2, backend1, backend2, hc string) string { + return fmt.Sprintf(` +resource "google_compute_target_ssl_proxy" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + backend_service = "${google_compute_backend_service.bar.self_link}" + ssl_certificates = ["${google_compute_ssl_certificate.bar.name}"] + proxy_header = "PROXY_V1" +} + +resource "google_compute_ssl_certificate" "foo" { + name = "%s" + private_key = "${file("test-fixtures/ssl_cert/test.key")}" + certificate = "${file("test-fixtures/ssl_cert/test.crt")}" +} + +resource "google_compute_ssl_certificate" "bar" { + name = "%s" + private_key = "${file("test-fixtures/ssl_cert/test.key")}" + certificate = "${file("test-fixtures/ssl_cert/test.crt")}" +} + +resource "google_compute_backend_service" "foo" { + name = "%s" + protocol = "SSL" + health_checks = ["${google_compute_health_check.zero.self_link}"] +} + +resource "google_compute_backend_service" "bar" { + name = "%s" + protocol = "SSL" + health_checks = ["${google_compute_health_check.zero.self_link}"] +} + +resource "google_compute_health_check" "zero" { + name = "%s" + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "443" + } +} +`, target, sslCert1, sslCert2, backend1, backend2, hc) +} diff --git a/website/docs/r/compute_target_ssl_proxy.html.markdown b/website/docs/r/compute_target_ssl_proxy.html.markdown new file mode 100644 index 00000000000..0e57717196f --- /dev/null +++ b/website/docs/r/compute_target_ssl_proxy.html.markdown @@ -0,0 +1,86 @@ +--- +layout: "google" +page_title: "Google: google_compute_target_ssl_proxy" +sidebar_current: "docs-google-compute-target-ssl-proxy" +description: |- + Creates a Target SSL Proxy resource in GCE. +--- + +# google\_compute\_target\_ssl\_proxy + +Creates a target SSL proxy resource in GCE. For more information see +[the official +documentation](https://cloud.google.com/compute/docs/load-balancing/ssl-ssl/) and +[API](https://cloud.google.com/compute/docs/reference/latest/targetSslProxies). + + +## Example Usage + +```hcl +resource "google_compute_target_ssl_proxy" "default" { + name = "test" + backend_service = "${google_compute_backend_service.default.self_link}" + ssl_certificates = ["${google_compute_ssl_certificate.default.self_link}"] +} + +resource "google_compute_ssl_certificate" "default" { + name = "default-cert" + private_key = "${file("path/to/test.key")}" + certificate = "${file("path/to/test.crt")}" +} + +resource "google_compute_backend_service" "default" { + name = "default-backend" + protocol = "SSL" + health_checks = ["${google_compute_health_check.default.self_link}"] +} + +resource "google_compute_health_check" "default" { + name = "default-health-check" + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "443" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A unique name for the resource, required by GCE. Changing + this forces a new resource to be created. + +* `backend_service` - (Required) The URL of a Backend Service resource to receive the matched traffic. + +* `ssl_certificates` - (Required) The URLs of the SSL Certificate resources that + authenticate connections between users and load balancing. + +- - - + +* `proxy_header` - (Optional) Type of proxy header to append before sending + data to the backend, either NONE or PROXY_V1 (default NONE). + +* `description` - (Optional) A description of this resource. Changing this + forces a new resource to be created. + +* `project` - (Optional) The project in which the resource belongs. If it + is not provided, the provider project is used. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are +exported: + +* `proxy_id` - A unique ID assigned by GCE. + +* `self_link` - The URI of the created resource. + +## Import + +SSL proxy can be imported using the `name`, e.g. + +``` +$ terraform import google_compute_target_ssl_proxy.default test +``` \ No newline at end of file diff --git a/website/google.erb b/website/google.erb index 9bb5035a5be..160d6c92f27 100644 --- a/website/google.erb +++ b/website/google.erb @@ -244,6 +244,10 @@ google_compute_target_https_proxy +