-
Notifications
You must be signed in to change notification settings - Fork 9
Add statusoptional linter #73
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
Conversation
357ccbf
to
22348c8
Compare
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.
In the optionalorrequired
linter, we give the user a choice of which optional tag they prefer, we probably want to do the same here so that the suggested fix uses their preferred marker, right?
2f69191
to
10f15c4
Compare
// +kubebuilder:validation:Optional | ||
Reason string `json:"reason"` | ||
|
||
State `json:",inline"` // want "status field \"State\" must be marked as optional" |
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 correct. You can't mark an embedded field as optional, it would have no effect on the openapi schema.
The child of this embedded field however should be marked as optional, which I think you have already
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.
A go embed withjson:,inline
cannot be optional, but a non-inlined go embed can (embed must be a pointer). Today we embed ObjectMeta but don't inline it and tag it with json:metadata,omitempty
. ObjectMeta is not a great example because it cannot be empty, but a pointer embed struct can.
if you want full test coverage it's quite a few cases: Embed inlined, embed non-inlined (omit empty is allowed but doesn't do anything), pointer embed non-inlined, pointer embed non-inlined omitempty. On and we've got omitzero support coming so: embed non-inlined omitzero and pointer embed non-inlined omitzero too..
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.
Added more test cases.
} | ||
|
||
// MyResourceStatus is a sample status with a mix of optional and non-optional fields. | ||
type MyResourceStatus struct { |
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 add an optional struct field to this type that then has required members within it. The linter shouldn't report anything for the members of the new struct as they would be second level
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.
Added a test case for it.
@yongruilin just wanted to check if you saw #73 (review) |
} | ||
|
||
// MyResourceStatus is a sample status with a mix of optional and non-optional fields. | ||
type MyResourceStatus struct { |
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.
When the following field is added to struct
// +required
RequiredField string `json:"requiredField"`
The diagnostic works fine, but the suggesting results in followng.
//+required
//+optional
RequiredField string `json"requiredField"`
Fixed code can compile, but the schema doesn't make sense. So we should handle it if the filed has already required
field.
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.
Good catch, I updated to handle this case.
14cf805
to
025fc57
Compare
3db7fa8
to
511c165
Compare
Apologize, I was a bit tied up last week. |
// OptionalMarker is the marker that indicates that a field is optional. | ||
OptionalMarker = "optional" | ||
|
||
// KubebuilderOptionalMarker is the marker that indicates that a field is optional in kubebuilder. | ||
KubebuilderOptionalMarker = "kubebuilder:validation:Optional" | ||
|
||
// RequiredMarker is the marker that indicates that a field is required. | ||
RequiredMarker = "required" | ||
|
||
// KubebuilderRequiredMarker is the marker that indicates that a field is required in kubebuilder. | ||
KubebuilderRequiredMarker = "kubebuilder:validation:Required" |
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.
// OptionalMarker is the marker that indicates that a field is optional. | |
OptionalMarker = "optional" | |
// KubebuilderOptionalMarker is the marker that indicates that a field is optional in kubebuilder. | |
KubebuilderOptionalMarker = "kubebuilder:validation:Optional" | |
// RequiredMarker is the marker that indicates that a field is required. | |
RequiredMarker = "required" | |
// KubebuilderRequiredMarker is the marker that indicates that a field is required in kubebuilder. | |
KubebuilderRequiredMarker = "kubebuilder:validation:Required" |
You can delete these entirely, then suggest to use markers constant instead.
https://github.com/kubernetes-sigs/kube-api-linter/blob/main/pkg/markers/markers.go
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.
Updated, is it recently added?
|
||
func Test(t *testing.T) { | ||
testdata := analysistest.TestData() | ||
analysistest.RunWithSuggestedFixes(t, testdata, newAnalyzer("optional"), "a") |
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.
Should add a test case for the alternative optional marker too, should be fairly simple to copy the existing cases across
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.
Added the test cases for kubebuilder optional and k8s:optional.
// Handle standard required markers | ||
if fieldMarkers.Has(RequiredMarker) { | ||
for _, marker := range fieldMarkers[RequiredMarker] { | ||
textEdits = append(textEdits, analysis.TextEdit{ | ||
Pos: marker.Pos, | ||
End: marker.End, | ||
// Remove the marker completely (will add preferred one separately) | ||
NewText: []byte(""), | ||
}) | ||
} | ||
} |
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.
In other packages we have helpers for removing the markers, perhaps we can copy that pattern and extract this to a helper to reduce the duplication between these lines and the block below
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.
Left a TODO on it. I prefer to have a separate PR to consolidate the usage.
pkg/config/linters_config.go
Outdated
type StatusOptionalConfig struct { | ||
// preferredOptionalMarker is the preferred marker to use for optional fields. | ||
// If this field is not set, the default value is "optional". | ||
// Valid values are "optional" and "kubebuilder:validation:Optional". |
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.
And the K8s version now right? Or do you not want to include that in the documentation until the declarative validation project is further along?
pkg/validation/linters_config.go
Outdated
fieldErrors := field.ErrorList{} | ||
|
||
switch soc.PreferredOptionalMarker { | ||
case "", markers.OptionalMarker, markers.KubebuilderOptionalMarker: |
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.
As above, should this include the K8s version?
pkg/validation/linters_config.go
Outdated
switch soc.PreferredOptionalMarker { | ||
case "", markers.OptionalMarker, markers.KubebuilderOptionalMarker, markers.K8sOptionalMarker: | ||
default: | ||
fieldErrors = append(fieldErrors, field.Invalid(fldPath.Child("preferredOptionalMarker"), soc.PreferredOptionalMarker, fmt.Sprintf("invalid value, must be one of %q, %q or omitted", markers.OptionalMarker, markers.KubebuilderOptionalMarker))) |
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 line needs updating to include the K8s version of the optional
This commit introduces a new linter, `statusoptional`, which checks that all first-level children fields within a status struct are marked as optional. It adds functionality to automatically suggest fixes for fields that are missing the appropriate markers.
I think this is looking all good and correct now, @yongruilin are you happy with it? |
Yea, it looks good to me as well now. |
@JoelSpeed anything else in your mind for this PR? |
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: JoelSpeed, yongruilin 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 |
This PR introduces a new linter,
statusoptional
, which checks that all first-level children fields within a status struct are marked as optional. It adds functionality to automatically suggest fixes for fields that are missing the appropriate markers.Fix #31