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

Adds support for creating KMS CryptoKeys resources #692

Merged
merged 29 commits into from
Nov 14, 2017

Conversation

amfarrell
Copy link
Contributor

No description provided.

@amfarrell
Copy link
Contributor Author

Still need to add docs.

Pushing now so that I can see the result of running the acceptance test because I've not yet figured out access to a setup with permissions to run acceptance tests.

@danawillow
Copy link
Contributor

Hey @amfarrell, looks like this doesn't build. Happy to run the tests for you once that's all squared away.

@tragiclifestories
Copy link
Contributor

@danawillow This should now build.

Interface looks like

resource "google_kms_key_ring" "mykeyring" {
  location = "global"
  name     = "mykeyring"
  project  = "myproject"
}

resource "google_kms_crypto_key" "mykey" {
  name     = "mykey"
  key_ring = "${google_kms_key_ring.mykeyring.id}"
}

resource "google_kms_crypto_key" "myrotatingkey" {
  name            = "myrotatingkey"
  key_ring        = "${google_kms_key_ring.mykeyring.id}"
  # Next rotation time will be set automatically to now + rotation_period
  rotation_period = "100000s"                             
}

@tragiclifestories
Copy link
Contributor

cc @mrparkers


log.Printf("[WARNING] KMS CryptoKey resources cannot be deleted from GCP. This CryptoKey %s will be removed from Terraform state, but will still be present on the server.", cryptoKeyId.cryptoKeyId())

d.SetId("")
Copy link
Contributor

Choose a reason for hiding this comment

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

We probably shouldn't do this until the very end of the function. clearCryptoKeyVersions could still error.

return err
}

log.Printf("[WARNING] KMS CryptoKey resources cannot be deleted from GCP. This CryptoKey %s will be removed from Terraform state, but will still be present on the server.", cryptoKeyId.cryptoKeyId())
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be helpful to add the bit about destroying all of the CryptoKey's versions to this warning message.


if cryptoKeyIdWithoutProjectRegex.MatchString(id) {
if config.Project == "" {
return nil, fmt.Errorf("The default project for the provider must be set when using the `{location}/{cryptoKeyName}` id format.")
Copy link
Contributor

Choose a reason for hiding this comment

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

You're missing the keyRingName in the example format

}, nil
}

return nil, fmt.Errorf("Invalid CryptoKey id format, expecting `{projectId}/{locationId}/{KeyringName}/{cryptoKeyName}` or `{locationId}/{KeyringName}/{cryptoKeyName}.`")
Copy link
Contributor

@mrparkers mrparkers Nov 9, 2017

Choose a reason for hiding this comment

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

Nitpick: Could you use keyRingName instead of KeyringName here?


