Skip to content

Commit

Permalink
frontend: Refactor KubeObject class, deprecate makeKubeObject
Browse files Browse the repository at this point in the history
Signed-off-by: Oleksandr Dubenko <[email protected]>
  • Loading branch information
sniok committed Oct 24, 2024
1 parent c6fc8b3 commit 58c51a5
Show file tree
Hide file tree
Showing 114 changed files with 1,367 additions and 1,258 deletions.
2 changes: 1 addition & 1 deletion frontend/src/components/App/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ function HomeComponent(props: HomeComponentProps) {
/>
}
>
<ResourceTable
<ResourceTable<any>
defaultSortingColumn={{ id: 'name', desc: false }}
columns={[
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react';
import { Provider } from 'react-redux';
import { useDispatch } from 'react-redux';
import { MemoryRouter } from 'react-router-dom';
import { makeMockKubeObject } from '../../test/mocker';
import { SectionBox } from '../common';
import DetailsViewSection, { DetailsViewSectionProps } from './DetailsViewSection';
import { setDetailsView } from './detailsViewSectionSlice';
Expand Down Expand Up @@ -58,10 +59,10 @@ const Template: StoryFn<DetailsViewSectionProps> = args => {

export const MatchRenderIt = Template.bind({});
MatchRenderIt.args = {
resource: { kind: 'Node' },
resource: makeMockKubeObject({ kind: 'Node' }),
};

export const NoMatchNoRender = Template.bind({});
NoMatchNoRender.args = {
resource: { kind: 'DoesNotExist' },
resource: makeMockKubeObject({ kind: 'DoesNotExist' }),
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { isValidElement, ReactElement, ReactNode, useMemo } from 'react';
import { KubeObject } from '../../lib/k8s/cluster';
import { KubeObject } from '../../lib/k8s/KubeObject';
import { HeadlampEventType, useEventCallback } from '../../redux/headlampEventSlice';
import { useTypedSelector } from '../../redux/reducers/reducers';
import ErrorBoundary from '../common/ErrorBoundary';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import detailsViewSectionReducer, {
addDetailsViewSectionsProcessor,
DefaultDetailsViewSection,
DetailsViewSection,
DetailsViewSectionProcessorType,
DetailsViewsSectionProcessor,
setDetailsView,
setDetailsViewSection,
Expand Down Expand Up @@ -67,7 +66,7 @@ describe('detailsViewSectionSlice', () => {
it('should add a new details view sections processor when provided as an object', () => {
const processor: DetailsViewsSectionProcessor = {
id: 'test-processor',
processor: info => info.actions,
processor: () => [{ id: 'test-section' }],
};
store.dispatch(addDetailsViewSectionsProcessor(processor));
expect(store.getState().detailsViewSection.detailsViewSectionsProcessors).toEqual([
Expand All @@ -76,7 +75,7 @@ describe('detailsViewSectionSlice', () => {
});

it('should add a new details view sections processor when provided as a function', () => {
const processorFunc: DetailsViewSectionProcessorType = info => info.actions;
const processorFunc = () => [{ id: 'test-section' }];
store.dispatch(addDetailsViewSectionsProcessor(processorFunc));
const processors = store.getState().detailsViewSection.detailsViewSectionsProcessors;
expect(processors).toHaveLength(1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { get, set } from 'lodash';
import { KubeObject } from '../../lib/k8s/cluster';
import { ReactNode } from 'react';
import { KubeObject } from '../../lib/k8s/KubeObject';
import { DetailsViewSectionType } from './DetailsViewSection';

export type DetailsViewSection = {
Expand All @@ -20,7 +21,7 @@ export enum DefaultDetailsViewSection {

type HeaderActionFuncType = (
resource: KubeObject | null,
sections: DetailsViewSection[]
sections: (DetailsViewSection | ReactNode)[]
) => DetailsViewSection[];

export type DetailsViewsSectionProcessor = {
Expand Down
14 changes: 7 additions & 7 deletions frontend/src/components/cluster/Charts.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import '../../i18n/config';
import { useTheme } from '@mui/material/styles';
import { useTranslation } from 'react-i18next';
import { KubeObject } from '../../lib/k8s/cluster';
import { KubeMetrics } from '../../lib/k8s/cluster';
import Node from '../../lib/k8s/node';
import Pod from '../../lib/k8s/pod';
import { parseCpu, parseRam, TO_GB, TO_ONE_CPU } from '../../lib/units';
Expand All @@ -14,11 +14,11 @@ export function MemoryCircularChart(props: ResourceCircularChartProps) {
const { noMetrics } = props;
const { t } = useTranslation(['translation', 'glossary']);

function memoryUsedGetter(item: KubeObject) {
function memoryUsedGetter(item: KubeMetrics) {
return parseRam(item.usage.memory) / TO_GB;
}

function memoryAvailableGetter(item: KubeObject) {
function memoryAvailableGetter(item: Node | Pod) {
return parseRam(item.status!.capacity.memory) / TO_GB;
}

Expand Down Expand Up @@ -50,11 +50,11 @@ export function CpuCircularChart(props: ResourceCircularChartProps) {
const { noMetrics } = props;
const { t } = useTranslation(['translation', 'glossary']);

function cpuUsedGetter(item: KubeObject) {
function cpuUsedGetter(item: KubeMetrics) {
return parseCpu(item.usage.cpu) / TO_ONE_CPU;
}

function cpuAvailableGetter(item: KubeObject) {
function cpuAvailableGetter(item: Node | Pod) {
return parseCpu(item.status!.capacity.cpu) / TO_ONE_CPU;
}

Expand Down Expand Up @@ -82,7 +82,7 @@ export function CpuCircularChart(props: ResourceCircularChartProps) {
);
}

export function PodsStatusCircleChart(props: Pick<ResourceCircularChartProps, 'items'>) {
export function PodsStatusCircleChart(props: { items: Pod[] | null }) {
const theme = useTheme();
const { items } = props;
const { t } = useTranslation(['translation', 'glossary']);
Expand Down Expand Up @@ -143,7 +143,7 @@ export function PodsStatusCircleChart(props: Pick<ResourceCircularChartProps, 'i
);
}

export function NodesStatusCircleChart(props: Pick<ResourceCircularChartProps, 'items'>) {
export function NodesStatusCircleChart(props: { items: Node[] | null }) {
const theme = useTheme();
const { items } = props;
const { t } = useTranslation(['translation', 'glossary']);
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/common/Link.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import MuiLink from '@mui/material/Link';
import React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import { makeKubeObject } from '../../lib/k8s/cluster';
import { KubeObject } from '../../lib/k8s/KubeObject';
import { createRouteURL, RouteURLProps } from '../../lib/router';
import { LightTooltip } from './Tooltip';

Expand All @@ -24,16 +24,16 @@ export interface LinkProps extends LinkBaseProps {
}

export interface LinkObjectProps extends LinkBaseProps {
kubeObject: InstanceType<ReturnType<typeof makeKubeObject>>;
kubeObject?: KubeObject | null;
[prop: string]: any;
}

function PureLink(props: React.PropsWithChildren<LinkProps | LinkObjectProps>) {
if ((props as LinkObjectProps).kubeObject) {
const { kubeObject, ...otherProps } = props as LinkObjectProps;
return (
<MuiLink component={RouterLink} to={kubeObject.getDetailsLink()} {...otherProps}>
{props.children || kubeObject.getName()}
<MuiLink component={RouterLink} to={kubeObject!.getDetailsLink()} {...otherProps}>
{props.children || kubeObject!.getName()}
</MuiLink>
);
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/common/ObjectEventList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { KubeObject } from '../../lib/k8s/cluster';
import Event, { KubeEvent } from '../../lib/k8s/event';
import { KubeObject } from '../../lib/k8s/KubeObject';
import { localeDate, timeAgo } from '../../lib/util';
import { HeadlampEventType, useEventCallback } from '../../redux/headlampEventSlice';
import { HoverInfoLabel, SectionBox, SimpleTable } from '../common';
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/components/common/Resource/AuthVisible.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useQuery } from '@tanstack/react-query';
import React, { useEffect } from 'react';
import { KubeObject } from '../../../lib/k8s/cluster';
import { KubeObject } from '../../../lib/k8s/KubeObject';
import { KubeObjectClass } from '../../../lib/k8s/KubeObject';

export interface AuthVisibleProps extends React.PropsWithChildren<{}> {
/** The item for which auth will be checked or a resource class (e.g. Job). */
item: KubeObject;
item: KubeObject | KubeObjectClass | null;
/** The verb associated with the permissions being verifying. See https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb . */
authVerb: string;
/** The subresource for which the permissions are being verifyied (e.g. "log" when checking for a pod's log). */
Expand All @@ -31,7 +32,7 @@ export default function AuthVisible(props: AuthVisibleProps) {
queryKey: ['authVisible', item, authVerb, subresource, namespace],
queryFn: async () => {
try {
const res = await item.getAuthorization(authVerb, { subresource, namespace });
const res = await item!.getAuthorization(authVerb, { subresource, namespace });
return res;
} catch (e: any) {
onError?.(e);
Expand Down
15 changes: 9 additions & 6 deletions frontend/src/components/common/Resource/CircularChart.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import '../../../i18n/config';
import _ from 'lodash';
import _, { List } from 'lodash';
import { useTranslation } from 'react-i18next';
import { KubeMetrics, KubeObject } from '../../../lib/k8s/cluster';
import { KubeMetrics } from '../../../lib/k8s/cluster';
import { KubeObject } from '../../../lib/k8s/KubeObject';
import Node from '../../../lib/k8s/node';
import Pod from '../../../lib/k8s/pod';
import { PercentageCircleProps } from '../Chart';
import TileChart from '../TileChart';

export interface CircularChartProps extends Omit<PercentageCircleProps, 'data'> {
/** Items to display in the chart (should have a corresponding value in @param itemsMetrics) */
items: KubeObject[] | null;
items: Node[] | Pod[] | null;
/** Metrics to display in the chart (for items in @param items) */
itemsMetrics: KubeMetrics[] | null;
/** Whether no metrics are available. If true, then instead of a chart, a message will be displayed */
noMetrics?: boolean;
/** Function to get the "used" value for the metrics in question */
resourceUsedGetter?: (node: KubeObject) => number;
resourceUsedGetter?: (node: KubeMetrics) => number;
/** Function to get the "available" value for the metrics in question */
resourceAvailableGetter?: (node: KubeObject) => number;
resourceAvailableGetter?: (node: Node | Pod) => number;
/** Function to create a legend for the data */
getLegend?: (used: number, available: number) => string;
/** Tooltip to display when hovering over the chart */
Expand Down Expand Up @@ -56,7 +59,7 @@ export function CircularChart(props: CircularChartProps) {

const nodeMetrics = filterMetrics(items, itemsMetrics);
const usedValue = _.sumBy(nodeMetrics, resourceUsedGetter);
const availableValue = _.sumBy(items, resourceAvailableGetter);
const availableValue = _.sumBy(items as List<Node | Pod>, resourceAvailableGetter);

return [usedValue, availableValue];
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/common/Resource/CreateButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { useClusterGroup } from '../../../lib/k8s';
import { apply } from '../../../lib/k8s/apiProxy';
import { KubeObjectInterface } from '../../../lib/k8s/cluster';
import { KubeObjectInterface } from '../../../lib/k8s/KubeObject';
import { clusterAction } from '../../../redux/clusterActionSlice';
import {
EventStatus,
Expand Down Expand Up @@ -83,7 +83,7 @@ export default function CreateButton(props: CreateButtonProps) {
if (massagedNewItemDefs[i].kind === 'List') {
// flatten this List kind with the items that it has which is a list of valid k8s resources
const deletedItem = massagedNewItemDefs.splice(i, 1);
massagedNewItemDefs = massagedNewItemDefs.concat(deletedItem[0].items);
massagedNewItemDefs = massagedNewItemDefs.concat(deletedItem[0].items!);
}
if (!massagedNewItemDefs[i].metadata?.name) {
setErrorMessage(
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/common/Resource/DeleteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { KubeObject } from '../../../lib/k8s/cluster';
import { KubeObject } from '../../../lib/k8s/KubeObject';
import { CallbackActionOptions, clusterAction } from '../../../redux/clusterActionSlice';
import {
EventStatus,
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/common/Resource/EditButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { KubeObject, KubeObjectInterface } from '../../../lib/k8s/cluster';
import { KubeObject } from '../../../lib/k8s/KubeObject';
import { KubeObjectInterface } from '../../../lib/k8s/KubeObject';
import { CallbackActionOptions, clusterAction } from '../../../redux/clusterActionSlice';
import {
EventStatus,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/common/Resource/EditorDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { KubeObjectInterface } from '../../../lib/k8s/cluster';
import { KubeObjectInterface } from '../../../lib/k8s/KubeObject';
import { getThemeName } from '../../../lib/themes';
import { useId } from '../../../lib/util';
import ConfirmButton from '../ConfirmButton';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Paper from '@mui/material/Paper';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom';
import { KubeObject } from '../../../../lib/k8s/cluster';
import { KubeObject } from '../../../../lib/k8s/KubeObject';
import { createRouteURL } from '../../../../lib/router';
import { HeaderAction } from '../../../../redux/actionButtonsSlice';
import Loader from '../../../common/Loader';
Expand All @@ -13,16 +13,13 @@ import SectionBox from '../../SectionBox';
import { MetadataDisplay } from '../MetadataDisplay';
import { MainInfoHeader } from './MainInfoSectionHeader';

export interface MainInfoSectionProps {
resource: KubeObject | null;
headerSection?: ((resource: KubeObject | null) => React.ReactNode) | React.ReactNode;
export interface MainInfoSectionProps<T extends KubeObject = KubeObject> {
resource: T | null;
headerSection?: ((resource: T | null) => React.ReactNode) | React.ReactNode;
title?: string;
extraInfo?:
| ((resource: KubeObject | null) => NameValueTableRow[] | null)
| NameValueTableRow[]
| null;
extraInfo?: ((resource: T | null) => NameValueTableRow[] | null) | NameValueTableRow[] | null;
actions?:
| ((resource: KubeObject | null) => React.ReactNode[] | HeaderAction[] | null)
| ((resource: T | null) => React.ReactNode[] | HeaderAction[] | null)
| React.ReactNode[]
| null
| HeaderAction[];
Expand All @@ -33,7 +30,7 @@ export interface MainInfoSectionProps {
error?: string | Error | null;
}

export function MainInfoSection(props: MainInfoSectionProps) {
export function MainInfoSection<T extends KubeObject>(props: MainInfoSectionProps<T>) {
const {
resource,
headerSection,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { has } from 'lodash';
import React, { isValidElement } from 'react';
import { useLocation } from 'react-router-dom';
import { KubeObject } from '../../../../lib/k8s/cluster';
import { KubeObject } from '../../../../lib/k8s/KubeObject';
import {
DefaultHeaderAction,
HeaderAction,
Expand All @@ -15,12 +15,12 @@ import EditButton from '../EditButton';
import { RestartButton } from '../RestartButton';
import ScaleButton from '../ScaleButton';

export interface MainInfoHeaderProps {
resource: KubeObject | null;
headerSection?: ((resource: KubeObject | null) => React.ReactNode) | React.ReactNode;
export interface MainInfoHeaderProps<T extends KubeObject> {
resource: T | null;
headerSection?: ((resource: T | null) => React.ReactNode) | React.ReactNode;
title?: string;
actions?:
| ((resource: KubeObject | null) => React.ReactNode[] | HeaderAction[] | null)
| ((resource: T | null) => React.ReactNode[] | HeaderAction[] | null)
| React.ReactNode[]
| null
| HeaderAction[];
Expand All @@ -30,7 +30,7 @@ export interface MainInfoHeaderProps {
backLink?: string | ReturnType<typeof useLocation> | null;
}

export function MainInfoHeader(props: MainInfoHeaderProps) {
export function MainInfoHeader<T extends KubeObject>(props: MainInfoHeaderProps<T>) {
const { resource, title, actions = [], headerStyle = 'main', noDefaultActions = false } = props;
const headerActions = useTypedSelector(state => state.actionButtons.headerActions);
const headerActionsProcessors = useTypedSelector(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Meta, StoryFn } from '@storybook/react';
import { KubeObjectInterface } from '../../../lib/k8s/cluster';
import { KubeObjectInterface } from '../../../lib/k8s/KubeObject';
import { TestContext } from '../../../test';
import {
MetadataDisplay as MetadataDisplayComponent,
Expand All @@ -18,7 +18,7 @@ export default {
],
} as Meta;

const Template: StoryFn<MetadataDisplayProps> = args => <MetadataDisplayComponent {...args} />;
const Template: StoryFn<MetadataDisplayProps<any>> = args => <MetadataDisplayComponent {...args} />;

const mockResource: KubeObjectInterface = {
kind: 'MyKind',
Expand Down
Loading

0 comments on commit 58c51a5

Please sign in to comment.