Skip to content

Commit 6f89efd

Browse files
committed
Resolve robscott feedback
Signed-off-by: Daneyon Hansen <[email protected]>
1 parent 69c946d commit 6f89efd

File tree

8 files changed

+105
-203
lines changed

8 files changed

+105
-203
lines changed

.golangci-kal.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ linters:
3232
policy: SuggestFix # SuggestFix | Warn # The policy for pointers in optional fields. Defaults to `SuggestFix`.
3333
omitempty:
3434
policy: SuggestFix # SuggestFix | Warn | Ignore # The policy for omitempty in optional fields. Defaults to `SuggestFix`.
35+
omitzero:
36+
policy: Forbid # Enforce the `omitempty` route with a pointer for all optional structs.
3537
exclusions:
3638
generated: strict
3739
paths:

api/v1/inferencepool_types.go

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,10 @@ type InferencePool struct {
4343
// Status defines the observed state of the InferencePool.
4444
//
4545
// +optional
46-
//nolint:kubeapilinter // ignore kubeapilinter to follow K8s conventions of optional but non-pointer.
4746
Status InferencePoolStatus `json:"status,omitempty"`
4847
}
4948

50-
// InferencePoolList contains a list InferencePools.
49+
// InferencePoolList contains a list of InferencePools.
5150
//
5251
// +kubebuilder:object:root=true
5352
type InferencePoolList struct {
@@ -67,8 +66,7 @@ type InferencePoolSpec struct {
6766
// this configuration into a Service resource.
6867
//
6968
// +required
70-
//nolint:kubeapilinter // ignore kubeapilinter here as we don't want to use pointer here.
71-
Selector LabelSelector `json:"selector"`
69+
Selector LabelSelector `json:"selector,omitzero"`
7270

7371
// TargetPorts defines a list of ports that are exposed by this InferencePool.
7472
// Currently, the list may only include a single port definition.
@@ -77,8 +75,7 @@ type InferencePoolSpec struct {
7775
// +kubebuilder:validation:MaxItems=1
7876
// +listType=atomic
7977
// +required
80-
//nolint:kubeapilinter // ignore kubeapilinter here since the field is required and we don't want omitempty tag.
81-
TargetPorts []Port `json:"targetPorts"`
78+
TargetPorts []Port `json:"targetPorts,omitzero"`
8279

8380
// EndpointPickerRef is a reference to the Endpoint Picker extension and its
8481
// associated configuration.
@@ -93,8 +90,7 @@ type Port struct {
9390
// The number must be in the range 1 to 65535.
9491
//
9592
// +required
96-
//nolint:kubeapilinter // ignore kubeapilinter here since the field is required and we don't want omitempty tag.
97-
Number PortNumber `json:"number"`
93+
Number PortNumber `json:"number,omitzero"`
9894
}
9995

10096
// EndpointPickerRef specifies a reference to an Endpoint Picker extension and its
@@ -126,23 +122,20 @@ type EndpointPickerRef struct {
126122
// Name is the name of the referent API object.
127123
//
128124
// +required
129-
//nolint:kubeapilinter // ignore kubeapilinter here as we want to use pointer since empty means default value.
130-
Name ObjectName `json:"name"`
125+
Name ObjectName `json:"name,omitzero"`
131126

132127
// PortNumber is the port number of the Endpoint Picker extension service. When unspecified,
133128
// implementations SHOULD infer a default value of 9002 when the kind field is "Service" or
134129
// unspecified (defaults to "Service").
135130
//
136131
// +optional
137-
//nolint:kubeapilinter // ignore kubeapilinter here as we want to use pointer here as 0 usually means all ports.
138132
PortNumber *PortNumber `json:"portNumber,omitempty"`
139133

140134
// FailureMode configures how the parent handles the case when the Endpoint Picker extension
141135
// is non-responsive. When unspecified, defaults to "FailClose".
142136
//
143137
// +optional
144138
// +kubebuilder:default="FailClose"
145-
//nolint:kubeapilinter // ignore kubeapilinter here as we want to use pointer since empty means default value.
146139
FailureMode *EndpointPickerFailureMode `json:"failureMode,omitempty"`
147140
}
148141

@@ -185,18 +178,18 @@ type ParentStatus struct {
185178
// state of the InferencePool. This field is required to be set by the controller that
186179
// manages the InferencePool.
187180
//
188-
// Known condition types are:
181+
// Supported condition types are:
189182
//
190183
// * "Accepted"
191184
// * "ResolvedRefs"
192185
//
193-
// +required
186+
// +optional
194187
// +listType=map
195188
// +listMapKey=type
196-
// +kubebuilder:validation:MinItems=1
189+
// +patchStrategy=merge
190+
// +patchMergeKey=type
197191
// +kubebuilder:validation:MaxItems=8
198-
//nolint:kubeapilinter // ignore kubeapilinter here as we want conditions to be required.
199-
Conditions []metav1.Condition `json:"conditions"`
192+
Conditions []metav1.Condition `json:"conditions,omitempty"`
200193

201194
// ParentRef is used to identify the parent resource that this status
202195
// is associated with. It is used to match the InferencePool with the parent
@@ -222,7 +215,7 @@ const (
222215
//
223216
// Possible reasons for this condition to be False are:
224217
//
225-
// * "NotSupportedByParent"
218+
// * "Accepted"
226219
// * "HTTPRouteNotAccepted"
227220
//
228221
// Possible reasons for this condition to be Unknown are:
@@ -236,22 +229,18 @@ const (
236229
// InferencePoolReasonAccepted is a reason used with the "Accepted" condition
237230
// when the InferencePool is accepted by a Parent because the Parent supports
238231
// InferencePool as a backend.
239-
InferencePoolReasonAccepted InferencePoolReason = "SupportedByParent"
232+
InferencePoolReasonAccepted InferencePoolReason = "Accepted"
240233

241234
// InferencePoolReasonNotSupportedByParent is a reason used with the "Accepted"
242235
// condition when the InferencePool has not been accepted by a Parent because
243236
// the Parent does not support InferencePool as a backend.
244237
InferencePoolReasonNotSupportedByParent InferencePoolReason = "NotSupportedByParent"
245238

246-
// InferencePoolReasonHTTPRouteNotAccepted is a reason used with the "Accepted"
247-
// condition when the InferencePool is referenced by an HTTPRoute that has been
248-
// rejected by the Parent. The user should inspect the status of the referring
249-
// HTTPRoute for the specific reason.
239+
// InferencePoolReasonHTTPRouteNotAccepted is an optional reason used with the
240+
// "Accepted" condition when the InferencePool is referenced by an HTTPRoute that
241+
// has been rejected by the Parent. The user should inspect the status of the
242+
// referring HTTPRoute for the specific reason.
250243
InferencePoolReasonHTTPRouteNotAccepted InferencePoolReason = "HTTPRouteNotAccepted"
251-
252-
// This reason is used with the "Accepted" when a controller has not yet
253-
// reconciled the InferencePool.
254-
InferencePoolReasonPending InferencePoolReason = "Pending"
255244
)
256245

257246
const (
@@ -295,19 +284,21 @@ type ParentReference struct {
295284
//
296285
// +optional
297286
// +kubebuilder:default=Gateway
298-
//nolint:kubeapilinter // ignore kubeapilinter here as we want to use pointer here as empty means default value.
299287
Kind *Kind `json:"kind,omitempty"`
300288

301289
// Name is the name of the referent API object.
302290
//
303291
// +required
304-
Name ObjectName `json:"name,omitempty"`
292+
Name ObjectName `json:"name,omitzero"`
305293

306-
// Namespace is the namespace of the referent API object. When unspecified,
307-
// the namespace of the referent is assumed to be the same as the namespace
308-
// of the referring object.
294+
// Namespace is the namespace of the referenced object. When unspecified, the local
295+
// namespace is inferred.
296+
//
297+
// Note that when a namespace different than the local namespace is specified,
298+
// a ReferenceGrant object is required in the referent namespace to allow that
299+
// namespace's owner to accept the reference. See the ReferenceGrant
300+
// documentation for details: https://gateway-api.sigs.k8s.io/api-types/referencegrant/
309301
//
310302
// +optional
311-
//nolint:kubeapilinter // ignore kubeapilinter here as we want to use pointer here as empty means same namespace.
312303
Namespace *Namespace `json:"namespace,omitempty"`
313304
}

api/v1/shared_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,5 +139,5 @@ type LabelSelector struct {
139139
// +required
140140
// +kubebuilder:validation:MinItems=1
141141
// +kubebuilder:validation:MaxItems=64
142-
MatchLabels map[LabelKey]LabelValue `json:"matchLabels,omitempty"`
142+
MatchLabels map[LabelKey]LabelValue `json:"matchLabels,omitzero"`
143143
}

apix/v1alpha2/inferencepool_conversion.go

Lines changed: 67 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,8 @@ package v1alpha2
1818

1919
import (
2020
"errors"
21-
"fmt"
22-
"time"
2321

2422
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26-
runtime "k8s.io/apimachinery/pkg/runtime"
2723
"k8s.io/utils/ptr"
2824

2925
v1 "sigs.k8s.io/gateway-api-inference-extension/api/v1"
@@ -43,9 +39,6 @@ func (src *InferencePool) ConvertTo(dst *v1.InferencePool) error {
4339
return err
4440
}
4541

46-
// v1 requires at least one condition per parent.
47-
ensureV1StatusConditionsMinimum(v1Status)
48-
4942
meta := metav1.TypeMeta{
5043
Kind: src.Kind,
5144
APIVersion: v1.GroupVersion.String(), // Ensure the API version is set correctly.
@@ -102,31 +95,24 @@ func convertStatusToV1(src *InferencePoolStatus) (*v1.InferencePoolStatus, error
10295
if src == nil {
10396
return nil, errors.New("src cannot be nil")
10497
}
105-
u, err := toUnstructured(src)
106-
if err != nil {
107-
return nil, err
108-
}
109-
110-
// v1alpha2 used json:"parent" (singular) v1 uses json:"parents"
111-
if v, ok := u.Object["parent"]; ok {
112-
u.Object["parents"] = v
113-
delete(u.Object, "parent")
114-
}
115-
116-
out, err := convert[v1.InferencePoolStatus](u)
117-
if err != nil {
118-
return nil, err
98+
out := &v1.InferencePoolStatus{
99+
Parents: make([]v1.ParentStatus, 0, len(src.Parents)),
119100
}
120-
121-
// Map condition reason string: "Accepted" (v1alpha2) -> "SupportedByParent" (v1)
122-
for i := range out.Parents {
123-
for j := range out.Parents[i].Conditions {
124-
c := &out.Parents[i].Conditions[j]
125-
if c.Type == string(v1.InferencePoolConditionAccepted) &&
126-
c.Reason == string(InferencePoolReasonAccepted) {
127-
c.Reason = string(v1.InferencePoolReasonAccepted)
101+
for _, p := range src.Parents {
102+
ps := v1.ParentStatus{
103+
ParentRef: toV1ParentRef(p.GatewayRef),
104+
Conditions: make([]metav1.Condition, 0, len(p.Conditions)),
105+
}
106+
for _, c := range p.Conditions {
107+
cc := c
108+
// v1alpha2: "Accepted" -> v1: "SupportedByParent"
109+
if cc.Type == string(v1.InferencePoolConditionAccepted) &&
110+
cc.Reason == string(InferencePoolReasonAccepted) {
111+
cc.Reason = string(v1.InferencePoolReasonAccepted)
128112
}
113+
ps.Conditions = append(ps.Conditions, cc)
129114
}
115+
out.Parents = append(out.Parents, ps)
130116
}
131117
return out, nil
132118
}
@@ -135,33 +121,64 @@ func convertStatusFromV1(src *v1.InferencePoolStatus) (*InferencePoolStatus, err
135121
if src == nil {
136122
return nil, errors.New("src cannot be nil")
137123
}
138-
u, err := toUnstructured(src)
139-
if err != nil {
140-
return nil, err
124+
out := &InferencePoolStatus{
125+
Parents: make([]PoolStatus, 0, len(src.Parents)),
141126
}
142-
143-
// v1 uses json:"parents"; v1alpha2 used json:"parent" (singular)
144-
if v, ok := u.Object["parents"]; ok {
145-
u.Object["parent"] = v
146-
delete(u.Object, "parents")
127+
for _, p := range src.Parents {
128+
ps := PoolStatus{
129+
GatewayRef: fromV1ParentRef(p.ParentRef),
130+
Conditions: make([]metav1.Condition, 0, len(p.Conditions)),
131+
}
132+
for _, c := range p.Conditions {
133+
cc := c
134+
// v1: "SupportedByParent" -> v1alpha2: "Accepted"
135+
if cc.Type == string(v1.InferencePoolConditionAccepted) &&
136+
cc.Reason == string(v1.InferencePoolReasonAccepted) {
137+
cc.Reason = string(InferencePoolReasonAccepted)
138+
}
139+
ps.Conditions = append(ps.Conditions, cc)
140+
}
141+
out.Parents = append(out.Parents, ps)
147142
}
143+
return out, nil
144+
}
148145

149-
out, err := convert[InferencePoolStatus](u)
150-
if err != nil {
151-
return nil, err
146+
func toV1ParentRef(in ParentGatewayReference) v1.ParentReference {
147+
out := v1.ParentReference{
148+
Name: v1.ObjectName(in.Name),
149+
}
150+
if in.Group != nil {
151+
g := v1.Group(*in.Group)
152+
out.Group = &g
153+
}
154+
if in.Kind != nil {
155+
k := v1.Kind(*in.Kind)
156+
out.Kind = &k
152157
}
158+
if in.Namespace != nil {
159+
ns := v1.Namespace(*in.Namespace)
160+
out.Namespace = &ns
161+
}
162+
return out
163+
}
153164

154-
// Map condition reason string: "SupportedByParent" (v1) -> "Accepted" (v1alpha2)
155-
for i := range out.Parents {
156-
for j := range out.Parents[i].Conditions {
157-
c := &out.Parents[i].Conditions[j]
158-
if c.Type == string(v1.InferencePoolConditionAccepted) &&
159-
c.Reason == string(v1.InferencePoolReasonAccepted) {
160-
c.Reason = string(InferencePoolReasonAccepted)
161-
}
162-
}
165+
func fromV1ParentRef(in v1.ParentReference) ParentGatewayReference {
166+
out := ParentGatewayReference{
167+
Name: ObjectName(in.Name),
163168
}
164-
return out, nil
169+
if in.Group != nil {
170+
g := Group(*in.Group)
171+
out.Group = &g
172+
}
173+
if in.Kind != nil {
174+
k := Kind(*in.Kind)
175+
out.Kind = &k
176+
}
177+
if in.Namespace != nil {
178+
ns := Namespace(*in.Namespace)
179+
out.Namespace = &ns
180+
}
181+
return out
165182
}
166183

167184
func convertExtensionRefToV1(src *Extension) (v1.EndpointPickerRef, error) {
@@ -206,39 +223,3 @@ func convertEndpointPickerRefFromV1(src *v1.EndpointPickerRef) (Extension, error
206223
}
207224
return extension, nil
208225
}
209-
210-
func toUnstructured(obj any) (*unstructured.Unstructured, error) {
211-
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
212-
if err != nil {
213-
return nil, err
214-
}
215-
return &unstructured.Unstructured{Object: u}, nil
216-
}
217-
218-
func convert[T any](u *unstructured.Unstructured) (*T, error) {
219-
var res T
220-
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &res); err != nil {
221-
return nil, fmt.Errorf("error converting unstructured to T: %v", err)
222-
}
223-
return &res, nil
224-
}
225-
226-
// Ensure v1's PoolStatus.Conditions has at least one entry.
227-
func ensureV1StatusConditionsMinimum(s *v1.InferencePoolStatus) {
228-
if s == nil {
229-
return
230-
}
231-
epoch := metav1.NewTime(time.Unix(0, 0))
232-
for i := range s.Parents {
233-
ps := &s.Parents[i]
234-
if len(ps.Conditions) == 0 {
235-
ps.Conditions = []metav1.Condition{{
236-
Type: string(v1.InferencePoolConditionAccepted),
237-
Status: metav1.ConditionUnknown,
238-
Reason: string(v1.InferencePoolReasonPending),
239-
Message: "Waiting for controller",
240-
LastTransitionTime: epoch,
241-
}}
242-
}
243-
}
244-
}

0 commit comments

Comments
 (0)