-
Notifications
You must be signed in to change notification settings - Fork 2.3k
feat: emitStringData
boolean for secretGenerator
#5894
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
base: master
Are you sure you want to change the base?
feat: emitStringData
boolean for secretGenerator
#5894
Conversation
This PR has multiple commits, and the default merge method is: merge. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
14da6ae
to
905d18d
Compare
stringData
from secretGeneratorstringData
from secretGenerator
/assign |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🚀
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: matheuscscp, stealthybox The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
905d18d
to
9e14bdb
Compare
I've just done a fresh rebase. |
@stealthybox
|
How about |
@matheuscscp For example, in the case of |
9e14bdb
to
2e90352
Compare
bab4e87
to
67fc288
Compare
@koba1t, thanks for pointing out this out.
The 4 cases where fields override using a different encoding output duplicate fields. Here is an example of what that fix could look like: |
Regarding the field name, I understand the concern of In the most recent issue, A separate end-user suggests the boolean field should be called The title of unrelated issue (#1261) is "Support StringData option in SecretArgs". Considering end-user, contributor, and maintainer responses,
I'd also challenge that
The new field isn't a passthrough value field -- it changes generator behavior.
This is fair. there are a lot of fields when you consider input fields like
I disagree that I think these are decent options for field names:
The default behavior of a configMapGenerator already includes heuristic auto-encoding behavior for binaryData, and that doesn't show up in any field names. It's just the way a kustomize generator works. Thanks for considering these points. Do you like |
Yeah this field has to explain itself, it has to have |
unrelated to field names, here's one way to fix the existing broken configMapGenerator behavior: |
I think "stringData" would be a deceiving name for this new field. My suggestion today is "emitStringData". |
67fc288
to
c0b3af1
Compare
I've proactively renamed the field to |
stringData
from secretGeneratoremitStringData
boolean for secretGenerator
@koba1t and I considered in our Slack dm's whether a string value field could work: field: "data" # "stringData" | "binaryData" This could be a generic way to solve this problem for both ConfigMap and Secret generators, but it also creates more error states and is harder to grep for in existing code + discover in auto-complete. I prefer the |
Hi friends, if there's no further contest on the field name, I'd love to get this reviewed for a merge. I want to draft a change for the merge behavior bugfix in stealthybox@e95e854 . After these changes make their way into kustomize, kubectl needs to update kustomize before we can pull this feature into Flux. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have not reviewed the secrets_test.go file yet.
// A Secret's `stringData` field is similar to ConfigMap's `data` field. | ||
// `stringData` allows specifying non-binary, UTF-8 secret data in string form. | ||
// It is provided as a write-only input field for convenience. | ||
// All keys and values are merged into the data field on write, overwriting any | ||
// existing values. The stringData field is never output when reading from the API. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. Thank you for this.
} | ||
} else { | ||
if err = rn.LoadMapIntoSecretData(m); err != nil { | ||
return nil, fmt.Errorf("Failed to load map into Secret data: %w", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise with the capitalization.
return nil, err | ||
if args.EmitStringData { | ||
if err = rn.LoadMapIntoSecretStringData(m); err != nil { | ||
return nil, fmt.Errorf("Failed to load map into Secret stringData: %w", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure whether kustomize eschews the general advice for Go programs to not capitalize the first word in an error message, assuming that this message might wind up serving as a suffix for more prefixes to be added onto it higher up in the call stack. If this capitalization isn't us playing along with the rest of the code base, consider this alternative:
return nil, fmt.Errorf("Failed to load map into Secret stringData: %w", err) | |
return nil, fmt.Errorf(`loading map into Secret "stringData" field: %w`, err) |
The message adopts the advice from Preslav Rachev's essay Go's Error Handling Is a Form of Storytelling.
`, | ||
}, | ||
}, | ||
"construct secret from a binary file and fallback to data from stringData": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"construct secret from a binary file and fallback to data from stringData": { | |
"construct secret from a binary file and fall back to data from stringData": { |
} | ||
|
||
func (rn *RNode) LoadMapIntoSecretStringData(m map[string]string) error { | ||
for _, k := range SortedMapKeys(m) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the significance of walking the keys in lexicographical order? Is that to avoid any nondeterminism? Since the key names must be unique, they can't conflict with one another in the "m" map, but perhaps we're wary of what the SetField
function will do against the RNode
's existing fields. I'm still not sure if that's a problem, though.
If you walked the map by its keys and values together, you could avoid the index expression on the next line.
return nil | ||
} | ||
|
||
// valid UTF-8 strings can be stringData, but fallback to Base64 for non UTF-8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the documentation capitalizes "Base64" inconsistently, often writing it as all-lowercase.
// valid UTF-8 strings can be stringData, but fallback to Base64 for non UTF-8 | |
// makeSecretStringValueRNode creates a scalar node for one of two potential fields. | |
// If the supplied node's value is a valid UTF-8 string, it use the "stringData" field, but | |
// otherwise falls back to placing the Base64-encoded value in the "data" field for | |
// non-UTF-8 strings. |
if err != nil { | ||
return nil | ||
} | ||
result := map[string]string{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd like to know how many fields are present ahead of time. We should not call (*RNode).Fields
, because (*RNode).VisitFields
calls it immediately, and Fields
allocates.
Should we estimate a likely count here, so that we could write the following?
result := map[string]string{} | |
result := make(map[string]string, 10) // Estimate field count |
Perhaps the cost of starting with a too-small map and needing it to grow later isn't a real problem.
I see that this is mimicking the longstanding (*RNode).GetDataMap
method, so if it's not a problem there, it's probably not a problem here either.
|
||
func (rn *RNode) SetStringDataMap(m map[string]string) { | ||
if rn == nil { | ||
log.Fatal("cannot set stringData map on nil Rnode") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we change this here, we should change it in (*RNode).SetDataMap
and (*RNode).SetBinaryDataMap
consistently.
log.Fatal("cannot set stringData map on nil Rnode") | |
log.Fatal("cannot set stringData map on nil RNode") |
|
||
* **emitStringData** (bool), optional | ||
|
||
emitStringData if true generates a v1/Secret with plain-text stringData fields |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend that we adapt the proposed struct field documentation here. I see that you made them consistent originally, so I'm just reminding you to update this paragraph if you decide to update the struct field documentation.
- year=2025 | ||
- crisis=true | ||
`) | ||
th.WriteF("service.yaml", ` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the significance of including the Service in this kustomization?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think just that the generator properly appends to other resources in the output.
This test is a functional copy of TestGeneratorIntVsStringNoMerge
from configmaps_test.go but for secrets. All of the copied test-cases are added in their own commit before the new ones from this patch are added.
`) | ||
} | ||
|
||
// Generate Secrets similar to TestGeneratorBasics with emitStringData enabled and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you intend to finish this sentence? It ends with a dangling "and".
// The resulting Secret will have a duplicate key in both data and stringData | ||
// The stringData override will work, because the kube API considers stringData authoritative and write-only |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// The resulting Secret will have a duplicate key in both data and stringData | |
// The stringData override will work, because the kube API considers stringData authoritative and write-only | |
// The resulting Secret will have a duplicate key in both its "data" and "stringData" fields. | |
// The "stringData" field's override will work, because the Kubernetes API considers the "stringData" field to be authoritative. |
// The resulting Secret will have a duplicate key in both data and stringData | ||
// The data override will fail, because the kube API considers the older value in stringData authoritative and write-only |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// The resulting Secret will have a duplicate key in both data and stringData | |
// The data override will fail, because the kube API considers the older value in stringData authoritative and write-only | |
// The resulting Secret will have a duplicate key in both its "data" and "stringData" fields. | |
// The "data" field's override will fail, because the Kubernetes API considers the sibling value in the "stringData" field to be authoritative. |
// TODO: This should be an error instead. However, we can't strict unmarshal until we have a yaml | ||
// lib that support case-insensitive keys and anchors. | ||
// See https://github.com/kubernetes-sigs/kustomize/issues/5061 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// TODO: This should be an error instead. However, we can't strict unmarshal until we have a yaml | |
// lib that support case-insensitive keys and anchors. | |
// See https://github.com/kubernetes-sigs/kustomize/issues/5061 | |
// TODO: This should be an error instead. However, we can't unmarshal strictly until | |
// we have a YAML library that support case-insensitive keys and anchors. | |
// See https://github.com/kubernetes-sigs/kustomize/issues/5061. |
This allows users to pass
emitStringData: true
to the secretGenerator, alongside thetype
option.When enabled, UTF-8 strings are output in plainText stringData, and non-UTF strings and any binary data fallback to the default behavior of being base64 encoded in
data
.This is very similar to the default, kyaml behavior of loading values into ConfigMap's
data
andbinaryData
fields.This feature provides a general U/X improvement for people using kustomize interactively, and also allows Flux users to template into generated secrets.
(Flux users already can and do template into ConfigMaps, so we want to give people more secure mechanisms in kustomize-controller)
resolves #5142 #1444 #1261 #793