Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare 1.33 Release #297

Merged
merged 12 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -901,8 +901,10 @@ export type ServingRuntime = K8sResourceCommon & {
image: string;
name: string;
resources: ContainerResources;
volumeMounts?: VolumeMount[];
}[];
supportedModelFormats: SupportedModelFormats[];
replicas: number;
volumes?: Volume[];
};
};
14 changes: 10 additions & 4 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,26 @@ When building new client features, there are a few things worth noting about the

Tests can be divided into the following categories: unit, integration, accessibility, and end to end testing. To keep organized of the different types of tests, there will be a test folder at the root of the frontend project with the following structure.

E2e and integration tests are located in a single root directory:
```
/frontend/tests
/integration => ComponentName.stories.tsx, ComponentName.spec.ts
/unit => functionName.test.ts
/frontend/src/__tests__
/e2e => storyName.spec.ts
/integration => ComponentName.stories.tsx, ComponentName.spec.ts
```

Some nesting can be used to organize testing groups together. For example, the _projects_ page has screens for _details_, _projects_, and, _spawner_ which can be all grouped together under a projects folder.

Unit tests are co-located in a `__tests__` directory adjacent to the target source file they are testing.
```
/frontend/src/**/__tests__
/targetFile.spec.ts
```

#### Testing Types

##### Unit Testing

Unit tests cover util functions and other non React based functions. These tests are stored in the `/unit `folder and can be organized into folders depending on their parent page and/or screen. Use Jest to test each function using `describe` to group together the utils file and the specific function. Then each test is described using `it`. Some functions are very basic and don't need a test. Use your best judgment if a test is needed.
Unit tests cover util functions and other non React based functions. Use Jest to test each function using `describe` to group together the utils file and the specific function. Then each test is described using `it`.

_Example_

Expand Down
9 changes: 6 additions & 3 deletions frontend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
// https://jestjs.io/docs/en/configuration.html

