Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap up kubeflow deployment #662

Merged
merged 1 commit into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading