diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 0c78649cf7..a127c9edf6 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -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", diff --git a/plugins/kubernetes/src/components/Cluster/Cluster.tsx b/plugins/kubernetes/src/components/Cluster/Cluster.tsx index d238396f53..416d6e23e3 100644 --- a/plugins/kubernetes/src/components/Cluster/Cluster.tsx +++ b/plugins/kubernetes/src/components/Cluster/Cluster.tsx @@ -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) => { + + + diff --git a/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx new file mode 100644 index 0000000000..992fa6c469 --- /dev/null +++ b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx @@ -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 ( + + + + + + + + + + {cronJob.spec?.suspend ? ( + Suspended + ) : ( + Active + )} + + + Schedule:{' '} + {cronJob.spec?.schedule + ? `${cronJob.spec.schedule} ( + ${cronstrue.toString(cronJob.spec.schedule)})` + : '???'} + + + + ); +}; + +const CronJobAccordion = ({ cronJob, ownedJobs }: CronJobAccordionProps) => { + return ( + + }> + + + + + + + ); +}; + +export const CronJobsAccordions = ({}: CronJobsAccordionsProps) => { + const groupedResponses = useContext(GroupedResponsesContext); + + return ( + + {groupedResponses.cronJobs.map((cronJob, i) => ( + + + + + + ))} + + ); +}; diff --git a/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsDrawer.tsx b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsDrawer.tsx new file mode 100644 index 0000000000..758f17ade0 --- /dev/null +++ b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsDrawer.tsx @@ -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 ( + ({ + schedule: cronJobObj.spec?.schedule ?? '???', + startingDeadlineSeconds: + cronJobObj.spec?.startingDeadlineSeconds ?? '???', + concurrencyPolicy: cronJobObj.spec?.concurrencyPolicy ?? '???', + lastScheduleTime: cronJobObj.status?.lastScheduleTime ?? '???', + })} + > + + + + {cronJob.metadata?.name ?? 'unknown object'} + + + + + CronJob + + + {namespace && ( + + + + )} + + + ); +}; diff --git a/plugins/kubernetes/src/components/CronJobsAccordions/index.ts b/plugins/kubernetes/src/components/CronJobsAccordions/index.ts new file mode 100644 index 0000000000..e72e3f7e6d --- /dev/null +++ b/plugins/kubernetes/src/components/CronJobsAccordions/index.ts @@ -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'; diff --git a/plugins/kubernetes/src/components/DeploymentsAccordions/DeploymentsAccordions.tsx b/plugins/kubernetes/src/components/DeploymentsAccordions/DeploymentsAccordions.tsx index e27c2bfbe1..2fa140b5f7 100644 --- a/plugins/kubernetes/src/components/DeploymentsAccordions/DeploymentsAccordions.tsx +++ b/plugins/kubernetes/src/components/DeploymentsAccordions/DeploymentsAccordions.tsx @@ -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[] = [ + { + 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 = ({ /> - + ); diff --git a/plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.tsx b/plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.tsx new file mode 100644 index 0000000000..5c95dfbb6d --- /dev/null +++ b/plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.tsx @@ -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 ( + + + + + + + + + + {job.status?.succeeded && Succeeded} + {job.status?.active && Running} + {job.status?.failed && Failed} + + Start time: {job.status?.startTime} + {job.status?.completionTime && ( + Completion time: {job.status.completionTime} + )} + + + ); +}; + +const JobAccordion = ({ job, ownedPods }: JobAccordionProps) => { + return ( + + }> + + + + + + + ); +}; + +export const JobsAccordions = ({ jobs }: JobsAccordionsProps) => { + const groupedResponses = useContext(GroupedResponsesContext); + + return ( + + {jobs.map((job, i) => ( + + + + + + ))} + + ); +}; diff --git a/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.tsx b/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.tsx new file mode 100644 index 0000000000..d81e099acc --- /dev/null +++ b/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.tsx @@ -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 ( + { + return { + parallelism: jobObj.spec?.parallelism ?? '???', + completions: jobObj.spec?.completions ?? '???', + backOffLimit: jobObj.spec?.backoffLimit ?? '???', + startTime: jobObj.status?.startTime ?? '???', + }; + }} + > + + + + {job.metadata?.name ?? 'unknown object'} + + + + + Job + + + + + ); +}; diff --git a/plugins/kubernetes/src/components/JobsAccordions/index.ts b/plugins/kubernetes/src/components/JobsAccordions/index.ts new file mode 100644 index 0000000000..392309de79 --- /dev/null +++ b/plugins/kubernetes/src/components/JobsAccordions/index.ts @@ -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'; diff --git a/plugins/kubernetes/src/components/Pods/PodsTable.tsx b/plugins/kubernetes/src/components/Pods/PodsTable.tsx index 7b5497ff37..8ce5f813b3 100644 --- a/plugins/kubernetes/src/components/Pods/PodsTable.tsx +++ b/plugins/kubernetes/src/components/Pods/PodsTable.tsx @@ -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[] = [ +const DEFAULT_COLUMNS: TableColumn[] = [ { title: 'name', highlight: true, @@ -34,29 +30,19 @@ const columns: TableColumn[] = [ 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[]; 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) => { ); diff --git a/plugins/kubernetes/src/hooks/GroupedResponses.ts b/plugins/kubernetes/src/hooks/GroupedResponses.ts index d000086b7c..35f7e10b5c 100644 --- a/plugins/kubernetes/src/hooks/GroupedResponses.ts +++ b/plugins/kubernetes/src/hooks/GroupedResponses.ts @@ -24,5 +24,7 @@ export const GroupedResponsesContext = React.createContext({ configMaps: [], horizontalPodAutoscalers: [], ingresses: [], + jobs: [], + cronJobs: [], customResources: [], }); diff --git a/plugins/kubernetes/src/types/types.ts b/plugins/kubernetes/src/types/types.ts index 05337086be..d6df99cb95 100644 --- a/plugins/kubernetes/src/types/types.ts +++ b/plugins/kubernetes/src/types/types.ts @@ -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[]; } diff --git a/plugins/kubernetes/src/utils/response.ts b/plugins/kubernetes/src/utils/response.ts index 97501bb102..cfe3d20580 100644 --- a/plugins/kubernetes/src/utils/response.ts +++ b/plugins/kubernetes/src/utils/response.ts @@ -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, ); diff --git a/yarn.lock b/yarn.lock index 1628c2eea1..8f92fd2316 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11978,6 +11978,11 @@ create-require@^1.1.0: resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +cronstrue@^1.122.0: + version "1.122.0" + resolved "https://registry.npmjs.org/cronstrue/-/cronstrue-1.122.0.tgz#bd6838077b476d28f61d381398b47b8c3912a126" + integrity sha512-PFuhZd+iPQQ0AWTXIEYX+t3nFGzBrWxmTWUKJOrsGRewaBSLKZ4I1f8s2kryU75nNxgyugZgiGh2OJsCTA/XlA== + cross-env@^7.0.0: version "7.0.3" resolved "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"