Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into f/accelerator-admin…
Browse files Browse the repository at this point in the history
…-support
  • Loading branch information
Gkrumbach07 committed Nov 6, 2023
2 parents d15f8aa + 1fa191e commit 0b7492e
Show file tree
Hide file tree
Showing 74 changed files with 16,830 additions and 6,381 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: Pull request
on:
pull_request:
branches: [ main, f/** ]

jobs:
Tests:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/vuln_scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner for filesystem
uses: aquasecurity/trivy-action@0.11.2
uses: aquasecurity/trivy-action@0.13.1
with:
scan-type: 'fs'
scan-ref: '.'
Expand Down
1 change: 1 addition & 0 deletions backend/.eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
package.json
jest.config.js
7 changes: 7 additions & 0 deletions backend/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
transform: {
'node_modules': 'ts-jest',
},
preset: 'ts-jest',
testEnvironment: 'node',
};
9,319 changes: 6,794 additions & 2,525 deletions backend/package-lock.json

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@
"start:dev": "npm run clean && export NODE_TLS_REJECT_UNAUTHORIZED=0 && export NODE_ENV=development && nodemon src/server.ts --log=1 --registry=localhost:50051",
"debug": "npm run tsc && export NODE_TLS_REJECT_UNAUTHORIZED=0 && export NODE_ENV=development && node --inspect ./dist/server.js --log=1 --registry=localhost:50051",
"build-only": "tsc -p . && node ./dist/server.js --log=1 --registry=localhost:50051 --buildonly",
"build": "npm run build:clean; npm run tsc",
"build": "npm run build:clean; npm run tsc:prod",
"build:clean": "rimraf ./dist",
"test": "npm run test:lint; npm run test:type-check",
"test": "npm run test:lint; npm run test:type-check; npm run test:jest",
"test:lint": "eslint --max-warnings 0 --ext .json,.js,.ts src/plugins src/routes src/utils",
"test:fix": "eslint --ext .json,.js,.ts src/plugins src/routes src/utils --fix",
"test:type-check": "tsc --noEmit",
"test:jest": "jest",
"server": "NODE_ENV=production node ./dist/server.js",
"tsc": "tsc -p .",
"tsc:prod": "tsc -p tsconfig.prod.json",
"lint": "eslint ./src/",
"watch": "tsc -p . -w"
},
Expand Down Expand Up @@ -66,13 +68,16 @@
"typescript": "^4.0.3"
},
"optionalDependencies": {
"@types/jest": "^29.5.3",
"eslint": "^6.8.0",
"eslint-config-esnext": "^4.1.0",
"eslint-config-node": "^4.1.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-babel": "^5.3.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.4.0"
"eslint-plugin-prettier": "^3.4.0",
"jest": "^29.6.1",
"ts-jest": "^29.1.1"
}
}
77 changes: 77 additions & 0 deletions backend/src/__tests__/dockerRepositoryURL.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// https://cloud.google.com/artifact-registry/docs/docker/names
// The full name for a container image is one of the following formats:
// LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE
// LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE:TAG
// LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE@IMAGE-DIGEST

import { parseImageURL } from '../routes/api/images/imageUtils';

test('Invalid URL: space string', () => {
const url = ' ';
const { fullURL, host } = parseImageURL(url);
expect(fullURL).toBe('');
expect(host).toBeUndefined();
});

test('Invalid URL: no match', () => {
const url = '/';
const { host, tag } = parseImageURL(url);
expect(host).toBeUndefined();
expect(tag).toBeUndefined();
});

test('Invalid URL: host only', () => {
const url = 'docker.io';
const { host } = parseImageURL(url);
expect(host).toBe('');
});

test('Invalid URL: host and repo, no image', () => {
const url = 'docker.io/opendatahub';
const { host } = parseImageURL(url);
expect(host).toBe('');
});

test('Valid URL with spaces on both sides', () => {
const url = ' docker.io/library/mysql:test ';
const { fullURL, host, tag } = parseImageURL(url);
expect(fullURL).toBe('docker.io/library/mysql:test');
expect(host).toBe('docker.io');
expect(tag).toBe('test');
});

test('Docker container URL without tag', () => {
const url = 'docker.io/library/mysql';
const { host, tag } = parseImageURL(url);
expect(host).toBe('docker.io');
expect(tag).toBeUndefined();
});

test('Docker container URL with tag', () => {
const url = 'docker.io/library/mysql:test-tag';
const { host, tag } = parseImageURL(url);
expect(host).toBe('docker.io');
expect(tag).toBe('test-tag');
});

test('OpenShift internal registry URL without tag', () => {
const url = 'image-registry.openshift-image-registry.svc:5000/opendatahub/s2i-minimal-notebook';
const { host, tag } = parseImageURL(url);
expect(host).toBe('image-registry.openshift-image-registry.svc:5000');
expect(tag).toBeUndefined();
});

test('OpenShift internal registry URL with tag', () => {
const url =
'image-registry.openshift-image-registry.svc:5000/opendatahub/s2i-minimal-notebook:v0.3.0-py36';
const { host, tag } = parseImageURL(url);
expect(host).toBe('image-registry.openshift-image-registry.svc:5000');
expect(tag).toBe('v0.3.0-py36');
});

test('Quay URL with port and tag', () => {
const url = 'quay.io:443/opendatahub/odh-dashboard:main-55e19fa';
const { host, tag } = parseImageURL(url);
expect(host).toBe('quay.io:443');
expect(tag).toBe('main-55e19fa');
});
12 changes: 12 additions & 0 deletions backend/src/routes/api/dsc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { KubeFastifyInstance } from '../../../types';
import { secureRoute } from '../../../utils/route-security';
import { getClusterStatus } from '../../../utils/dsc';

module.exports = async (fastify: KubeFastifyInstance) => {
fastify.get(
'/status',
secureRoute(fastify)(async () => {
return getClusterStatus(fastify);
}),
);
};
79 changes: 41 additions & 38 deletions backend/src/routes/api/groups-config/groupsConfigUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,12 @@ const SYSTEM_AUTHENTICATED = 'system:authenticated';
export const getGroupsConfig = async (fastify: KubeFastifyInstance): Promise<GroupsConfig> => {
const customObjectsApi = fastify.kube.customObjectsApi;

try {
const groupsCluster = await getAllGroups(customObjectsApi);
const groupsData = getGroupsCR();
const groupsProcessed = processGroupData(groupsData);
const groupsConfigProcessed = processGroupConfig(groupsProcessed, groupsCluster);
await removeDeletedGroups(fastify, groupsData, groupsConfigProcessed.groupsCRData);

return groupsConfigProcessed.groupsConfig;
} catch (e) {
fastify.log.error(e, 'Error retrieving group configuration.');
const error = createError(500, 'Error retrieving group configuration');
throw error;
}
const groupsCluster = await getAllGroups(customObjectsApi);
const groupsData = getGroupsCR();
const groupsProcessed = processGroupData(groupsData);
const groupsConfigProcessed = processGroupConfig(fastify, groupsProcessed, groupsCluster);
await removeDeletedGroups(fastify, groupsData, groupsConfigProcessed.groupsCRData);
return groupsConfigProcessed.groupsConfig;
};

const transformGroupsConfig = (groupStatus: GroupStatus[]): string[] => {
Expand All @@ -38,7 +31,7 @@ const transformGroupsConfig = (groupStatus: GroupStatus[]): string[] => {
export const updateGroupsConfig = async (
fastify: KubeFastifyInstance,
request: FastifyRequest<{ Body: GroupsConfig }>,
): Promise<{ success: GroupsConfig | null; error: string | null }> => {
): Promise<GroupsConfig> => {
const customObjectsApi = fastify.kube.customObjectsApi;
const { namespace } = fastify.kube;

Expand All @@ -58,26 +51,17 @@ export const updateGroupsConfig = async (
const error = createError(403, 'Error, groups cannot be empty');
throw error;
}
try {
const dataUpdated: GroupsConfigBody = {
adminGroups: adminConfig.join(','),
allowedGroups: allowedConfig.join(','),
};

const groupsData = await updateGroupsCR(fastify, dataUpdated);
const groupsProcessed = processGroupData(groupsData);
const groupsCluster = await getAllGroups(customObjectsApi);
const updatedConfig = processGroupConfig(groupsProcessed, groupsCluster);
await removeDeletedGroups(fastify, groupsData, updatedConfig.groupsCRData);
return {
success: updatedConfig.groupsConfig,
error: null,
};
} catch (e) {
fastify.log.error(e, 'Error updating group configuration.');
const error = createError(500, 'Error updating group configuration');
throw error;
}
const dataUpdated: GroupsConfigBody = {
adminGroups: adminConfig.join(','),
allowedGroups: allowedConfig.join(','),
};

const groupsData = await updateGroupsCR(fastify, dataUpdated);
const groupsProcessed = processGroupData(groupsData);
const groupsCluster = await getAllGroups(customObjectsApi);
const updatedConfig = processGroupConfig(fastify, groupsProcessed, groupsCluster);
await removeDeletedGroups(fastify, groupsData, updatedConfig.groupsCRData);
return updatedConfig.groupsConfig;
};

const processGroupData = (groupsData: GroupsConfigBody): GroupsConfigBodyList => {
Expand Down Expand Up @@ -105,6 +89,7 @@ const mapListToGroupStatus =
* @returns Processed object with the groups, removing missing groups that might be selected
*/
const processGroupConfig = (
fastify: KubeFastifyInstance,
groupsDataList: GroupsConfigBodyList,
groups: string[],
): { groupsConfig: GroupsConfig; groupsCRData: GroupsConfigBody } => {
Expand All @@ -120,9 +105,14 @@ const processGroupConfig = (
const groupsConfig: GroupsConfig = {
adminGroups: adminGroupsConfig,
allowedGroups: allowedGroupsConfig,
errorAdmin: getError(groupsDataList.adminGroups, (group) => !groups.includes(group)),
errorAdmin: getError(
fastify,
groupsDataList.adminGroups.filter((group) => group),
(group) => !groups.includes(group),
),
errorUser: getError(
groupsDataList.allowedGroups,
fastify,
groupsDataList.allowedGroups.filter((group) => group),
(group) => !groups.includes(group) && group !== SYSTEM_AUTHENTICATED,
),
};
Expand All @@ -137,13 +127,26 @@ const processGroupConfig = (
return { groupsConfig, groupsCRData: updatedBody };
};

const getError = (array: string[], predicate: (group: string) => boolean): string | undefined => {
const getError = (
fastify: KubeFastifyInstance,
array: string[],
predicate: (group: string) => boolean,
): string | undefined => {
let error;
if (array.length === 0) {
error = 'No group is set in the group config, please set one or more group.';
fastify.log.error(error);
return error;
}

const missingItems = array.filter(predicate);
if (missingItems.length === 0) return undefined;

return `The group${missingItems.length === 1 ? '' : 's'} ${missingItems.join(
error = `The group${missingItems.length === 1 ? '' : 's'} ${missingItems.join(
', ',
)} no longer exists in OpenShift and has been removed from the selected group list.`;
fastify.log.error(error);
return error;
};

/**
Expand Down
9 changes: 7 additions & 2 deletions backend/src/routes/api/groups-config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ import { secureAdminRoute } from '../../../utils/route-security';
export default async (fastify: FastifyInstance): Promise<void> => {
fastify.get(
'/',
secureAdminRoute(fastify)(async () => {
return getGroupsConfig(fastify);
secureAdminRoute(fastify)(async (request, reply) => {
return getGroupsConfig(fastify).catch((e) => {
fastify.log.error(
`Error retrieving group configuration, ${e.response?.body?.message || e.message}`,
);
reply.status(500).send({ message: e.response?.body?.message || e.message });
});
}),
);

Expand Down
Loading

0 comments on commit 0b7492e

Please sign in to comment.