module.exports = {
roots: ['<rootDir>/src/__tests__/unit'],
testMatch: ['**/?(*.)+(spec|test).ts?(x)'],
roots: ['<rootDir>/src/'],
testMatch: [
'**/src/__tests__/unit/**/?(*.)+(spec|test).ts?(x)',
'**/__tests__/?(*.)+(spec|test).ts?(x)',
],

// Automatically clear mock calls and instances between every test
clearMocks: true,
Expand All @@ -23,7 +26,7 @@ module.exports = {
testEnvironment: 'jest-environment-jsdom',

// include projects from node_modules as required
transformIgnorePatterns: ['node_modules/(?!yaml)'],
transformIgnorePatterns: ['node_modules/(?!yaml|@openshift|lodash-es|uuid)'],

// A list of paths to snapshot serializer modules Jest should use for snapshot testing
snapshotSerializers: [],
Expand Down
49 changes: 0 additions & 49 deletions frontend/src/__tests__/dockerRepositoryURL.spec.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,33 @@ test('Legacy Serving Runtime', async ({ page }) => {
await page.waitForSelector('text=Add server');

// Check that the legacy serving runtime is shown with the default runtime name
expect(await page.getByText('ovms')).toBeTruthy();
expect(page.getByText('ovms')).toBeTruthy();

// Check that the legacy serving runtime displays the correct Serving Runtime
expect(await page.getByText('OpenVINO Model Server')).toBeTruthy();
expect(page.getByText('OpenVINO Model Server')).toBeTruthy();

// Check that the legacy serving runtime has tokens disabled
expect(await page.getByText('Tokens disabled')).toBeTruthy();
expect(page.getByText('Tokens disabled')).toBeTruthy();

// Check that the serving runtime is shown with the default runtime name
expect(await page.getByText('OVMS Model Serving')).toBeTruthy();
expect(page.getByText('OVMS Model Serving')).toBeTruthy();

// Check that the serving runtime displays the correct Serving Runtime
expect(await page.getByText('OpenVINO Serving Runtime (Supports GPUs)')).toBeTruthy();
expect(page.getByText('OpenVINO Serving Runtime (Supports GPUs)')).toBeTruthy();

// Get the first and second row
const firstButton = page.getByRole('button', { name: 'ovms', exact: true });
const secondButton = page.getByRole('button', { name: 'OVMS Model Serving', exact: true });
const firstRow = page.getByRole('rowgroup').filter({ has: firstButton });
const secondRow = page.getByRole('rowgroup').filter({ has: secondButton });

// Check that both of the rows are not expanded
await expect(firstRow).not.toHaveClass('pf-m-expanded');
await expect(secondRow).not.toHaveClass('pf-m-expanded');

await firstButton.click();

// Check that the first row is expanded while the second is not
await expect(firstRow).toHaveClass('pf-m-expanded');
await expect(secondRow).not.toHaveClass('pf-m-expanded');
});
12 changes: 1 addition & 11 deletions frontend/src/api/k8s/notebooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,7 @@ import {
} from '~/concepts/pipelines/elyra/utils';
import { createRoleBinding } from '~/api';
import { Volume, VolumeMount } from '~/types';
import { assemblePodSpecOptions } from './utils';

const getshmVolumeMount = (): VolumeMount => ({
name: 'shm',
mountPath: '/dev/shm',
});

const getshmVolume = (): Volume => ({
name: 'shm',
emptyDir: { medium: 'Memory' },
});
import { assemblePodSpecOptions, getshmVolume, getshmVolumeMount } from './utils';

const assembleNotebook = (
data: StartNotebookData,
Expand Down
29 changes: 22 additions & 7 deletions frontend/src/api/k8s/servingRuntimes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { getModelServingRuntimeName } from '~/pages/modelServing/utils';
import { getDisplayNameFromK8sResource, translateDisplayNameForK8s } from '~/pages/projects/utils';
import { applyK8sAPIOptions } from '~/api/apiMergeUtils';
import { getModelServingProjects } from './projects';
import { assemblePodSpecOptions } from './utils';
import { assemblePodSpecOptions, getshmVolume, getshmVolumeMount } from './utils';

const assembleServingRuntime = (
data: CreatingServingRuntimeObject,
Expand Down Expand Up @@ -79,12 +79,27 @@ const assembleServingRuntime = (

const { affinity, tolerations, resources } = assemblePodSpecOptions(resourceSettings, gpus);

updatedServingRuntime.spec.containers = servingRuntime.spec.containers.map((container) => ({
...container,
resources,
affinity,
tolerations,
}));
const volumes = updatedServingRuntime.spec.volumes || [];
if (!volumes.find((volume) => volume.name === 'shm')) {
volumes.push(getshmVolume('2Gi'));
}

updatedServingRuntime.spec.volumes = volumes;

updatedServingRuntime.spec.containers = servingRuntime.spec.containers.map((container) => {
const volumeMounts = container.volumeMounts || [];
if (!volumeMounts.find((volumeMount) => volumeMount.mountPath === '/dev/shm')) {
volumeMounts.push(getshmVolumeMount());
}

return {
...container,
resources,
affinity,
tolerations,
volumeMounts,
};
});

return updatedServingRuntime;
};
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/api/k8s/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
PodToleration,
TolerationSettings,
ContainerResourceAttributes,
VolumeMount,
Volume,
} from '~/types';
import { determineTolerations } from '~/utilities/tolerations';

Expand Down Expand Up @@ -54,3 +56,13 @@ export const assemblePodSpecOptions = (
const tolerations = determineTolerations(gpus > 0, tolerationSettings);
return { affinity, tolerations, resources };
};

export const getshmVolumeMount = (): VolumeMount => ({
name: 'shm',
mountPath: '/dev/shm',
});

export const getshmVolume = (sizeLimit?: string): Volume => ({
name: 'shm',
emptyDir: { medium: 'Memory', ...(sizeLimit && { sizeLimit }) },
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { AWS_KEYS } from '~/pages/projects/dataConnections/const';
import { PipelineServerConfigType } from '~/concepts/pipelines/content/configurePipelinesServer/types';
import { createDSPipelineResourceSpec } from '~/concepts/pipelines/content/configurePipelinesServer/utils';

describe('configure pipeline server utils', () => {
describe('createDSPipelineResourceSpec', () => {
const createPipelineServerConfig = () =>
({
database: {
useDefault: true,
value: [],
},
objectStorage: {
useExisting: true,
existingName: '',
existingValue: [],
},
} as PipelineServerConfigType);

type SecretsResponse = Parameters<typeof createDSPipelineResourceSpec>[1];

const createSecretsResponse = (
databaseSecret?: SecretsResponse[0],
objectStorageSecret?: SecretsResponse[1],
): SecretsResponse => [databaseSecret, objectStorageSecret ?? { secretName: '', awsData: [] }];

it('should create resource spec', () => {
const spec = createDSPipelineResourceSpec(
createPipelineServerConfig(),
createSecretsResponse(),
);
expect(spec).toEqual({
database: undefined,
objectStorage: {
externalStorage: {
bucket: '',
host: '',
s3CredentialsSecret: {
accessKey: 'AWS_ACCESS_KEY_ID',
secretKey: 'AWS_SECRET_ACCESS_KEY',
secretName: '',
},
scheme: 'https',
},
},
});
});

it('should parse S3 endpoint with scheme', () => {
const secretsResponse = createSecretsResponse();
secretsResponse[1].awsData = [
{ key: AWS_KEYS.S3_ENDPOINT, value: 'http://s3.amazonaws.com' },
];
const spec = createDSPipelineResourceSpec(createPipelineServerConfig(), secretsResponse);
expect(spec.objectStorage.externalStorage?.scheme).toBe('http');
expect(spec.objectStorage.externalStorage?.host).toBe('s3.amazonaws.com');
});

it('should parse S3 endpoint without scheme', () => {
const secretsResponse = createSecretsResponse();

secretsResponse[1].awsData = [{ key: AWS_KEYS.S3_ENDPOINT, value: 's3.amazonaws.com' }];
const spec = createDSPipelineResourceSpec(createPipelineServerConfig(), secretsResponse);
expect(spec.objectStorage.externalStorage?.scheme).toBe('https');
expect(spec.objectStorage.externalStorage?.host).toBe('s3.amazonaws.com');
});

it('should include bucket', () => {
const secretsResponse = createSecretsResponse();
secretsResponse[1].awsData = [{ key: AWS_KEYS.AWS_S3_BUCKET, value: 'my-bucket' }];
const spec = createDSPipelineResourceSpec(createPipelineServerConfig(), secretsResponse);
expect(spec.objectStorage.externalStorage?.bucket).toBe('my-bucket');
});

it('should create spec with database object', () => {
const config = createPipelineServerConfig();
config.database.value = [
{
key: 'Username',
value: 'test-user',
},
{
key: 'Port',
value: '8080',
},
{
key: 'Host',
value: 'test.host.com',
},
{
key: 'Database',
value: 'db-name',
},
];
const spec = createDSPipelineResourceSpec(
config,
createSecretsResponse({
key: 'password-key',
name: 'password-name',
}),
);
expect(spec).toEqual({
objectStorage: {
externalStorage: {
bucket: '',
host: '',
s3CredentialsSecret: {
accessKey: 'AWS_ACCESS_KEY_ID',
secretKey: 'AWS_SECRET_ACCESS_KEY',
secretName: '',
},
scheme: 'https',
},
},
database: {
externalDB: {
host: 'test.host.com',
passwordSecret: {
key: 'password-key',
name: 'password-name',
},
pipelineDBName: 'db-name',
port: '8080',
username: 'test-user',
},
},
});
});
});
});
Loading