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

add secretmanager resources and secret_version data source #3007

Merged
merged 14 commits into from
Jan 31, 2020

Conversation

megan07
Copy link
Contributor

@megan07 megan07 commented Jan 22, 2020

Fixes hashicorp/terraform-provider-google#5168

Added secretmanager service with secret and secret_version resources and secret_version data source. I have not yet added the ability to enable/disable the secret_version resource.

Release Note Template for Downstream PRs (will be copied)

secretmanager: added `secret` and `secret_version` resources and `secret_version` datasource

@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician. Your PR generated some diffs in downstreams - here they are.

Diff report:

Terraform GA: Diff
Terraform Beta: Diff
Ansible: Diff
TF Conversion: Diff
Inspec: Diff

@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician, I work on Magic Modules.
I see that this PR has already had some downstream PRs generated. Any open downstreams are already updated to your most recent commit, fda1c6d.

Pull request statuses

No diff detected in terraform-google-conversion.
No diff detected in Ansible.
No diff detected in Inspec.

New Pull Requests

I built this PR into one or more new PRs on other repositories, and when those are closed, this PR will also be merged and closed.
depends: hashicorp/terraform-provider-google-beta#1669
depends: hashicorp/terraform-provider-google#5473

@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician. Your PR generated some diffs in downstreams - here they are.

Diff report:

Terraform GA: Diff
Terraform Beta: Diff
Ansible: Diff
TF Conversion: Diff
Inspec: Diff

@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician, I work on Magic Modules.
I see that this PR has already had some downstream PRs generated. Any open downstreams are already updated to your most recent commit, cebf43e.

Pull request statuses

terraform-provider-google-beta already has an open PR.
No diff detected in terraform-google-conversion.
terraform-provider-google already has an open PR.
No diff detected in Ansible.
No diff detected in Inspec.

New Pull Requests

I didn't open any new pull requests because of this PR.

- !ruby/object:Provider::Terraform::Examples
name: "secret_version_basic"
primary_resource_id: "secret-version-basic"
primary_resource_name: "fmt.Sprintf(\"test-secret-version-basic%s\", context[\"random_suffix\"])"
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you need this field if there is no IAM policy for this resource

parts := secretVersionRegex.FindStringSubmatch(version["name"].(string))
// should return [full string, project number, secret name, version number]
if len(parts) != 4 {
return fmt.Errorf("secret name, %s, does not match format, projects/{{project}}/secrets/{{secret}}/versions/{{versions}}", version["name"].(string))
Copy link
Contributor

Choose a reason for hiding this comment

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

{{versions}} -> {{version}}

}

