add cronjobs/jobs to k8s tab
Signed-off-by: Andrew Tran <atran@brex.com>
This commit is contained in:
@@ -42,6 +42,7 @@
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.57",
|
||||
"cronstrue": "^1.122.0",
|
||||
"js-yaml": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^2.0.2",
|
||||
|
||||
@@ -29,6 +29,7 @@ import { DeploymentsAccordions } from '../DeploymentsAccordions';
|
||||
import { groupResponses } from '../../utils/response';
|
||||
import { IngressesAccordions } from '../IngressesAccordions';
|
||||
import { ServicesAccordions } from '../ServicesAccordions';
|
||||
import { CronJobsAccordions } from '../CronJobsAccordions';
|
||||
import { CustomResources } from '../CustomResources';
|
||||
import {
|
||||
ClusterContext,
|
||||
@@ -133,6 +134,9 @@ export const Cluster = ({ clusterObjects, podsWithErrors }: ClusterProps) => {
|
||||
<Grid item>
|
||||
<ServicesAccordions />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<CronJobsAccordions />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React, { useContext } from 'react';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Divider,
|
||||
Grid,
|
||||
} from '@material-ui/core';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import { V1CronJob, V1Job } from '@kubernetes/client-node';
|
||||
import { JobsAccordions } from '../JobsAccordions';
|
||||
import { CronJobDrawer } from './CronJobsDrawer';
|
||||
import { getOwnedResources } from '../../utils/owner';
|
||||
import { GroupedResponsesContext } from '../../hooks';
|
||||
import { StatusError, StatusOK } from '@backstage/core-components';
|
||||
import cronstrue from 'cronstrue';
|
||||
|
||||
type CronJobsAccordionsProps = {
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
type CronJobAccordionProps = {
|
||||
cronJob: V1CronJob;
|
||||
ownedJobs: V1Job[];
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
type CronJobSummaryProps = {
|
||||
cronJob: V1CronJob;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
const CronJobSummary = ({ cronJob }: CronJobSummaryProps) => {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid xs={3} item>
|
||||
<CronJobDrawer cronJob={cronJob} />
|
||||
</Grid>
|
||||
<Grid item xs={1}>
|
||||
<Divider style={{ height: '5em' }} orientation="vertical" />
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
xs={5}
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<Grid item>
|
||||
{cronJob.spec?.suspend ? (
|
||||
<StatusError>Suspended</StatusError>
|
||||
) : (
|
||||
<StatusOK>Active</StatusOK>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item>
|
||||
Schedule:{' '}
|
||||
{cronJob.spec?.schedule
|
||||
? `${cronJob.spec.schedule} (
|
||||
${cronstrue.toString(cronJob.spec.schedule)})`
|
||||
: '???'}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const CronJobAccordion = ({ cronJob, ownedJobs }: CronJobAccordionProps) => {
|
||||
return (
|
||||
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<CronJobSummary cronJob={cronJob} />
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<JobsAccordions jobs={ownedJobs.reverse()} />
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export const CronJobsAccordions = ({}: CronJobsAccordionsProps) => {
|
||||
const groupedResponses = useContext(GroupedResponsesContext);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
{groupedResponses.cronJobs.map((cronJob, i) => (
|
||||
<Grid container item key={i} xs>
|
||||
<Grid item xs>
|
||||
<CronJobAccordion
|
||||
ownedJobs={getOwnedResources(cronJob, groupedResponses.jobs)}
|
||||
cronJob={cronJob}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { V1CronJob } from '@kubernetes/client-node';
|
||||
import { KubernetesDrawer } from '../KubernetesDrawer/KubernetesDrawer';
|
||||
import { Typography, Grid, Chip } from '@material-ui/core';
|
||||
|
||||
export const CronJobDrawer = ({
|
||||
cronJob,
|
||||
expanded,
|
||||
}: {
|
||||
cronJob: V1CronJob;
|
||||
expanded?: boolean;
|
||||
}) => {
|
||||
const namespace = cronJob.metadata?.namespace;
|
||||
return (
|
||||
<KubernetesDrawer
|
||||
object={cronJob}
|
||||
expanded={expanded}
|
||||
kind="CronJob"
|
||||
renderObject={(cronJobObj: V1CronJob) => ({
|
||||
schedule: cronJobObj.spec?.schedule ?? '???',
|
||||
startingDeadlineSeconds:
|
||||
cronJobObj.spec?.startingDeadlineSeconds ?? '???',
|
||||
concurrencyPolicy: cronJobObj.spec?.concurrencyPolicy ?? '???',
|
||||
lastScheduleTime: cronJobObj.status?.lastScheduleTime ?? '???',
|
||||
})}
|
||||
>
|
||||
<Grid
|
||||
container
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
spacing={0}
|
||||
>
|
||||
<Grid item>
|
||||
<Typography variant="h5">
|
||||
{cronJob.metadata?.name ?? 'unknown object'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography color="textSecondary" variant="body1">
|
||||
CronJob
|
||||
</Typography>
|
||||
</Grid>
|
||||
{namespace && (
|
||||
<Grid item>
|
||||
<Chip size="small" label={`namespace: ${namespace}`} />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</KubernetesDrawer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { CronJobsAccordions } from './CronJobsAccordions';
|
||||
@@ -36,11 +36,12 @@ import {
|
||||
getOwnedPodsThroughReplicaSets,
|
||||
getMatchingHpa,
|
||||
} from '../../utils/owner';
|
||||
import { containersReady, totalRestarts } from '../../utils/pod';
|
||||
import {
|
||||
GroupedResponsesContext,
|
||||
PodNamesWithErrorsContext,
|
||||
} from '../../hooks';
|
||||
import { StatusError, StatusOK } from '@backstage/core-components';
|
||||
import { StatusError, StatusOK, TableColumn } from '@backstage/core-components';
|
||||
|
||||
type DeploymentsAccordionsProps = {
|
||||
children?: React.ReactNode;
|
||||
@@ -61,6 +62,20 @@ type DeploymentSummaryProps = {
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
const deploymentPodColumns: TableColumn<V1Pod>[] = [
|
||||
{
|
||||
title: 'containers ready',
|
||||
align: 'center',
|
||||
render: containersReady,
|
||||
},
|
||||
{
|
||||
title: 'total restarts',
|
||||
align: 'center',
|
||||
render: totalRestarts,
|
||||
type: 'numeric',
|
||||
},
|
||||
];
|
||||
|
||||
const DeploymentSummary = ({
|
||||
deployment,
|
||||
numberOfCurrentPods,
|
||||
@@ -161,7 +176,7 @@ const DeploymentAccordion = ({
|
||||
/>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<PodsTable pods={ownedPods} />
|
||||
<PodsTable pods={ownedPods} extraColumns={deploymentPodColumns} />
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React, { useContext } from 'react';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Divider,
|
||||
Grid,
|
||||
} from '@material-ui/core';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import { V1Job, V1Pod } from '@kubernetes/client-node';
|
||||
import { PodsTable } from '../Pods';
|
||||
import { JobDrawer } from './JobsDrawer';
|
||||
import { getOwnedResources } from '../../utils/owner';
|
||||
import { GroupedResponsesContext } from '../../hooks';
|
||||
import {
|
||||
StatusError,
|
||||
StatusOK,
|
||||
StatusPending,
|
||||
} from '@backstage/core-components';
|
||||
|
||||
type JobsAccordionsProps = {
|
||||
jobs: V1Job[];
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
type JobAccordionProps = {
|
||||
job: V1Job;
|
||||
ownedPods: V1Pod[];
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
type JobSummaryProps = {
|
||||
job: V1Job;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
const JobSummary = ({ job }: JobSummaryProps) => {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid xs={3} item>
|
||||
<JobDrawer job={job} />
|
||||
</Grid>
|
||||
<Grid item xs={1}>
|
||||
<Divider style={{ height: '5em' }} orientation="vertical" />
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
xs={8}
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<Grid item>
|
||||
{job.status?.succeeded && <StatusOK>Succeeded</StatusOK>}
|
||||
{job.status?.active && <StatusPending>Running</StatusPending>}
|
||||
{job.status?.failed && <StatusError>Failed</StatusError>}
|
||||
</Grid>
|
||||
<Grid item>Start time: {job.status?.startTime}</Grid>
|
||||
{job.status?.completionTime && (
|
||||
<Grid item>Completion time: {job.status.completionTime}</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const JobAccordion = ({ job, ownedPods }: JobAccordionProps) => {
|
||||
return (
|
||||
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<JobSummary job={job} />
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<PodsTable pods={ownedPods} />
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export const JobsAccordions = ({ jobs }: JobsAccordionsProps) => {
|
||||
const groupedResponses = useContext(GroupedResponsesContext);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
{jobs.map((job, i) => (
|
||||
<Grid container item key={i} xs>
|
||||
<Grid item xs>
|
||||
<JobAccordion
|
||||
ownedPods={getOwnedResources(job, groupedResponses.pods)}
|
||||
job={job}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { V1Job } from '@kubernetes/client-node';
|
||||
import { KubernetesDrawer } from '../KubernetesDrawer/KubernetesDrawer';
|
||||
import { Typography, Grid } from '@material-ui/core';
|
||||
|
||||
export const JobDrawer = ({
|
||||
job,
|
||||
expanded,
|
||||
}: {
|
||||
job: V1Job;
|
||||
expanded?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<KubernetesDrawer
|
||||
object={job}
|
||||
expanded={expanded}
|
||||
kind="Job"
|
||||
renderObject={(jobObj: V1Job) => {
|
||||
return {
|
||||
parallelism: jobObj.spec?.parallelism ?? '???',
|
||||
completions: jobObj.spec?.completions ?? '???',
|
||||
backOffLimit: jobObj.spec?.backoffLimit ?? '???',
|
||||
startTime: jobObj.status?.startTime ?? '???',
|
||||
};
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
container
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
spacing={0}
|
||||
>
|
||||
<Grid item>
|
||||
<Typography variant="h6">
|
||||
{job.metadata?.name ?? 'unknown object'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography color="textSecondary" variant="body1">
|
||||
Job
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</KubernetesDrawer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { JobsAccordions } from './JobsAccordions';
|
||||
+6
-20
@@ -17,14 +17,10 @@
|
||||
import React from 'react';
|
||||
import { V1Pod } from '@kubernetes/client-node';
|
||||
import { PodDrawer } from './PodDrawer';
|
||||
import {
|
||||
containersReady,
|
||||
containerStatuses,
|
||||
totalRestarts,
|
||||
} from '../../utils/pod';
|
||||
import { containerStatuses } from '../../utils/pod';
|
||||
import { Table, TableColumn } from '@backstage/core-components';
|
||||
|
||||
const columns: TableColumn<V1Pod>[] = [
|
||||
const DEFAULT_COLUMNS: TableColumn<V1Pod>[] = [
|
||||
{
|
||||
title: 'name',
|
||||
highlight: true,
|
||||
@@ -34,29 +30,19 @@ const columns: TableColumn<V1Pod>[] = [
|
||||
title: 'phase',
|
||||
render: (pod: V1Pod) => pod.status?.phase ?? 'unknown',
|
||||
},
|
||||
{
|
||||
title: 'containers ready',
|
||||
align: 'center',
|
||||
render: containersReady,
|
||||
},
|
||||
{
|
||||
title: 'total restarts',
|
||||
align: 'center',
|
||||
render: totalRestarts,
|
||||
type: 'numeric',
|
||||
},
|
||||
{
|
||||
title: 'status',
|
||||
render: containerStatuses,
|
||||
},
|
||||
];
|
||||
|
||||
type DeploymentTablesProps = {
|
||||
type PodsTablesProps = {
|
||||
pods: V1Pod[];
|
||||
extraColumns?: TableColumn<V1Pod>[];
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const PodsTable = ({ pods }: DeploymentTablesProps) => {
|
||||
export const PodsTable = ({ pods, extraColumns = [] }: PodsTablesProps) => {
|
||||
const tableStyle = {
|
||||
minWidth: '0',
|
||||
width: '100%',
|
||||
@@ -67,7 +53,7 @@ export const PodsTable = ({ pods }: DeploymentTablesProps) => {
|
||||
<Table
|
||||
options={{ paging: true, search: false }}
|
||||
data={pods}
|
||||
columns={columns}
|
||||
columns={DEFAULT_COLUMNS.concat(extraColumns)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -24,5 +24,7 @@ export const GroupedResponsesContext = React.createContext<GroupedResponses>({
|
||||
configMaps: [],
|
||||
horizontalPodAutoscalers: [],
|
||||
ingresses: [],
|
||||
jobs: [],
|
||||
cronJobs: [],
|
||||
customResources: [],
|
||||
});
|
||||
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
V1Service,
|
||||
V1ConfigMap,
|
||||
ExtensionsV1beta1Ingress,
|
||||
V1Job,
|
||||
V1CronJob,
|
||||
} from '@kubernetes/client-node';
|
||||
|
||||
export interface DeploymentResources {
|
||||
@@ -35,6 +37,8 @@ export interface GroupedResponses extends DeploymentResources {
|
||||
services: V1Service[];
|
||||
configMaps: V1ConfigMap[];
|
||||
ingresses: ExtensionsV1beta1Ingress[];
|
||||
jobs: V1Job[];
|
||||
cronJobs: V1CronJob[];
|
||||
customResources: any[];
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ export const groupResponses = (
|
||||
case 'ingresses':
|
||||
prev.ingresses.push(...next.resources);
|
||||
break;
|
||||
case 'jobs':
|
||||
prev.jobs.push(...next.resources);
|
||||
break;
|
||||
case 'cronjobs':
|
||||
prev.cronJobs.push(...next.resources);
|
||||
break;
|
||||
case 'customresources':
|
||||
prev.customResources.push(...next.resources);
|
||||
break;
|
||||
@@ -60,6 +66,8 @@ export const groupResponses = (
|
||||
configMaps: [],
|
||||
horizontalPodAutoscalers: [],
|
||||
ingresses: [],
|
||||
jobs: [],
|
||||
cronJobs: [],
|
||||
customResources: [],
|
||||
} as GroupedResponses,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user