Skip to content
This repository was archived by the owner on Jul 24, 2025. It is now read-only.

Commit 9f3abad

Browse files
authored
Create HTTPRoute as a child resource of ModelService (#210)
* WIP Signed-off-by: Jing Chen <[email protected]> * Test WIP Signed-off-by: Jing Chen <[email protected]> * Working impl Signed-off-by: Jing Chen <[email protected]> * Update samples baseconfig Signed-off-by: Jing Chen <[email protected]> * Trigger CI Signed-off-by: Jing Chen <[email protected]> * Fix e2e test Signed-off-by: Jing Chen <[email protected]> --------- Signed-off-by: Jing Chen <[email protected]>
1 parent 6983154 commit 9f3abad

22 files changed

+6747
-254
lines changed

api/v1alpha1/modelservice_types.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
corev1 "k8s.io/api/core/v1"
55
res "k8s.io/apimachinery/pkg/api/resource"
66
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
78
)
89

910
// ModelService is the Schema for the modelservices API.
@@ -179,6 +180,79 @@ type Routing struct {
179180
// These can be referenced by name in configuration of base configuration or model services
180181
// +optional
181182
Ports []Port `json:"ports,omitempty"`
183+
184+
// GatewayRef is merged to baseconfig based on the Name field.
185+
// Directly from Gateway API: https://gateway-api.sigs.k8s.io/reference/spec/#commonroutespec
186+
// ParentRefs references the resources (usually Gateways) that a Route wants
187+
// to be attached to. Note that the referenced parent resource needs to
188+
// allow this for the attachment to be complete. For Gateways, that means
189+
// the Gateway needs to allow attachment from Routes of this kind and
190+
// namespace. For Services, that means the Service must either be in the same
191+
// namespace for a "producer" route, or the mesh implementation must support
192+
// and allow "consumer" routes for the referenced Service. ReferenceGrant is
193+
// not applicable for governing ParentRefs to Services - it is not possible to
194+
// create a "producer" route for a Service in a different namespace from the
195+
// Route.
196+
//
197+
// There are two kinds of parent resources with "Core" support:
198+
//
199+
// * Gateway (Gateway conformance profile)
200+
// * Service (Mesh conformance profile, ClusterIP Services only)
201+
//
202+
// This API may be extended in the future to support additional kinds of parent
203+
// resources.
204+
//
205+
// ParentRefs must be _distinct_. This means either that:
206+
//
207+
// * They select different objects. If this is the case, then parentRef
208+
// entries are distinct. In terms of fields, this means that the
209+
// multi-part key defined by `group`, `kind`, `namespace`, and `name` must
210+
// be unique across all parentRef entries in the Route.
211+
// * They do not select different objects, but for each optional field used,
212+
// each ParentRef that selects the same object must set the same set of
213+
// optional fields to different values. If one ParentRef sets a
214+
// combination of optional fields, all must set the same combination.
215+
//
216+
// Some examples:
217+
//
218+
// * If one ParentRef sets `sectionName`, all ParentRefs referencing the
219+
// same object must also set `sectionName`.
220+
// * If one ParentRef sets `port`, all ParentRefs referencing the same
221+
// object must also set `port`.
222+
// * If one ParentRef sets `sectionName` and `port`, all ParentRefs
223+
// referencing the same object must also set `sectionName` and `port`.
224+
//
225+
// It is possible to separately reference multiple distinct objects that may
226+
// be collapsed by an implementation. For example, some implementations may
227+
// choose to merge compatible Gateway Listeners together. If that is the
228+
// case, the list of routes attached to those resources should also be
229+
// merged.
230+
//
231+
// Note that for ParentRefs that cross namespace boundaries, there are specific
232+
// rules. Cross-namespace references are only valid if they are explicitly
233+
// allowed by something in the namespace they are referring to. For example,
234+
// Gateway has the AllowedRoutes field, and ReferenceGrant provides a
235+
// generic way to enable other kinds of cross-namespace reference.
236+
//
237+
// <gateway:experimental:description>
238+
// ParentRefs from a Route to a Service in the same namespace are "producer"
239+
// routes, which apply default routing rules to inbound connections from
240+
// any namespace to the Service.
241+
//
242+
// ParentRefs from a Route to a Service in a different namespace are
243+
// "consumer" routes, and these routing rules are only applied to outbound
244+
// connections originating from the same namespace as the Route, for which
245+
// the intended destination of the connections are a Service targeted as a
246+
// ParentRef of the Route.
247+
// </gateway:experimental:description>
248+
//
249+
// +optional
250+
// +kubebuilder:validation:MaxItems=32
251+
// <gateway:standard:validation:XValidation:message="sectionName must be specified when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) || p1.sectionName == '') == (!has(p2.sectionName) || p2.sectionName == '')) : true))">
252+
// <gateway:standard:validation:XValidation:message="sectionName must be unique when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName))))">
253+
// <gateway:experimental:validation:XValidation:message="sectionName or port must be specified when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) || p1.sectionName == '') == (!has(p2.sectionName) || p2.sectionName == '') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) || p2.port == 0)): true))">
254+
// <gateway:experimental:validation:XValidation:message="sectionName or port must be unique when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || ( has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)) && (((!has(p1.port) || p1.port == 0) && (!has(p2.port) || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port))))">
255+
GatewayRefs []gatewayv1.ParentReference `json:"gatewayRefs,omitempty"`
182256
}
183257

