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

Check example-checksum label for consistency in ConfigMap webhook. #1366

Merged
merged 12 commits into from
Jun 2, 2020

Conversation

markusthoemmes
Copy link
Contributor

@markusthoemmes markusthoemmes commented May 29, 2020

We've seen users try to edit our configuration and falling into the trap of editing the '_example' block a lot. This attempts at guiding the users to do "the right thing" by checking the ConfigMap's '_example' value (if present) against a precomputed hash of the same value (if present). The idea is that we precompute this has using the tool herein in code generation and thus allow us to easily change the example block automatically while making it hard to change it on ppurpose. If the hashes don't match on an upgrade, the webhook will return an error synchronously, guiding the user to the correct behavior.

Downstream PRs:
knative/eventing#3231
knative/serving#8123

/assign @mattmoor

We've seen users try to edit our configuration and falling into the trap of editing the '_example' block a lot. This attempts at guiding the users to do "the right thing" by checking the ConfigMap's '_example' value (if present) against a precomputed hash of the same value (if present). The idea is that we precompute this has using the tool herein in code generation and thus allow us to easily change the example block automatically while making it hard to change it on ppurpose. If the hashes don't match on an upgrade, the webhook will return an error synchronously, guiding the user to the correct behavior.
@googlebot googlebot added the cla: yes Indicates the PR's author has signed the CLA. label May 29, 2020
@knative-prow-robot knative-prow-robot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label May 29, 2020
exampleHash != configmap.ExampleHash(exampleData) {
return fmt.Errorf(
"%q block edited, you likely wanted to create an unindented configuration",
configmap.ExampleKey)
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we should include the diff 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Diff of the update or diff of the hash?

Copy link
Contributor

@antoineco antoineco left a comment

Choose a reason for hiding this comment

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

Good idea! A few nits.

configmap/example.go Outdated Show resolved Hide resolved
configmap/example.go Outdated Show resolved Hide resolved
configmap/example_test.go Outdated Show resolved Hide resolved
configmap/hash-gen/main.go Show resolved Hide resolved
configmap/hash-gen/testdata/add.yaml Outdated Show resolved Hide resolved
configmap/hash-gen/testdata/add.yaml Outdated Show resolved Hide resolved
configmap/example.go Outdated Show resolved Hide resolved
import "testing"

func TestExampleHash(t *testing.T) {
if got, want := ExampleHash(""), "e3b0c4429"; got != want {
Copy link
Contributor

Choose a reason for hiding this comment

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

It feels as if this deserves to be a constant :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Which one exactly?

Copy link
Contributor

Choose a reason for hiding this comment

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

The empty string. Since this is what we'll have if example section is deleted. But doesn't matter anyway.

configmap/hash-gen/main.go Outdated Show resolved Hide resolved
configmap/hash-gen/main.go Show resolved Hide resolved
configmap/hash-gen/main.go Outdated Show resolved Hide resolved
configmap/hash-gen/main.go Outdated Show resolved Hide resolved
}
content := doc.Content[0]

example := traverse(content, "data", configmap.ExampleKey)
Copy link
Contributor

Choose a reason for hiding this comment

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

If we cared to constant-ize Example key, why not "data" then as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cause it's the default key for a ConfigMap. The example key was also a constant before so 🤷‍♀️. We'd have to go create constants for "metadata" and "labels" as well. Felt like overkill.

labels := traverse(content, "metadata", "labels")
if labels == nil {
// TODO(markusthoemmes): Potentially handle missing metadata and labels?
return nil, errors.New("'metadata.labels' not found")
Copy link
Contributor

Choose a reason for hiding this comment

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

So why not inject one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See comment. I can do that but it didn't feel worth it given all ConfigMaps we have already have the relevant fields and working with these Nodes is not super straightforward. I can add them if you feel like we should.

configmap/hash-gen/main.go Outdated Show resolved Hide resolved
}

buffer := &bytes.Buffer{}
encoder := yaml.NewEncoder(buffer)
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 don't care much here, but we can just pass in the bufio.Writer wrapping around the file, but this is not a service, so 🤷

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 thought about the same, but opening a file for writing and making sure the stars all align felt like more ceremony than it was worth it so I just kept it simple here. Doesn't matter performancewise anyway.

@markusthoemmes
Copy link
Contributor Author

@antoineco and @vagababov thanks for the thorough reviews (and sorry for the somewhat subpar implementation). I think this is ready for another look. I'm not sure I feel strongly about the checksum algorithm. Just sticking to SHA256 sounded fair to me, given git uses it in the same way (which is why I picked it too) and given performance doesn't matter.

@markusthoemmes markusthoemmes changed the title Check example-hash label for consistency in ConfigMap webhook. Check example-checksum label for consistency in ConfigMap webhook. Jun 2, 2020
@antoineco
Copy link
Contributor

I don't feel strongly about the algorithm either, I just figured SHA was often used when it is not desired that clashes can be introduced, while the use case here doesn't have such constraint. Feel free to discard my comment.

configmap/hash-gen/main.go Outdated Show resolved Hide resolved
configmap/hash-gen/main.go Outdated Show resolved Hide resolved
webhook/configmaps/configmaps.go Outdated Show resolved Hide resolved
webhook/configmaps/configmaps.go Outdated Show resolved Hide resolved
Copy link
Member

@mattmoor mattmoor left a comment

Choose a reason for hiding this comment

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

/lgtm
/approve

Looking forward to this 🤩

@knative-prow-robot knative-prow-robot added the lgtm Indicates that a PR is ready to be merged. label Jun 2, 2020
@knative-prow-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: markusthoemmes, mattmoor

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@knative-prow-robot knative-prow-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jun 2, 2020
@knative-metrics-robot
Copy link

The following is the coverage report on the affected files.
Say /test pull-knative-pkg-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
configmap/example.go Do not exist 100.0%
configmap/hash-gen/main.go Do not exist 65.2%
webhook/configmaps/configmaps.go 93.9% 94.3% 0.3

@knative-prow-robot knative-prow-robot merged commit f45b641 into knative:master Jun 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cla: yes Indicates the PR's author has signed the CLA. lgtm Indicates that a PR is ready to be merged. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants