Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New resource/datasource: SSL Policy #1247

Merged
merged 22 commits into from
Mar 26, 2018
Merged

New resource/datasource: SSL Policy #1247

merged 22 commits into from
Mar 26, 2018

Conversation

nickjacques
Copy link
Contributor

@nickjacques nickjacques commented Mar 23, 2018

  • Add new datasource for SSL Policy
  • Add new resource for SSL Policy
  • Add docs for datasource and resource
  • Add unit tests for datasource and resource

Open questions:

  • In scenarios where the user has not provided an alphabetized list of custom_features, TF will show a "perpetual diff" of the same contents in a different order. Since I'm a bit new to both developing TF resources and developing in Go, I'm seeking suggestions in how to best handle this.
    • example terraform:
      resource "google_compute_ssl_policy" "test" {
        name = "test"
        profile         = "CUSTOM"
        custom_features = ["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_3DES_EDE_CBC_SHA"]
      }
      
    • example terraform plan output, post-creation of resource:
      Terraform will perform the following actions:
      
        ~ google_compute_ssl_policy.test
            custom_features.0: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
            custom_features.1: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" => "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
      
      
      Plan: 0 to add, 1 to change, 0 to destroy.
      
  • In scenarios where the user attempts to update two resource attributes at once, terraform apply will fail with:
    ~ google_compute_ssl_policy.nlj-test2
        min_tls_version:   "TLS_1_2" => "TLS_1_0"
        profile:           "RESTRICTED" => "COMPATIBLE"
    
    ...
    
      Error: Error applying plan:
    
    1 error(s) occurred:
    
    * google_compute_ssl_policy.test: 1 error(s) occurred:
    
    * google_compute_ssl_policy.test: Error updating SSL Policy: googleapi: Error 412: Invalid fingerprint., conditionNotMet
    
    • Presumably this is related to partial updates and the fingerprint changing between the two. Is there a good reference for updates that are not partial? I think a "consolidated" or full update may prevent this issue, as it will build the entire object (including all changed parameters) and thus the fingerprint won't change until that update has been processed by the API.

This PR will resolve #1228 and paves the way for #1229 and #1230

@danawillow danawillow self-requested a review March 23, 2018 16:17
@danawillow danawillow self-assigned this Mar 23, 2018
Copy link
Contributor

@danawillow danawillow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @nickjacques, this looks awesome!

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

func dataSourceGoogleComputeSslPolicy() *schema.Resource {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We actually have some helpers that can simplify this code a fair bit- take a look at data_source_google_container_cluster.go for an example. These'll help with code re-use, making it easier to keep the datasource/resource in sync in the future.

Schema: map[string]*schema.Schema{

"custom_features": &schema.Schema{
Type: schema.TypeList,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To answer your question- what you want here is a schema.TypeSet. There are a whole bunch of examples of these around the codebase but let me know if you get stuck with how to use it!

ValidateFunc: validation.StringInSlice([]string{"TLS_1_0", "TLS_1_1", "TLS_1_2", "TLS_1_3"}, false),
},

"name": &schema.Schema{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're trying right now to put attributes in the order Required -> Optional -> Computed-only, if you don't mind reordering these

return fmt.Errorf("Error creating SSL Policy: %s", err)
}

err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutDelete).Minutes()), "Creating SSL Policy")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: schema.TimeoutCreate

return err
}

d.SetId(sslPolicy.Name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually prefer to set the id between the Insert call and the Wait call- this way, if the terraform process stops for whatever reason, the Id is still set in state, meaning a subsequent refresh will get the created resource. Then if the wait operation stuff fails, you can do a d.SetId("") before returning the error

"google_compute_ssl_policy.update", &sslPolicy),
resource.TestCheckResourceAttr(
"google_compute_ssl_policy.update", "profile", "MODERN"),
//resource.TestCheckResourceAttr(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be uncommented?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this was a result of the Fingerprint issue (the test was originally written to update two attributes). I'll make the update a full instead of partial and these tests should work properly with two attributes.

@@ -0,0 +1,50 @@

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra newline


```tf
data "google_compute_ssl_policy" "my-ssl-policy" {
name = "production-ssl-policy"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra spaces between name and =


* `custom_features` - If the `profile` is `CUSTOM`, these are the custom encryption
ciphers supported by the profile. If the `profile` is *not* `CUSTOM`, this
attribute will be nil.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: since nil isn't really a terraform concept, let's say empty instead

"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is surprising to me, can you confirm that the description can't be changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went off of the details provided in gcloud beta compute ssl-policies update, which doesn't state that description is an updatable field, but I'll try against the API directly and see if it can be patched.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried updating two fields via API Explorer: minTlsVersion and description. The change was applied to minTlsVersion but not description, so I believe the ForceNew: true is valid. If someone wants to change the resource description, a new resource must be created.

@nickjacques
Copy link
Contributor Author

nickjacques commented Mar 23, 2018

Okay, I think these latest commits resolve most of the comments - thanks for the tip on using Sets @danawillow, that worked perfectly! The only issue I'm encountering now is when updating a resource from:

profile         = "CUSTOM"
custom_features = ["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_3DES_EDE_CBC_SHA"]

to something like:

profile = "MODERN"

In this scenario, I receive an error from the underlying API:

google_compute_ssl_policy.test: Modifying... (ID: test)
  custom_features.#:          "3" => "0"
  custom_features.2273472042: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" => ""
  custom_features.3685213100: "TLS_RSA_WITH_3DES_EDE_CBC_SHA" => ""
  custom_features.933849229:  "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => ""
  profile:                    "CUSTOM" => "COMPATIBLE"

Error: Error applying plan:

1 error(s) occurred:

* google_compute_ssl_policy.test: 1 error(s) occurred:

* google_compute_ssl_policy.test: Error updating SSL Policy: googleapi: Error 400: Invalid value for field 'resource.customFeatures': ''. Custom features cannot be specified when the SslPolicy profile is not 'CUSTOM', invalid

So it seems that I need to delete the sslPolicy.CustomFeatures node before I send the request to the patch API, but I'm unsure of how to do that. I tried setting sslPolicy.CustomFeatures = nil if the length is 0, but I'm still hitting the same error. Once this defect is fixed, I can write a test to ensure we don't regress in the future.

Copy link
Contributor

@danawillow danawillow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the question about updating- the client library has a feature where it doesn't send fields that are set to the zero value to the API. You have to explicitly include them with ForceSendFields or NullFields. We use these a fair bit in the provider so you should be able to find an example. When you're testing it out, I also highly recommend running with debug logs on (TF_LOG=DEBUG) so you can see the requests/responses to/from the API. That'll help verify that the request you're sending is the one you expect. Hope that helps :)

addRequiredFieldsToSchema(dsSchema, "name")

// Set 'Optional' schema elements
addOptionalFieldsToSchema(dsSchema, "custom_features", "description", "min_tls_version", "profile", "project")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably just want "project" here- an optional field in a data source is one that you can choose to set in the data source itself, rather than all the fields that it might return. Setting something like custom_features wouldn't have any effect, because the data source is read-only.

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's distressing. Is there an issue in the public issue tracker that we could link to here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that I'm aware of, but I can file one if you'd like! Relevant lines are 21109-21118 in compute/v0.beta/compute-gen.go.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't mind, that would be great! That way if someone stumbles across this part of the code and sees a comment with a link to an issue that's been fixed, we'd be able to add TLS_1_3 real quick

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, can do! Which product should I file it under? Cloud SDK?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not super familiar with how the products are laid out in components, but if there's one for compute admin api, or networking api, or something along those lines. I'm not going to block this PR on getting that link in there though. I'll finish up the review now :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, I just added a link to the issue that Aaron created. (Thanks Aaron!)

return fmt.Errorf("Error updating SSL Policy: %s", err)
}

err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutCreate).Minutes()), "Updating SSL Policy")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be schema.TimeoutUpdate

return nil
}

func testAccCheckComputeSslPolicyCustomFeatures(sslPolicy *computeBeta.SslPolicy, n string) resource.TestCheckFunc {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can actually get rid of this test function- the import check will verify this on its own (it'll call read and then try to populate state from it and compare it to the config, so if custom_features wasn't set upstream then the import would fail)

Gets an SSL Policy within GCE, for use with Target HTTPS and Target SSL Proxies.
---

# google\_compute\_ssl\_policies
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/policies/policy

Manages an SSL Policy within GCE, for use with Target HTTPS and Target SSL Proxies.
---

# google\_compute\_ssl\_policies
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

@nickjacques
Copy link
Contributor Author

Thanks @danawillow NullFields was indeed exactly what I needed! Updates that enable and disable custom_features now work as expected, and I also have included tests for these scenarios. I've also added enabled_features as a computed field that folks can use to output what cipher suites resulted from their policy configuration. I think this is ready to go if things look good to you!

Copy link
Contributor

@danawillow danawillow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Just a few small nitpicks, if you don't mind :)

Fingerprint: d.Get("fingerprint").(string),
}

if v, ok := d.GetOk("profile"); ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: There isn't actually any difference between doing it this way and just setting it in the struct, since if the value is empty it'll be the empty string in the struct regardless.

resource.TestCheckResourceAttr(
"google_compute_ssl_policy.update", "min_tls_version", "TLS_1_0"),
),
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's also add the import test step here (and similarly after all test steps) juuuuuuuust in case

@nickjacques
Copy link
Contributor Author

No worries at all! I've added Profile and minTlsVersion back into the struct, and dropped in imports between steps in the multi-step tests.

@danawillow
Copy link
Contributor

👍

@danawillow danawillow merged commit 72efdb1 into hashicorp:master Mar 26, 2018
chrisst pushed a commit to chrisst/terraform-provider-google that referenced this pull request Nov 9, 2018
* 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
@ghost
Copy link

ghost commented Nov 19, 2018

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 hashibot-feedback@hashicorp.com. Thanks!

@ghost ghost locked and limited conversation to collaborators Nov 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[new resource] Support SSL Policies
2 participants