-
Notifications
You must be signed in to change notification settings - Fork 440
✨ Change CRD generation logic to honor k8s:immutable #1216
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: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,6 +106,9 @@ var FieldOnlyMarkers = []*definitionWithHelp{ | |
|
||
must(markers.MakeAnyTypeDefinition("kubebuilder:title", markers.DescribesField, Title{})). | ||
WithHelp(Title{}.Help()), | ||
|
||
must(markers.MakeDefinition("k8s:immutable", markers.DescribesField, Immutable{})). | ||
WithHelp(Immutable{}.Help()), | ||
} | ||
|
||
// ValidationIshMarkers are field-and-type markers that don't fall under the | ||
|
@@ -325,6 +328,19 @@ type XIntOrString struct{} | |
// to be used only as a last resort. | ||
type Schemaless struct{} | ||
|
||
// +controllertools:marker:generateHelp:category="CRD validation" | ||
// Immutable marks a field as immutable. | ||
// The value of an immutable field may not be changed after creation. | ||
type Immutable struct{} | ||
|
||
func (Immutable) ApplyToSchema(schema *apiext.JSONSchemaProps) error { | ||
schema.XValidations = append(schema.XValidations, apiext.ValidationRule{ | ||
Message: "Value is immutable", | ||
Rule: "self == oldSelf", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Won't this error out in the create case as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It won't, there is always a "oldSelf" since the rule is attached to the field and only takes effect when the field exists. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Not quite true, but it's still safe. Rules that include It will only run a transition rule when it has a value for both But, you could have a rule without There's also Which brings me to... The semantic here is that I could have an object with a field we say is immutable. What does it actually mean to be immutable? There are two interpretations IMO:
This implements the first of those, how does it work in the declarative validation project? If it is the latter, we would need an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you @JoelSpeed for providing insights on CEL rules. +k8s:immutable in Declarative validation project can allow setting empty value to non-empty value during update, but it doesn't allow changing value from one non-empty to another non-empty. So, it is the following interpretation. It doesn't allow setting non-empty to empty in update. Which the current cel expression will allow. I will see how this can be achieved. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a way for us to have tests that actually test the contents of the CEL rule? This seems pretty easy to get wrong and once we've released something like this, fixing it up will be extremely difficult/impossible There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could set up an envtest style integration suite and create a series of Create/Update calls to test the validations. That would be my preferred route for testing these at least
The only way I've found to do this is to make sure that the nearest required parent has a rule that says if the field exists, it cannot be removed. But there may be something smarter we can do |
||
}) | ||
return nil | ||
} | ||
|
||
func hasNumericType(schema *apiext.JSONSchemaProps) bool { | ||
return schema.Type == string(Integer) || schema.Type == string(Number) | ||
} | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
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.
Let's expand the semantics here to clarify what exact transitions are allowed.
Let's use the term "set" instead of "creation" here since immutable fields can set after initial resource creation.