184258
// ModelArtifacts describes the source of the model
@@ -290,6 +364,11 @@ type ModelServiceStatus struct {
290364
// this reference will be nil
291365
//
292366
EppDeploymentRef *string `json:"eppDeploymentRef,omitempty"`
367+
// HTTPRoute identifies the HTTPRoute resource
368+
// if HTTPRoute is yet to be created,
369+
// this reference will be nil
370+
//
371+
HTTPRouteRef *string `json:"httpRouteRef,omitempty"`
293372
// InferenceModelRef identifies the inference model resource
294373
// if inference model is yet to be created,
295374
// this reference will be nil

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/generate.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
msv1alpha1 "github.com/llm-d/llm-d-model-service/api/v1alpha1"
1919
"github.com/llm-d/llm-d-model-service/internal/controller"
2020
giev1alpha2 "sigs.k8s.io/gateway-api-inference-extension/api/v1alpha2"
21+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
2122
)
2223

2324
func readModelService(ctx context.Context, filename string, logger logr.Logger) (*msv1alpha1.ModelService, error) {
@@ -101,6 +102,11 @@ func generateManifests(ctx context.Context, manifestFile string, configFile stri
101102
logger.Info("unable to add model service to scheme")
102103
return nil, err
103104
}
105+
err = gatewayv1.Install(scheme.Scheme)
106+
if err != nil {
107+
logger.Info("unable to add gateway api extension to scheme")
108+
return nil, err
109+
}
104110
err = giev1alpha2.Install(scheme.Scheme)
105111
if err != nil {
106112
logger.Info("unable to add gateway api extension to scheme")

cmd/run.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"go.uber.org/zap/zapcore"
3030
"sigs.k8s.io/controller-runtime/pkg/log/zap"
3131
giev1alpha2 "sigs.k8s.io/gateway-api-inference-extension/api/v1alpha2"
32+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
3233
// +kubebuilder:scaffold:imports
3334
)
3435

@@ -83,6 +84,7 @@ func runController() {
8384
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
8485

8586
utilruntime.Must(msv1alpha1.AddToScheme(scheme))
87+
utilruntime.Must(gatewayv1.Install(scheme))
8688
utilruntime.Must(giev1alpha2.Install(scheme))
8789
var opts = zap.Options{
8890
Development: false,

0 commit comments

Comments
 (0)