Skip to content

Conversation

danehans
Copy link
Contributor

@danehans danehans commented Aug 21, 2025

What type of PR is this?

/kind cleanup

What this PR does / why we need it:

  • Refactors InferencePool v1 status.
  • Expands godocs for InferencePool v1 status.
  • Makes the Namespace and Kind fields of a ParentRef optional to match Gateway API ref semantics.
  • Adds the correct typeMeta version in InferencePool v1 <> v1alpha2 conversion.
  • Commit 77831c7 renames the FailureMode types from ExtensionXX to EndpointPickerXXX.
  • Commit 4abcaab aligns v1 InferencePool API types with K8s built-in API and Gateway API conventions, e.g., optional fields are pointer types.
  • Commit 88990b0 runs the API docs generators.

Which issue(s) this PR fixes:

Supports: #1173

Does this PR introduce a user-facing change?:

Make v1 Pool status conditions required.

@k8s-ci-robot k8s-ci-robot added kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Aug 21, 2025
@k8s-ci-robot
Copy link
Contributor

[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 /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot requested review from ahg-g and liu-cong August 21, 2025 17:00
@k8s-ci-robot k8s-ci-robot added approved Indicates a PR has been approved by an approver from all required OWNERS files. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Aug 21, 2025
Copy link

netlify bot commented Aug 21, 2025

Deploy Preview for gateway-api-inference-extension ready!

Name Link
🔨 Latest commit 6f89efd
🔍 Latest deploy log https://app.netlify.com/projects/gateway-api-inference-extension/deploys/68a8c7ce41379f00081177c4
😎 Deploy Preview https://deploy-preview-1427--gateway-api-inference-extension.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@danehans danehans force-pushed the remove_inf_pool_def_condition branch from 20b8deb to d11b1b8 Compare August 21, 2025 17:15
@robscott
Copy link
Member

/cc @robscott @capri-xiyue @zetxqx

@danehans danehans force-pushed the remove_inf_pool_def_condition branch from d11b1b8 to 06a15b7 Compare August 21, 2025 17:25

meta := metav1.TypeMeta{
Kind: src.Kind,
APIVersion: v1.GroupVersion.Version, // Ensure the API version is set correctly.
Copy link
Contributor

@capri-xiyue capri-xiyue Aug 21, 2025

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

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor Author

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.

@danehans danehans force-pushed the remove_inf_pool_def_condition branch from 06a15b7 to 574e13e Compare August 21, 2025 17:53
@danehans danehans force-pushed the remove_inf_pool_def_condition branch from 574e13e to 78c9e01 Compare August 21, 2025 19:26
@danehans
Copy link
Contributor Author

@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.

// 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
Copy link
Contributor Author

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

Copy link
Contributor Author

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
Copy link
Contributor Author

@danehans danehans Aug 21, 2025

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.

@danehans danehans force-pushed the remove_inf_pool_def_condition branch 2 times, most recently from 8e50ce9 to 77831c7 Compare August 21, 2025 21:35
@danehans danehans changed the title Refactors InferencePool v1 Status Refactors InferencePool v1 Status and FailureModes Aug 21, 2025
@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Aug 21, 2025
return convert[v1.InferencePoolStatus](u)

// v1alpha2 used json:"parent" (singular) v1 uses json:"parents"
if v, ok := u.Object["parent"]; ok {
Copy link
Contributor

@capri-xiyue capri-xiyue Aug 21, 2025

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 {
Copy link
Contributor

Choose a reason for hiding this comment

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

same

Copy link
Member

@robscott robscott left a 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

@@ -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.
Copy link
Member

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?

// +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"`
Copy link
Member

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.

Copy link
Contributor Author

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"
Copy link
Member

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.

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 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".

Copy link
Contributor Author

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.

Copy link
Member

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.

// 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"
Copy link
Member

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.

Copy link
Contributor Author

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:
Copy link
Member

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!

//
// +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.
Copy link
Member

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.

Copy link
Contributor Author

@danehans danehans Aug 22, 2025

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

  1. Use a pointer type (FailureMode *ExtensionFailureMode) or have a built-in nil value (maps and slices). The only caveat to this rule is InferencePoolStatus due to K8s API conventions for the status subresource.
  2. Have the +optional kubebuilder marker.
  3. Are marked with the omitempty JSON tag.

Required Fields

  1. Are explicitly marked as required with the +required kubebuilder marker.
  2. Are NOT marked with the omitempty JSON tag since the field should not be empty when being serialized.
  3. Do not use pointer types (Name ObjectName).
  4. Use the omitzero JSON tag to avoid marshalling the zero value.
  5. 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.

Copy link
Member

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.

//
// +required
Name ObjectName `json:"name,omitempty"`
//nolint:kubeapilinter // ignore kubeapilinter here as we want to use pointer since empty means default value.
Copy link
Member

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

//
// +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"`
Copy link
Member

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

Comment on lines 80 to 81
//nolint:kubeapilinter // ignore kubeapilinter here since the field is required and we don't want omitempty tag.
TargetPorts []Port `json:"targetPorts"`
Copy link
Member

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.

@danehans
Copy link
Contributor Author

@robscott here is how LabelSelector is defined in this PR:

type LabelSelector struct {
	...
	// +required
	// +kubebuilder:validation:MinItems=1
	// +kubebuilder:validation:MaxItems=64
	MatchLabels map[LabelKey]LabelValue `json:"matchLabels"`
}

We will need to transition MatchLabels to +optional when additional fields are added to LabelSelector, so I'm going to change this field to optional unless I hear otherwise.

@robscott
Copy link
Member

We will need to transition MatchLabels to +optional when additional fields are added to LabelSelector, so I'm going to change this field to optional unless I hear otherwise.

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 MatchExpressions, at which point we'd require that at least one of those fields was set.

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 22, 2025
Signed-off-by: Daneyon Hansen <[email protected]>
Signed-off-by: Daneyon Hansen <[email protected]>
Signed-off-by: Daneyon Hansen <[email protected]>
@danehans danehans force-pushed the remove_inf_pool_def_condition branch from 8550ca8 to 6f89efd Compare August 22, 2025 19:40
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 22, 2025
@k8s-ci-robot
Copy link
Contributor

@danehans: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
pull-gateway-api-inference-extension-test-unit-main 6f89efd link true /test pull-gateway-api-inference-extension-test-unit-main

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.

@capri-xiyue
Copy link
Contributor

I forked it and made it pass the linter #1438
But I need to change the the InferencePoolStatus to become an pointer as zero value is valid now. I think either it's a pointer or non-pointer, both works?
Screenshot 2025-08-22 at 3 37 38 PM

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 23, 2025
@k8s-ci-robot
Copy link
Contributor

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.

@danehans
Copy link
Contributor Author

Closing in favor of #1441

@danehans danehans closed this Aug 25, 2025
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. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants