Skip to content

Commit

Permalink
Wrap up kubeflow deployment (#662)
Browse files Browse the repository at this point in the history
Signed-off-by: lucferbux <lferrnan@redhat.com>
  • Loading branch information
lucferbux authored Dec 20, 2024
1 parent 62f6707 commit 7249588
Show file tree
Hide file tree
Showing 40 changed files with 401 additions and 154 deletions.
3 changes: 3 additions & 0 deletions clients/ui/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
APP_ENV=development
MOCK_AUTH=true
DEPLOYMENT_MODE=standalone
1 change: 1 addition & 0 deletions clients/ui/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
APP_ENV=production
2 changes: 1 addition & 1 deletion clients/ui/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dev-install-dependencies:

.PHONY: dev-bff
dev-bff:
cd bff && make run PORT=4000 MOCK_K8S_CLIENT=true MOCK_MR_CLIENT=true
cd bff && make run PORT=4000 MOCK_K8S_CLIENT=true MOCK_MR_CLIENT=true DEV_MODE=true STANDALONE_MODE=true

.PHONY: dev-frontend
dev-frontend:
Expand Down
3 changes: 2 additions & 1 deletion clients/ui/bff/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ MOCK_K8S_CLIENT ?= false
MOCK_MR_CLIENT ?= false
DEV_MODE ?= false
DEV_MODE_PORT ?= 8080
STANDALONE_MODE ?= true
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.29.0

Expand Down Expand Up @@ -47,7 +48,7 @@ build: fmt vet test ## Builds the project to produce a binary executable.
.PHONY: run
run: fmt vet envtest ## Runs the project.
ENVTEST_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" \
go run ./cmd/main.go --port=$(PORT) --mock-k8s-client=$(MOCK_K8S_CLIENT) --mock-mr-client=$(MOCK_MR_CLIENT) --dev-mode=$(DEV_MODE) --dev-mode-port=$(DEV_MODE_PORT)
go run ./cmd/main.go --port=$(PORT) --mock-k8s-client=$(MOCK_K8S_CLIENT) --mock-mr-client=$(MOCK_MR_CLIENT) --dev-mode=$(DEV_MODE) --dev-mode-port=$(DEV_MODE_PORT) --standalone-mode=$(STANDALONE_MODE)

.PHONY: docker-build
docker-build: ## Builds a container for the project.
Expand Down
1 change: 1 addition & 0 deletions clients/ui/bff/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func main() {
flag.BoolVar(&cfg.MockMRClient, "mock-mr-client", false, "Use mock Model Registry client")
flag.BoolVar(&cfg.DevMode, "dev-mode", false, "Use development mode for access to local K8s cluster")
flag.IntVar(&cfg.DevModePort, "dev-mode-port", getEnvAsInt("DEV_MODE_PORT", 8080), "Use port when in development mode")
flag.BoolVar(&cfg.StandaloneMode, "standalone-mode", false, "Use standalone mode for enabling endpoints in standalone mode")
flag.Parse()

logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
Expand Down
9 changes: 5 additions & 4 deletions clients/ui/bff/internal/api/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package api
import (
"context"
"fmt"
"log/slog"
"net/http"

"github.com/kubeflow/model-registry/ui/bff/internal/config"
"github.com/kubeflow/model-registry/ui/bff/internal/integrations"
"github.com/kubeflow/model-registry/ui/bff/internal/repositories"
"log/slog"
"net/http"

"github.com/julienschmidt/httprouter"
"github.com/kubeflow/model-registry/ui/bff/internal/mocks"
Expand Down Expand Up @@ -110,8 +111,8 @@ func (app *App) Routes() http.Handler {
router.GET(UserPath, app.UserHandler)
// Perform SAR to Get List Services by Namspace
router.GET(ModelRegistryListPath, app.AttachNamespace(app.PerformSARonGetListServicesByNamespace(app.ModelRegistryHandler)))
if app.config.DevMode {
router.GET(NamespaceListPath, app.AttachNamespace(app.GetNamespacesHandler))
if app.config.StandaloneMode {
router.GET(NamespaceListPath, app.GetNamespacesHandler)
}

return app.RecoverPanic(app.enableCORS(app.InjectUserHeaders(router)))
Expand Down
11 changes: 6 additions & 5 deletions clients/ui/bff/internal/config/environment.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package config

type EnvConfig struct {
Port int
MockK8Client bool
MockMRClient bool
DevMode bool
DevModePort int
Port int
MockK8Client bool
MockMRClient bool
DevMode bool
StandaloneMode bool
DevModePort int
}
3 changes: 3 additions & 0 deletions clients/ui/frontend/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
APP_ENV=development
MOCK_AUTH=true
DEPLOYMENT_MODE=standalone
2 changes: 1 addition & 1 deletion clients/ui/frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ COPY . /usr/src/app

RUN npm cache clean --force
RUN npm ci --omit=optional
RUN npm run build
RUN npm run build:prod

FROM nginxinc/nginx-unprivileged

Expand Down
20 changes: 15 additions & 5 deletions clients/ui/frontend/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import {
} from '@patternfly/react-core';
import ToastNotifications from '~/shared/components/ToastNotifications';
import { useSettings } from '~/shared/hooks/useSettings';
import { isMUITheme, Theme, AUTH_HEADER, DEV_MODE } from '~/shared/utilities/const';
import { isMUITheme, Theme, AUTH_HEADER, MOCK_AUTH } from '~/shared/utilities/const';
import { logout } from '~/shared/utilities/appUtils';
import { NamespaceSelectorContext } from '~/shared/context/NamespaceSelectorContext';
import NavSidebar from './NavSidebar';
import AppRoutes from './AppRoutes';
import { AppContext } from './AppContext';
Expand All @@ -29,6 +30,8 @@ const App: React.FC = () => {
loadError: configError,
} = useSettings();

const { namespacesLoaded, namespacesLoadError } = React.useContext(NamespaceSelectorContext);

const username = userSettings?.userId;

React.useEffect(() => {
Expand All @@ -41,7 +44,7 @@ const App: React.FC = () => {
}, []);

React.useEffect(() => {
if (DEV_MODE && username) {
if (MOCK_AUTH && username) {
localStorage.setItem(AUTH_HEADER, username);
} else {
localStorage.removeItem(AUTH_HEADER);
Expand All @@ -59,16 +62,22 @@ const App: React.FC = () => {
[configSettings, userSettings],
);

const error = configError || namespacesLoadError;

// We lack the critical data to startup the app
if (configError) {
if (error) {
// There was an error fetching critical data
return (
<Page>
<PageSection>
<Stack hasGutter>
<StackItem>
<Alert variant="danger" isInline title="General loading error">
<p>{configError.message || 'Unknown error occurred during startup.'}</p>
<p>
{configError?.message ||
namespacesLoadError?.message ||
'Unknown error occurred during startup.'}
</p>
<p>Logging out and logging back in may solve the issue.</p>
</Alert>
</StackItem>
Expand All @@ -87,7 +96,8 @@ const App: React.FC = () => {
}

// Waiting on the API to finish
const loading = !configLoaded || !userSettings || !configSettings || !contextValue;
const loading =
!configLoaded || !userSettings || !configSettings || !contextValue || !namespacesLoaded;

return loading ? (
<Bullseye>
Expand Down
1 change: 1 addition & 0 deletions clients/ui/frontend/src/app/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const AppRoutes: React.FC = () => {
<Route path="/" element={<Navigate to="/modelRegistry" replace />} />
<Route path="/modelRegistry/*" element={<ModelRegistryRoutes />} />
<Route path="*" element={<NotFound />} />
{/* TODO: [Conditional render] Follow up add testing and conditional rendering when in standalone mode*/}
{clusterAdmin && (
<Route path="/modelRegistrySettings/*" element={<ModelRegistrySettingsRoutes />} />
)}
Expand Down
25 changes: 14 additions & 11 deletions clients/ui/frontend/src/app/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,25 @@ import {
ToolbarGroup,
ToolbarItem,
} from '@patternfly/react-core';
import { SimpleSelect, SimpleSelectOption } from '@patternfly/react-templates';
import { SimpleSelect } from '@patternfly/react-templates';
import { NamespaceSelectorContext } from '~/shared/context/NamespaceSelectorContext';

interface NavBarProps {
username?: string;
onLogout: () => void;
}

const Options: SimpleSelectOption[] = [{ content: 'All Namespaces', value: 'All' }];

const NavBar: React.FC<NavBarProps> = ({ username, onLogout }) => {
const [selected, setSelected] = React.useState<string | undefined>('All');
const { namespaces, preferredNamespace, updatePreferredNamespace } =
React.useContext(NamespaceSelectorContext);

const [userMenuOpen, setUserMenuOpen] = React.useState(false);

const initialOptions = React.useMemo<SimpleSelectOption[]>(
() => Options.map((o) => ({ ...o, selected: o.value === selected })),
[selected],
);
const options = namespaces.map((namespace) => ({
content: namespace.name,
value: namespace.name,
selected: namespace.name === preferredNamespace?.name,
}));

const handleLogout = () => {
setUserMenuOpen(false);
Expand All @@ -51,9 +53,10 @@ const NavBar: React.FC<NavBarProps> = ({ username, onLogout }) => {
<ToolbarGroup variant="action-group-plain" align={{ default: 'alignStart' }}>
<ToolbarItem>
<SimpleSelect
isDisabled
initialOptions={initialOptions}
onSelect={(_ev, selection) => setSelected(String(selection))}
initialOptions={options}
onSelect={(_ev, selection) => {
updatePreferredNamespace({ name: String(selection) });
}}
/>
</ToolbarItem>
</ToolbarGroup>
Expand Down
9 changes: 7 additions & 2 deletions clients/ui/frontend/src/app/context/ModelRegistryContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import * as React from 'react';
import { BFF_API_VERSION } from '~/app/const';
import useModelRegistryAPIState, { ModelRegistryAPIState } from './useModelRegistryAPIState';
import useQueryParamNamespaces from '~/shared/hooks/useQueryParamNamespaces';
import useModelRegistryAPIState, {
ModelRegistryAPIState,
} from '~/app/hooks/useModelRegistryAPIState';

export type ModelRegistryContextType = {
apiState: ModelRegistryAPIState;
Expand All @@ -26,7 +29,9 @@ export const ModelRegistryContextProvider: React.FC<ModelRegistryContextProvider
? `/api/${BFF_API_VERSION}/model_registry/${modelRegistryName}`
: null;

const [apiState, refreshAPIState] = useModelRegistryAPIState(hostPath);
const queryParams = useQueryParamNamespaces();

const [apiState, refreshAPIState] = useModelRegistryAPIState(hostPath, queryParams);

return (
<ModelRegistryContext.Provider
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { ModelRegistry } from '~/app/types';
import useModelRegistries from '~/app/hooks/useModelRegistries';
import useQueryParamNamespaces from '~/shared/hooks/useQueryParamNamespaces';

export type ModelRegistrySelectorContextType = {
modelRegistriesLoaded: boolean;
Expand Down Expand Up @@ -33,7 +34,9 @@ export const ModelRegistrySelectorContextProvider: React.FC<
const EnabledModelRegistrySelectorContextProvider: React.FC<
ModelRegistrySelectorContextProviderProps
> = ({ children }) => {
const [modelRegistries, isLoaded, error] = useModelRegistries();
const queryParams = useQueryParamNamespaces();

const [modelRegistries, isLoaded, error] = useModelRegistries(queryParams);
const [preferredModelRegistry, setPreferredModelRegistry] =
React.useState<ModelRegistrySelectorContextType['preferredModelRegistry']>(undefined);

Expand Down
10 changes: 8 additions & 2 deletions clients/ui/frontend/src/app/hooks/useModelRegistries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ import useFetchState, {
} from '~/shared/utilities/useFetchState';
import { ModelRegistry } from '~/app/types';
import { getListModelRegistries } from '~/shared/api/k8s';
import { useDeepCompareMemoize } from '~/shared/utilities/useDeepCompareMemoize';

const useModelRegistries = (): FetchState<ModelRegistry[]> => {
const listModelRegistries = React.useMemo(() => getListModelRegistries(''), []);
const useModelRegistries = (queryParams: Record<string, unknown>): FetchState<ModelRegistry[]> => {
const paramsMemo = useDeepCompareMemoize(queryParams);

const listModelRegistries = React.useMemo(
() => getListModelRegistries('', paramsMemo),
[paramsMemo],
);
const callback = React.useCallback<FetchStateCallbackPromise<ModelRegistry[]>>(
(opts) => listModelRegistries(opts),
[listModelRegistries],
Expand Down
2 changes: 1 addition & 1 deletion clients/ui/frontend/src/app/hooks/useModelRegistryAPI.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { ModelRegistryAPIState } from '~/app/context/useModelRegistryAPIState';
import { ModelRegistryAPIState } from '~/app/hooks/useModelRegistryAPIState';
import { ModelRegistryContext } from '~/app/context/ModelRegistryContext';

type UseModelRegistryAPI = ModelRegistryAPIState & {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,31 @@ export type ModelRegistryAPIState = APIState<ModelRegistryAPIs>;

const useModelRegistryAPIState = (
hostPath: string | null,
queryParameters?: Record<string, unknown>,
): [apiState: ModelRegistryAPIState, refreshAPIState: () => void] => {
const createAPI = React.useCallback(
(path: string) => ({
createRegisteredModel: createRegisteredModel(path),
createModelVersion: createModelVersion(path),
createModelVersionForRegisteredModel: createModelVersionForRegisteredModel(path),
createModelArtifact: createModelArtifact(path),
createModelArtifactForModelVersion: createModelArtifactForModelVersion(path),
getRegisteredModel: getRegisteredModel(path),
getModelVersion: getModelVersion(path),
getModelArtifact: getModelArtifact(path),
listModelArtifacts: getListModelArtifacts(path),
listModelVersions: getListModelVersions(path),
listRegisteredModels: getListRegisteredModels(path),
getModelVersionsByRegisteredModel: getModelVersionsByRegisteredModel(path),
getModelArtifactsByModelVersion: getModelArtifactsByModelVersion(path),
patchRegisteredModel: patchRegisteredModel(path),
patchModelVersion: patchModelVersion(path),
patchModelArtifact: patchModelArtifact(path),
createRegisteredModel: createRegisteredModel(path, queryParameters),
createModelVersion: createModelVersion(path, queryParameters),
createModelVersionForRegisteredModel: createModelVersionForRegisteredModel(
path,
queryParameters,
),
createModelArtifact: createModelArtifact(path, queryParameters),
createModelArtifactForModelVersion: createModelArtifactForModelVersion(path, queryParameters),
getRegisteredModel: getRegisteredModel(path, queryParameters),
getModelVersion: getModelVersion(path, queryParameters),
getModelArtifact: getModelArtifact(path, queryParameters),
listModelArtifacts: getListModelArtifacts(path, queryParameters),
listModelVersions: getListModelVersions(path, queryParameters),
listRegisteredModels: getListRegisteredModels(path, queryParameters),
getModelVersionsByRegisteredModel: getModelVersionsByRegisteredModel(path, queryParameters),
getModelArtifactsByModelVersion: getModelArtifactsByModelVersion(path, queryParameters),
patchRegisteredModel: patchRegisteredModel(path, queryParameters),
patchModelVersion: patchModelVersion(path, queryParameters),
patchModelArtifact: patchModelArtifact(path, queryParameters),
}),
[],
[queryParameters],
);

return useAPIState(hostPath, createAPI);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { ModelRegistryContext } from '~/app/context/ModelRegistryContext';
import { ModelRegistryAPIState } from '~/app/context/useModelRegistryAPIState';
import { ModelRegistryAPIState } from '~/app/hooks/useModelRegistryAPIState';
import useUser from '~/app/hooks/useUser';

type RegistrationCommonState = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ModelVersion,
RegisteredModel,
} from '~/app/types';
import { ModelRegistryAPIState } from '~/app/context/useModelRegistryAPIState';
import { ModelRegistryAPIState } from '~/app/hooks/useModelRegistryAPIState';
import { objectStorageFieldsToUri } from '~/app/pages/modelRegistry/screens/utils';
import {
ModelLocationType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import ApplicationsPage from '~/shared/components/ApplicationsPage';
import useModelRegistries from '~/app/hooks/useModelRegistries';
import TitleWithIcon from '~/shared/components/design/TitleWithIcon';
import { ProjectObjectType } from '~/shared/components/design/utils';
import useQueryParamNamespaces from '~/shared/hooks/useQueryParamNamespaces';
import ModelRegistriesTable from './ModelRegistriesTable';

const ModelRegistrySettings: React.FC = () => {
const [modelRegistries, loaded, loadError] = useModelRegistries();
const queryParams = useQueryParamNamespaces();

const [modelRegistries, loaded, loadError] = useModelRegistries(queryParams);
return (
<>
<ApplicationsPage
Expand Down
5 changes: 4 additions & 1 deletion clients/ui/frontend/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ThemeProvider, createTheme } from '@mui/material/styles';
import App from './app/App';
import { BrowserStorageContextProvider } from './shared/components/browserStorage/BrowserStorageContext';
import { NotificationContextProvider } from './app/context/NotificationContext';
import { NamespaceSelectorContextProvider } from './shared/context/NamespaceSelectorContext';

const theme = createTheme({ cssVariables: true });
const root = ReactDOM.createRoot(document.getElementById('root')!);
Expand All @@ -15,7 +16,9 @@ root.render(
<BrowserStorageContextProvider>
<ThemeProvider theme={theme}>
<NotificationContextProvider>
<App />
<NamespaceSelectorContextProvider>
<App />
</NamespaceSelectorContextProvider>
</NotificationContextProvider>
</ThemeProvider>
</BrowserStorageContextProvider>
Expand Down
Loading

0 comments on commit 7249588

Please sign in to comment.