diff --git a/backend/src/routes/api/modelRegistries/modelRegistryUtils.ts b/backend/src/routes/api/modelRegistries/modelRegistryUtils.ts index 967aa5c4f4..4d12194b53 100644 --- a/backend/src/routes/api/modelRegistries/modelRegistryUtils.ts +++ b/backend/src/routes/api/modelRegistries/modelRegistryUtils.ts @@ -1,11 +1,16 @@ -import { MODEL_REGISTRY_NAMESPACE } from '../../../utils/constants'; import { KubeFastifyInstance, ModelRegistryKind, RecursivePartial } from '../../../types'; import { PatchUtils, V1Secret, V1Status } from '@kubernetes/client-node'; +import { getClusterStatus } from '../../../utils/dsc'; const MODEL_REGISTRY_API_GROUP = 'modelregistry.opendatahub.io'; const MODEL_REGISTRY_API_VERSION = 'v1alpha1'; const MODEL_REGISTRY_PLURAL = 'modelregistries'; +export const getModelRegistryNamespace = async (fastify: KubeFastifyInstance): Promise => { + const modelRegistryNamespace = await getClusterStatus(fastify); + return modelRegistryNamespace.components.modelregistry.registriesNamespace; +}; + const base64encode = (value?: string): string => { // This usage of toString is fine for encoding // eslint-disable-next-line no-restricted-properties @@ -33,7 +38,7 @@ export const listModelRegistries = async ( const response = await (fastify.kube.customObjectsApi.listNamespacedCustomObject( MODEL_REGISTRY_API_GROUP, MODEL_REGISTRY_API_VERSION, - MODEL_REGISTRY_NAMESPACE, + await getModelRegistryNamespace(fastify), MODEL_REGISTRY_PLURAL, undefined, undefined, @@ -59,7 +64,7 @@ const createDatabasePasswordSecret = async ( apiVersion: 'v1', metadata: { generateName: `${modelRegistry.metadata.name}-db-`, - namespace: MODEL_REGISTRY_NAMESPACE, + namespace: await getModelRegistryNamespace(fastify), annotations: { 'template.openshift.io/expose-database_name': "{.data['database-name']}", 'template.openshift.io/expose-username': "{.data['database-user']}", @@ -74,7 +79,7 @@ const createDatabasePasswordSecret = async ( type: 'Opaque', }; const response = await fastify.kube.coreV1Api.createNamespacedSecret( - MODEL_REGISTRY_NAMESPACE, + await getModelRegistryNamespace(fastify), secret, undefined, dryRun ? 'All' : undefined, @@ -108,7 +113,7 @@ const createModelRegistry = async ( const response = await (fastify.kube.customObjectsApi.createNamespacedCustomObject( MODEL_REGISTRY_API_GROUP, MODEL_REGISTRY_API_VERSION, - MODEL_REGISTRY_NAMESPACE, + await getModelRegistryNamespace(fastify), MODEL_REGISTRY_PLURAL, modelRegistryWithSecretRef, undefined, @@ -147,7 +152,7 @@ export const getModelRegistry = async ( const response = await (fastify.kube.customObjectsApi.getNamespacedCustomObject( MODEL_REGISTRY_API_GROUP, MODEL_REGISTRY_API_VERSION, - MODEL_REGISTRY_NAMESPACE, + await getModelRegistryNamespace(fastify), MODEL_REGISTRY_PLURAL, modelRegistryName, // getNamespacedCustomObject doesn't support TS generics and returns body as `object`, so we assert its real type @@ -165,7 +170,7 @@ const getDatabasePasswordSecret = async ( } const response = await fastify.kube.coreV1Api.readNamespacedSecret( secretRef.name, - MODEL_REGISTRY_NAMESPACE, + await getModelRegistryNamespace(fastify), ); return { secret: response.body, passwordDataKey: secretRef.key }; }; @@ -193,7 +198,7 @@ const deleteDatabasePasswordSecret = async ( } const response = await fastify.kube.coreV1Api.deleteNamespacedSecret( existingSecret.metadata.name, - MODEL_REGISTRY_NAMESPACE, + await getModelRegistryNamespace(fastify), undefined, dryRun ? 'All' : undefined, ); @@ -209,7 +214,7 @@ const patchModelRegistry = async ( const response = await (fastify.kube.customObjectsApi.patchNamespacedCustomObject( MODEL_REGISTRY_API_GROUP, MODEL_REGISTRY_API_VERSION, - MODEL_REGISTRY_NAMESPACE, + await getModelRegistryNamespace(fastify), MODEL_REGISTRY_PLURAL, modelRegistryName, patchBody, @@ -235,7 +240,7 @@ const updateDatabasePassword = async ( if (databasePassword) { await fastify.kube.coreV1Api.patchNamespacedSecret( secret.metadata.name, - MODEL_REGISTRY_NAMESPACE, + await getModelRegistryNamespace(fastify), { data: { ...secret.data, @@ -280,7 +285,7 @@ export const deleteModelRegistryAndSecret = async ( const response = await fastify.kube.customObjectsApi.deleteNamespacedCustomObject( MODEL_REGISTRY_API_GROUP, MODEL_REGISTRY_API_VERSION, - MODEL_REGISTRY_NAMESPACE, + await getModelRegistryNamespace(fastify), MODEL_REGISTRY_PLURAL, modelRegistryName, undefined, diff --git a/backend/src/routes/api/service/modelregistry/index.ts b/backend/src/routes/api/service/modelregistry/index.ts index 6f307b10e9..5f65162848 100644 --- a/backend/src/routes/api/service/modelregistry/index.ts +++ b/backend/src/routes/api/service/modelregistry/index.ts @@ -1,5 +1,5 @@ +import { getModelRegistryNamespace } from '../../../api/modelRegistries/modelRegistryUtils'; import { ServiceAddressAnnotation } from '../../../../types'; -import { MODEL_REGISTRY_NAMESPACE } from '../../../../utils/constants'; import { proxyService } from '../../../../utils/proxy'; export default proxyService( @@ -7,7 +7,7 @@ export default proxyService( { addressAnnotation: ServiceAddressAnnotation.EXTERNAL_REST, internalPort: 8080, - namespace: MODEL_REGISTRY_NAMESPACE, + namespace: getModelRegistryNamespace, }, { // Use port forwarding for local development: diff --git a/backend/src/types.ts b/backend/src/types.ts index 9bcbb0cb31..97099d2ff2 100644 --- a/backend/src/types.ts +++ b/backend/src/types.ts @@ -1004,6 +1004,11 @@ type ComponentNames = | 'workbenches'; export type DataScienceClusterKindStatus = { + components: { + modelregistry: { + registriesNamespace: string + } + } conditions: K8sCondition[]; installedComponents: { [key in ComponentNames]?: boolean }; phase?: string; diff --git a/backend/src/utils/constants.ts b/backend/src/utils/constants.ts index 4c9c5547a7..e935938365 100644 --- a/backend/src/utils/constants.ts +++ b/backend/src/utils/constants.ts @@ -142,5 +142,3 @@ export const THANOS_RBAC_PORT = '9092'; export const THANOS_INSTANCE_NAME = 'thanos-querier'; export const THANOS_NAMESPACE = 'openshift-monitoring'; export const LABEL_SELECTOR_DASHBOARD_RESOURCE = `${KnownLabels.DASHBOARD_RESOURCE}=true`; - -export const MODEL_REGISTRY_NAMESPACE = 'odh-model-registries'; diff --git a/backend/src/utils/proxy.ts b/backend/src/utils/proxy.ts index b7b327402f..cddfd27f71 100644 --- a/backend/src/utils/proxy.ts +++ b/backend/src/utils/proxy.ts @@ -37,7 +37,7 @@ export const proxyService = internalPort: number | string; prefix?: string; suffix?: string; - namespace?: string; + namespace?: string | ((fastify: KubeFastifyInstance) => Promise); }, { constructUrl: (resource: K) => string; diff --git a/backend/src/utils/route-security.ts b/backend/src/utils/route-security.ts index 66c560a78b..8a00f63739 100644 --- a/backend/src/utils/route-security.ts +++ b/backend/src/utils/route-security.ts @@ -4,7 +4,7 @@ import { createCustomError } from './requestUtils'; import { isUserAdmin } from './adminUtils'; import { getNamespaces } from './notebookUtils'; import { logRequestDetails } from './fileUtils'; -import { DEV_MODE, MODEL_REGISTRY_NAMESPACE } from './constants'; +import { DEV_MODE } from './constants'; import { K8sNamespacedResourceCommon, KubeFastifyInstance, @@ -12,6 +12,7 @@ import { NotebookState, OauthFastifyRequest, } from '../types'; +import { getModelRegistryNamespace } from '../routes/api/modelRegistries/modelRegistryUtils'; const testAdmin = async ( fastify: KubeFastifyInstance, @@ -73,7 +74,11 @@ const requestSecurityGuard = async ( const isReadRequest = request.method.toLowerCase() === 'get'; // Check to see if a request was made against one of our namespaces - if (![notebookNamespace, dashboardNamespace, MODEL_REGISTRY_NAMESPACE].includes(namespace)) { + if ( + ![notebookNamespace, dashboardNamespace, await getModelRegistryNamespace(fastify)].includes( + namespace, + ) + ) { // Not a valid namespace -- cannot make direct calls to just any namespace no matter who you are fastify.log.error( `User requested a resource that was not in our namespaces. Namespace: ${namespace}`, diff --git a/frontend/src/__mocks__/mockDscStatus.ts b/frontend/src/__mocks__/mockDscStatus.ts index b589cb3795..a3ad631cc6 100644 --- a/frontend/src/__mocks__/mockDscStatus.ts +++ b/frontend/src/__mocks__/mockDscStatus.ts @@ -12,6 +12,11 @@ export const mockDscStatus = ({ conditions = [], phase = 'Ready', }: MockDscStatus): DataScienceClusterKindStatus => ({ + components: { + modelregistry: { + registriesNamespace: 'odh-model-registries', + }, + }, conditions: [ ...[ { diff --git a/frontend/src/__mocks__/mockModelRegistry.ts b/frontend/src/__mocks__/mockModelRegistry.ts index b0b2abd01a..66cda6238c 100644 --- a/frontend/src/__mocks__/mockModelRegistry.ts +++ b/frontend/src/__mocks__/mockModelRegistry.ts @@ -1,4 +1,3 @@ -import { MODEL_REGISTRY_DEFAULT_NAMESPACE } from '~/concepts/modelRegistry/const'; import { ModelRegistryKind } from '~/k8sTypes'; type MockModelRegistryType = { @@ -8,7 +7,7 @@ type MockModelRegistryType = { export const mockModelRegistry = ({ name = 'modelregistry-sample', - namespace = MODEL_REGISTRY_DEFAULT_NAMESPACE, + namespace = 'odh-model-registries', }: MockModelRegistryType): ModelRegistryKind => ({ apiVersion: 'modelregistry.opendatahub.io/v1alpha1', kind: 'ModelRegistry', diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelRegistry.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelRegistry.cy.ts index 912d675f5c..b9e0075edc 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelRegistry.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelRegistry.cy.ts @@ -1,5 +1,5 @@ /* eslint-disable camelcase */ -import { mockK8sResourceList } from '~/__mocks__'; +import { mockDscStatus, mockK8sResourceList } from '~/__mocks__'; import { mockComponents } from '~/__mocks__/mockComponents'; import { mockDashboardConfig } from '~/__mocks__/mockDashboardConfig'; import { mockRegisteredModelList } from '~/__mocks__/mockRegisteredModelsList'; @@ -72,6 +72,15 @@ const initIntercepts = ({ ], allowed = true, }: HandlersProps) => { + cy.interceptOdh( + 'GET /api/dsc/status', + mockDscStatus({ + installedComponents: { + 'model-registry-operator': true, + }, + }), + ); + cy.interceptOdh( 'GET /api/config', mockDashboardConfig({ diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionArchive.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionArchive.cy.ts index bd9bf54f53..03610b9aed 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionArchive.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionArchive.cy.ts @@ -1,5 +1,5 @@ /* eslint-disable camelcase */ -import { mockK8sResourceList } from '~/__mocks__'; +import { mockDscStatus, mockK8sResourceList } from '~/__mocks__'; import { mockDashboardConfig } from '~/__mocks__/mockDashboardConfig'; import { mockRegisteredModelList } from '~/__mocks__/mockRegisteredModelsList'; import { ServiceModel } from '~/__tests__/cypress/cypress/utils/models'; @@ -47,6 +47,15 @@ const initIntercepts = ({ mockModelVersion({ id: '3', name: 'model version 3' }), ], }: HandlersProps) => { + cy.interceptOdh( + 'GET /api/dsc/status', + mockDscStatus({ + installedComponents: { + 'model-registry-operator': true, + }, + }), + ); + cy.interceptOdh( 'GET /api/config', mockDashboardConfig({ diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionDetails.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionDetails.cy.ts index 03165c7151..a0d747537b 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionDetails.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionDetails.cy.ts @@ -11,6 +11,7 @@ import { mockServingRuntimeK8sResource, mockInferenceServiceK8sResource, mockProjectK8sResource, + mockDscStatus, } from '~/__mocks__'; import { @@ -33,6 +34,16 @@ const initIntercepts = () => { disableModelRegistry: false, }), ); + + cy.interceptOdh( + 'GET /api/dsc/status', + mockDscStatus({ + installedComponents: { + 'model-registry-operator': true, + }, + }), + ); + cy.interceptOdh('GET /api/components', { query: { installed: 'true' } }, mockComponents()); cy.interceptK8sList( diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersions.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersions.cy.ts index 78bb89178d..a536f5f371 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersions.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersions.cy.ts @@ -1,5 +1,5 @@ /* eslint-disable camelcase */ -import { mockK8sResourceList } from '~/__mocks__'; +import { mockDscStatus, mockK8sResourceList } from '~/__mocks__'; import { mockDashboardConfig } from '~/__mocks__/mockDashboardConfig'; import { mockModelVersionList } from '~/__mocks__/mockModelVersionList'; import { mockRegisteredModelList } from '~/__mocks__/mockRegisteredModelsList'; @@ -47,6 +47,15 @@ const initIntercepts = ({ mockModelVersion({ id: '2', name: 'model version' }), ], }: HandlersProps) => { + cy.interceptOdh( + 'GET /api/dsc/status', + mockDscStatus({ + installedComponents: { + 'model-registry-operator': true, + }, + }), + ); + cy.interceptOdh( 'GET /api/config', mockDashboardConfig({ diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/registeredModelArchive.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/registeredModelArchive.cy.ts index 9495185943..19d02673ef 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/registeredModelArchive.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/registeredModelArchive.cy.ts @@ -1,5 +1,5 @@ /* eslint-disable camelcase */ -import { mockK8sResourceList } from '~/__mocks__'; +import { mockDscStatus, mockK8sResourceList } from '~/__mocks__'; import { mockDashboardConfig } from '~/__mocks__/mockDashboardConfig'; import { mockRegisteredModelList } from '~/__mocks__/mockRegisteredModelsList'; import { ServiceModel } from '~/__tests__/cypress/cypress/utils/models'; @@ -58,6 +58,15 @@ const initIntercepts = ({ }), ); + cy.interceptOdh( + 'GET /api/dsc/status', + mockDscStatus({ + installedComponents: { + 'model-registry-operator': true, + }, + }), + ); + cy.interceptK8sList( ServiceModel, mockK8sResourceList([ diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistrySettings/modelRegistryPermissions.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistrySettings/modelRegistryPermissions.cy.ts index 98bdd9ce41..ad5dbf699a 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistrySettings/modelRegistryPermissions.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistrySettings/modelRegistryPermissions.cy.ts @@ -1,4 +1,4 @@ -import { mockK8sResourceList, mockProjectK8sResource } from '~/__mocks__'; +import { mockDscStatus, mockK8sResourceList, mockProjectK8sResource } from '~/__mocks__'; import { mock200Status } from '~/__mocks__/mockK8sStatus'; import { mockRoleBindingK8sResource } from '~/__mocks__/mockRoleBindingK8sResource'; import { be } from '~/__tests__/cypress/cypress/utils/should'; @@ -51,6 +51,7 @@ const initIntercepts = ({ isEmpty = false, hasPermission = true }: HandlersProps } else { asProductAdminUser(); } + cy.interceptOdh('GET /api/dsc/status', mockDscStatus({})); cy.interceptK8sList( ModelRegistryModel, mockK8sResourceList([ diff --git a/frontend/src/concepts/modelRegistry/apiHooks/__tests__/useModelRegistryServices.spec.ts b/frontend/src/concepts/modelRegistry/apiHooks/__tests__/useModelRegistryServices.spec.ts index f23dd42879..3ee218d370 100644 --- a/frontend/src/concepts/modelRegistry/apiHooks/__tests__/useModelRegistryServices.spec.ts +++ b/frontend/src/concepts/modelRegistry/apiHooks/__tests__/useModelRegistryServices.spec.ts @@ -93,10 +93,10 @@ describe('useModelRegistryServices', () => { expect(isLoaded).toBe(true); expect(mockGetResource).toHaveBeenCalledTimes(2); expect(mockGetResource).toHaveBeenCalledWith({ - queryOptions: { name: 'service-1', ns: 'odh-model-registries' }, + queryOptions: { name: 'service-1', ns: 'test-namespace' }, }); expect(mockGetResource).toHaveBeenCalledWith({ - queryOptions: { name: 'service-2', ns: 'odh-model-registries' }, + queryOptions: { name: 'service-2', ns: 'test-namespace' }, }); }); diff --git a/frontend/src/concepts/modelRegistry/apiHooks/useModelRegistryServices.ts b/frontend/src/concepts/modelRegistry/apiHooks/useModelRegistryServices.ts index 4d3761aba9..de6f5bf4a9 100644 --- a/frontend/src/concepts/modelRegistry/apiHooks/useModelRegistryServices.ts +++ b/frontend/src/concepts/modelRegistry/apiHooks/useModelRegistryServices.ts @@ -3,13 +3,11 @@ import { k8sGetResource } from '@openshift/dynamic-plugin-sdk-utils'; import useFetchState, { FetchStateCallbackPromise, NotReadyError } from '~/utilities/useFetchState'; import { AccessReviewResourceAttributes, ServiceKind } from '~/k8sTypes'; import { ServiceModel, useAccessReview, useRulesReview, listServices } from '~/api'; -import { MODEL_REGISTRY_DEFAULT_NAMESPACE } from '~/concepts/modelRegistry/const'; const accessReviewResource: AccessReviewResourceAttributes = { group: 'user.openshift.io', resource: 'services', verb: 'list', - namespace: MODEL_REGISTRY_DEFAULT_NAMESPACE, }; const getServiceByName = (name: string, namespace: string): Promise => @@ -37,6 +35,7 @@ const listServicesOrFetchThemByNames = async ( allowList: boolean, accessReviewLoaded: boolean, rulesReviewLoaded: boolean, + namespace: string, serviceNames?: string[], ): Promise => { if (!accessReviewLoaded || !rulesReviewLoaded) { @@ -44,8 +43,8 @@ const listServicesOrFetchThemByNames = async ( } const services = allowList - ? await listServices(MODEL_REGISTRY_DEFAULT_NAMESPACE) - : await fetchServices(serviceNames || [], MODEL_REGISTRY_DEFAULT_NAMESPACE); + ? await listServices(namespace) + : await fetchServices(serviceNames || [], namespace); return services; }; @@ -58,7 +57,7 @@ export type ModelRegistryServicesResult = { }; export const useModelRegistryServices = (namespace: string): ModelRegistryServicesResult => { - const [allowList, accessReviewLoaded] = useAccessReview(accessReviewResource); + const [allowList, accessReviewLoaded] = useAccessReview({ ...accessReviewResource, namespace }); const [rulesReviewStatus, rulesReviewLoaded, refreshRulesReview] = useRulesReview(namespace); const serviceNames = React.useMemo(() => { @@ -79,9 +78,10 @@ export const useModelRegistryServices = (namespace: string): ModelRegistryServic allowList, accessReviewLoaded, rulesReviewLoaded, + namespace, serviceNames, ), - [allowList, accessReviewLoaded, rulesReviewLoaded, serviceNames], + [allowList, accessReviewLoaded, rulesReviewLoaded, serviceNames, namespace], ); const [modelRegistryServices, isLoaded, error] = useFetchState(callback, [], { diff --git a/frontend/src/concepts/modelRegistry/const.ts b/frontend/src/concepts/modelRegistry/const.ts index ced9db3475..111b11ae7f 100644 --- a/frontend/src/concepts/modelRegistry/const.ts +++ b/frontend/src/concepts/modelRegistry/const.ts @@ -1,2 +1 @@ -export const MODEL_REGISTRY_DEFAULT_NAMESPACE = 'odh-model-registries'; // The default namespace for the model registry service, controlled by the operator. export const MODEL_REGISTRY_API_VERSION = 'v1alpha3'; diff --git a/frontend/src/concepts/modelRegistry/context/ModelRegistrySelectorContext.tsx b/frontend/src/concepts/modelRegistry/context/ModelRegistrySelectorContext.tsx index 7e75eb56ac..565cda2156 100644 --- a/frontend/src/concepts/modelRegistry/context/ModelRegistrySelectorContext.tsx +++ b/frontend/src/concepts/modelRegistry/context/ModelRegistrySelectorContext.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { ServiceKind } from '~/k8sTypes'; import useModelRegistryEnabled from '~/concepts/modelRegistry/useModelRegistryEnabled'; import { useModelRegistryServices } from '~/concepts/modelRegistry/apiHooks/useModelRegistryServices'; -import { MODEL_REGISTRY_DEFAULT_NAMESPACE } from '~/concepts/modelRegistry/const'; +import { AreaContext } from '~/concepts/areas/AreaContext'; export type ModelRegistrySelectorContextType = { modelRegistryServicesLoaded: boolean; @@ -42,12 +42,13 @@ export const ModelRegistrySelectorContextProvider: React.FC< const EnabledModelRegistrySelectorContextProvider: React.FC< ModelRegistrySelectorContextProviderProps > = ({ children }) => { + const { dscStatus } = React.useContext(AreaContext); const { modelRegistryServices = [], isLoaded, error, refreshRulesReview, - } = useModelRegistryServices(MODEL_REGISTRY_DEFAULT_NAMESPACE); + } = useModelRegistryServices(dscStatus?.components.modelregistry.registriesNamespace || ''); const [preferredModelRegistry, setPreferredModelRegistry] = React.useState< ServiceKind | undefined >(undefined); diff --git a/frontend/src/k8sTypes.ts b/frontend/src/k8sTypes.ts index 190c07e300..229ac3b7b7 100644 --- a/frontend/src/k8sTypes.ts +++ b/frontend/src/k8sTypes.ts @@ -1355,6 +1355,11 @@ export type K8sResourceListResult> /** We don't need or should ever get the full kind, this is the status section */ export type DataScienceClusterKindStatus = { + components: { + modelregistry: { + registriesNamespace: string; + }; + }; conditions: K8sCondition[]; installedComponents: { [key in StackComponent]?: boolean }; phase?: string; diff --git a/frontend/src/pages/modelRegistrySettings/CreateModal.tsx b/frontend/src/pages/modelRegistrySettings/CreateModal.tsx index a115321dfb..56e0da2872 100644 --- a/frontend/src/pages/modelRegistrySettings/CreateModal.tsx +++ b/frontend/src/pages/modelRegistrySettings/CreateModal.tsx @@ -11,13 +11,13 @@ import { import PasswordInput from '~/components/PasswordInput'; import DashboardModalFooter from '~/concepts/dashboard/DashboardModalFooter'; import { ModelRegistryKind } from '~/k8sTypes'; -import { MODEL_REGISTRY_DEFAULT_NAMESPACE } from '~/concepts/modelRegistry/const'; import { ModelRegistryModel } from '~/api'; import { createModelRegistryBackend } from '~/services/modelRegistrySettingsService'; import { isValidK8sName, translateDisplayNameForK8s } from '~/concepts/k8s/utils'; import NameDescriptionField from '~/concepts/k8s/NameDescriptionField'; import { NameDescType } from '~/pages/projects/types'; import FormSection from '~/components/pf-overrides/FormSection'; +import { AreaContext } from '~/concepts/areas/AreaContext'; type CreateModalProps = { isOpen: boolean; @@ -44,6 +44,7 @@ const CreateModal: React.FC = ({ isOpen, onClose, refresh }) = const [isPasswordTouched, setIsPasswordTouched] = React.useState(false); const [isDatabaseTouched, setIsDatabaseTouched] = React.useState(false); const [showPassword, setShowPassword] = React.useState(false); + const { dscStatus } = React.useContext(AreaContext); const onBeforeClose = () => { setIsSubmitting(false); @@ -75,7 +76,7 @@ const CreateModal: React.FC = ({ isOpen, onClose, refresh }) = kind: 'ModelRegistry', metadata: { name: nameDesc.k8sName || translateDisplayNameForK8s(nameDesc.name), - namespace: MODEL_REGISTRY_DEFAULT_NAMESPACE, + namespace: dscStatus?.components.modelregistry.registriesNamespace || '', annotations: { 'openshift.io/description': nameDesc.description, 'openshift.io/display-name': nameDesc.name.trim(), diff --git a/frontend/src/pages/modelRegistrySettings/ModelRegistriesPermissions.tsx b/frontend/src/pages/modelRegistrySettings/ModelRegistriesPermissions.tsx index f420262b3f..b1d4f74512 100644 --- a/frontend/src/pages/modelRegistrySettings/ModelRegistriesPermissions.tsx +++ b/frontend/src/pages/modelRegistrySettings/ModelRegistriesPermissions.tsx @@ -15,20 +15,22 @@ import { useGroups } from '~/api'; import RoleBindingPermissions from '~/concepts/roleBinding/RoleBindingPermissions'; import { useContextResourceData } from '~/utilities/useContextResourceData'; import ApplicationsPage from '~/pages/ApplicationsPage'; -import { MODEL_REGISTRY_DEFAULT_NAMESPACE } from '~/concepts/modelRegistry/const'; import { SupportedArea } from '~/concepts/areas'; import { RoleBindingPermissionsRoleType } from '~/concepts/roleBinding/types'; import { useModelRegistryNamespaceCR } from '~/concepts/modelRegistry/context/useModelRegistryNamespaceCR'; +import { AreaContext } from '~/concepts/areas/AreaContext'; import useModelRegistryRoleBindings from './useModelRegistryRoleBindings'; import ProjectsSettingsTab from './ProjectsTab/ProjectsSettingsTab'; const ModelRegistriesManagePermissions: React.FC = () => { + const { dscStatus } = React.useContext(AreaContext); + const modelRegistryNamespace = dscStatus?.components.modelregistry.registriesNamespace; const [activeTabKey, setActiveTabKey] = React.useState('users'); const [ownerReference, setOwnerReference] = React.useState(); const [groups] = useGroups(); const roleBindings = useContextResourceData(useModelRegistryRoleBindings()); const { mrName } = useParams(); - const state = useModelRegistryNamespaceCR(MODEL_REGISTRY_DEFAULT_NAMESPACE, mrName || ''); + const state = useModelRegistryNamespaceCR(modelRegistryNamespace || '', mrName || ''); const [modelRegistryCR, crLoaded] = state; const filteredRoleBindings = roleBindings.data.filter( (rb) => rb.metadata.labels?.['app.kubernetes.io/name'] === mrName, @@ -109,7 +111,7 @@ const ModelRegistriesManagePermissions: React.FC = () => { 'app.kubernetes.io/name': mrName || '', component: SupportedArea.MODEL_REGISTRY, }} - projectName={MODEL_REGISTRY_DEFAULT_NAMESPACE} + projectName={modelRegistryNamespace || ''} description={ <> To enable access for all cluster users, add{' '} @@ -148,7 +150,7 @@ const ModelRegistriesManagePermissions: React.FC = () => { 'app.kubernetes.io/name': mrName || '', component: SupportedArea.MODEL_REGISTRY, }} - projectName={MODEL_REGISTRY_DEFAULT_NAMESPACE} + projectName={modelRegistryNamespace || ''} isProjectSubject={activeTabKey === 'projects'} roleBindingPermissionsRB={{ ...roleBindings, data: filteredRoleBindings }} /> diff --git a/frontend/src/pages/modelRegistrySettings/useModelRegistryRoleBindings.ts b/frontend/src/pages/modelRegistrySettings/useModelRegistryRoleBindings.ts index 38d1a06fca..277f623e88 100644 --- a/frontend/src/pages/modelRegistrySettings/useModelRegistryRoleBindings.ts +++ b/frontend/src/pages/modelRegistrySettings/useModelRegistryRoleBindings.ts @@ -1,14 +1,16 @@ import * as React from 'react'; import { listRoleBindings } from '~/api'; -import { MODEL_REGISTRY_DEFAULT_NAMESPACE } from '~/concepts/modelRegistry/const'; +import { AreaContext } from '~/concepts/areas/AreaContext'; import { KnownLabels, RoleBindingKind } from '~/k8sTypes'; import useFetchState, { FetchState } from '~/utilities/useFetchState'; const useModelRegistryRoleBindings = (): FetchState => { + const { dscStatus } = React.useContext(AreaContext); + const getRoleBindings = React.useCallback( () => listRoleBindings( - MODEL_REGISTRY_DEFAULT_NAMESPACE, + dscStatus?.components.modelregistry.registriesNamespace, KnownLabels.LABEL_SELECTOR_MODEL_REGISTRY, ).catch((e) => { if (e.statusObject?.code === 404) { @@ -16,7 +18,7 @@ const useModelRegistryRoleBindings = (): FetchState => { } throw e; }), - [], + [dscStatus?.components.modelregistry.registriesNamespace], ); return useFetchState(getRoleBindings, []);