-
Notifications
You must be signed in to change notification settings - Fork 40.1k
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 --append-hash flag to kubectl create configmap/secret #49961
Conversation
/unassign @dims |
this seems like a rarely called-for option that I find it odd to bake into these commands... if you want a name based on a hash of the contents, you can create that yourself external to the command. cc @kubernetes/sig-cli-pr-reviews |
I don't think I'd call it "rarely called for." The recommended workflow for
rolling out new ConfigMaps and Secrets is to create new ones and update
references. The new ones need new names. Most workflows that generate and
roll out new ConfigMaps and Secrets via `--from-file` thus need to generate
new names, in which case they can all just use `--hash` instead of coming
up with fragmented, bespoke solutions.
…On Tue, Aug 1, 2017 at 9:59 AM, Jordan Liggitt ***@***.***> wrote:
this seems like a rarely called-for option that I find it odd to bake into
these commands... if you want a name based on a hash of the contents, you
can create that yourself external to the command.
cc @kubernetes/sig-cli-pr-reviews
<https://github.com/orgs/kubernetes/teams/sig-cli-pr-reviews>
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#49961 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA3Jwdm8ItKXROuuqa5lms7_DwPTNQ0nks5sT1l2gaJpZM4Op-cM>
.
--
Michael Taufen
MTV SWE
|
@liggitt This is one part of a solution to #22368. My expected usage model is for users to do |
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 approach your taking will concatenate the upper 40 bits of the SHA2-256 hash of the contents with a user supplied name to provide a unique name for a ConfigMap. This differs from the approach taken by the controllers when generating unique names in that it doesn't handle collisions. Given the low probability of collision based on the expected number "versions" of the ConfigMap, the collision resistance of SHA2-256, and the use case, this is probably okay, but, if a collision does occur how does a user resolve it? Would they just delete the existing ConfigMap and create a new one?
pkg/kubectl/util/hash/hash.go
Outdated
) | ||
|
||
// HashConfigMap returns a hash of the EncodeConfigMap encoding of `cm` | ||
func HashConfigMap(cm *api.ConfigMap) 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.
hash.Hash stutters - linter will not like it
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.
Yeah just noticed that. Thanks.
pkg/kubectl/util/hash/hash.go
Outdated
} | ||
|
||
// HashSecret returns a hash of the EncodeSecret encoding of `sec` | ||
func HashSecret(sec *api.Secret) 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.
Same as above - linter will be pissed
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.
Thanks
I don't think we should resolve collisions. The chance of a collision is very low (~1 in 200 million, with 100 concurrent ConfigMaps with the same name prefix). I think the reduced complexity is worth those odds. If someone does hit a collision, it's pretty easy to manually change the source data by adding a bogus ConfigMap key. And you should never have to do this twice in your lifetime (assuming <100 ConfigMaps that share a prefix, and infrequent changes, which is reasonable). |
/retest |
+1 to @mtaufen's proposal of how to deal with collisions. I think we can do without additional complexity for now. |
pkg/kubectl/util/hash/hash.go
Outdated
// with `,`. If m is non-empty, there is a trailing comma in the pre-hash serialization. If m is empty, | ||
// there is no trailing comma. The entire encoding starts with "{" and ends with "}". | ||
func encodeMapStringString(m map[string]string) string { | ||
kv := make([][]string, len(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.
[]string
should be enough? Store keys of m
in it and sort those keys. You can get value from m[key]
when encoding to a 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.
Done, thanks Janet!
pkg/kubectl/util/hash/hash.go
Outdated
|
||
// encodeConfigMap encodes a ConfigMap by encoding the Data with EncodeMapStringString and wrapping with {} braces | ||
func encodeConfigMap(cm *api.ConfigMap) string { | ||
return fmt.Sprintf("{data:%s,}", encodeMapStringString(cm.Data)) |
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.
including the pre-hash name and the kind (Secret, ConfigMap, etc) would prevent comparison between two hash suffixes based solely on the name of the object (e.g. you could deduce secrets foo-014fbff9a07c
and bar-014fbff9a07c
likely contain identical data based only on their names)
pkg/kubectl/util/hash/hash.go
Outdated
|
||
// encodeSecret encodes a Secret by encoding the Data with EncodeMapStringByte and | ||
// appending ",type:"+sec.Type+"," to the encoded string, and wraps with {} braces | ||
func encodeSecret(sec *api.Secret) 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.
same here, json.Marshal(map[string]interface{}{"kind":"Secret","name":sec.Name,"data":sec.Data})
seems more straightforward
pkg/kubectl/util/hash/hash.go
Outdated
// We picked some arbitrary consonants to map to from the same character set as GenerateName. | ||
// See: https://github.com/kubernetes/apimachinery/blob/dc1f89aff9a7509782bde3b68824c8043a3e58cc/pkg/util/rand/rand.go#L75 | ||
func encodeHash(hex string) string { | ||
enc := []rune(hex[:10]) |
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.
length check on hex
0c0b9b8
to
dbbb846
Compare
@@ -333,7 +332,8 @@ func Convert_extensions_DaemonSetList_To_v1beta1_DaemonSetList(in *extensions.Da | |||
|
|||
func autoConvert_v1beta1_DaemonSetSpec_To_extensions_DaemonSetSpec(in *v1beta1.DaemonSetSpec, out *extensions.DaemonSetSpec, s conversion.Scope) error { | |||
out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector)) | |||
if err := api_v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { | |||
// TODO: Inefficient conversion - can we improve it? |
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.
you got a bad generation here... these diffs shouldn't exist
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.
Huh. I'll look into it.
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.
fixed
pkg/kubectl/util/hash/hash.go
Outdated
// Data, Kind, and Name are taken into account. | ||
func encodeConfigMap(cm *api.ConfigMap) (string, error) { | ||
// json.Marshal sorts the keys in a stable order in the encoding | ||
data, err := json.Marshal(cm.Data) |
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.
any reason not to delegate the full serialization to json.Marshal rather than mixing with sprinf?
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.
Including all of ObjectMeta makes the hash dependent on too many unnecessary fields, IMO. And it would automatically bind us to any future ObjectMeta (or Secret/ConfigMap) fields, which we may not want to include in the hash.
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.
Cleared up on Slack; comment recommended json.Marshal(map[string]interface{})
over Sprintf
. Agree this is better.
other than fixing the generated change and the preference for delegating the serialization fully to json.Marshal, no other comments from me |
Specifying this new flag will automatically hash the configmap/secret contents with sha256 and append the first 40 hex-encoded bits of the hash to the name of the configmap/secret. This is especially useful for workflows that generate configmaps/secrets from files (e.g. --from-file). Note that vowels and vowel-like characters in the hash are remapped to consonants to make it more difficult to accidentally form bad words. See this Google doc for more background: https://docs.google.com/document/d/1x1fJ3pGRx20ujR-Y89HUAw8glUL8-ygaztLkkmQeCdU/edit
/retest |
LGTM |
/lgtm |
/approve no-issue |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: apelisse, janetkuo, mtaufen Associated issue requirement bypassed by: janetkuo The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these OWNERS Files:
You can indicate your approval by writing |
/retest Review the full test history for this PR. |
/test all [submit-queue is verifying that this PR is safe to merge] |
Automatic merge from submit-queue (batch tested with PRs 49961, 50005, 50738, 51045, 49927) |
Repeating my comment on the "background doc" here for visibility: [When used on Secrets] This exposes the contents of the secret in the hash, and could be used in a dictionary attack to discover the plaintext secret. My recommendation is to add a sizeable salt when hashing Secrets, and include the salt in the output hash value (unfortunately making it longer). |
What this PR does / why we need it:
Specifying this new flag will automatically hash the configmap/secret
contents with sha256 and append the first 40 hex-encoded bits of the
hash to the name of the configmap/secret. This is especially useful for
workflows that generate configmaps/secrets from files (e.g.
--from-file).
See this Google doc for more background:
https://docs.google.com/document/d/1x1fJ3pGRx20ujR-Y89HUAw8glUL8-ygaztLkkmQeCdU/edit
Release note: