-
Notifications
You must be signed in to change notification settings - Fork 149
Refactors InferencePool v1 Status and FailureModes #1427
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
Refactors InferencePool v1 Status and FailureModes #1427
Conversation
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: danehans 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 |
✅ Deploy Preview for gateway-api-inference-extension ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
20b8deb
to
d11b1b8
Compare
d11b1b8
to
06a15b7
Compare
|
||
meta := metav1.TypeMeta{ | ||
Kind: src.Kind, | ||
APIVersion: v1.GroupVersion.Version, // Ensure the API version is set correctly. |
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.
api version is not just version. API version is a combination of Group and Version. You can see how the UT fails now. See an example here https://kubernetes.io/docs/concepts/overview/working-with-objects/#describing-a-kubernetes-object
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 in 574e13e
@@ -39,11 +41,20 @@ func (src *InferencePool) ConvertTo(dst *v1.InferencePool) error { | |||
if err != nil { | |||
return err | |||
} | |||
dst.TypeMeta = src.TypeMeta |
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.
Why not use this. TypeMeta is all built-in string type, see https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#TypeMeta?
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.
See #1427 (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.
Based on https://kubernetes.slack.com/archives/C08E3RZMT2P/p1755801705394689, consensus is for the conversion functions to convert APIVersion instead of keeping the source APIVersion.
06a15b7
to
574e13e
Compare
574e13e
to
78c9e01
Compare
@capri-xiyue @capri-xiyue based on https://kubernetes.slack.com/archives/C08E3RZMT2P/p1755801705394689, consensus is for the conversion functions to convert APIVersion instead of keeping the source APIVersion. |
api/v1/inferencepool_types.go
Outdated
// Defaults to failClose. | ||
// | ||
// +optional | ||
// +kubebuilder:default="FailClose" | ||
FailureMode ExtensionFailureMode `json:"failureMode,omitempty"` | ||
} | ||
|
||
// ExtensionFailureMode defines the options for how the gateway handles the case when the extension is not | ||
// ExtensionFailureMode defines the options for how the parent handles the case when the extension is not |
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.
@robscott since we renamed ExtensionRef
to EndpointPickerRef
, it seems like FailureModes should be EPP-specific, e.g., ExtensionFailureMode
-> PickerFailureMode
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.
Commit 77831c7 renames the FailureMode types from ExtensionXX to EndpointPickerXXX.
FailOpen ExtensionFailureMode = "FailOpen" | ||
// FailClose specifies that the proxy should drop the request when the Endpoint Picker fails. | ||
FailClose ExtensionFailureMode = "FailClose" | ||
) | ||
|
||
// InferencePoolStatus defines the observed state of InferencePool. | ||
// +kubebuilder:validation:MinProperties=1 |
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 removed +kubebuilder:validation:MinProperties=1
since the struct contains only one field, and the field is optional.
8e50ce9
to
77831c7
Compare
return convert[v1.InferencePoolStatus](u) | ||
|
||
// v1alpha2 used json:"parent" (singular) v1 uses json:"parents" | ||
if v, ok := u.Object["parent"]; ok { |
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'm wondering instead of leverage unstructured and then change it, can we just use manual conversion without u, err := toUnstructured(src)
, will it be more clear? For ExtensionRef conversion, it is all manual conversion instead of using toUnstructured. Manual conversion could be cumbersome, but I think ai coding tool can help write it.
return convert[InferencePoolStatus](u) | ||
|
||
// v1 uses json:"parents"; v1alpha2 used json:"parent" (singular) | ||
if v, ok := u.Object["parents"]; ok { |
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
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 @danehans! A few nits but mostly LGTM
api/v1/inferencepool_types.go
Outdated
@@ -65,19 +67,24 @@ type InferencePoolSpec struct { | |||
// this configuration into a Service resource. | |||
// | |||
// +required | |||
Selector LabelSelector `json:"selector,omitempty,omitzero"` | |||
//nolint:kubeapilinter // ignore kubeapilinter here as we don't want to use pointer here. |
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.
Why would kubeapilinter want us to use a pointer here?
api/v1/inferencepool_types.go
Outdated
// +kubebuilder:default={{type: "Accepted", status: "Unknown", reason:"Pending", message:"Waiting for controller", lastTransitionTime: "1970-01-01T00:00:00Z"}} | ||
Conditions []metav1.Condition `json:"conditions,omitempty"` | ||
//nolint:kubeapilinter // ignore kubeapilinter here as we want conditions to be required. | ||
Conditions []metav1.Condition `json:"conditions"` |
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.
My understanding is that omitempty
should still be used everywhere, even for required fields. It likely doesn't matter here, but probably worth being consistent with the rest of the API.
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.
My understanding is that omitempty should still be used everywhere, even for required fields.
I reviewed several K8s API and GW API types, and I do not see the omitempty
JSON tag for any required fields. However, I do see Conditions
set as +optional
and the K8s API conventions state Conditions
should be +optional
, so I'm going to revert this portion of the PR. Since the field will be +optional
, it will also include omitempty
.
// | ||
// Possible reasons for this condition to be True are: | ||
// | ||
// * "Accepted" | ||
// * "SupportedByParent" |
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've always used Accepted
as the only positive polarity reason for a resource to be Accepted
in Gateway API, recommend keeping that here.
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 have always found the Accepted
reason to be unusual. The condition type is Accepted
, and when being set to False
, the reason is "NotSupportedByParent". The opposite should be set when Accepted: True
, e.g., "SupportedByParent".
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.
@robscott While on the topic of status condition reasons, PTAL at the following:
// This reason is used with the "Accepted" when a controller has not yet
// reconciled the InferencePool.
InferencePoolReasonPending InferencePoolReason = "Pending"
Now that the default status condition has been removed, I question whether this reason is still applicable. If a controller has not yet reconciled the resource, something else is required to set Accepted: Unknown
with Reason: Pending
.
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.
Now that the default status condition has been removed, I question whether this reason is still applicable.
+1, let's remove it.
I have always found the Accepted reason to be unusual. The condition type is Accepted, and when being set to False, the reason is "NotSupportedByParent".
The problem is when you have multiple reasons that a condition could be false. Take HTTPRoute for an example. To follow your example, the positive condition would be something like "AllowedByListenersAndHasMatchingHostnameAndParentAndNoUnsupportedValues".
I think the odds that we add more reasons to be false in the future are quite here, so I'd rather stick with a simpler reason for the positive state here and follow what GW API has done.
api/v1/inferencepool_types.go
Outdated
// This reason is used with the "Accepted" condition when the InferencePool is | ||
// referenced by an HTTPRoute that has been rejected by the Gateway. The user | ||
// should inspect the status of the referring HTTPRoute for the specific reason. | ||
// InferencePoolReasonHTTPRouteNotAccepted is a reason used with the "Accepted" |
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.
Not a blocker, but it could be worth clarifying that this is optional. I don't think we want to require Gateways to jump through to HTTPRoute backends if the HTTPRoute is invalid. It's certainly nice to have, but seems rather onerous to require.
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.
Will do. This reason would be set with Accepted: False
if the referent HTTPRoute has a negative condition, e.g. Accepted: False
, correct?
properties: | ||
parent: | ||
parents: |
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 a great catch, no idea how I'd missed this in review, thanks!
api/v1/inferencepool_types.go
Outdated
// | ||
// +optional | ||
// +kubebuilder:default="FailClose" | ||
FailureMode ExtensionFailureMode `json:"failureMode,omitempty"` | ||
//nolint:kubeapilinter // ignore kubeapilinter here as we want to use pointer since empty means default value. |
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 a pointer is required to set a default value, just "omitempty". My understanding is that this is only ambiguous for structs.
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'm trying to provide consistency across the API as follows:
Optional Fields
- Use a pointer type (
FailureMode *ExtensionFailureMode
) or have a built-in nil value (maps and slices). The only caveat to this rule isInferencePoolStatus
due to K8s API conventions for the status subresource. - Have the
+optional
kubebuilder marker. - Are marked with the
omitempty
JSON tag.
Required Fields
- Are explicitly marked as required with the
+required
kubebuilder marker. - Are NOT marked with the
omitempty
JSON tag since the field should not be empty when being serialized. - Do not use pointer types (
Name ObjectName
). - Use the
omitzero
JSON tag to avoid marshalling the zero value. - K8s API conventions states "required fields where the zero value is a valid value must use pointer types, paired with an omitempty struct tag to avoid spurious null serializations.".
Selector
is a good example of this rule, but the v1 InferencePool API does not support an empty selector, e.g. select all Pods.
^ seems to be consistent with K8s API and GW API 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.
This approach SGTM. While I think this likely undoes some of the less controversial kube-api-linter changes, it is consistent with how all previous releases of GW API have worked, so I'm fine with the idea of following some other project for bigger changes instead of being first here.
api/v1/inferencepool_types.go
Outdated
// | ||
// +required | ||
Name ObjectName `json:"name,omitempty"` | ||
//nolint:kubeapilinter // ignore kubeapilinter here as we want to use pointer since empty means default value. |
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're not using pointer? (I don't think we want to here, I'm just guessing this is copied from another nolint line). In this case I think we can likely remove the nolint
api/v1/inferencepool_types.go
Outdated
// | ||
// +required | ||
Name ObjectName `json:"name,omitempty"` | ||
//nolint:kubeapilinter // ignore kubeapilinter here as we want to use pointer since empty means default value. | ||
Name ObjectName `json:"name"` |
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 think all fields should still have omitempty
api/v1/inferencepool_types.go
Outdated
//nolint:kubeapilinter // ignore kubeapilinter here since the field is required and we don't want omitempty tag. | ||
TargetPorts []Port `json:"targetPorts"` |
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 think omitempty is fine here and all other fields which would allow us to remove nolint
line.
@robscott here is how type LabelSelector struct {
...
// +required
// +kubebuilder:validation:MinItems=1
// +kubebuilder:validation:MaxItems=64
MatchLabels map[LabelKey]LabelValue `json:"matchLabels"`
} We will need to transition |
We can always move from required to optional in the future, but we can't go the other way. My vote would be to keep this required until we add |
Signed-off-by: Daneyon Hansen <[email protected]>
Signed-off-by: Daneyon Hansen <[email protected]>
Signed-off-by: Daneyon Hansen <[email protected]>
Signed-off-by: Daneyon Hansen <[email protected]>
Signed-off-by: Daneyon Hansen <[email protected]>
8550ca8
to
6f89efd
Compare
@danehans: The following test failed, say
Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
I forked it and made it pass the linter #1438 |
PR needs rebase. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
Closing in favor of #1441 |
What type of PR is this?
/kind cleanup
What this PR does / why we need it:
Which issue(s) this PR fixes:
Supports: #1173
Does this PR introduce a user-facing change?: