Skip to content

Commit

Permalink
New resource/datasource: SSL Policy (#1247)
Browse files Browse the repository at this point in the history
* Add SSL Policy to provider

* Add resource for SSL Policy

* Add SSL Policy data source

* Add tests for SSL Policy resource

* Add documentation for SSL Policy resource

* Add SSL Policy datasource docs

* Add test for SSL Policy datasource

* Update SSL Policy datasource docs

* Make full update for SSL Policy resource

* SSL Policy resource test multi-attrib update

* Clean up SSL Policy datasource

* Set-ify custom_features in SSL Policy resource

* Document description ForceNew rationale

* Remove refs to TLS_1_3

* Update docs: plural -> singular

* Remove extraneous attrs from datasource

* Fix update logic for custom_features and add enabled_features

* Update docs to include enabled_features

* Add test for updating to/from custom_features

* Add TLS 1.3 bug link

* Add import between multi-step test configs

* Move Profile and minTlsVersion back into sslPolicy struct
  • Loading branch information
nickjacques authored and danawillow committed Mar 26, 2018
1 parent a608554 commit 72efdb1
Show file tree
Hide file tree
Showing 8 changed files with 897 additions and 0 deletions.
29 changes: 29 additions & 0 deletions google/data_source_google_compute_ssl_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package google

import (
"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceGoogleComputeSslPolicy() *schema.Resource {
// Generate datasource schema from resource
dsSchema := datasourceSchemaFromResourceSchema(resourceComputeSslPolicy().Schema)

// Set 'Required' schema elements
addRequiredFieldsToSchema(dsSchema, "name")

// Set 'Optional' schema elements
addOptionalFieldsToSchema(dsSchema, "project")

return &schema.Resource{
Read: datasourceComputeSslPolicyRead,
Schema: dsSchema,
}
}

func datasourceComputeSslPolicyRead(d *schema.ResourceData, meta interface{}) error {
policyName := d.Get("name").(string)

d.SetId(policyName)

return resourceComputeSslPolicyRead(d, meta)
}
83 changes: 83 additions & 0 deletions google/data_source_google_compute_ssl_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package google

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccDataSourceGoogleSslPolicy(t *testing.T) {
t.Parallel()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDataSourceGoogleSslPolicy(),
Check: resource.ComposeTestCheckFunc(
testAccDataSourceGoogleSslPolicyCheck("data.google_compute_ssl_policy.ssl_policy", "google_compute_ssl_policy.foobar"),
),
},
},
})
}

func testAccDataSourceGoogleSslPolicyCheck(data_source_name string, resource_name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
ds, ok := s.RootModule().Resources[data_source_name]
if !ok {
return fmt.Errorf("root module has no resource called %s", data_source_name)
}

rs, ok := s.RootModule().Resources[resource_name]
if !ok {
return fmt.Errorf("can't find %s in state", resource_name)
}

ds_attr := ds.Primary.Attributes
rs_attr := rs.Primary.Attributes

ssl_policy_attrs_to_test := []string{
"id",
"self_link",
"name",
"description",
"min_tls_version",
"profile",
"custom_features",
}

for _, attr_to_check := range ssl_policy_attrs_to_test {
if ds_attr[attr_to_check] != rs_attr[attr_to_check] {
return fmt.Errorf(
"%s is %s; want %s",
attr_to_check,
ds_attr[attr_to_check],
rs_attr[attr_to_check],
)
}
}

return nil
}
}

func testAccDataSourceGoogleSslPolicy() string {
return fmt.Sprintf(`
resource "google_compute_ssl_policy" "foobar" {
name = "%s"
description = "my-description"
min_tls_version = "TLS_1_2"
profile = "MODERN"
}
data "google_compute_ssl_policy" "ssl_policy" {
name = "${google_compute_ssl_policy.foobar.name}"
}
`, acctest.RandomWithPrefix("test-ssl-policy"))
}
2 changes: 2 additions & 0 deletions google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func Provider() terraform.ResourceProvider {
"google_compute_region_instance_group": dataSourceGoogleComputeRegionInstanceGroup(),
"google_compute_vpn_gateway": dataSourceGoogleComputeVpnGateway(),
"google_compute_forwarding_rule": dataSourceGoogleComputeForwardingRule(),
"google_compute_ssl_policy": dataSourceGoogleComputeSslPolicy(),
"google_container_cluster": dataSourceGoogleContainerCluster(),
"google_container_engine_versions": dataSourceGoogleContainerEngineVersions(),
"google_container_registry_repository": dataSourceGoogleContainerRepo(),
Expand Down Expand Up @@ -130,6 +131,7 @@ func Provider() terraform.ResourceProvider {
"google_compute_shared_vpc_host_project": resourceComputeSharedVpcHostProject(),
"google_compute_shared_vpc_service_project": resourceComputeSharedVpcServiceProject(),
"google_compute_ssl_certificate": resourceComputeSslCertificate(),
"google_compute_ssl_policy": resourceComputeSslPolicy(),
"google_compute_subnetwork": resourceComputeSubnetwork(),
"google_compute_target_http_proxy": resourceComputeTargetHttpProxy(),
"google_compute_target_https_proxy": resourceComputeTargetHttpsProxy(),
Expand Down
239 changes: 239 additions & 0 deletions google/resource_compute_ssl_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
package google

import (
"fmt"
"time"

"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
computeBeta "google.golang.org/api/compute/v0.beta"
)

func resourceComputeSslPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceComputeSslPolicyCreate,
Read: resourceComputeSslPolicyRead,
Update: resourceComputeSslPolicyUpdate,
Delete: resourceComputeSslPolicyDelete,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(2 * time.Minute),
Update: schema.DefaultTimeout(2 * time.Minute),
Delete: schema.DefaultTimeout(2 * time.Minute),
},

Schema: map[string]*schema.Schema{

"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"custom_features": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},

"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true, // currently, the beta patch API call does not allow updating the description
},

"min_tls_version": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "TLS_1_0",
// Although compute-gen.go says that TLS_1_3 is a valid value, the API currently (26 Mar 2018)
// responds with an HTTP 200 but doesn't actually create/update the policy. Open bug for this:
// https://issuetracker.google.com/issues/76433946
ValidateFunc: validation.StringInSlice([]string{"TLS_1_0", "TLS_1_1", "TLS_1_2"}, false),
},