data "google_secretmanager_secret_version" "basic" {
provider = google-beta
Copy link
Contributor

Choose a reason for hiding this comment

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

Mixed tabs + spaces in this file


The following attributes are exported:

* `secret_data` - The secret data. No larger than 64KiB. A base64-encoded string.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we have a data source that exposes secret_data when we are using ignore_read on the field in the TF resource? This field corresponds to SecretVersion.payload.data right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, read the ds more closely. We make a separate call to :access to retrieve the data?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea. I wasn't exactly sure how to handle this in the resource, and it's the same case with :enable and :disable, they're all separate calls. I'm going to spend some more time looking into it, but if you have any suggestions, please let me know!


parts := secretVersionRegex.FindStringSubmatch(version["name"].(string))
// should return [full string, project number, secret name, version number]
if len(parts) != 4 {
Copy link
Contributor

Choose a reason for hiding this comment

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

There is no way this should fail, right?

version["name"] is coming back from the API, not user input. If it isn't in the correct format then something has gone horribly wrong and the user cannot fix it

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 believe so. Does this mean we don't want the error here? I just had it in as a just-in-case since we access parts[3] later.

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 probably fine. It would be confusing for a user to see this error and think that they need to fix it, and a panic might be more obvious that the error is in the provider (or API), but I don't have strong opinions and checking before array access seems like a good idea

versionNum := d.Get("version")

if versionNum != "" {
url, err = replaceVars(d, config, "{{SecretmanagerBasePath}}projects/{{project}}/secrets/{{secret}}/versions/{{version}}")
Copy link
Contributor

Choose a reason for hiding this comment

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

These will always pick up the default project config rather than the project contained in secret if it is a long name?

If the config looks like:

provider {
  project = "my-other-project"
}

"google_secret_version" "my-secret" {
  secret = "projects/my-project1/secrets/my-secret"
}

We will pick up project as "my-other-project" I believe

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, yes, that makes sense. Should I d.Set("project", fv.Project) up by line 65 then?

Copy link
Contributor

Choose a reason for hiding this comment

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

But then we will override an explicit project set on the secret_version itself. Like:

"google_secret_version" "my-secret" {
  project = "my-other-project"
  secret = "projects/my-project1/secrets/my-secret"
}

But that might be ok? Maybe check d.Get("project") and if set & not equal to fv.Project then throw an error. Otherwise d.Set("project", fv.Project)?

# limitations under the License.
--- !ruby/object:Provider::Terraform::Config
overrides: !ruby/object:Overrides::ResourceOverrides
Secret: !ruby/object:Overrides::Terraform::ResourceOverride
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 you will need to add custom import_formats due to the URL containing secret_id instead of name. If you look at https://github.com/terraform-providers/terraform-provider-google-beta/pull/1669/files#diff-cc818fd7ea3159b3e18bf79d36939d33R283

I think this will cause imports to fail.

I would also add handwritten import tests because we don't generate them for beta resources

custom_flatten: templates/terraform/custom_flatten/object_to_bool.go.erb
custom_expand: templates/terraform/custom_expand/bool_to_object.go.erb

SecretVersion: !ruby/object:Overrides::Terraform::ResourceOverride
Copy link
Contributor

Choose a reason for hiding this comment

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

How will import work for this resource if the important part (payload.data) is ignore_read?

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'm not sure. I just wrote the test, and it doesn't work. Hopefully, if I can figure out how to deal with the :access, :enable and :disable on this resource, it will solve this as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

Possibly a custom flatten on payload.data that calls the :access endpoint?

We have delete_url that you can specify on a resource, but I'm not sure how the :enable and :disable actually work for this API

description: |
The canonical IDs of the location to replicate data. For example: "us-east1".
- !ruby/object:Api::Resource
name: SecretVersion
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 updatable? Looking at the API it doesn't look like it

@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician. Your PR generated some diffs in downstreams - here they are.

Diff report:

Terraform GA: Diff
Terraform Beta: Diff
Ansible: Diff
TF Conversion: Diff
Inspec: Diff

1 similar comment
@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician. Your PR generated some diffs in downstreams - here they are.

Diff report:

Terraform GA: Diff
Terraform Beta: Diff
Ansible: Diff
TF Conversion: Diff
Inspec: Diff

@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician, I work on Magic Modules.
I see that this PR has already had some downstream PRs generated. Any open downstreams are already updated to your most recent commit, 70e7f73.

Pull request statuses

terraform-provider-google-beta already has an open PR.
No diff detected in terraform-google-conversion.
terraform-provider-google already has an open PR.
No diff detected in Ansible.
No diff detected in Inspec.

New Pull Requests

I didn't open any new pull requests because of this PR.

# limitations under the License.

--- !ruby/object:Api::Product
name: Secretmanager
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
name: Secretmanager
name: SecretManager

I think google_secret_manager_ is more in line with the naming convention we tend to use on new products. Is there something I'm missing that would make us prefer the secretmanager slug?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you! I had that originally then changed my mind after looking at a different service. I'll change it back, as I like this better too. Thanks!

# See the License for the specific language governing permissions and
# limitations under the License.
-%>
func expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI I copied this & object_to_bool into another PR that is in master now, so when you rebase they should already exist

@@ -94,7 +94,7 @@ def updatable?(resource, properties)

def force_new?(property, resource)
!property.output &&
(property.input || (resource.input && property.update_url.nil? &&
(property.input || (resource.input && property.update_url.nil? && property.input.nil? &&
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 added this in here so that if a resource is input: true, a child property can overwrite that. In this case, I added an Update method in the resource_definition that would allow enabled to be updated (even though SecretVersion has no update method, only :enable or :disable), when the resource technically can't be updated. This is an assumption on my part that an explicit input: false on a child property can override this, but I'm not familiar with Ruby, or with all resources in the project to know if this will be problematic or not.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason to not mark the individual fields that cannot be updated as input: true and leave the enabled as the only one that is not input or output?

I think this would accomplish the same thing without weird input negation via overridden properties

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 tried that, however then it seems to generate the Update function, and in the generated Update function it makes a Put request, which isn't an option on the object. So I needed an Update function that only called the expand function (because its in that function that I call the :enable or :disable. Does that make sense?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yeah, and you can only have a single endpoint as the update_url on a field. In general you can have custom update_url and update_verb or something, but I don't think that will work in this case because it has to be :enable or :disable depending on the state

@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician. Your PR generated some diffs in downstreams - here they are.

Diff report:

Terraform GA: Diff
Terraform Beta: Diff


* `destroy_time` - The time at which the Secret was destroyed. Only present if state is DESTROYED.

* `state` - The current state of the SecretVersion.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I change this to be enabled to be consistent with the resource? Or since it's read-only, does it matter?

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 be consistent with the resource, but I don't think it's a big deal?

@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician. Your PR generated some diffs in downstreams - here they are.

Diff report:

Terraform GA: Diff
Terraform Beta: Diff

@@ -94,7 +94,7 @@ def updatable?(resource, properties)

def force_new?(property, resource)
!property.output &&
(property.input || (resource.input && property.update_url.nil? &&
(property.input || (resource.input && property.update_url.nil? && property.input.nil? &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason to not mark the individual fields that cannot be updated as input: true and leave the enabled as the only one that is not input or output?

I think this would accomplish the same thing without weird input negation via overridden properties

accessRes, err = sendRequest(config, "GET", project, url, nil)
if err != nil {
// if this secret version is disabled, the api will return an error, as the value cannot be accessed
if strings.Contains(err.Error(), "is not in ENABLED state") && d.Get("enabled").(bool) == false {
Copy link
Contributor

Choose a reason for hiding this comment

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

What would happen if the API error message changed at some point in the future? Trying to read or do any operations on a disabled secret version would throw an error, right?

Is there a way we can check the state of enabled and if it is disabled, not even make this call?

Though it might entail depending on the order of the calls of the flatten functions, and I'm not sure that's a change we want to make

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that's correct. The error bubbles up to the flattenedProp loop, which is less than ideal because it panics when it tries to cast it into a string, but yea - I suppose I can add a check in there to see if it's an error so that it errors nicely in that case.
I tried checking the state of enabled, but yea, it was an order deal, I think. I'll run through again and see specifically what case that was in. I did a lot of testing/moving around code with this one, so it could be that after I fixed something else, this might work now.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would try to get this flatten call to happen after we flatten the state so that we can check d.Get("state") and not make the :access call if state is disabled. Maybe moving the fields around within api.yaml can fix it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That worked! Thanks!

@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician. Your PR generated some diffs in downstreams - here they are.

Diff report:

Terraform GA: Diff
Terraform Beta: Diff


// Get the payload
url = fmt.Sprintf("%s:access", url)
resp, err := sendRequest(config, "GET", project, url, nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

This will error if the secret version is disabled. Do we want to check the state here and only read the payload if it is enabled?

Or throw an error saying that we can't retrieve the payload if it is disabled

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. I updated it so that secret_data would just return "" on disabled, but I can see both ways.
In one way, throwing an error would stay consistent with the API, but in another way (and I'm not sure of a use case for this) if the user wanted to see the other information associated with the version, it would still retrieve that information.
I could go either way.

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 lean towards not throwing an error if we know how to prevent it. The downside of returning "" is pretty minimal

@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician. Your PR generated some diffs in downstreams - here they are.

Diff report:

Terraform GA: Diff
Terraform Beta: Diff

@slevenick slevenick self-requested a review January 31, 2020 17:33
@modular-magician
Copy link
Collaborator

Hi! I'm the modular magician. Your PR generated some diffs in downstreams - here they are.

Diff report:

Terraform GA: Diff ( 135 files changed, 3146 insertions(+), 3199 deletions(-))
Terraform Beta: Diff ( 164 files changed, 4164 insertions(+), 6720 deletions(-))

@megan07 megan07 merged commit dcadfcf into GoogleCloudPlatform:master Jan 31, 2020
payload.data: !ruby/object:Overrides::Terraform::PropertyOverride
name: secret_data
sensitive: true
custom_expand: templates/terraform/custom_expand/base64.go.erb
Copy link
Member

Choose a reason for hiding this comment

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

@megan07 @slevenick - the secret should not need to be base64-encoded. That feels like unnecessary user friction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature Request: Add support for GCP Secret Manager
7 participants