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

Possibility to use template and template-file annotations without sec… #505

Merged
merged 8 commits into from
Aug 16, 2023

Conversation

saturn4er
Copy link
Contributor

Summary

This Pull Request introduces enhancements to the Vault Kubernetes integration by simplifying the secret injection process. Specifically, it removes the need for the redundant and ineffective vault.hashicorp.com/agent-inject-secret-foo: someKey annotation.

Motivation

In the current implementation, users are required to include the vault.hashicorp.com/agent-inject-secret-foo: someKey annotation in their configuration. However, this annotation doesn't contribute to the functionality and only adds unnecessary complexity.

Changes Introduced

With this PR, users will no longer need to specify the vault.hashicorp.com/agent-inject-secret-foo: someKey annotation. Instead, the necessary information about secrets can be fully determined by the provided templates or template files, such as vault.hashicorp.com/agent-inject-template-foo: "{{- with secret "someKey" -}} {{- end }}" or vault.hashicorp.com/agent-inject-template-file-foo: "someFile".
This results in a cleaner and more intuitive configuration.

@hashicorp-cla
Copy link

hashicorp-cla commented Jul 30, 2023

CLA assistant check
All committers have signed the CLA.

Copy link
Contributor

@tomhjp tomhjp left a comment

Choose a reason for hiding this comment

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

I really like the concept for this, and would love to get it merged. My comments just resolve around wanting to preserve backwards compatibility so we don't break existing users. We can discuss a breaking change if you think it's necessary, but my read is that it shouldn't be too hard to avoid it.

agent-inject/agent/annotations.go Show resolved Hide resolved
agent-inject/agent/annotations_test.go Outdated Show resolved Hide resolved
agent-inject/agent/annotations_test.go Outdated Show resolved Hide resolved
agent-inject/agent/config_test.go Outdated Show resolved Hide resolved
@saturn4er
Copy link
Contributor Author

@tomhjp Ready, old test cases are now works and I added new ones to cover case when only template or template-file annotation set up

Comment on lines 627 to 641
for _, secret := range templateSecrets {
secrets = append(secrets, secret)
}

templateName := fmt.Sprintf("%s-%s", AnnotationAgentInjectTemplate, raw)
if val, ok := a.Annotations[templateName]; ok {
s.Template = val
}
if s.Template == "" {
templateFileAnnotation := fmt.Sprintf("%s-%s", AnnotationAgentInjectTemplateFile, raw)
if val, ok := a.Annotations[templateFileAnnotation]; ok {
s.TemplateFile = val
}
}
for _, secret := range simpleSecrets {
if templateSecrets[secret.Name] == nil && templateFileSecrets[secret.Name] == nil {
secrets = append(secrets, secret)
}
}

s.MountPath = a.Annotations[AnnotationVaultSecretVolumePath]
mountPathAnnotationName := fmt.Sprintf("%s-%s", AnnotationVaultSecretVolumePath, raw)
if val, ok := a.Annotations[mountPathAnnotationName]; ok {
s.MountPath = val
}
for _, secret := range templateFileSecrets {
if templateSecrets[secret.Name] == nil {
secrets = append(secrets, secret)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The order of precedence here doesn't look quite right to me. To make the new behaviour a superset of the previous behaviour, we should always set secret.Path and secret.Template if those annotations are present. Then we should set secret.TemplateFile if that annotation is present and secret.Template == "".

I think we could probably simplify by separating the logic for parsing secret names from the logic for reading in config for each secret. It might not be quite right, but I pushed b00bf8d to illustrate the idea.

Copy link
Contributor Author

@saturn4er saturn4er Aug 4, 2023

Choose a reason for hiding this comment

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

As you can check, Path is only required in case when Template is not set. to put this value in template, so my changes won't affect anything. Here's the only one place where Path is used: https://github.com/hashicorp/vault-k8s/blob/main/agent-inject/agent/config.go#L186.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So if we're setting Path unconditionally, why we should set TemplateFile conditionally? I pushed some changes, please check if you're ok with them

@saturn4er
Copy link
Contributor Author

@tomhjp any issues with this implementation?

Copy link
Contributor

@tomhjp tomhjp left a comment

Choose a reason for hiding this comment

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

Sorry for the delay doing another round of reviewing. This looks really close now, one change that I think is a requirement and the other comments are just nits.

if len(agent.Secrets) == 0 {
t.Error("Secrets length was zero, it shouldn't have been")
}
var found bool
Copy link
Contributor

Choose a reason for hiding this comment

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

The updated assertions for this test are reasonable, but please could we tighten them a bit further? The test case input should probably now specify an array of struct { key, template string } to expect so it can handle the fourth test case precisely.

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 rewrote it to maps

@@ -495,7 +526,7 @@ func TestSecretTemplateFileAnnotations(t *testing.T) {
"vault.hashicorp.com/agent-inject-secret-foobar": "test1",
"vault.hashicorp.com/agent-inject-template-foobar": "foobarTemplate",
"vault.hashicorp.com/agent-inject-template-file-foobar": "/etc/config.tmpl",
}, "foobar", "foobarTemplate", "",
}, "foobar", "foobarTemplate", "/etc/config.tmpl",
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 this change makes sense in the context of what the test is trying to assert. And I think it's highlighting a backwards incompatibility. I'll leave another comment on the actual fix site.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As you wish :)