"profile": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "COMPATIBLE",
ValidateFunc: validation.StringInSlice([]string{"COMPATIBLE", "MODERN", "RESTRICTED", "CUSTOM"}, false),
},

"project": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},

"enabled_features": &schema.Schema{
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},

"fingerprint": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},

"self_link": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},

CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error {
profile := diff.Get("profile")
customFeaturesCount := diff.Get("custom_features.#")

// Validate that policy configs aren't incompatible during all phases
// CUSTOM profile demands non-zero custom_features, and other profiles (i.e., not CUSTOM) demand zero custom_features
if diff.HasChange("profile") || diff.HasChange("custom_features") {
if profile.(string) == "CUSTOM" {
if customFeaturesCount.(int) == 0 {
return fmt.Errorf("Error in SSL Policy %s: the profile is set to %s but no custom_features are set.", diff.Get("name"), profile.(string))
}
} else {
if customFeaturesCount != 0 {
return fmt.Errorf("Error in SSL Policy %s: the profile is set to %s but using custom_features requires the profile to be CUSTOM.", diff.Get("name"), profile.(string))
}
}
return nil
}
return nil
},
}
}

func resourceComputeSslPolicyCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

project, err := getProject(d, config)
if err != nil {
return err
}

sslPolicy := &computeBeta.SslPolicy{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
Profile: d.Get("profile").(string),
MinTlsVersion: d.Get("min_tls_version").(string),
CustomFeatures: convertStringSet(d.Get("custom_features").(*schema.Set)),
}

op, err := config.clientComputeBeta.SslPolicies.Insert(project, sslPolicy).Do()
if err != nil {
return fmt.Errorf("Error creating SSL Policy: %s", err)
}

d.SetId(sslPolicy.Name)

err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutCreate).Minutes()), "Creating SSL Policy")
if err != nil {
d.SetId("") // if insert fails, remove from state
return err
}

return resourceComputeSslPolicyRead(d, meta)
}

func resourceComputeSslPolicyRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

project, err := getProject(d, config)
if err != nil {
return err
}

name := d.Id()

sslPolicy, err := config.clientComputeBeta.SslPolicies.Get(project, name).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("SSL Policy %q", name))
}

d.Set("name", sslPolicy.Name)
d.Set("description", sslPolicy.Description)
d.Set("min_tls_version", sslPolicy.MinTlsVersion)
d.Set("profile", sslPolicy.Profile)
d.Set("fingerprint", sslPolicy.Fingerprint)
d.Set("project", project)
d.Set("self_link", ConvertSelfLinkToV1(sslPolicy.SelfLink))
d.Set("enabled_features", convertStringArrToInterface(sslPolicy.EnabledFeatures))
d.Set("custom_features", convertStringArrToInterface(sslPolicy.CustomFeatures))

d.SetId(sslPolicy.Name)

return nil
}

func resourceComputeSslPolicyUpdate(d *schema.ResourceData, meta interface{}) error {

config := meta.(*Config)

project, err := getProject(d, config)
if err != nil {
return err
}

name := d.Get("name").(string)

sslPolicy := &computeBeta.SslPolicy{
Fingerprint: d.Get("fingerprint").(string),
Profile: d.Get("profile").(string),
MinTlsVersion: d.Get("min_tls_version").(string),
}

if v, ok := d.Get("custom_features").(*schema.Set); ok {
if v.Len() > 0 {
sslPolicy.CustomFeatures = convertStringSet(v)
} else {
sslPolicy.NullFields = append(sslPolicy.NullFields, "CustomFeatures")
}
}

op, err := config.clientComputeBeta.SslPolicies.Patch(project, name, sslPolicy).Do()
if err != nil {
return fmt.Errorf("Error updating SSL Policy: %s", err)
}

err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutUpdate).Minutes()), "Updating SSL Policy")
if err != nil {
return err
}

return resourceComputeSslPolicyRead(d, meta)
}

func resourceComputeSslPolicyDelete(d *schema.ResourceData, meta interface{}) error {

config := meta.(*Config)

project, err := getProject(d, config)
if err != nil {
return err
}

name := d.Get("name").(string)

op, err := config.clientComputeBeta.SslPolicies.Delete(project, name).Do()
if err != nil {
return fmt.Errorf("Error deleting SSL Policy: %s", err)
}

err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutDelete).Minutes()), "Deleting Subnetwork")
if err != nil {
return err
}

d.SetId("")

return nil
}
Loading

0 comments on commit 72efdb1

Please sign in to comment.