Skip to content

Commit

Permalink
Merge pull request #2945 from christianvogt/dev-flags
Browse files Browse the repository at this point in the history
support feature flag overrides via query string
  • Loading branch information
openshift-merge-bot[bot] authored Jun 26, 2024
2 parents 077bcd4 + c39a37f commit fce1bf5
Show file tree
Hide file tree
Showing 53 changed files with 695 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const updateClusterSettings = async (
notebookTolerationSettings,
modelServingPlatformEnabled,
} = request.body;
const dashConfig = getDashboardConfig();
const dashConfig = getDashboardConfig(request);
const isJupyterEnabled = checkJupyterEnabled();
try {
if (
Expand Down Expand Up @@ -124,10 +124,11 @@ export const updateClusterSettings = async (

export const getClusterSettings = async (
fastify: KubeFastifyInstance,
request: FastifyRequest,
): Promise<ClusterSettings | string> => {
const coreV1Api = fastify.kube.coreV1Api;
const namespace = fastify.kube.namespace;
const dashConfig = getDashboardConfig();
const dashConfig = getDashboardConfig(request);
const isJupyterEnabled = checkJupyterEnabled();
const clusterSettings: ClusterSettings = {
...DEFAULT_CLUSTER_SETTINGS,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/routes/api/cluster-settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default async (fastify: FastifyInstance): Promise<void> => {
fastify.get(
'/',
secureAdminRoute(fastify)(async (request: FastifyRequest, reply: FastifyReply) => {
return getClusterSettings(fastify)
return getClusterSettings(fastify, request)
.then((res) => {
return res;
})
Expand Down
4 changes: 2 additions & 2 deletions backend/src/routes/api/storage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default async (fastify: FastifyInstance): Promise<void> => {
reply: FastifyReply,
) => {
try {
const dashConfig = getDashboardConfig();
const dashConfig = getDashboardConfig(request);
if (dashConfig?.spec.dashboardConfig.disableS3Endpoint !== false) {
reply.code(404).send('Not found');
return reply;
Expand Down Expand Up @@ -49,7 +49,7 @@ export default async (fastify: FastifyInstance): Promise<void> => {
reply: FastifyReply,
) => {
try {
const dashConfig = getDashboardConfig();
const dashConfig = getDashboardConfig(request);
if (dashConfig?.spec.dashboardConfig.disableS3Endpoint !== false) {
reply.code(404).send('Not found');
return reply;
Expand Down
29 changes: 27 additions & 2 deletions backend/src/utils/resourceUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { getIsAppEnabled, getRouteForApplication, getRouteForClusterId } from '.
import { createCustomError } from './requestUtils';
import { getDetectedAccelerators } from '../routes/api/accelerators/acceleratorUtils';
import { RecursivePartial } from '../typeHelpers';
import { FastifyRequest } from 'fastify';

const dashboardConfigMapName = 'odh-dashboard-config';
const consoleLinksGroup = 'console.openshift.io';
Expand Down Expand Up @@ -548,8 +549,32 @@ export const initializeWatchedResources = (fastify: KubeFastifyInstance): void =
consoleLinksWatcher = new ResourceWatcher<ConsoleLinkKind>(fastify, fetchConsoleLinks);
};

export const getDashboardConfig = (): DashboardConfig => {
return dashboardConfigWatcher.getResources()?.[0];
const FEATURE_FLAGS_HEADER = 'x-odh-feature-flags';

// if inspecting feature flags, provide the request to ensure overridden feature flags are considered
export const getDashboardConfig = (request?: FastifyRequest): DashboardConfig => {
const dashboardConfig = dashboardConfigWatcher.getResources()?.[0];
if (request) {
const flagsHeader = request.headers[FEATURE_FLAGS_HEADER];
if (typeof flagsHeader === 'string') {
try {
const featureFlags = JSON.parse(flagsHeader);
return {
...dashboardConfig,
spec: {
...dashboardConfig.spec,
dashboardConfig: {
...dashboardConfig.spec.dashboardConfig,
...featureFlags,
},
},
};
} catch {
// ignore
}
}
}
return dashboardConfig;
};

export const updateDashboardConfig = (): Promise<void> => {
Expand Down
18 changes: 14 additions & 4 deletions frontend/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -111,18 +111,25 @@
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "^axios$",
"importNames": ["default"],
"message": "Import from `~/utilities/axios` instead."
}
],
"patterns": [
{
"group": ["~/api/**"],
"message": "Read from '~/api' instead."
"message": "Import from '~/api' instead."
},
{
"group": ["~/components/table/**", "!~/components/table/useTableColumnSort"],
"message": "Read from '~/components/table' instead."
"message": "Import from '~/components/table' instead."
},
{
"group": ["~/concepts/area/**"],
"message": "Read from '~/concepts/area' instead."
"message": "Import from '~/concepts/area' instead."
},
{
"group": ["~/components/table/useTableColumnSort"],
Expand Down Expand Up @@ -290,7 +297,10 @@
],
"overrides": [
{
"files": ["./src/__tests__/cypress/cypress/pages/*.ts", "./src/__tests__/cypress/cypress/tests/e2e/*.ts"],
"files": [
"./src/__tests__/cypress/cypress/pages/*.ts",
"./src/__tests__/cypress/cypress/tests/e2e/*.ts"
],
"rules": {
"no-restricted-syntax": [
"error",
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/api/k8s/__tests__/projects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
k8sUpdateResource,
k8sDeleteResource,
} from '@openshift/dynamic-plugin-sdk-utils';
import axios from 'axios';
import axios from '~/utilities/axios';
import { mockProjectK8sResource } from '~/__mocks__/mockProjectK8sResource';
import { mockK8sResourceList } from '~/__mocks__/mockK8sResourceList';
import { mockAxiosError } from '~/__mocks__/mockAxiosError';
Expand Down Expand Up @@ -40,7 +40,7 @@ jest.mock('~/api/k8s/servingRuntimes.ts', () => ({
listServingRuntimes: jest.fn(),
}));

jest.mock('axios');
jest.mock('~/utilities/axios');

const mockedAxios = jest.mocked(axios);
const k8sListResourceMock = jest.mocked(k8sListResource<ProjectKind>);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/k8s/projects.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import axios from 'axios';
import {
k8sCreateResource,
k8sDeleteResource,
k8sListResource,
K8sResourceCommon,
k8sUpdateResource,
} from '@openshift/dynamic-plugin-sdk-utils';
import axios from '~/utilities/axios';
import { CustomWatchK8sResult } from '~/types';
import { K8sAPIOptions, ProjectKind } from '~/k8sTypes';
import { ProjectModel, ProjectRequestModel } from '~/api/models';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { act } from '@testing-library/react';
import axios from 'axios';
import axios from '~/utilities/axios';
import { mockPrometheusQueryVectorResponse } from '~/__mocks__/mockPrometheusQueryVectorResponse';
import { mockWorkloadK8sResource } from '~/__mocks__/mockWorkloadK8sResource';
import { WorkloadKind, WorkloadOwnerType } from '~/k8sTypes';
Expand Down Expand Up @@ -310,7 +310,7 @@ describe('getTopResourceConsumingWorkloads', () => {
});
});

jest.mock('axios', () => ({
jest.mock('~/utilities/axios', () => ({
post: jest.fn(),
}));

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/api/prometheus/__tests__/pvcs.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { act } from '@testing-library/react';
import axios from 'axios';
import axios from '~/utilities/axios';
import { mockPVCK8sResource } from '~/__mocks__/mockPVCK8sResource';
import { mockPrometheusQueryResponse } from '~/__mocks__/mockPrometheusQueryResponse';
import { testHook } from '~/__tests__/unit/testUtils/hooks';
import { usePVCFreeAmount } from '~/api/prometheus/pvcs';
import { POLL_INTERVAL } from '~/utilities/const';

jest.mock('axios', () => ({
jest.mock('~/utilities/axios', () => ({
post: jest.fn(),
}));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { act } from '@testing-library/react';
import axios from 'axios';
import axios from '~/utilities/axios';
import { mockPrometheusQueryResponse } from '~/__mocks__/mockPrometheusQueryResponse';
import { standardUseFetchState, testHook } from '~/__tests__/unit/testUtils/hooks';
import usePrometheusQuery from '~/api/prometheus/usePrometheusQuery';

jest.mock('axios', () => ({
jest.mock('~/utilities/axios', () => ({
post: jest.fn(),
}));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import axios from 'axios';
import { act } from '@testing-library/react';
import axios from '~/utilities/axios';
import { testHook } from '~/__tests__/unit/testUtils/hooks';
import { mockPrometheusServing } from '~/__mocks__/mockPrometheusServing';
import usePrometheusQueryRange from '~/api/prometheus/usePrometheusQueryRange';
import { PrometheusQueryRangeResponseData } from '~/types';

jest.mock('axios', () => ({
jest.mock('~/utilities/axios', () => ({
post: jest.fn(),
}));

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/prometheus/usePrometheusQuery.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import axios from 'axios';
import axios from '~/utilities/axios';
import { PrometheusQueryResponse } from '~/types';
import useFetchState, { FetchOptions, FetchState, NotReadyError } from '~/utilities/useFetchState';

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/prometheus/usePrometheusQueryRange.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import axios from 'axios';
import axios from '~/utilities/axios';

import useFetchState, {
FetchOptions,
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import useDetectUser from '~/utilities/useDetectUser';
import ProjectsContextProvider from '~/concepts/projects/ProjectsContext';
import useStorageClasses from '~/concepts/k8s/useStorageClasses';
import AreaContextProvider from '~/concepts/areas/AreaContext';
import useDevFeatureFlags from './useDevFeatureFlags';
import Header from './Header';
import AppRoutes from './AppRoutes';
import NavSidebar from './NavSidebar';
Expand All @@ -29,6 +30,7 @@ import { useApplicationSettings } from './useApplicationSettings';
import TelemetrySetup from './TelemetrySetup';
import { logout } from './appUtils';
import QuickStarts from './QuickStarts';
import DevFeatureFlagsBanner from './DevFeatureFlagsBanner';

import './App.scss';

Expand All @@ -38,11 +40,14 @@ const App: React.FC = () => {

const buildStatuses = useWatchBuildStatus();
const {
dashboardConfig,
dashboardConfig: dashboardConfigFromServer,
loaded: configLoaded,
loadError: fetchConfigError,
} = useApplicationSettings();

const { dashboardConfig, ...devFeatureFlagsProps } =
useDevFeatureFlags(dashboardConfigFromServer);

const [storageClasses] = useStorageClasses();

useDetectUser();
Expand Down Expand Up @@ -115,6 +120,10 @@ const App: React.FC = () => {
data-testid={DASHBOARD_MAIN_CONTAINER_ID}
>
<ErrorBoundary>
<DevFeatureFlagsBanner
dashboardConfig={dashboardConfig.spec.dashboardConfig}
{...devFeatureFlagsProps}
/>
<ProjectsContextProvider>
<QuickStarts>
<AppRoutes />
Expand Down
Loading

0 comments on commit fce1bf5

Please sign in to comment.