Skip to content

Commit

Permalink
API submit logic and enabling feature flag by default
Browse files Browse the repository at this point in the history
  • Loading branch information
ppadti committed Jan 7, 2025
1 parent 14b1c4d commit c6301d8
Show file tree
Hide file tree
Showing 15 changed files with 243 additions and 53 deletions.
15 changes: 9 additions & 6 deletions backend/src/routes/api/modelRegistries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import {
patchModelRegistryAndUpdatePassword,
} from './modelRegistryUtils';

type ModelRegistryAndDBPassword = {
type ModelRegistryAndCredentials = {
modelRegistry: ModelRegistryKind;
databasePassword?: string;
databaseCACertificate?: string;
};

// Lists ModelRegistries directly (does not look up passwords from associated Secrets, you must make a direct request to '/:modelRegistryName' for that)
Expand Down Expand Up @@ -48,19 +49,20 @@ export default async (fastify: KubeFastifyInstance): Promise<void> => {
async (
request: FastifyRequest<{
Querystring: { dryRun?: string };
Body: ModelRegistryAndDBPassword;
Body: ModelRegistryAndCredentials;
}>,
reply: FastifyReply,
) => {
const { dryRun } = request.query;
const { modelRegistry, databasePassword } = request.body;
const { modelRegistry, databasePassword, databaseCACertificate } = request.body;
try {
const modelRegistryNamespace = getModelRegistryNamespace(fastify);
return createModelRegistryAndSecret(
fastify,
modelRegistry,
modelRegistryNamespace,
databasePassword,
databaseCACertificate,
!!dryRun,
).catch((e) => {
throw createError(e.statusCode, e?.body?.message);
Expand Down Expand Up @@ -98,7 +100,7 @@ export default async (fastify: KubeFastifyInstance): Promise<void> => {
modelRegistry,
modelRegistryNamespace,
);
return { modelRegistry, databasePassword } satisfies ModelRegistryAndDBPassword;
return { modelRegistry, databasePassword } satisfies ModelRegistryAndCredentials;
} catch (e) {
fastify.log.error(
`ModelRegistry ${modelRegistryName} could not be read, ${
Expand All @@ -120,13 +122,13 @@ export default async (fastify: KubeFastifyInstance): Promise<void> => {
request: FastifyRequest<{
Querystring: { dryRun?: string };
Params: { modelRegistryName: string };
Body: RecursivePartial<ModelRegistryAndDBPassword>;
Body: RecursivePartial<ModelRegistryAndCredentials>;
}>,
reply: FastifyReply,
) => {
const { dryRun } = request.query;
const { modelRegistryName } = request.params;
const { modelRegistry: patchBody, databasePassword } = request.body;
const { modelRegistry: patchBody, databasePassword, databaseCACertificate } = request.body;
try {
const modelRegistryNamespace = getModelRegistryNamespace(fastify);
const modelRegistry = await patchModelRegistryAndUpdatePassword(
Expand All @@ -135,6 +137,7 @@ export default async (fastify: KubeFastifyInstance): Promise<void> => {
modelRegistryNamespace,
patchBody,
databasePassword,
databaseCACertificate,
!!dryRun,
);
return { modelRegistry, databasePassword };
Expand Down
60 changes: 59 additions & 1 deletion backend/src/routes/api/modelRegistries/modelRegistryUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { KubeFastifyInstance, ModelRegistryKind, RecursivePartial } from '../../../types';
import { PatchUtils, V1Secret, V1Status } from '@kubernetes/client-node';
import { PatchUtils, V1ConfigMap, V1Secret, V1Status } from '@kubernetes/client-node';
import { getClusterStatus } from '../../../utils/resourceUtils';

const MODEL_REGISTRY_API_GROUP = 'modelregistry.opendatahub.io';
Expand Down Expand Up @@ -130,13 +130,51 @@ const createModelRegistry = async (
return response.body;
};

const createConfigMapForCACertficate = async (
fastify: KubeFastifyInstance,
namespace: string,
name: string,
databaseCACertificate: string,
): Promise<V1ConfigMap> => {
const body: V1ConfigMap = {
metadata: {
name,
namespace,
},
data: { 'ca.crt': databaseCACertificate },
};
return await fastify.kube.coreV1Api
.createNamespacedConfigMap(namespace, body)
.then((response) => response.body);
};

export const createModelRegistryAndSecret = async (
fastify: KubeFastifyInstance,
modelRegistry: ModelRegistryKind,
modelRegistryNamespace: string,
databasePassword?: string,
databaseCACertificate?: string,
dryRunOnly = false,
): Promise<ModelRegistryKind> => {
let newCACertificateConfigMap: { name: string; key: string };
if (databaseCACertificate) {
const newConfigMap = await createConfigMapForCACertficate(
fastify,
modelRegistryNamespace,
modelRegistry.spec.mysql.sslRootCertificateConfigMap.name,
databaseCACertificate,
);

newCACertificateConfigMap = {
name: newConfigMap.metadata.name,
key: Object.keys(newConfigMap.data || {})[0],
};

if (modelRegistry.spec.mysql) {
modelRegistry.spec.mysql.sslRootCertificateConfigMap = newCACertificateConfigMap;
}
}

const createBoth = async (dryRun = false) => {
const dbSpec = getDatabaseSpec(modelRegistry);
const newSecret =
Expand Down Expand Up @@ -294,8 +332,28 @@ export const patchModelRegistryAndUpdatePassword = async (
modelRegistryNamespace: string,
patchBody: RecursivePartial<ModelRegistryKind>,
databasePassword?: string,
databaseCACertificate?: string,
dryRunOnly = false,
): Promise<ModelRegistryKind> => {
let newCACertificateConfigMap: { name: string; key: string };
if (databaseCACertificate) {
const newConfigMap = await createConfigMapForCACertficate(
fastify,
modelRegistryNamespace,
patchBody.spec.mysql.sslRootCertificateConfigMap.name,
databaseCACertificate,
);

newCACertificateConfigMap = {
name: newConfigMap.metadata.name,
key: Object.keys(newConfigMap.data || {})[0],
};

if (patchBody.spec.mysql) {
patchBody.spec.mysql.sslRootCertificateConfigMap = newCACertificateConfigMap;
}
}

const patchBoth = async (dryRun = false) => {
const modelRegistry = await patchModelRegistry(
fastify,
Expand Down
15 changes: 14 additions & 1 deletion backend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1201,7 +1201,20 @@ export type ModelRegistryKind = K8sResourceCommon & {
port?: number;
skipDBCreation?: boolean;
username?: string;
};
} & EitherNotBoth<
{
sslRootCertificateConfigMap?: {
name: string;
key: string;
};
},
{
sslRootCertificateSecret?: {
name: string;
key: string;
};
}
>;
},
{
postgres?: {
Expand Down
2 changes: 1 addition & 1 deletion backend/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const blankDashboardCR: DashboardConfig = {
disableHardwareProfiles: true,
disableDistributedWorkloads: false,
disableModelRegistry: false,
disableModelRegistrySecureDB: true,
disableModelRegistrySecureDB: false,
disableServingRuntimeParams: false,
disableConnectionTypes: false,
disableStorageClasses: false,
Expand Down
2 changes: 1 addition & 1 deletion docs/dashboard-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The following are a list of features that are supported, along with there defaul
| disablePerformanceMetrics | false | Disables Endpoint Performance tab from Model Serving metrics. |
| disableDistributedWorkloads | false | Disables Distributed Workload Metrics from the dashboard. |
| disableModelRegistry | false | Disables Model Registry from the dashboard. |
| disableModelRegistrySecureDB | true | Disables Model Registry Secure DB from the dashboard. |
| disableModelRegistrySecureDB | false | Disables Model Registry Secure DB from the dashboard. |
| disableServingRuntimeParams | false | Disables Serving Runtime params from the dashboard. |
| disableStorageClasses | false | Disables storage classes settings nav item from the dashboard. |
| disableNIMModelServing | true | Disables components of NIM Model UI from the dashboard. |
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/__mocks__/mockDashboardConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const mockDashboardConfig = ({
disableTrustyBiasMetrics = false,
disableDistributedWorkloads = false,
disableModelRegistry = false,
disableModelRegistrySecureDB = true,
disableModelRegistrySecureDB = false,
disableServingRuntimeParams = false,
disableConnectionTypes = true,
disableStorageClasses = false,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/concepts/areas/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const allFeatureFlags: string[] = Object.keys({
disableHardwareProfiles: false,
disableDistributedWorkloads: false,
disableModelRegistry: false,
disableModelRegistrySecureDB: true,
disableModelRegistrySecureDB: false,
disableServingRuntimeParams: false,
disableConnectionTypes: false,
disableStorageClasses: false,
Expand Down
15 changes: 14 additions & 1 deletion frontend/src/k8sTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1322,7 +1322,20 @@ export type ModelRegistryKind = K8sResourceCommon & {
port?: number;
skipDBCreation?: boolean;
username?: string;
};
} & EitherNotBoth<
{
sslRootCertificateConfigMap?: {
name: string;
key: string;
} | null;
},
{
sslRootCertificateSecret?: {
name: string;
key: string;
} | null;
}
>;
},
{
postgres?: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useState } from 'react';
import { FormGroup, Radio, Alert, MenuItem, MenuGroup, Tooltip } from '@patternfly/react-core';
import SearchSelector from '~/components/searchSelector/SearchSelector';
import { translateDisplayNameForK8s } from '~/concepts/k8s/utils';
import { RecursivePartial } from '~/typeHelpers';
import { ConfigSecretItem } from '~/k8sTypes';
import { ODH_PRODUCT_NAME } from '~/utilities/const';
import { PemFileUpload } from './PemFileUpload';
import { CA_BUNDLE_CRT, ODH_CA_BUNDLE_CRT, ODH_TRUSTED_BUNDLE } from './const';

export enum SecureDBRType {
CLUSTER_WIDE = 'cluster-wide',
Expand All @@ -16,7 +16,7 @@ export enum SecureDBRType {

export interface SecureDBInfo {
type: SecureDBRType;
configMap: string;
resourceName: string;
key: string;
certificate: string;
nameSpace: string;
Expand All @@ -27,7 +27,7 @@ export interface SecureDBInfo {
interface CreateMRSecureDBSectionProps {
secureDBInfo: SecureDBInfo;
modelRegistryNamespace: string;
nameDesc: { name: string };
k8sName: string;
existingCertConfigMaps: ConfigSecretItem[];
existingCertSecrets: ConfigSecretItem[];
setSecureDBInfo: (info: SecureDBInfo) => void;
Expand All @@ -36,16 +36,25 @@ interface CreateMRSecureDBSectionProps {
export const CreateMRSecureDBSection: React.FC<CreateMRSecureDBSectionProps> = ({
secureDBInfo,
modelRegistryNamespace,
nameDesc,
k8sName,
existingCertConfigMaps,
existingCertSecrets,
setSecureDBInfo,
}) => {
const [searchConfigSecretName, setSearchConfigSecretName] = useState('');
const [searchKey, setSearchKey] = useState('');
const ODH_TRUSTED_BUNDLE = 'odh-trusted-ca-bundle';
const CA_BUNDLE_CRT = 'ca-bundle.crt';
const ODH_CA_BUNDLE_CRT = 'odh-ca-bundle.crt';

let newConfigMapName = 'db-credential';
if (k8sName) {
newConfigMapName = `${k8sName}-db-credential`;
let suffix = 1;
const existingConfigMaps = existingCertConfigMaps.map((cofigmap) => cofigmap.name);

while (existingConfigMaps.includes(newConfigMapName)) {
suffix++;
newConfigMapName = `${k8sName}-db-credential-${suffix}`;
}
}

const hasContent = (value: string): boolean => !!value.trim().length;

Expand All @@ -55,7 +64,7 @@ export const CreateMRSecureDBSection: React.FC<CreateMRSecureDBSectionProps> = (
return true;
}
if (fullInfo.type === SecureDBRType.EXISTING) {
return hasContent(fullInfo.configMap) && hasContent(fullInfo.key);
return hasContent(fullInfo.resourceName) && hasContent(fullInfo.key);
}
if (fullInfo.type === SecureDBRType.NEW) {
return hasContent(fullInfo.certificate);
Expand Down Expand Up @@ -88,7 +97,7 @@ export const CreateMRSecureDBSection: React.FC<CreateMRSecureDBSectionProps> = (
type,
nameSpace: '',
key: '',
configMap: '',
resourceName: '',
resourceType: undefined,
certificate: '',
};
Expand All @@ -103,7 +112,7 @@ export const CreateMRSecureDBSection: React.FC<CreateMRSecureDBSectionProps> = (

const newInfo = {
...secureDBInfo,
configMap: selectedName,
resourceName: selectedName,
key: '',
resourceType,
};
Expand Down Expand Up @@ -239,7 +248,7 @@ export const CreateMRSecureDBSection: React.FC<CreateMRSecureDBSectionProps> = (
onSearchChange={(newValue) => setSearchConfigSecretName(newValue)}
onSearchClear={() => setSearchConfigSecretName('')}
searchValue={searchConfigSecretName}
toggleText={secureDBInfo.configMap || 'Select a ConfigMap or a Secret'}
toggleText={secureDBInfo.resourceName || 'Select a ConfigMap or a Secret'}
>
{getFilteredExistingCAResources()}
</SearchSelector>
Expand All @@ -254,12 +263,12 @@ export const CreateMRSecureDBSection: React.FC<CreateMRSecureDBSectionProps> = (
isFullWidth
dataTestId="existing-ca-key-selector"
onSearchChange={(newValue) => setSearchKey(newValue)}
isDisabled={!secureDBInfo.configMap}
isDisabled={!secureDBInfo.resourceName}
onSearchClear={() => setSearchKey('')}
searchValue={searchKey}
toggleText={
secureDBInfo.key ||
(!secureDBInfo.configMap
(!secureDBInfo.resourceName
? 'Select a resource to view its available keys'
: 'Select a key')
}
Expand All @@ -268,7 +277,7 @@ export const CreateMRSecureDBSection: React.FC<CreateMRSecureDBSectionProps> = (
secureDBInfo.resourceType === 'ConfigMap'
? existingCertConfigMaps
: existingCertSecrets,
secureDBInfo.configMap,
secureDBInfo.resourceName,
)
.filter((item) => item.toLowerCase().includes(searchKey.toLowerCase()))
.map((item, index) => (
Expand Down Expand Up @@ -305,8 +314,7 @@ export const CreateMRSecureDBSection: React.FC<CreateMRSecureDBSectionProps> = (
variant="info"
style={{ marginLeft: 'var(--pf-t--global--spacer--lg)' }}
>
Uploading a certificate below creates the{' '}
<strong>{translateDisplayNameForK8s(nameDesc.name)}-db-credential</strong> ConfigMap
Uploading a certificate below creates the <strong>{newConfigMapName}</strong> ConfigMap
with the <strong>ca.crt</strong> key. If you&apos;d like to upload the certificate as a
Secret instead, see the documentation for more details.
</Alert>
Expand All @@ -319,6 +327,7 @@ export const CreateMRSecureDBSection: React.FC<CreateMRSecureDBSectionProps> = (
onChange={(value) => {
const newInfo = {
...secureDBInfo,
resourceName: newConfigMapName,
certificate: value,
};
setSecureDBInfo({ ...newInfo, isValid: isValid(newInfo) });
Expand Down
Loading

0 comments on commit c6301d8

Please sign in to comment.