From f74212567cc7b4f667e0302d97a4a94481b82f78 Mon Sep 17 00:00:00 2001 From: Gage Krumbach Date: Mon, 26 Aug 2024 15:21:37 -0500 Subject: [PATCH 1/3] add backend route for storage class metadata --- backend/src/routes/api/storage-class/index.ts | 26 ++++++++ backend/src/routes/api/storage-class/utils.ts | 61 +++++++++++++++++++ backend/src/types.ts | 8 +++ frontend/src/services/StorageClassService.ts | 16 +++++ 4 files changed, 111 insertions(+) create mode 100644 backend/src/routes/api/storage-class/index.ts create mode 100644 backend/src/routes/api/storage-class/utils.ts create mode 100644 frontend/src/services/StorageClassService.ts diff --git a/backend/src/routes/api/storage-class/index.ts b/backend/src/routes/api/storage-class/index.ts new file mode 100644 index 0000000000..863be72111 --- /dev/null +++ b/backend/src/routes/api/storage-class/index.ts @@ -0,0 +1,26 @@ +import { FastifyReply, FastifyRequest } from 'fastify'; +import { StorageClassConfig, KubeFastifyInstance } from '../../../types'; +import { secureAdminRoute } from '../../../utils/route-security'; +import { updateStorageClassMetadata } from './utils'; + +export default async (fastify: KubeFastifyInstance): Promise => { + fastify.put( + '/:storageClassName/metadata', + secureAdminRoute(fastify)( + async ( + request: FastifyRequest<{ + Body: StorageClassConfig; + }>, + reply: FastifyReply, + ) => { + return updateStorageClassMetadata(fastify, request) + .then((res) => { + return res; + }) + .catch((res) => { + reply.send(res); + }); + }, + ), + ); +}; diff --git a/backend/src/routes/api/storage-class/utils.ts b/backend/src/routes/api/storage-class/utils.ts new file mode 100644 index 0000000000..82cb8b730c --- /dev/null +++ b/backend/src/routes/api/storage-class/utils.ts @@ -0,0 +1,61 @@ +import { FastifyRequest } from 'fastify'; +import { KubeFastifyInstance, StorageClassConfig } from '../../../types'; +import { isHttpError } from '../../../utils'; +import { errorHandler } from '../../../utils'; + +export async function updateStorageClassMetadata( + fastify: KubeFastifyInstance, + request: FastifyRequest<{ Body: StorageClassConfig }>, +): Promise<{ success: boolean; error: string }> { + const { namespace } = fastify.kube; + const params = request.params as { storageClassName: string }; + const body = request.body as StorageClassConfig; + + try { + // Fetch the existing custom object for the StorageClass + const storageClass = await fastify.kube.customObjectsApi.getNamespacedCustomObject( + 'storage.k8s.io', + 'v1', + namespace, + 'storageclasses', + params.storageClassName, + ); + + // Extract and update the annotations + const annotations = (storageClass.body as any).metadata.annotations || {}; + annotations['opendatahub.io/sc-config'] = JSON.stringify(body); + + // Patch the StorageClass with the new annotations + await fastify.kube.customObjectsApi.patchNamespacedCustomObject( + 'storage.k8s.io', + 'v1', + namespace, + 'storageclasses', + params.storageClassName, + { + metadata: { + annotations, + }, + }, + undefined, + undefined, + undefined, + { + headers: { + 'Content-Type': 'application/merge-patch+json', + }, + }, + ); + + return { success: true, error: '' }; + } catch (e) { + if (!isHttpError(e) || e.statusCode !== 404) { + fastify.log.error(e, 'Unable to update storage class metadata.'); + return { + success: false, + error: `Unable to update storage class metadata: ${errorHandler(e)}`, + }; + } + throw e; + } +} diff --git a/backend/src/types.ts b/backend/src/types.ts index e11a848ad7..a51a31f4b9 100644 --- a/backend/src/types.ts +++ b/backend/src/types.ts @@ -624,6 +624,14 @@ export type ImageTagInfo = { export type ImageType = 'byon' | 'jupyter' | 'other'; +export type StorageClassConfig = { + displayName: string; + isEnabled: boolean; + isDefault: boolean; + lastModified: string; + description?: string; +}; + export type PersistentVolumeClaimKind = { apiVersion?: string; kind?: string; diff --git a/frontend/src/services/StorageClassService.ts b/frontend/src/services/StorageClassService.ts new file mode 100644 index 0000000000..52e9ce8ef2 --- /dev/null +++ b/frontend/src/services/StorageClassService.ts @@ -0,0 +1,16 @@ +import axios from '~/utilities/axios'; +import { StorageClassConfig } from '~/k8sTypes'; +import { ResponseStatus } from '~/types'; + +export const updateStorageClassMetadata = ( + name: string, + spec: StorageClassConfig, +): Promise => { + const url = `/api/storage-class/${name}/metadata`; + return axios + .put(url, spec) + .then((response) => response.data) + .catch((e) => { + throw new Error(e.response.data.message); + }); +}; From 782fbe7ffd7e87ddd091eaa7050b68518b22c424 Mon Sep 17 00:00:00 2001 From: Gage Krumbach Date: Mon, 26 Aug 2024 15:50:44 -0500 Subject: [PATCH 2/3] refactor: Update storage class metadata handling and add role for storage class --- backend/src/routes/api/storage-class/utils.ts | 11 ++++------- manifests/core-bases/base/role.yaml | 9 +++++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/backend/src/routes/api/storage-class/utils.ts b/backend/src/routes/api/storage-class/utils.ts index 82cb8b730c..d7ed40d253 100644 --- a/backend/src/routes/api/storage-class/utils.ts +++ b/backend/src/routes/api/storage-class/utils.ts @@ -1,5 +1,5 @@ import { FastifyRequest } from 'fastify'; -import { KubeFastifyInstance, StorageClassConfig } from '../../../types'; +import { K8sResourceCommon, KubeFastifyInstance, StorageClassConfig } from '../../../types'; import { isHttpError } from '../../../utils'; import { errorHandler } from '../../../utils'; @@ -7,29 +7,26 @@ export async function updateStorageClassMetadata( fastify: KubeFastifyInstance, request: FastifyRequest<{ Body: StorageClassConfig }>, ): Promise<{ success: boolean; error: string }> { - const { namespace } = fastify.kube; const params = request.params as { storageClassName: string }; const body = request.body as StorageClassConfig; try { // Fetch the existing custom object for the StorageClass - const storageClass = await fastify.kube.customObjectsApi.getNamespacedCustomObject( + const storageClass = await fastify.kube.customObjectsApi.getClusterCustomObject( 'storage.k8s.io', 'v1', - namespace, 'storageclasses', params.storageClassName, ); // Extract and update the annotations - const annotations = (storageClass.body as any).metadata.annotations || {}; + const annotations = (storageClass.body as K8sResourceCommon).metadata.annotations || {}; annotations['opendatahub.io/sc-config'] = JSON.stringify(body); // Patch the StorageClass with the new annotations - await fastify.kube.customObjectsApi.patchNamespacedCustomObject( + await fastify.kube.customObjectsApi.patchClusterCustomObject( 'storage.k8s.io', 'v1', - namespace, 'storageclasses', params.storageClassName, { diff --git a/manifests/core-bases/base/role.yaml b/manifests/core-bases/base/role.yaml index f48ec75ad9..2c856fd436 100644 --- a/manifests/core-bases/base/role.yaml +++ b/manifests/core-bases/base/role.yaml @@ -3,6 +3,15 @@ apiVersion: rbac.authorization.k8s.io/v1 metadata: name: odh-dashboard rules: + - verbs: + - get + - list + - update + - patch + apiGroups: + - storage.k8s.io + resources: + - storageclasses - verbs: - create - get From cafdf34eecd2c11d9db04bb16f7ac613da557915 Mon Sep 17 00:00:00 2001 From: Gage Krumbach Date: Tue, 27 Aug 2024 10:48:42 -0500 Subject: [PATCH 3/3] update naming and types --- backend/src/routes/api/storage-class/index.ts | 7 ++++--- backend/src/routes/api/storage-class/utils.ts | 17 +++++++++++------ backend/src/types.ts | 5 +++++ frontend/src/services/StorageClassService.ts | 4 ++-- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/backend/src/routes/api/storage-class/index.ts b/backend/src/routes/api/storage-class/index.ts index 863be72111..15d7446902 100644 --- a/backend/src/routes/api/storage-class/index.ts +++ b/backend/src/routes/api/storage-class/index.ts @@ -1,19 +1,20 @@ import { FastifyReply, FastifyRequest } from 'fastify'; import { StorageClassConfig, KubeFastifyInstance } from '../../../types'; import { secureAdminRoute } from '../../../utils/route-security'; -import { updateStorageClassMetadata } from './utils'; +import { updateStorageClassConfig } from './utils'; export default async (fastify: KubeFastifyInstance): Promise => { fastify.put( - '/:storageClassName/metadata', + '/:storageClassName/config', secureAdminRoute(fastify)( async ( request: FastifyRequest<{ Body: StorageClassConfig; + Params: { storageClassName: string }; }>, reply: FastifyReply, ) => { - return updateStorageClassMetadata(fastify, request) + return updateStorageClassConfig(fastify, request) .then((res) => { return res; }) diff --git a/backend/src/routes/api/storage-class/utils.ts b/backend/src/routes/api/storage-class/utils.ts index d7ed40d253..73469a750d 100644 --- a/backend/src/routes/api/storage-class/utils.ts +++ b/backend/src/routes/api/storage-class/utils.ts @@ -1,14 +1,19 @@ import { FastifyRequest } from 'fastify'; -import { K8sResourceCommon, KubeFastifyInstance, StorageClassConfig } from '../../../types'; +import { + K8sResourceCommon, + KubeFastifyInstance, + ResponseStatus, + StorageClassConfig, +} from '../../../types'; import { isHttpError } from '../../../utils'; import { errorHandler } from '../../../utils'; -export async function updateStorageClassMetadata( +export async function updateStorageClassConfig( fastify: KubeFastifyInstance, - request: FastifyRequest<{ Body: StorageClassConfig }>, -): Promise<{ success: boolean; error: string }> { - const params = request.params as { storageClassName: string }; - const body = request.body as StorageClassConfig; + request: FastifyRequest<{ Body: StorageClassConfig; Params: { storageClassName: string } }>, +): Promise { + const params = request.params; + const body = request.body; try { // Fetch the existing custom object for the StorageClass diff --git a/backend/src/types.ts b/backend/src/types.ts index a51a31f4b9..0c0eb2da83 100644 --- a/backend/src/types.ts +++ b/backend/src/types.ts @@ -1199,3 +1199,8 @@ export type ModelRegistryKind = K8sResourceCommon & { conditions?: K8sCondition[]; }; }; + +export type ResponseStatus = { + success: boolean; + error: string; +}; diff --git a/frontend/src/services/StorageClassService.ts b/frontend/src/services/StorageClassService.ts index 52e9ce8ef2..7fc662aacf 100644 --- a/frontend/src/services/StorageClassService.ts +++ b/frontend/src/services/StorageClassService.ts @@ -2,11 +2,11 @@ import axios from '~/utilities/axios'; import { StorageClassConfig } from '~/k8sTypes'; import { ResponseStatus } from '~/types'; -export const updateStorageClassMetadata = ( +export const updateStorageClassConfig = ( name: string, spec: StorageClassConfig, ): Promise => { - const url = `/api/storage-class/${name}/metadata`; + const url = `/api/storage-class/${name}/config`; return axios .put(url, spec) .then((response) => response.data)