-
Notifications
You must be signed in to change notification settings - Fork 25
Added enums linter #180
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?
Added enums linter #180
Conversation
fa477bf to
a76ac80
Compare
|
/label tide/merge-method-squash |
JoelSpeed
left a comment
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.
Please review the kubebuilder validations documentation for how the kubebuilder version of the enum marker works, this PR is not handling it correctly at the moment
docs/linters.md
Outdated
|
|
||
| This provides better API evolution, self-documentation, and validation compared to plain strings. | ||
|
|
||
| By default, `enums` is not enabled. |
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.
It probably should be
| The `duplicatemarkers` linter can automatically fix all markers that are exact match to another markers. | ||
| If there are duplicates across fields and their underlying type, the marker on the type will be preferred and the marker on the field will be removed. | ||
|
|
||
| ## Enums |
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 are likely to need configuration on this linter for accepted exceptions to the rules here
| package a | ||
|
|
||
| // Valid enum type with proper marker | ||
| // +kubebuilder:validation:Enum |
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.
This isn't actually equivalent to +enum. +enum will pick up constants for the type, this marker requires them to be specified explicitly
+kubebuilder:validation:Enum=Pending;Running;Succeeded;Failed
| ) | ||
|
|
||
| // Alternative marker format | ||
| // +k8s:enum |
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.
Does this exist already? I wasn't sure if DV was at this point
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.
| ) | ||
|
|
||
| // Invalid: Missing +enum marker | ||
| // This type doesn't have +enum marker, so it will be flagged when used in 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.
What triggers this to be flag? A string type alias with some constants assigned?
| } | ||
|
|
||
| // isPascalCase checks if a string follows PascalCase naming convention. | ||
| func isPascalCase(s string) bool { |
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 suspect checking pascal case is quite hard. What is the logic here trying to check? First character is capital and that not all characters are upper? All characters upper may actually be valid (e.g. HTTP or HTTPS would be valid)
pkg/analysis/enums/doc.go
Outdated
| 1. Fields must use type aliases, not plain strings: String fields that represent enums should | ||
| use a type alias with an +enum marker rather than a raw string type. |
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.
This is and isn't correct. If you see +kubebuilder:validation:Enum on a field, and it's using a plain string, recommending to use a type alias is valid.
For a generic string, this is not true
pkg/analysis/enums/doc.go
Outdated
| 2. Type aliases must have +enum marker: Type aliases used for enumerated values must be | ||
| annotated with either // +kubebuilder:validation:Enum or // +k8s:enum. |
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.
Only if we think they should be enums, which means they have a number of constants associated. There may be use cases for free-form strings
pkg/analysis/enums/doc.go
Outdated
| 2. Type aliases must have +enum marker: Type aliases used for enumerated values must be | ||
| annotated with either // +kubebuilder:validation:Enum or // +k8s:enum. | ||
| 3. Enum values must be PascalCase: Constant values for enums should follow PascalCase naming | ||
| (e.g., "PhasePending", "StateRunning") rather than lowercase, snake_case, or SCREAMING_SNAKE_CASE. |
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.
TIL that SCREAMING_SNAKE_CASE is a thing 😂
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.
😂😂😂
pkg/analysis/enums/doc.go
Outdated
| Note: This linter is disabled by default as enum usage is recommended but not strictly | ||
| required for all Kubernetes APIs. Enable it in your configuration to enforce these conventions. |
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 get this linter right, it should be on by default IMO
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.
linter now enabled by default so updated/fixed failing integration tests 👍🏻
modified: docs/linters.md
new file: pkg/analysis/enums/analyzer.go
new file: pkg/analysis/enums/analyzer_test.go
new file: pkg/analysis/enums/config.go
new file: pkg/analysis/enums/doc.go
new file: pkg/analysis/enums/initializer.go
new file: pkg/analysis/enums/testdata/src/a/a.go
new file: pkg/analysis/enums/testdata/src/b/b.go
modified: pkg/registration/doc.go
10b6e09 to
4822af7
Compare
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: krishagarwal278 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 |
| - **Declarative Validation Marker** (`+k8s:enum`): Used in Kubernetes core API types for declarative validation. | ||
|
|
||
| **Enum Marker Modes:** | ||
| - **Auto-discovery** (`+k8s:enum` or `+kubebuilder:validation:Enum`): Validates that constants follow PascalCase |
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 kubebuilder version doesn't work like this, it must have explicit values
|
|
||
| **Enum Marker Types:** | ||
| - **CRD Validation Marker** (`+kubebuilder:validation:Enum`): Used for CRD validation in projects with CustomResourceDefinitions. Processed by controller-gen to generate OpenAPI schema validation. | ||
| - **Declarative Validation Marker** (`+k8s:enum`): Used in Kubernetes core API types for declarative validation. |
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 about plan +enum?
| allowlist: | ||
| - kubectl | ||
| - docker | ||
| # When true, flags plain string fields (default: false) |
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.
Probably should default to true, I think this is preferred in Kube API conventions over strings with other validation
| @@ -0,0 +1,141 @@ | |||
| package a | |||
|
|
|||
| // Valid: +kubebuilder:validation:Enum without explicit values (auto-discovers constants) | |||
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.
This comment is not accurate, as you have constants?
| ) | ||
|
|
||
| // Valid: +kubebuilder:validation:Enum with explicit values (doesn't check constants) | ||
| // +kubebuilder:validation:Enum=Pending;helper |
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.
helper should be flagged here as it isn't pascal case
| Good: | ||
| // +kubebuilder:validation:Enum |
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.
| // +kubebuilder:validation:Enum | |
| // +enum |
|
|
||
| // validateConfig implements validation of the enums linter config. | ||
| func validateConfig(cfg *Config, fldPath *field.Path) field.ErrorList { | ||
| // Config is optional, allowlist can be empty |
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 probably want to validate that the allowlist does not contain duplicates
| // RequireTypeAliasForEnums when true, enforces that string fields representing enums | ||
| // must use type aliases instead of plain string types. | ||
| // Default: false (plain strings are allowed) | ||
| RequireTypeAliasForEnums bool `yaml:"requireTypeAliasForEnums" json:"requireTypeAliasForEnums"` |
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 try to avoid bools in favour of descriptive enum values, so that we can evolve in the future if we need to.
Maybe this needs to be KubebuilderEnumPolicy: RequireTypeAlias | AllowPlainString?
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 don't think this is an accurate set of changes. I think these should be reverted.
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 purpose of the integration tests is not to change the content of the files, but add extra want statements when there are new issues. Please revert the changes you've made here and instead add the newly wanted issues
| // | ||
| // Returns false for: | ||
| // - +kubebuilder:validation:Enum=value1;value2 (CRD validation marker with explicit values) | ||
| func usesAutoDiscovery(pass *analysis.Pass, typeSpec *ast.TypeSpec) bool { |
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 provide markerAccess to get markers directly, you don't need to re-parse them here
| // isPascalCase checks if a string follows PascalCase naming convention. | ||
| // Allows: PascalCase (Running), all-uppercase acronyms (HTTP, API), single letters (A). | ||
| // Rejects: lowercase start (running), snake_case (phase_pending), kebab-case (phase-pending). | ||
| func isPascalCase(s string) bool { |
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.
Probably best to use a regex here rather than manually parsing
Description
This PR introduces a new
enums linterto enforce best practices for enumerated fields in Kubernetes APIs.The new linter,
enums, achieves the following:+enummarkers instead of plain strings. Ensures enum values are explicitly documented in OpenAPI schemas with clear justifications for their purpose.Fixes #17