Skip to content

Commit 416b36a

Browse files
authored
Handle RawDeployment mode in endpoint details page (#128)
* added check for raw deployment and showing it on frontend Signed-off-by: Harshit Nayan <[email protected]> * fixed linting Signed-off-by: Harshit Nayan <[email protected]> * fixed python linting Signed-off-by: Harshit Nayan <[email protected]> * fixed python linting Signed-off-by: Harshit Nayan <[email protected]> * fixed python linting Signed-off-by: Harshit Nayan <[email protected]> * fixed python linting finally Signed-off-by: Harshit Nayan <[email protected]> --------- Signed-off-by: Harshit Nayan <[email protected]>
1 parent 71807ef commit 416b36a

File tree

6 files changed

+300
-44
lines changed

6 files changed

+300
-44
lines changed

backend/apps/common/routes/get.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,21 @@ def get_inference_services(namespace):
2222
@bp.route("/api/namespaces/<namespace>/inferenceservices/<name>")
2323
def get_inference_service(namespace, name):
2424
"""Return an InferenceService CR as a json object."""
25-
inference_service = api.get_custom_rsrc(**versions.inference_service_gvk(),
26-
namespace=namespace, name=name)
25+
inference_service = api.get_custom_rsrc(
26+
**versions.inference_service_gvk(),
27+
namespace=namespace, name=name)
2728
if request.args.get("logs", "false") == "true":
2829
# find the logs
2930
return api.success_response(
3031
"serviceLogs", get_inference_service_logs(inference_service),
3132
)
3233

34+
# deployment mode information to the response
35+
deployment_mode = ("RawDeployment"
36+
if utils.is_raw_deployment(inference_service)
37+
else "Serverless")
38+
inference_service["deploymentMode"] = deployment_mode
39+
3340
return api.success_response("inferenceService", inference_service)
3441

3542

@@ -111,3 +118,45 @@ def get_inference_service_events(namespace, name):
111118
return api.success_response(
112119
"events", api.serialize(events),
113120
)
121+
122+
123+
# RawDeployment mode endpoints
124+
@bp.route("/api/namespaces/<namespace>/deployments/<name>")
125+
def get_kubernetes_deployment(namespace, name):
126+
"""Return a Kubernetes Deployment object as json."""
127+
deployment = api.get_custom_rsrc(**versions.K8S_DEPLOYMENT,
128+
namespace=namespace, name=name)
129+
return api.success_response("deployment", deployment)
130+
131+
132+
@bp.route("/api/namespaces/<namespace>/services/<name>")
133+
def get_kubernetes_service(namespace, name):
134+
"""Return a Kubernetes Service object as json."""
135+
service = api.get_custom_rsrc(**versions.K8S_SERVICE,
136+
namespace=namespace, name=name)
137+
return api.success_response("service", service)
138+
139+
140+
@bp.route("/api/namespaces/<namespace>/hpas/<name>")
141+
def get_kubernetes_hpa(namespace, name):
142+
"""Return a Kubernetes HPA object as json."""
143+
hpa = api.get_custom_rsrc(**versions.K8S_HPA,
144+
namespace=namespace, name=name)
145+
return api.success_response("hpa", hpa)
146+
147+
148+
@bp.route("/api/namespaces/<namespace>/inferenceservices/<name>/"
149+
"rawdeployment/<component>")
150+
def get_raw_deployment_objects(namespace, name, component):
151+
"""Return all Kubernetes native resources for a RawDeployment component."""
152+
153+
inference_service = api.get_custom_rsrc(
154+
**versions.inference_service_gvk(),
155+
namespace=namespace, name=name)
156+
157+
if not utils.is_raw_deployment(inference_service):
158+
return api.error_response(
159+
"InferenceService is not in RawDeployment mode", 400)
160+
161+
objects = utils.get_raw_deployment_objects(inference_service, component)
162+
return api.success_response("rawDeploymentObjects", objects)

backend/apps/common/utils.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33

44
from kubeflow.kubeflow.crud_backend import api, helpers, logging
5+
from . import versions
56

67
log = logging.getLogger(__name__)
78

@@ -124,3 +125,82 @@ def get_components_revisions_dict(components, svc):
124125
)
125126

126127
return revisions_dict
128+
129+
130+
def is_raw_deployment(svc):
131+
"""
132+
Check if an InferenceService is using RawDeployment mode.
133+
134+
Returns True if the service uses RawDeployment, False for Serverless mode.
135+
"""
136+
annotations = svc.get("metadata", {}).get("annotations", {})
137+
138+
# Check for the new KServe annotation
139+
deployment_mode = annotations.get("serving.kserve.io/deploymentMode", "")
140+
if deployment_mode.lower() == "rawdeployment":
141+
return True
142+
143+
# Check for legacy annotation (backward compatibility)
144+
raw_mode = annotations.get("serving.kubeflow.org/raw", "false")
145+
if raw_mode.lower() == "true":
146+
return True
147+
148+
return False
149+
150+
151+
def get_raw_deployment_objects(svc, component):
152+
"""
153+
Get Kubernetes native resources for a RawDeployment InferenceService
154+
component.
155+
156+
Returns a dictionary with deployment, service, and hpa objects.
157+
"""
158+
namespace = svc["metadata"]["namespace"]
159+
svc_name = svc["metadata"]["name"]
160+
161+
# RawDeployment resources follow naming convention: {isvc-name}-{component}
162+
resource_name = f"{svc_name}-{component}"
163+
164+
objects = {
165+
"deployment": None,
166+
"service": None,
167+
"hpa": None,
168+
}
169+
170+
try:
171+
# Get Deployment
172+
deployment = api.get_custom_rsrc(
173+
**versions.K8S_DEPLOYMENT,
174+
namespace=namespace,
175+
name=resource_name
176+
)
177+
objects["deployment"] = deployment
178+
log.info(f"Found deployment {resource_name} for component {component}")
179+
except Exception as e:
180+
log.warning(f"Could not find deployment {resource_name}: {e}")
181+
182+
try:
183+
# Get Service
184+
service = api.get_custom_rsrc(
185+
**versions.K8S_SERVICE,
186+
namespace=namespace,
187+
name=resource_name
188+
)
189+
objects["service"] = service
190+
log.info(f"Found service {resource_name} for component {component}")
191+
except Exception as e:
192+
log.warning(f"Could not find service {resource_name}: {e}")
193+
194+
try:
195+
# Get HPA (optional)
196+
hpa = api.get_custom_rsrc(
197+
**versions.K8S_HPA,
198+
namespace=namespace,
199+
name=resource_name
200+
)
201+
objects["hpa"] = hpa
202+
log.info(f"Found HPA {resource_name} for component {component}")
203+
except Exception as e:
204+
log.debug(f"No HPA found for {resource_name}: {e}")
205+
206+
return objects

backend/apps/common/versions.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@
1414
"version": "v1",
1515
"kind": "services"}
1616

17+
# Kubernetes native resources for RawDeployment mode
18+
K8S_DEPLOYMENT = {"group": "apps",
19+
"version": "v1",
20+
"kind": "deployments"}
21+
K8S_SERVICE = {"group": "",
22+
"version": "v1",
23+
"kind": "services"}
24+
K8S_HPA = {"group": "autoscaling",
25+
"version": "v2",
26+
"kind": "horizontalpodautoscalers"}
27+
1728

1829
def inference_service_gvk():
1930
"""

frontend/src/app/pages/server-info/server-info.component.ts

Lines changed: 81 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Component, OnInit, OnDestroy } from '@angular/core';
22
import { Observable, of, forkJoin, Subscription } from 'rxjs';
3-
import { tap, map, concatMap, timeout } from 'rxjs/operators';
3+
import { tap, map, concatMap, timeout, catchError } from 'rxjs/operators';
44
import { Router, ActivatedRoute } from '@angular/router';
55
import {
66
NamespaceService,
@@ -246,46 +246,85 @@ export class ServerInfoComponent implements OnInit, OnDestroy {
246246
return of([component, {}]);
247247
}
248248

249-
const revName = svc.status.components[component].latestCreatedRevision;
250-
const objects: ComponentOwnedObjects = {
251-
revision: undefined,
252-
configuration: undefined,
253-
knativeService: undefined,
254-
route: undefined,
255-
};
256-
257-
return this.backend.getKnativeRevision(this.namespace, revName).pipe(
258-
tap(r => (objects.revision = r)),
259-
260-
// GET the configuration
261-
map(r => {
262-
return r.metadata.ownerReferences[0].name;
263-
}),
264-
concatMap(confName => {
265-
return this.backend.getKnativeConfiguration(this.namespace, confName);
266-
}),
267-
tap(c => (objects.configuration = c)),
268-
269-
// GET the Knative service
270-
map(c => {
271-
return c.metadata.ownerReferences[0].name;
272-
}),
273-
concatMap(svcName => {
274-
return this.backend.getKnativeService(this.namespace, svcName);
275-
}),
276-
tap(knativeSvc => (objects.knativeService = knativeSvc)),
277-
278-
// GET the Knative route
279-
map(knativeSvc => {
280-
return knativeSvc.metadata.name;
281-
}),
282-
concatMap(routeName => {
283-
return this.backend.getKnativeRoute(this.namespace, routeName);
284-
}),
285-
tap(route => (objects.route = route)),
286-
287-
// return the final list of objects
288-
map(_ => [component, objects]),
289-
);
249+
// Check if this is a RawDeployment InferenceService
250+
const isRawDeployment = this.isRawDeployment(svc);
251+
252+
if (isRawDeployment) {
253+
// Handle RawDeployment mode
254+
return this.backend
255+
.getRawDeploymentObjects(this.namespace, svc.metadata.name, component)
256+
.pipe(
257+
map(objects => [component, objects]),
258+
catchError(error => {
259+
console.error(
260+
`Error fetching RawDeployment objects for ${component}:`,
261+
error,
262+
);
263+
return of([component, {}]);
264+
}),
265+
);
266+
} else {
267+
// Handle Serverless mode
268+
const revName = svc.status.components[component].latestCreatedRevision;
269+
const objects: ComponentOwnedObjects = {
270+
revision: undefined,
271+
configuration: undefined,
272+
knativeService: undefined,
273+
route: undefined,
274+
};
275+
276+
return this.backend.getKnativeRevision(this.namespace, revName).pipe(
277+
tap(r => (objects.revision = r)),
278+
279+
// GET the configuration
280+
map(r => {
281+
return r.metadata.ownerReferences[0].name;
282+
}),
283+
concatMap(confName => {
284+
return this.backend.getKnativeConfiguration(this.namespace, confName);
285+
}),
286+
tap(c => (objects.configuration = c)),
287+
288+
// GET the Knative service
289+
map(c => {
290+
return c.metadata.ownerReferences[0].name;
291+
}),
292+
concatMap(svcName => {
293+
return this.backend.getKnativeService(this.namespace, svcName);
294+
}),
295+
tap(knativeSvc => (objects.knativeService = knativeSvc)),
296+
297+
// GET the Knative route
298+
map(knativeSvc => {
299+
return knativeSvc.metadata.name;
300+
}),
301+
concatMap(routeName => {
302+
return this.backend.getKnativeRoute(this.namespace, routeName);
303+
}),
304+
tap(route => (objects.route = route)),
305+
306+
// return the final list of objects
307+
map(_ => [component, objects]),
308+
);
309+
}
310+
}
311+
312+
private isRawDeployment(svc: InferenceServiceK8s): boolean {
313+
const annotations = svc.metadata?.annotations || {};
314+
315+
// Check for the KServe annotation
316+
const deploymentMode =
317+
annotations['serving.kserve.io/deploymentMode'] || '';
318+
if (deploymentMode.toLowerCase() === 'rawdeployment') {
319+
return true;
320+
}
321+
322+
// Check for legacy annotation
323+
const rawMode = annotations['serving.kubeflow.org/raw'] || 'false';
324+
if (rawMode.toLowerCase() === 'true') {
325+
return true;
326+
}
327+
328+
return false;
290329
}
291330
}

frontend/src/app/services/backend.service.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,66 @@ export class MWABackendService extends BackendService {
184184
.pipe(catchError(error => this.handleError(error)));
185185
}
186186

187+
/*
188+
* RawDeployment mode methods
189+
*/
190+
public getKubernetesDeployment(
191+
namespace: string,
192+
name: string,
193+
): Observable<K8sObject> {
194+
const url = `api/namespaces/${namespace}/deployments/${name}`;
195+
196+
return this.http.get<MWABackendResponse>(url).pipe(
197+
catchError(error => this.handleError(error)),
198+
map((resp: MWABackendResponse) => {
199+
return resp.deployment;
200+
}),
201+
);
202+
}
203+
204+
public getKubernetesService(
205+
namespace: string,
206+
name: string,
207+
): Observable<K8sObject> {
208+
const url = `api/namespaces/${namespace}/services/${name}`;
209+
210+
return this.http.get<MWABackendResponse>(url).pipe(
211+
catchError(error => this.handleError(error)),
212+
map((resp: MWABackendResponse) => {
213+
return resp.service;
214+
}),
215+
);
216+
}
217+
218+
public getKubernetesHPA(
219+
namespace: string,
220+
name: string,
221+
): Observable<K8sObject> {
222+
const url = `api/namespaces/${namespace}/hpas/${name}`;
223+
224+
return this.http.get<MWABackendResponse>(url).pipe(
225+
catchError(error => this.handleError(error)),
226+
map((resp: MWABackendResponse) => {
227+
return resp.hpa;
228+
}),
229+
);
230+
}
231+
232+
public getRawDeploymentObjects(
233+
namespace: string,
234+
name: string,
235+
component: string,
236+
): Observable<any> {
237+
const url = `api/namespaces/${namespace}/inferenceservices/${name}/rawdeployment/${component}`;
238+
239+
return this.http.get<MWABackendResponse>(url).pipe(
240+
catchError(error => this.handleError(error)),
241+
map((resp: MWABackendResponse) => {
242+
return resp.rawDeploymentObjects;
243+
}),
244+
);
245+
}
246+
187247
/*
188248
* DELETE
189249
*/

0 commit comments

Comments
 (0)