Skip to content

Commit daf936f

Browse files
committed
Upgrade the inferencePool selector to a struct from a map.
1 parent 69dfa9b commit daf936f

16 files changed

+613
-135
lines changed

api/v1/inferencepool_types.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,16 @@ type InferencePoolList struct {
5151

5252
// InferencePoolSpec defines the desired state of InferencePool
5353
type InferencePoolSpec struct {
54-
// Selector defines a map of labels to watch model server Pods
55-
// that should be included in the InferencePool.
56-
// In some cases, implementations may translate this field to a Service selector, so this matches the simple
57-
// map used for Service selectors instead of the full Kubernetes LabelSelector type.
58-
// If specified, it will be applied to match the model server pods in the same namespace as the InferencePool.
59-
// Cross namesoace selector is not supported.
54+
// Selector determines which Pods are members of this inference pool.
55+
// It matches Pods by their labels only within the same namespace; cross-namespace
56+
// selection is not supported.
57+
//
58+
// The structure of this LabelSelector is intentionally simple to be compatible
59+
// with Kubernetes Service selectors, as some implementations may translate
60+
// this configuration into a Service resource.
6061
//
6162
// +kubebuilder:validation:Required
62-
Selector map[LabelKey]LabelValue `json:"selector"`
63+
Selector LabelSelector `json:"selector"`
6364

6465
// TargetPortNumber defines the port number to access the selected model server Pods.
6566
// The number must be in the range 1 to 65535.

api/v1/shared_types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,13 @@ type LabelKey string
127127
// +kubebuilder:validation:MaxLength=63
128128
// +kubebuilder:validation:Pattern=`^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$`
129129
type LabelValue string
130+
131+
// LabelSelector defines a query for resources based on their labels.
132+
// This simplified version uses only the matchLabels field.
133+
type LabelSelector struct {
134+
// matchLabels contains a set of required {key,value} pairs.
135+
// An object must match every label in this map to be selected.
136+
// The matching logic is an AND operation on all entries.
137+
// +optional
138+
MatchLabels map[LabelKey]LabelValue `json:"matchLabels,omitempty" protobuf:"bytes,1,rep,name=matchLabels"`
139+
}

api/v1/zz_generated.deepcopy.go

Lines changed: 23 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha2
18+
19+
import (
20+
"fmt"
21+
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
24+
runtime "k8s.io/apimachinery/pkg/runtime"
25+
v1 "sigs.k8s.io/gateway-api-inference-extension/api/v1"
26+
)
27+
28+
// ConvertTo converts this InferencePool (v1alpha2) to the v1 version.
29+
func (src *InferencePool) ConvertTo() (*v1.InferencePool, error) {
30+
if src == nil {
31+
return nil, nil
32+
}
33+
34+
v1Status, err := converStatusToV1(src.Status)
35+
if err != nil {
36+
return nil, err
37+
}
38+
dst := &v1.InferencePool{
39+
TypeMeta: src.TypeMeta,
40+
ObjectMeta: src.ObjectMeta,
41+
Spec: v1.InferencePoolSpec{
42+
TargetPortNumber: src.Spec.TargetPortNumber,
43+
EndpointPickerConfig: convertEndpointPickerConfigToV1(&src.Spec.EndpointPickerConfig),
44+
},
45+
Status: *v1Status,
46+
}
47+
if src.Spec.Selector != nil {
48+
dst.Spec.Selector.MatchLabels = make(map[v1.LabelKey]v1.LabelValue, len(src.Spec.Selector))
49+
for k, v := range src.Spec.Selector {
50+
dst.Spec.Selector.MatchLabels[v1.LabelKey(k)] = v1.LabelValue(v)
51+
}
52+
}
53+
return dst, nil
54+
}
55+
56+
// ConvertFrom converts from the v1 version to this version (v1alpha2).
57+
func ConvertFrom(src *v1.InferencePool) (*InferencePool, error) {
58+
if src == nil {
59+
return nil, nil
60+
}
61+
62+
status, err := converStatusFromV1(src.Status)
63+
if err != nil {
64+
return nil, err
65+
}
66+
dst := &InferencePool{
67+
TypeMeta: metav1.TypeMeta{
68+
Kind: "InferencePool",
69+
APIVersion: "inference.networking.x-k8s.io/v1alpha2",
70+
},
71+
ObjectMeta: src.ObjectMeta,
72+
Spec: InferencePoolSpec{
73+
TargetPortNumber: src.Spec.TargetPortNumber,
74+
EndpointPickerConfig: convertEndpointPickerConfigFromV1(&src.Spec.EndpointPickerConfig),
75+
},
76+
Status: *status,
77+
}
78+
79+
if src.Spec.Selector.MatchLabels != nil {
80+
dst.Spec.Selector = make(map[LabelKey]LabelValue, len(src.Spec.Selector.MatchLabels))
81+
for k, v := range src.Spec.Selector.MatchLabels {
82+
dst.Spec.Selector[LabelKey(k)] = LabelValue(v)
83+
}
84+
}
85+
86+
return dst, nil
87+
}
88+
89+
func converStatusToV1(src InferencePoolStatus) (v1Status *v1.InferencePoolStatus, err error) {
90+
u, err := toUnstructured(&src)
91+
if err != nil {
92+
return nil, err
93+
}
94+
return convert[v1.InferencePoolStatus](u)
95+
}
96+
97+
func converStatusFromV1(src v1.InferencePoolStatus) (v1Status *InferencePoolStatus, err error) {
98+
u, err := toUnstructured(&src)
99+
if err != nil {
100+
return nil, err
101+
}
102+
return convert[InferencePoolStatus](u)
103+
}
104+
105+
func convertEndpointPickerConfigToV1(src *EndpointPickerConfig) v1.EndpointPickerConfig {
106+
if src == nil || src.ExtensionRef == nil {
107+
return v1.EndpointPickerConfig{}
108+
}
109+
srcRef := src.ExtensionRef
110+
return v1.EndpointPickerConfig{
111+
ExtensionRef: &v1.Extension{
112+
ExtensionReference: v1.ExtensionReference{
113+
Group: (*v1.Group)(srcRef.Group),
114+
Kind: (*v1.Kind)(srcRef.Kind),
115+
Name: v1.ObjectName(srcRef.Name),
116+
PortNumber: (*v1.PortNumber)(srcRef.PortNumber),
117+
},
118+
ExtensionConnection: v1.ExtensionConnection{
119+
FailureMode: (*v1.ExtensionFailureMode)(srcRef.FailureMode),
120+
},
121+
},
122+
}
123+
}
124+
125+
func convertEndpointPickerConfigFromV1(src *v1.EndpointPickerConfig) EndpointPickerConfig {
126+
if src == nil || src.ExtensionRef == nil {
127+
return EndpointPickerConfig{}
128+
}
129+
srcRef := src.ExtensionRef
130+
return EndpointPickerConfig{
131+
ExtensionRef: &Extension{
132+
ExtensionReference: ExtensionReference{
133+
Group: (*Group)(srcRef.Group),
134+
Kind: (*Kind)(srcRef.Kind),
135+
Name: ObjectName(srcRef.Name),
136+
PortNumber: (*PortNumber)(srcRef.PortNumber),
137+
},
138+
ExtensionConnection: ExtensionConnection{
139+
FailureMode: (*ExtensionFailureMode)(srcRef.FailureMode),
140+
},
141+
},
142+
}
143+
}
144+
145+
func toUnstructured(obj any) (*unstructured.Unstructured, error) {
146+
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
147+
if err != nil {
148+
return nil, err
149+
}
150+
return &unstructured.Unstructured{Object: u}, nil
151+
}
152+
153+
func convert[T any](u *unstructured.Unstructured) (*T, error) {
154+
var res T
155+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &res); err != nil {
156+
return nil, fmt.Errorf("error converting unstructured to T: %v", err)
157+
}
158+
return &res, nil
159+
}

0 commit comments

Comments
 (0)