Skip to content

Commit

Permalink
Merge pull request #1967 from headlamp-k8s/kube-type
Browse files Browse the repository at this point in the history
frontend: Refactor KubeObject class
  • Loading branch information
joaquimrocha authored Oct 24, 2024
2 parents dd9697a + 5280cba commit bf8c8f0
Show file tree
Hide file tree
Showing 126 changed files with 1,562 additions and 1,436 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
2 changes: 1 addition & 1 deletion frontend/src/components/App/RouteSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ function PageTitle({
}

interface AuthRouteProps {
children: React.ReactNode | JSX.Element;
children: React.ReactNode;
sidebar: RouteType['sidebar'];
requiresAuth: boolean;
requiresCluster: boolean;
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 All @@ -8,7 +8,7 @@ export interface DetailsViewSectionProps {
resource: KubeObject;
}
export type DetailsViewSectionType =
| ((...args: any[]) => JSX.Element | null | ReactNode)
| ((...args: any[]) => ReactNode)
| null
| ReactElement
| ReactNode;
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/Chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Paper from '@mui/material/Paper';
import { useTheme } from '@mui/material/styles';
import { styled } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
import React from 'react';
import React, { ReactNode } from 'react';
import {
Bar,
BarChart,
Expand All @@ -28,8 +28,8 @@ export interface PercentageCircleProps {
size?: number;
dataKey?: string;
label?: string | null;
title?: string | JSX.Element | null;
legend?: string | null;
title?: ReactNode;
legend?: ReactNode;
total?: number;
totalProps?: {
[propName: string]: any;
Expand Down Expand Up @@ -170,7 +170,7 @@ const StyledBarChart = styled(BarChart)(({ theme }) => ({
export interface PercentageBarProps {
data: ChartDataPoint[];
total?: number;
tooltipFunc?: ((data: any) => JSX.Element | string) | null;
tooltipFunc?: ((data: any) => ReactNode) | null;
}

export function PercentageBar(props: PercentageBarProps) {
Expand Down
13 changes: 5 additions & 8 deletions frontend/src/components/common/ErrorBoundary/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { eventAction, HeadlampEventType } from '../../../redux/headlampEventSlic
import store from '../../../redux/stores/store';

export interface ErrorBoundaryProps {
fallback?: ComponentType<{ error: Error }> | ReactElement | null;
children: ReactNode;
fallback?: ReactElement<{ error: Error }> | ComponentType<{ error: Error }>;
children?: ReactNode;
}

interface State {
Expand Down Expand Up @@ -50,13 +50,10 @@ export default class ErrorBoundary extends Component<ErrorBoundaryProps, State>
if (!error) {
return this.props.children;
}
if (isValidElement(this.props.fallback)) {
return this.props.fallback;
const FallbackComponent = this.props.fallback;
if (isValidElement(FallbackComponent)) {
return FallbackComponent;
}
const FallbackComponent = this.props.fallback as
| ComponentType<{ error: Error }>
| undefined
| null;
return FallbackComponent ? <FallbackComponent error={error} /> : null;
}
}
2 changes: 1 addition & 1 deletion frontend/src/components/common/LabelListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { LightTooltip } from './Tooltip';

export interface LabelListItemProps {
labels: React.ReactNode[];
labels?: React.ReactNode[];
}

export default function LabelListItem(props: LabelListItemProps) {
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
4 changes: 2 additions & 2 deletions frontend/src/components/common/LogViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FitAddon } from '@xterm/addon-fit';
import { ISearchOptions, SearchAddon } from '@xterm/addon-search';
import { Terminal as XTerminal } from '@xterm/xterm';
import _ from 'lodash';
import React, { useEffect } from 'react';
import React, { ReactNode, useEffect } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import ActionButton from './ActionButton';
Expand All @@ -14,7 +14,7 @@ export interface LogViewerProps extends DialogProps {
title?: string;
downloadName?: string;
onClose: () => void;
topActions?: JSX.Element[];
topActions?: ReactNode[];
open: boolean;
xtermRef?: React.MutableRefObject<XTerminal | null>;
/**
Expand Down
13 changes: 5 additions & 8 deletions frontend/src/components/common/NameValueTable/NameValueTable.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Grid, GridProps } from '@mui/material';
import React from 'react';
import React, { ReactNode } from 'react';
import { ValueLabel } from '../Label';

// TODO: use ReactNode after migration to react 18
export interface NameValueTableRow {
/** The name (key) for this row */
name: string | JSX.Element;
name: ReactNode;
/** The value for this row */
value?: string | JSX.Element | JSX.Element[];
value?: ReactNode;
/** Whether this row should be hidden (can be a boolean or a function that will take the
* @param value and return a boolean) */
hide?: boolean | ((value: NameValueTableRow['value']) => boolean);
Expand All @@ -21,11 +22,7 @@ export interface NameValueTableProps {
valueCellProps?: GridProps;
}

function Value({
value,
}: {
value: string | JSX.Element | JSX.Element[] | undefined;
}): JSX.Element | null {
function Value({ value }: { value: ReactNode }): ReactNode {
if (typeof value === 'undefined') {
return null;
} else if (typeof value === 'string') {
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
Loading

0 comments on commit bf8c8f0

Please sign in to comment.