if d.Get("purpose") == "" {
d.Set("purpose", "ENCRYPT_DECRYPT")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this needed? purpose isn't defined in your resource schema.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's one of those weird ones you get in GCP (and AWS as well) where there is a parameter that accepts an enum with one possible value (see the REST API docs for CryptoKey). My feeling is that it is not necessary to support this parameter as an argument to the resource, and instead just set it 'on the quiet', until there is another purpose value defined, at which point it will in any case be necessary to add more validation. But I'm new to the Terraform codebases, so perhaps it is better to be explicit but have a default?

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like its only being set during import though, not creation. I think we should either set it in both places, or neither. Like you mentioned, there's only one possible value for this enum, so I think we should just get rid of it until more are added.

resource "google_kms_crypto_key" "crypto_key" {
name = "%s"
key_ring = "${google_kms_key_ring.key_ring.id}"
rotation_period = "100000s"
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you fix the formatting here?

@@ -0,0 +1,63 @@
---
layout: "google"
page_title: "Google: google_kms_key_ring"
Copy link
Contributor

@mrparkers mrparkers Nov 9, 2017

Choose a reason for hiding this comment

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

Could you fix this? Should be google_kms_crypto_key

name = "my-crypto-key"
key_ring = "my_project/us-central1/my-key-ring"
rotation_period = "100000s"
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be nice to include a google_kms_key_ring resource here as well, so you could demonstrate using string interpolation for the key_ring field.

```
$ terraform import google_kms_key_ring.my_key_ring my-gcp-project/us-central1/my-key-ring/my-crypto-key

$ terraform import google_kms_key_ring.my_key_ring us-central1/my-key-ring/my-crypto-key
Copy link
Contributor

Choose a reason for hiding this comment

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

The resource name should be google_kms_crypto_key.my_crypto_key

ExpectedCryptoKeyId: "projects/test-project/locations/us-central1/keyRings/test-key-ring/cryptoKeys/test-key-id",
Config: &Config{Project: "test-project"},
},
"id is in location/keyRingName/CyptoKeyID format without project in config": {
Copy link
Contributor

@mrparkers mrparkers Nov 9, 2017

Choose a reason for hiding this comment

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

Nitpick: For each of these test descriptions, could you use cryptoKeyName instead of CryptoKeyID? This way, the format will be consistent with the error messages in parseKmsCryptoKeyId

"rotation_period": &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.

I believe a CryptoKey's rotation period can be changed, or at least there's an API for it.

If this changed, Update would have to be implemented as well. This change probably doesn't need to hold up the PR, but @danawillow can make that call.

Copy link
Contributor

Choose a reason for hiding this comment

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

I did bump into this, actually, but decided to defer it.

On reflection, it might be worth doing given that you can't destroy the damn things properly...

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 fine with deferring it to a follow-up PR!

d.SetId(cryptoKeyId.terraformId())

return []*schema.ResourceData{d}, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be a good idea to set rotation_period if it exists.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done - further up so comment isn't hidden ...


resource "google_kms_crypto_key" "crypto_key" {
name = "%s"
key_ring = "${google_kms_key_ring.key_ring.id}"
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you fix this indentation?

resource "google_kms_crypto_key" "crypto_key" {
name = "%s"
key_ring = "${google_kms_key_ring.key_ring.id}"
rotation_period = "%s"
Copy link
Contributor

Choose a reason for hiding this comment

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

Here as well

@mrparkers
Copy link
Contributor

mrparkers commented Nov 9, 2017

@tragiclifestories sorry for the confusion about the indentation. Most of it was fine, there were just a couple of lines where spaces were used instead of tabs. It looks like they were all changed to use spaces, which will probably need to be changed back to tabs.

The only reason I'm asking is because I received similar comments on my PR 😉

Copy link
Contributor

@mrparkers mrparkers left a comment

Choose a reason for hiding this comment

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

Thanks for those changes, this looks good to me

@tragiclifestories
Copy link
Contributor

And thanks for a very thorough review - I'm quite new to mucking about with terraform internals so it's all helpful!

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 @amfarrell, this is super exciting!
And wow @mrparkers you rock, thanks for making my life a lot easier by reviewing this!

I have a bunch of comments, but most of them are pretty small- overall this looks really good.

"os"
)

func TestAccGoogleKmsCryptoKey_importBasic(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add t.Parallel() (and then a newline) to the beginning of all tests?

"rotation_period": &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.

I'm fine with deferring it to a follow-up PR!

if err != nil {
return fmt.Errorf("Error reading CryptoKey: %s", err)
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Generally we like to set as many fields as possible inside of Read, in order to catch out-of-band modifications.

Copy link
Contributor

Choose a reason for hiding this comment

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

Just curious, are you just looking for rotation_period here? Or would you also set name and key_ring by getting it from cryptoKeyId?

Copy link
Contributor

@tragiclifestories tragiclifestories Nov 10, 2017

Choose a reason for hiding this comment

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

I went for rotation_period only, since I believe the other fields are immutable anyway. although the docs are unclear about this. Actually, in relation to the import passthrough comment I realised that it would be better to set them all here anyway.

return nil
}

func validateKmsCryptoKeyRotationPeriod(period string) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

This could actually probably become a ValidateFunc, which would allow doing these checks during terraform plan instead of during terraform apply. Here's an example of what that would look like (this example has it inlined but it can certainly be a separate fn): https://github.com/terraform-providers/terraform-provider-google/blob/master/google/resource_dataproc_cluster.go#L39

return nil, fmt.Errorf("Invalid CryptoKey id format, expecting `{projectId}/{locationId}/{KeyringName}/{cryptoKeyName}` or `{locationId}/{keyRingName}/{cryptoKeyName}.`")
}

func resourceKmsCryptoKeyImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is actually similar enough to the Read fn that you don't need a separate Import fn, but instead could use the passthrough one:

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

Then inside the read function, you would do the same d.Set calls that you're doing here, plus an additional one for the rotation_period.

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGoogleKmsCryptoKeyWasRemovedFromState("google_kms_crypto_key.crypto_key"),
Copy link
Contributor

Choose a reason for hiding this comment

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

Since your second TestStep calls this already, this is just duplicating that check- not wrong, but also not necessary.


_, err = time.Parse(time.RFC3339Nano, getCryptoKeyResponse.NextRotationTime)

return err
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be worth it to wrap this error, something like return fmt.Errorf("Error parsing NextRotationTime: %s", err)

config := testAccProvider.Meta().(*Config)
gcpResourceUri := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", projectId, location, keyRingName, cryptoKeyName)

response, _ := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.CryptoKeyVersions.List(gcpResourceUri).Do()
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd feel more comfortable if this error were handled: if we got a mysterious server error, then response.CryptoKeyVersions would (I believe) be empty, which means this test would succeed, even though we don't actually know that the versions were destroyed.

}

resource "google_kms_key_ring" "key_ring" {
project = "${google_project.acceptance.project_id}"
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 use ${google_project_services.acceptance.project} like in the basic test, so that we guarantee the API is enabled before trying to use it.

}

resource "google_kms_key_ring" "key_ring" {
project = "${google_project.acceptance.project_id}"
Copy link
Contributor

Choose a reason for hiding this comment

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

here too

@danawillow
Copy link
Contributor

Whoops sorry @tragiclifestories, I should be thanking you!

@tragiclifestories
Copy link
Contributor

@danawillow I think that's everything ...

func parseKmsCryptoKeyId(id string, config *Config) (*kmsCryptoKeyId, error) {
parts := strings.Split(id, "/")

cryptoKeyIdRegex := regexp.MustCompile("^([a-z0-9-]+)/([a-z0-9-])+/([a-zA-Z0-9_-]{1,63})+/([a-zA-Z0-9_-]{1,63})$")
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry I didn't catch this earlier- shouldn't the third plus not be there since it already has the {1, 63}? I think that would end up essentially overriding the {1, 63} by allowing it to repeat some number of times

parts := strings.Split(id, "/")

cryptoKeyIdRegex := regexp.MustCompile("^([a-z0-9-]+)/([a-z0-9-])+/([a-zA-Z0-9_-]{1,63})+/([a-zA-Z0-9_-]{1,63})$")
cryptoKeyIdWithoutProjectRegex := regexp.MustCompile("^([a-z0-9-])+/([a-zA-Z0-9_-]{1,63})+/([a-zA-Z0-9_-]{1,63})$")
Copy link
Contributor

Choose a reason for hiding this comment

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

likewise

}

/*
Because KMS CryptoKey resources cannot be deleted on GCP, we are only going to remove it from state
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this comment has mismatched tabs/spaces for indentation, mind choosing one or the other?

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed

Name string
}

// TODO: Add the info about rotation frequency and start time.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can you be a bit more specific here as to what you're adding it to? I don't think it belongs in the id, which is the section of code this comment is living in.

@danawillow
Copy link
Contributor

$ make testacc TEST=./google TESTARGS='-run=TestCryptoKey'
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./google -v -run=TestCryptoKey -timeout 120m
=== RUN   TestCryptoKeyIdParsing
=== RUN   TestCryptoKeyNextRotationCalculation
=== RUN   TestCryptoKeyNextRotationCalculation_validation
--- PASS: TestCryptoKeyNextRotationCalculation_validation (0.00s)
--- PASS: TestCryptoKeyNextRotationCalculation (0.00s)
--- PASS: TestCryptoKeyIdParsing (0.01s)
PASS
ok  	github.com/terraform-providers/terraform-provider-google/google	0.167s

$ make testacc TEST=./google TESTARGS='-run=TestAccGoogleKmsCryptoKey'
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./google -v -run=TestAccGoogleKmsCryptoKey -timeout 120m
=== RUN   TestAccGoogleKmsCryptoKey_importBasic
=== RUN   TestAccGoogleKmsCryptoKey_basic
=== RUN   TestAccGoogleKmsCryptoKey_rotation
--- PASS: TestAccGoogleKmsCryptoKey_importBasic (58.22s)
--- PASS: TestAccGoogleKmsCryptoKey_rotation (58.62s)
--- PASS: TestAccGoogleKmsCryptoKey_basic (61.67s)
PASS
ok  	github.com/terraform-providers/terraform-provider-google/google	61.851s

Looks great, thanks so much @tragiclifestories!

@danawillow danawillow merged commit 56d633a into hashicorp:master Nov 14, 2017
@tragiclifestories
Copy link
Contributor

You're welcome, and thanks for the thorough review!

danawillow added a commit that referenced this pull request Nov 16, 2017
Somehow this snuck in to #692 but it doesn't look like it's actually used anywhere.
danawillow added a commit that referenced this pull request Nov 16, 2017
Somehow this snuck in to #692 but it doesn't look like it's actually used anywhere.
chrisst pushed a commit to chrisst/terraform-provider-google that referenced this pull request Nov 9, 2018
* Adds support for creating KMS CryptoKeys resources

* Destroy extant CryptoKeyVersions on CryptoKey destroy

* Inherit project, location etc from KeyRing in CryptoKey

* Add function to calculate next rotation

* Implement RotationPeriod parameter on CryptoKey

* Import CryptoKey state

* Uncommit my local acceptance test hacks

* Docs for google_kms_crypto_key

* Clear id at the end of CryptoKey deletion

Also add more detail to warning message.

* Fix parseCryptoKeyId error messages

* Use correct naming in CryptoKeyIdParsing test

* Check RotationPeriod is present in acceptance test

* Rename variable in test function for consistency

* Fix wrong resource name in cryptokey docs

* Add KeyRing to CryptoKey doc example

* Run test CryptoKey configs through terraform fmt

* Don't set CryptoKey purpose in terraform state on import

* Fix indentation in CryptoKey test

* Parallelise CryptoKey tests

* Set rotation_key on CryptoKey read

* Move RotationPeriod validation to planning phase

* Use import state passthrough for CryptoKey

* Correct casing issues in test case names

* Remove redundant CheckDestroy calls in CryptoKey tests

* Add explanatory comment about extra test steps

* More explicit error handling in CryptoKey tests

* Explicit dependency on project services in test keyring configs

* Clean up comments in cryptokey resource

* Do not repeat in cryptokey id regexes
luis-silva pushed a commit to luis-silva/terraform-provider-google that referenced this pull request May 21, 2019
@amfarrell amfarrell deleted the kms-cryptokeys branch September 12, 2019 10:46
@ghost
Copy link

ghost commented Sep 12, 2019

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 👉 [email protected]. Thanks!

@ghost ghost locked and limited conversation to collaborators Sep 12, 2019
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.

4 participants