Skip to content

Commit f15e003

Browse files
committed
deploy model from pvc
1 parent 90aefc5 commit f15e003

File tree

12 files changed

+476
-7
lines changed

12 files changed

+476
-7
lines changed

frontend/src/__mocks__/mockPVCK8sResource.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ type MockResourceConfigType = {
1111
uid?: string;
1212
status?: PersistentVolumeClaimKind['status'];
1313
accessModes?: AccessMode[];
14+
annotations?: Record<string, string>;
15+
labels?: Record<string, string>;
1416
};
1517

1618
export const mockPVCK8sResource = ({
@@ -28,18 +30,22 @@ export const mockPVCK8sResource = ({
2830
},
2931
},
3032
accessModes = [AccessMode.RWO],
33+
annotations = {},
34+
labels = {},
3135
}: MockResourceConfigType): PersistentVolumeClaimKind => ({
3236
kind: 'PersistentVolumeClaim',
3337
apiVersion: 'v1',
3438
metadata: {
3539
annotations: {
3640
'openshift.io/description': '',
3741
'openshift.io/display-name': displayName,
42+
...annotations,
3843
},
3944
name,
4045
namespace,
4146
labels: {
4247
[KnownLabels.DASHBOARD_RESOURCE]: 'true',
48+
...labels,
4349
},
4450
uid,
4551
},

frontend/src/__tests__/cypress/cypress/pages/modelServing.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ class InferenceServiceModal extends ServingModal {
249249
});
250250
}
251251

252+
findPVCSelect() {
253+
return this.find().findByTestId('pvc-connection-selector');
254+
}
255+
252256
findHardProfileSelection(): Cypress.Chainable<JQuery<HTMLElement>> {
253257
return cy.findByTestId('hardware-profile-select');
254258
}
@@ -552,6 +556,10 @@ class KServeModal extends InferenceServiceModal {
552556
findMemoryLimitButton(type: 'Plus' | 'Minus') {
553557
return this.find().findByTestId('memory-limit-input').findByRole('button', { name: type });
554558
}
559+
560+
findPVCConnectionOption() {
561+
return this.find().findByTestId('pvc-serving-radio');
562+
}
555563
}
556564
mixin(KServeModal, [ServingRuntimeModal, InferenceServiceModal]);
557565

frontend/src/__tests__/cypress/cypress/tests/mocked/modelServing/runtime/servingRuntimeList.cy.ts

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ type HandlersProps = {
9797
DscComponents?: DataScienceClusterKindStatus['components'];
9898
disableProjectScoped?: boolean;
9999
disableHardwareProfiles?: boolean;
100+
disablePVCServing?: boolean;
100101
};
101102
import { STOP_MODAL_PREFERENCE_KEY } from '#~/pages/modelServing/useStopModalPreference';
102103

@@ -110,6 +111,7 @@ const initIntercepts = ({
110111
projectEnableModelMesh,
111112
disableProjectScoped = true,
112113
disableHardwareProfiles = true,
114+
disablePVCServing = true,
113115
servingRuntimes = [
114116
mockServingRuntimeK8sResourceLegacy({ tolerations: [], nodeSelector: {} }),
115117
mockServingRuntimeK8sResource({
@@ -169,6 +171,7 @@ const initIntercepts = ({
169171
disableKServeRaw,
170172
disableProjectScoped,
171173
disableHardwareProfiles,
174+
disablePVCServing,
172175
}),
173176
);
174177
cy.interceptK8sList(PodModel, mockK8sResourceList([mockPodK8sResource({})]));
@@ -2356,6 +2359,122 @@ describe('Serving Runtime List', () => {
23562359
});
23572360
});
23582361
});
2362+
2363+
it('Deploy model with PVC', () => {
2364+
initIntercepts({
2365+
disableModelMeshConfig: false,
2366+
disableKServeConfig: false,
2367+
disableServingRuntimeParams: false,
2368+
disablePVCServing: false,
2369+
requiredCapabilities: [StackCapability.SERVICE_MESH, StackCapability.SERVICE_MESH_AUTHZ],
2370+
projectEnableModelMesh: false,
2371+
});
2372+
cy.intercept(
2373+
'GET',
2374+
'**/namespaces/test-project/persistentvolumeclaims?labelSelector=opendatahub.io%2Fdashboard%3Dtrue',
2375+
mockK8sResourceList([
2376+
mockPVCK8sResource({
2377+
name: 'test-pvc',
2378+
namespace: 'test-project',
2379+
displayName: 'Test PVC',
2380+
storageClassName: 'openshift-default-sc',
2381+
annotations: {
2382+
'dashboard.opendatahub.io/model-name': 'test-model',
2383+
'dashboard.opendatahub.io/model-path': 'test-path',
2384+
},
2385+
labels: {
2386+
'opendatahub.io/dashboard': 'true',
2387+
},
2388+
}),
2389+
]),
2390+
);
2391+
2392+
projectDetails.visitSection('test-project', 'model-server');
2393+
modelServingSection.findDeployModelButton().click();
2394+
2395+
kserveModal.shouldBeOpen();
2396+
2397+
kserveModal.findModelNameInput().type('Test Name');
2398+
kserveModal.findServingRuntimeTemplateSearchSelector().click();
2399+
kserveModal.findGlobalScopedTemplateOption('Caikit').click();
2400+
kserveModal.findModelFrameworkSelect().findSelectOption('onnx - 1').click();
2401+
// Auto-selects the only pvc
2402+
kserveModal.findPVCConnectionOption().should('be.visible').click();
2403+
kserveModal.findLocationPathInput().should('have.value', 'test-path');
2404+
kserveModal.findSubmitButton().should('be.enabled');
2405+
kserveModal.findSubmitButton().click();
2406+
kserveModal.shouldBeOpen(false);
2407+
2408+
// dry run request
2409+
cy.wait('@createServingRuntime').then((interception) => {
2410+
expect(interception.request.url).to.include('?dryRun=All');
2411+
expect(interception.request.body).to.containSubset({
2412+
metadata: {
2413+
name: 'test-name',
2414+
annotations: {
2415+
'openshift.io/display-name': 'test-name',
2416+
'opendatahub.io/apiProtocol': 'REST',
2417+
'opendatahub.io/template-display-name': 'Caikit',
2418+
'opendatahub.io/template-name': 'template-2',
2419+
},
2420+
namespace: 'test-project',
2421+
},
2422+
spec: {
2423+
protocolVersions: ['grpc-v1'],
2424+
supportedModelFormats: [
2425+
{ autoSelect: true, name: 'openvino_ir', version: 'opset1' },
2426+
{ autoSelect: true, name: 'onnx', version: '1' },
2427+
],
2428+
},
2429+
});
2430+
});
2431+
// Actual request
2432+
cy.wait('@createServingRuntime').then((interception) => {
2433+
expect(interception.request.url).not.to.include('?dryRun=All');
2434+
});
2435+
2436+
// the serving runtime should have been created
2437+
cy.get('@createServingRuntime.all').then((interceptions) => {
2438+
expect(interceptions).to.have.length(2); // 1 dry-run request and 1 actual request
2439+
});
2440+
cy.wait('@createInferenceService').then((interception) => {
2441+
expect(interception.request.url).to.include('?dryRun=All');
2442+
expect(interception.request.body).to.containSubset({
2443+
apiVersion: 'serving.kserve.io/v1beta1',
2444+
kind: 'InferenceService',
2445+
metadata: {
2446+
annotations: {
2447+
'openshift.io/display-name': 'Test Name',
2448+
'serving.knative.openshift.io/enablePassthrough': 'true',
2449+
'serving.kserve.io/deploymentMode': DeploymentMode.Serverless,
2450+
'sidecar.istio.io/inject': 'true',
2451+
'sidecar.istio.io/rewriteAppHTTPProbers': 'true',
2452+
},
2453+
labels: {
2454+
'opendatahub.io/dashboard': 'true',
2455+
'networking.knative.dev/visibility': 'cluster-local',
2456+
},
2457+
name: 'test-name',
2458+
namespace: 'test-project',
2459+
},
2460+
spec: {
2461+
predictor: {
2462+
minReplicas: 1,
2463+
maxReplicas: 1,
2464+
model: {
2465+
modelFormat: { name: 'onnx', version: '1' },
2466+
resources: {
2467+
requests: { cpu: '1', memory: '4Gi' },
2468+
limits: { cpu: '2', memory: '8Gi' },
2469+
},
2470+
runtime: 'test-name',
2471+
storageUri: 'pvc://test-pvc/test-path',
2472+
},
2473+
},
2474+
},
2475+
});
2476+
});
2477+
});
23592478
});
23602479

23612480
describe('ModelMesh model server', () => {

frontend/src/pages/modelServing/__tests__/utils.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import {
66
isOciModelUri,
77
getInferenceServiceStoppedStatus,
88
getServingRuntimeVersionStatus,
9+
getModelServingPVCAnnotations,
910
} from '#~/pages/modelServing/utils';
1011
import { mockServingRuntimeK8sResource } from '#~/__mocks__/mockServingRuntimeK8sResource';
12+
import { mockPVCK8sResource } from '#~/__mocks__/mockPVCK8sResource';
1113
import { ContainerResources } from '#~/types';
1214
import { mockServiceAccountK8sResource } from '#~/__mocks__/mockServiceAccountK8sResource';
1315
import { mockRoleBindingK8sResource } from '#~/__mocks__/mockRoleBindingK8sResource';
@@ -353,3 +355,26 @@ describe('getServingRuntimeVersionStatus', () => {
353355
);
354356
});
355357
});
358+
359+
describe('getModelServingPVCAnnotations', () => {
360+
it('should return the right annotations', () => {
361+
const pvc = mockPVCK8sResource({
362+
annotations: {
363+
'dashboard.opendatahub.io/model-name': 'test-model',
364+
'dashboard.opendatahub.io/model-path': 'test-path',
365+
},
366+
});
367+
expect(getModelServingPVCAnnotations(pvc)).toEqual({
368+
modelName: 'test-model',
369+
modelPath: 'test-path',
370+
});
371+
});
372+
373+
it('should return null if annotations are not present', () => {
374+
const pvc = mockPVCK8sResource({});
375+
expect(getModelServingPVCAnnotations(pvc)).toEqual({
376+
modelName: null,
377+
modelPath: null,
378+
});
379+
});
380+
});

0 commit comments

Comments
 (0)