From 257d3603ff90046a70414f6a0795d0b2f0fba5fc Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Mon, 9 Oct 2023 14:54:34 -0400 Subject: [PATCH] Check serving runtime kind at creation time --- .../mockServingRuntimeTemplateK8sResource.ts | 76 +++++++++++++++++++ .../modelServing/ServingRuntimeList.spec.ts | 1 + .../ServingRuntimeList.stories.tsx | 15 +++- .../CustomServingRuntimeAddTemplate.tsx | 10 +++ .../customServingRuntimes/utils.ts | 12 +-- .../ServingRuntimeTemplateSection.tsx | 14 +++- 6 files changed, 114 insertions(+), 14 deletions(-) diff --git a/frontend/src/__mocks__/mockServingRuntimeTemplateK8sResource.ts b/frontend/src/__mocks__/mockServingRuntimeTemplateK8sResource.ts index 2fed7615a9..d5ab4a42ab 100644 --- a/frontend/src/__mocks__/mockServingRuntimeTemplateK8sResource.ts +++ b/frontend/src/__mocks__/mockServingRuntimeTemplateK8sResource.ts @@ -92,3 +92,79 @@ export const mockTemplateK8sResource = ({ ], parameters: [], }); + +export const mockInvalidTemplateK8sResource = ({ + name = 'test-model-invalid', + namespace = 'opendatahub', +}: MockResourceConfigType): TemplateKind => ({ + apiVersion: 'template.openshift.io/v1', + kind: 'Template', + metadata: { + name: 'template-ar2pcd', + namespace, + uid: '31277020-b60a-40c9-91bc-5ee3e2bb25ed', + resourceVersion: '164740436', + creationTimestamp: '2023-05-03T21:58:17Z', + labels: { + 'opendatahub.io/dashboard': 'true', + }, + annotations: { + tags: 'new-one,servingruntime', + }, + }, + objects: [ + { + apiVersion: 'serving.kserve.io/v1alpha1', + kind: 'ServingRuntime', + metadata: { + name, + annotations: { + 'openshift.io/display-name': 'New OVMS Server Invalid', + }, + labels: { + 'opendatahub.io/dashboard': 'true', + }, + }, + spec: { + builtInAdapter: { + memBufferBytes: 134217728, + modelLoadingTimeoutMillis: 90000, + runtimeManagementPort: 8888, + serverType: 'ovms', + }, + containers: [ + { + args: [ + '--port=8001', + '--rest_port=8888', + '--config_path=/models/model_config_list.json', + '--file_system_poll_wait_seconds=0', + '--grpc_bind_address=127.0.0.1', + '--rest_bind_address=127.0.0.1', + '--target_device=NVIDIA', + ], + image: + 'quay.io/modh/openvino-model-server@sha256:c89f76386bc8b59f0748cf173868e5beef21ac7d2f78dada69089c4d37c44116', + name: 'ovms', + resources: { + limits: { + cpu: '0', + memory: '0Gi', + }, + requests: { + cpu: '0', + memory: '0Gi', + }, + }, + }, + ], + grpcDataEndpoint: 'port:8001', + grpcEndpoint: 'port:8085', + multiModel: true, + protocolVersions: ['grpc-v1'], + replicas: 1, + }, + }, + ], + parameters: [], +}); diff --git a/frontend/src/__tests__/integration/pages/modelServing/ServingRuntimeList.spec.ts b/frontend/src/__tests__/integration/pages/modelServing/ServingRuntimeList.spec.ts index b651e77b0d..cc021cf2ed 100644 --- a/frontend/src/__tests__/integration/pages/modelServing/ServingRuntimeList.spec.ts +++ b/frontend/src/__tests__/integration/pages/modelServing/ServingRuntimeList.spec.ts @@ -93,6 +93,7 @@ test('Add model server', async ({ page }) => { await page.getByLabel('Model server name *').fill('Test Name'); await page.locator('#serving-runtime-template-selection').click(); await page.getByRole('menuitem', { name: 'New OVMS Server' }).click(); + await expect(page.getByRole('menuitem', { name: 'New OVMS Server Invalid' })).toBeHidden(); await expect(page.getByRole('button', { name: 'Add', exact: true })).toBeEnabled(); // test the if the alert is visible when route is external while token is not set diff --git a/frontend/src/__tests__/integration/pages/modelServing/ServingRuntimeList.stories.tsx b/frontend/src/__tests__/integration/pages/modelServing/ServingRuntimeList.stories.tsx index d9cc3ad0b6..c3297d7544 100644 --- a/frontend/src/__tests__/integration/pages/modelServing/ServingRuntimeList.stories.tsx +++ b/frontend/src/__tests__/integration/pages/modelServing/ServingRuntimeList.stories.tsx @@ -20,7 +20,10 @@ import { import { mockProjectK8sResource } from '~/__mocks__/mockProjectK8sResource'; import { mockPVCK8sResource } from '~/__mocks__/mockPVCK8sResource'; import ProjectsRoutes from '~/concepts/projects/ProjectsRoutes'; -import { mockTemplateK8sResource } from '~/__mocks__/mockServingRuntimeTemplateK8sResource'; +import { + mockInvalidTemplateK8sResource, + mockTemplateK8sResource, +} from '~/__mocks__/mockServingRuntimeTemplateK8sResource'; import { mockDashboardConfig } from '~/__mocks__/mockDashboardConfig'; import { mockStatus } from '~/__mocks__/mockStatus'; import useDetectUser from '~/utilities/useDetectUser'; @@ -107,7 +110,15 @@ export default { ), rest.get( '/api/k8s/apis/template.openshift.io/v1/namespaces/opendatahub/templates', - (req, res, ctx) => res(ctx.json(mockK8sResourceList([mockTemplateK8sResource({})]))), + (req, res, ctx) => + res( + ctx.json( + mockK8sResourceList([ + mockTemplateK8sResource({}), + mockInvalidTemplateK8sResource({}), + ]), + ), + ), ), rest.get( '/api/k8s/apis/opendatahub.io/v1alpha/namespaces/opendatahub/odhdashboardconfigs/odh-dashboard-config', diff --git a/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAddTemplate.tsx b/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAddTemplate.tsx index 47fac284d0..5ea690bb5e 100644 --- a/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAddTemplate.tsx +++ b/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAddTemplate.tsx @@ -24,6 +24,7 @@ import { import { getServingRuntimeDisplayNameFromTemplate, getServingRuntimeNameFromTemplate, + isServingRuntimeKind, } from './utils'; import { CustomServingRuntimeContext } from './CustomServingRuntimeContext'; @@ -140,6 +141,15 @@ const CustomServingRuntimeAddTemplate: React.FC { setIsLoading(true); // TODO: Revert back to pass through api once we migrate admin panel + if (!isServingRuntimeKind(YAML.parse(code))) { + setError( + new Error( + 'The "kind" should be "ServingRuntime", and the following fields are required: container, supportedModelFormats.', + ), + ); + setIsLoading(false); + return; + } const onClickFunc = existingTemplate ? updateServingRuntimeTemplateBackend( existingTemplate.metadata.name, diff --git a/frontend/src/pages/modelServing/customServingRuntimes/utils.ts b/frontend/src/pages/modelServing/customServingRuntimes/utils.ts index e24abc320e..60bd7fad62 100644 --- a/frontend/src/pages/modelServing/customServingRuntimes/utils.ts +++ b/frontend/src/pages/modelServing/customServingRuntimes/utils.ts @@ -48,22 +48,16 @@ export const isServingRuntimeKind = (obj: K8sResourceCommon): obj is ServingRunt export const getServingRuntimeFromName = ( templateName: string, - templateList?: TemplateKind[], + templateList: TemplateKind[] = [], ): ServingRuntimeKind | undefined => { - if (!templateList) { - return undefined; - } const template = templateList.find((t) => getServingRuntimeNameFromTemplate(t) === templateName); - if (!template) { - return undefined; - } return getServingRuntimeFromTemplate(template); }; export const getServingRuntimeFromTemplate = ( - template: TemplateKind, + template?: TemplateKind, ): ServingRuntimeKind | undefined => { - if (!isServingRuntimeKind(template.objects[0])) { + if (!template || !isServingRuntimeKind(template.objects[0])) { return undefined; } return template.objects[0]; diff --git a/frontend/src/pages/modelServing/screens/projects/ServingRuntimeModal/ServingRuntimeTemplateSection.tsx b/frontend/src/pages/modelServing/screens/projects/ServingRuntimeModal/ServingRuntimeTemplateSection.tsx index 2cee7c6af1..93dcec7b9e 100644 --- a/frontend/src/pages/modelServing/screens/projects/ServingRuntimeModal/ServingRuntimeTemplateSection.tsx +++ b/frontend/src/pages/modelServing/screens/projects/ServingRuntimeModal/ServingRuntimeTemplateSection.tsx @@ -6,6 +6,7 @@ import { TemplateKind } from '~/k8sTypes'; import { getServingRuntimeDisplayNameFromTemplate, getServingRuntimeNameFromTemplate, + isServingRuntimeKind, } from '~/pages/modelServing/customServingRuntimes/utils'; import { isCompatibleWithAccelerator } from '~/pages/projects/screens/spawner/spawnerUtils'; import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; @@ -26,7 +27,12 @@ const ServingRuntimeTemplateSection: React.FC { - const options = templates.map((template) => ({ + const filteredTemplates = React.useMemo( + () => templates.filter((template) => isServingRuntimeKind(template.objects[0])), + [templates], + ); + + const options = filteredTemplates.map((template) => ({ key: getServingRuntimeNameFromTemplate(template), selectedLabel: getServingRuntimeDisplayNameFromTemplate(template), label: ( @@ -59,12 +65,14 @@ const ServingRuntimeTemplateSection: React.FC {