Skip to content

Commit d7ab27d

Browse files
committed
Upgrade the inferencePool selector to a struct from a map.
1 parent 2d7613c commit d7ab27d

22 files changed

+604
-146
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: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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+
26+
v1 "sigs.k8s.io/gateway-api-inference-extension/api/v1"
27+
)
28+
29+
// ConvertTo converts this InferencePool (v1alpha2) to the v1 version.
30+
func (src *InferencePool) ConvertTo() (*v1.InferencePool, error) {
31+
if src == nil {
32+
return nil, nil
33+
}
34+
35+
v1EndPointPickerConfig, err := convertEndpointPickerConfigToV1(&src.Spec.EndpointPickerConfig)
36+
if err != nil {
37+
return nil, err
38+
}
39+
v1Status, err := converStatusToV1(src.Status)
40+
if err != nil {
41+
return nil, err
42+
}
43+
dst := &v1.InferencePool{
44+
TypeMeta: src.TypeMeta,
45+
ObjectMeta: src.ObjectMeta,
46+
Spec: v1.InferencePoolSpec{
47+
TargetPortNumber: src.Spec.TargetPortNumber,
48+
EndpointPickerConfig: *v1EndPointPickerConfig,
49+
},
50+
Status: *v1Status,
51+
}
52+
if src.Spec.Selector != nil {
53+
dst.Spec.Selector.MatchLabels = make(map[v1.LabelKey]v1.LabelValue, len(src.Spec.Selector))
54+
for k, v := range src.Spec.Selector {
55+
dst.Spec.Selector.MatchLabels[v1.LabelKey(k)] = v1.LabelValue(v)
56+
}
57+
}
58+
return dst, nil
59+
}
60+
61+
// ConvertFrom converts from the v1 version to this version (v1alpha2).
62+
func ConvertFrom(src *v1.InferencePool) (*InferencePool, error) {
63+
if src == nil {
64+
return nil, nil
65+
}
66+
67+
endPointPickerConfig, err := convertEndpointPickerConfigFromV1(&src.Spec.EndpointPickerConfig)
68+
if err != nil {
69+
return nil, err
70+
}
71+
status, err := converStatusFromV1(src.Status)
72+
if err != nil {
73+
return nil, err
74+
}
75+
dst := &InferencePool{
76+
TypeMeta: metav1.TypeMeta{
77+
Kind: "InferencePool",
78+
APIVersion: "inference.networking.x-k8s.io/v1alpha2",
79+
},
80+
ObjectMeta: src.ObjectMeta,
81+
Spec: InferencePoolSpec{
82+
TargetPortNumber: src.Spec.TargetPortNumber,
83+
EndpointPickerConfig: *endPointPickerConfig,
84+
},
85+
Status: *status,
86+
}
87+
88+
if src.Spec.Selector.MatchLabels != nil {
89+
dst.Spec.Selector = make(map[LabelKey]LabelValue, len(src.Spec.Selector.MatchLabels))
90+
for k, v := range src.Spec.Selector.MatchLabels {
91+
dst.Spec.Selector[LabelKey(k)] = LabelValue(v)
92+
}
93+
}
94+
95+
return dst, nil
96+
}
97+
98+
func converStatusToV1(src InferencePoolStatus) (*v1.InferencePoolStatus, error) {
99+
u, err := toUnstructured(&src)
100+
if err != nil {
101+
return nil, err
102+
}
103+
return convert[v1.InferencePoolStatus](u)
104+
}
105+
106+
func converStatusFromV1(src v1.InferencePoolStatus) (*InferencePoolStatus, error) {
107+
u, err := toUnstructured(&src)
108+
if err != nil {
109+
return nil, err
110+
}
111+
return convert[InferencePoolStatus](u)
112+
}
113+
114+
func convertEndpointPickerConfigToV1(src *EndpointPickerConfig) (*v1.EndpointPickerConfig, error) {
115+
u, err := toUnstructured(&src)
116+
if err != nil {
117+
return nil, err
118+
}
119+
return convert[v1.EndpointPickerConfig](u)
120+
}
121+
122+
func convertEndpointPickerConfigFromV1(src *v1.EndpointPickerConfig) (*EndpointPickerConfig, error) {
123+
u, err := toUnstructured(&src)
124+
if err != nil {
125+
return nil, err
126+
}
127+
return convert[EndpointPickerConfig](u)
128+
}
129+
130+
func toUnstructured(obj any) (*unstructured.Unstructured, error) {
131+
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
132+
if err != nil {
133+
return nil, err
134+
}
135+
return &unstructured.Unstructured{Object: u}, nil
136+
}
137+
138+
func convert[T any](u *unstructured.Unstructured) (*T, error) {
139+
var res T
140+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &res); err != nil {
141+
return nil, fmt.Errorf("error converting unstructured to T: %v", err)
142+
}
143+
return &res, nil
144+
}

0 commit comments

Comments
 (0)