for _, secret := range secrets {
secret.Path = a.annotationsSecretValue(AnnotationAgentInjectSecret, secret.RawName, secret.Path)
secret.Template = a.annotationsSecretValue(AnnotationAgentInjectTemplate, secret.RawName, secret.Template)
secret.TemplateFile = a.annotationsSecretValue(AnnotationAgentInjectTemplateFile, secret.RawName, secret.TemplateFile)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
secret.TemplateFile = a.annotationsSecretValue(AnnotationAgentInjectTemplateFile, secret.RawName, secret.TemplateFile)
if secret.Template == "" {
secret.TemplateFile = a.annotationsSecretValue(AnnotationAgentInjectTemplateFile, secret.RawName, secret.TemplateFile)
}

I know you disagreed with this elsewhere, but I still think this is worth doing. Here's where the downstream processing of secret.Template and secret.TemplateFile takes place:

var templates []*Template
for _, secret := range a.Secrets {
template := secret.Template
templateFile := secret.TemplateFile
if templateFile == "" {
template = secret.Template
if template == "" {
switch a.DefaultTemplate {
case "json":
template = fmt.Sprintf(DefaultJSONTemplate, secret.Path)
case "map":
template = fmt.Sprintf(DefaultMapTemplate, secret.Path)
}
}
}
filePathAndName := fmt.Sprintf("%s/%s", secret.MountPath, secret.Name)
if secret.FilePathAndName != "" {
filePathAndName = filepath.Join(secret.MountPath, secret.FilePathAndName)
}
tmpl := &Template{
Source: templateFile,
Contents: template,
Destination: filePathAndName,
LeftDelim: "{{",
RightDelim: "}}",
Command: secret.Command,
}
if secret.FilePermission != "" {
tmpl.Perms = secret.FilePermission
}
templates = append(templates, tmpl)
}
return templates

The logic is kind of confusingly written, but take the case where both the template and template-file annotations are non-empty. Previously, secret.Template is non-empty and secret.TemplateFile is empty, and the same happens in tmpl. Now, both are non-empty in secret and the same in tmpl. Maybe we could chase the behaviour of what happens next all the way down into consul-template, but I'd rather just take the high confidence route of keeping the localised behaviour the same here.

secrets = append(secrets, s)
}
for _, secret := range secrets {
secret.Path = a.annotationsSecretValue(AnnotationAgentInjectSecret, secret.RawName, secret.Path)
Copy link
Contributor

Choose a reason for hiding this comment

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

It reads a little strangely to me to use the values of fields we haven't set yet as the default. Could we just pass in "" as the default for all the lines except volume path to make it a little easier to read?

Suggested change
secret.Path = a.annotationsSecretValue(AnnotationAgentInjectSecret, secret.RawName, secret.Path)
secret.Path = a.annotationsSecretValue(AnnotationAgentInjectSecret, secret.RawName, "")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I wrote like this just in case if there will be some default values setted at the top, so this one will not overwrite them.

@tomhjp
Copy link
Contributor

tomhjp commented Aug 16, 2023

@saturn4er I'm going to start kicking off a release soon, but I'd love to get this merged first. LMK if you're able to address the remaining comments - I pushed a commit that implements the comments here if that's helpful: 92153f6

@saturn4er
Copy link
Contributor Author

@tomhjp, I've made revisions based on your suggestions. Could you please review the changes?

Copy link
Contributor

@tomhjp tomhjp left a comment

Choose a reason for hiding this comment

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

Thanks! Looks great 👍

@tomhjp tomhjp merged commit e813ad9 into hashicorp:main Aug 16, 2023
1 check passed
@tomhjp tomhjp mentioned this pull request Aug 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants