From c8f72b29eb190524c1c0456db514b838a0fd847a Mon Sep 17 00:00:00 2001 From: Andrew Tran Date: Tue, 23 Nov 2021 09:32:59 -0600 Subject: [PATCH 1/6] add cronjobs/jobs to k8s tab Signed-off-by: Andrew Tran --- plugins/kubernetes/package.json | 1 + .../src/components/Cluster/Cluster.tsx | 4 + .../CronJobsAccordions/CronJobsAccordions.tsx | 124 ++++++++++++++++++ .../CronJobsAccordions/CronJobsDrawer.tsx | 67 ++++++++++ .../components/CronJobsAccordions/index.ts | 16 +++ .../DeploymentsAccordions.tsx | 19 ++- .../JobsAccordions/JobsAccordions.tsx | 123 +++++++++++++++++ .../components/JobsAccordions/JobsDrawer.tsx | 62 +++++++++ .../src/components/JobsAccordions/index.ts | 16 +++ .../src/components/Pods/PodsTable.tsx | 26 +--- .../kubernetes/src/hooks/GroupedResponses.ts | 2 + plugins/kubernetes/src/types/types.ts | 4 + plugins/kubernetes/src/utils/response.ts | 8 ++ yarn.lock | 5 + 14 files changed, 455 insertions(+), 22 deletions(-) create mode 100644 plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx create mode 100644 plugins/kubernetes/src/components/CronJobsAccordions/CronJobsDrawer.tsx create mode 100644 plugins/kubernetes/src/components/CronJobsAccordions/index.ts create mode 100644 plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.tsx create mode 100644 plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.tsx create mode 100644 plugins/kubernetes/src/components/JobsAccordions/index.ts 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" From ec25c346765cb97f9e446c00a940e67cbccd0eda Mon Sep 17 00:00:00 2001 From: Andrew Tran Date: Tue, 23 Nov 2021 11:29:22 -0600 Subject: [PATCH 2/6] add a few tests Signed-off-by: Andrew Tran --- plugins/kubernetes/dev/index.tsx | 28 ++ .../src/__fixtures__/1-cronjobs.json | 446 ++++++++++++++++++ .../src/__fixtures__/2-cronjobs.json | 385 +++++++++++++++ .../CronJobsAccordions.test.tsx | 50 ++ .../CronJobsAccordions/CronJobsAccordions.tsx | 14 +- .../CronJobsDrawer.test.tsx | 38 ++ .../JobsAccordions/JobsAccordions.test.tsx | 42 ++ .../JobsAccordions/JobsAccordions.tsx | 6 +- .../JobsAccordions/JobsDrawer.test.tsx | 35 ++ 9 files changed, 1037 insertions(+), 7 deletions(-) create mode 100644 plugins/kubernetes/src/__fixtures__/1-cronjobs.json create mode 100644 plugins/kubernetes/src/__fixtures__/2-cronjobs.json create mode 100644 plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.test.tsx create mode 100644 plugins/kubernetes/src/components/CronJobsAccordions/CronJobsDrawer.test.tsx create mode 100644 plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.test.tsx create mode 100644 plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.test.tsx diff --git a/plugins/kubernetes/dev/index.tsx b/plugins/kubernetes/dev/index.tsx index 12de5f098f..c7a99e7334 100644 --- a/plugins/kubernetes/dev/index.tsx +++ b/plugins/kubernetes/dev/index.tsx @@ -29,6 +29,8 @@ import { } from '@backstage/plugin-kubernetes-common'; import fixture1 from '../src/__fixtures__/1-deployments.json'; import fixture2 from '../src/__fixtures__/2-deployments.json'; +import fixture3 from '../src/__fixtures__/1-cronjobs.json'; +import fixture4 from '../src/__fixtures__/2-cronjobs.json'; import { TestApiProvider } from '@backstage/test-utils'; const mockEntity: Entity = { @@ -102,5 +104,31 @@ createDevApp() ), }) + .addPage({ + path: '/fixture-3', + title: 'Fixture 3', + element: ( + + + + + + ), + }) + .addPage({ + path: '/fixture-4', + title: 'Fixture 4', + element: ( + + + + + + ), + }) .registerPlugin(kubernetesPlugin) .render(); diff --git a/plugins/kubernetes/src/__fixtures__/1-cronjobs.json b/plugins/kubernetes/src/__fixtures__/1-cronjobs.json new file mode 100644 index 0000000000..1edc43710d --- /dev/null +++ b/plugins/kubernetes/src/__fixtures__/1-cronjobs.json @@ -0,0 +1,446 @@ +{ + "cronJobs": [ + { + "metadata": { + "name": "dice-roller-cronjob", + "namespace": "default", + "uid": "9ea073bc-7a4b-4b99-8321-0305bce85568", + "resourceVersion": "1362732552", + "creationTimestamp": "2021-07-16T22:39:58Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "schedule": "30 5 * * *", + "startingDeadlineSeconds": 1800, + "concurrencyPolicy": "Forbid", + "suspend": false, + "jobTemplate": { + "metadata": { "creationTimestamp": null }, + "spec": { + "backoffLimit": 2, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "backstage.io/kubernetes-id": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "containers": [ + { + "name": "busybox", + "image": "busybox", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "IfNotPresent", + "command": ["/bin/sh", "-c", "date; echo Rolling a die!"] + } + ], + "restartPolicy": "Never", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + } + }, + "successfulJobsHistoryLimit": 2, + "failedJobsHistoryLimit": 2 + }, + "status": { + "active": [ + { + "kind": "Job", + "namespace": "default", + "name": "dice-roller-cronjob-1637028600", + "uid": "32be1b89-5b53-45b2-aa84-277e75214f61", + "apiVersion": "batch/v1", + "resourceVersion": "1361174163" + } + ], + "lastScheduleTime": "2021-11-16T02:10:00Z" + } + } + ], + "jobs": [ + { + "metadata": { + "name": "dice-roller-cronjob-1637025000", + "namespace": "default", + "uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a", + "resourceVersion": "1361029181", + "creationTimestamp": "2021-11-16T01:10:24Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller" + }, + "ownerReferences": [ + { + "apiVersion": "batch/v1", + "kind": "CronJob", + "name": "dice-roller-cronjob", + "uid": "9ea073bc-7a4b-4b99-8321-0305bce85568", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "parallelism": 1, + "completions": 1, + "backoffLimit": 2, + "selector": { + "matchLabels": { + "controller-uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "controller-uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a", + "job-name": "dice-roller-cronjob-1637025000" + }, + "annotations": {} + }, + "spec": { + "containers": [ + { + "command": ["/bin/sh", "-c", "date; echo Rolling a die!"], + "image": "busybox", + "imagePullPolicy": "IfNotPresent", + "name": "busybox", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File" + } + ], + "restartPolicy": "Never", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + }, + "status": { + "conditions": [ + { + "type": "Complete", + "status": "True", + "lastProbeTime": "2021-11-16T01:11:31Z", + "lastTransitionTime": "2021-11-16T01:11:31Z" + } + ], + "startTime": "2021-11-16T01:10:24Z", + "completionTime": "2021-11-16T01:11:31Z", + "succeeded": 1 + } + }, + { + "metadata": { + "name": "dice-roller-cronjob-1637028600", + "namespace": "default", + "uid": "32be1b89-5b53-45b2-aa84-277e75214f61", + "resourceVersion": "1361174166", + "creationTimestamp": "2021-11-16T02:10:22Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller" + }, + "ownerReferences": [ + { + "apiVersion": "batch/v1", + "kind": "CronJob", + "name": "dice-roller-cronjob", + "uid": "9ea073bc-7a4b-4b99-8321-0305bce85568", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "parallelism": 1, + "completions": 1, + "backoffLimit": 2, + "selector": { + "matchLabels": { + "controller-uid": "32be1b89-5b53-45b2-aa84-277e75214f61" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "controller-uid": "32be1b89-5b53-45b2-aa84-277e75214f61", + "job-name": "dice-roller-cronjob-1637028600" + }, + "annotations": {} + }, + "spec": { + "containers": [ + { + "command": ["/bin/sh", "-c", "date; echo Rolling a die!"], + "image": "busybox", + "imagePullPolicy": "IfNotPresent", + "name": "busybox", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File" + } + ], + "restartPolicy": "Never", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + }, + "status": { "startTime": "2021-11-16T02:10:22Z", "active": 1 } + } + ], + "pods": [ + { + "metadata": { + "name": "dice-roller-cronjob-1637025000-gstc4", + "generateName": "dice-roller-cronjob-1637025000-", + "namespace": "default", + "uid": "3b0f2b65-5ae2-441a-beda-bdc92bcafaf0", + "resourceVersion": "1361029179", + "creationTimestamp": "2021-11-16T01:10:24Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "controller-uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a", + "job-name": "dice-roller-cronjob-1637025000" + }, + "annotations": {}, + "ownerReferences": [ + { + "apiVersion": "batch/v1", + "kind": "Job", + "name": "dice-roller-cronjob-1637025000", + "uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "volumes": [], + "containers": [ + { + "command": ["/bin/sh", "-c", "date; echo Rolling a die!"], + "image": "busybox", + "imagePullPolicy": "IfNotPresent", + "name": "busybox", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File" + } + ], + "restartPolicy": "Never", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "default", + "serviceAccount": "default", + "nodeName": "minikube", + "securityContext": {}, + "schedulerName": "default-scheduler", + "tolerations": [ + { + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 300 + }, + { + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 300 + } + ], + "priority": 0, + "enableServiceLinks": true + }, + "status": { + "phase": "Succeeded", + "conditions": [ + { + "type": "Initialized", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2021-11-16T01:10:27Z", + "reason": "PodCompleted" + }, + { + "type": "Ready", + "status": "False", + "lastProbeTime": null, + "lastTransitionTime": "2021-11-16T01:11:31Z", + "reason": "PodCompleted" + }, + { + "type": "ContainersReady", + "status": "False", + "lastProbeTime": null, + "lastTransitionTime": "2021-11-16T01:11:31Z", + "reason": "PodCompleted" + }, + { + "type": "PodScheduled", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2021-11-16T01:10:24Z" + } + ], + "hostIP": "192.168.49.2", + "podIP": "172.17.0.25", + "podIPs": [{ "ip": "172.17.0.25" }], + "startTime": "2021-11-16T01:10:24Z", + "containerStatuses": [ + { + "name": "busybox", + "state": { + "terminated": { + "exitCode": 0, + "reason": "Completed", + "startedAt": "2021-11-16T01:10:31Z", + "finishedAt": "2021-11-16T01:11:30Z", + "containerID": "docker://1062343e1bb3625e36717bc15617c1bbca976366c3c9dd87035c71f33d015615" + } + }, + "lastState": {}, + "ready": false, + "restartCount": 0, + "image": "busybox:latest/node", + "imageID": "docker-pullable://busybox@sha256:e7157b6d7ebbe2cce5eaa8cfe8aa4fa82d173999b9f90a9ec42e57323546c353", + "containerID": "docker://1062343e1bb3625e36717bc15617c1bbca976366c3c9dd87035c71f33d015615", + "started": false + } + ], + "qosClass": "Burstable" + } + }, + { + "metadata": { + "name": "dice-roller-cronjob-1637028600-p4mlc", + "generateName": "dice-roller-cronjob-1637028600-", + "namespace": "default", + "uid": "acddd5d2-ac7f-473b-a9d8-17a89f99ac39", + "resourceVersion": "1361174579", + "creationTimestamp": "2021-11-16T02:10:22Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "controller-uid": "32be1b89-5b53-45b2-aa84-277e75214f61", + "job-name": "dice-roller-cronjob-1637028600" + }, + "annotations": {}, + "ownerReferences": [ + { + "apiVersion": "batch/v1", + "kind": "Job", + "name": "dice-roller-cronjob-1637028600", + "uid": "32be1b89-5b53-45b2-aa84-277e75214f61", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "volumes": [], + "containers": [ + { + "command": ["/bin/sh", "-c", "date; echo Rolling a die!"], + "image": "busybox", + "imagePullPolicy": "IfNotPresent", + "name": "busybox", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File" + } + ], + "restartPolicy": "Never", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "default", + "serviceAccount": "default", + "nodeName": "minikube", + "securityContext": {}, + "schedulerName": "default-scheduler", + "tolerations": [ + { + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 300 + }, + { + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 300 + } + ], + "priority": 0, + "enableServiceLinks": true + }, + "status": { + "phase": "Running", + "conditions": [ + { + "type": "Initialized", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2021-11-16T02:10:25Z" + }, + { + "type": "Ready", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2021-11-16T02:10:35Z" + }, + { + "type": "ContainersReady", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2021-11-16T02:10:35Z" + }, + { + "type": "PodScheduled", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2021-11-16T02:10:22Z" + } + ], + "hostIP": "192.168.49.2", + "podIP": "172.17.0.25", + "podIPs": [{ "ip": "172.17.0.25" }], + "startTime": "2021-11-16T02:10:22Z", + "containerStatuses": [ + { + "name": "busybox", + "state": { + "running": { "startedAt": "2021-11-16T02:10:31Z" } + }, + "lastState": {}, + "ready": true, + "restartCount": 0, + "image": "busybox:latest", + "imageID": "docker-pullable://busybox@sha256:e7157b6d7ebbe2cce5eaa8cfe8aa4fa82d173999b9f90a9ec42e57323546c353", + "containerID": "docker://1062343e1bb3625e36717bc15617c1bbca976366c3c9dd87035c71f33d015615", + "started": true + } + ], + "qosClass": "Burstable" + } + } + ] +} diff --git a/plugins/kubernetes/src/__fixtures__/2-cronjobs.json b/plugins/kubernetes/src/__fixtures__/2-cronjobs.json new file mode 100644 index 0000000000..6c087023b7 --- /dev/null +++ b/plugins/kubernetes/src/__fixtures__/2-cronjobs.json @@ -0,0 +1,385 @@ +{ + "cronJobs": [ + { + "metadata": { + "name": "dice-roller-cronjob", + "namespace": "default", + "uid": "9ea073bc-7a4b-4b99-8321-0305bce85568", + "resourceVersion": "1362732552", + "creationTimestamp": "2021-07-16T22:39:58Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "schedule": "* */2 * * *", + "startingDeadlineSeconds": 1800, + "concurrencyPolicy": "Forbid", + "suspend": true, + "jobTemplate": { + "metadata": { "creationTimestamp": null }, + "spec": { + "backoffLimit": 2, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "backstage.io/kubernetes-id": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "containers": [ + { + "name": "busybox", + "image": "busybox", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "IfNotPresent", + "command": ["/bin/sh", "-c", "date; echo Rolling a die!"] + } + ], + "restartPolicy": "Never", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + } + }, + "successfulJobsHistoryLimit": 2, + "failedJobsHistoryLimit": 2 + }, + "status": { + "lastScheduleTime": "2021-11-16T02:10:00Z" + } + } + ], + "jobs": [ + { + "metadata": { + "name": "dice-roller-cronjob-1637025000", + "namespace": "default", + "uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a", + "resourceVersion": "1361029181", + "creationTimestamp": "2021-11-16T01:10:24Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller" + }, + "ownerReferences": [ + { + "apiVersion": "batch/v1", + "kind": "CronJob", + "name": "dice-roller-cronjob", + "uid": "9ea073bc-7a4b-4b99-8321-0305bce85568", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "parallelism": 1, + "completions": 1, + "backoffLimit": 2, + "selector": { + "matchLabels": { + "controller-uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "controller-uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a", + "job-name": "dice-roller-cronjob-1637025000" + }, + "annotations": {} + }, + "spec": { + "containers": [ + { + "command": ["/bin/sh", "-c", "date; echo Rolling a die!"], + "image": "busybox", + "imagePullPolicy": "IfNotPresent", + "name": "busybox", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File" + } + ], + "restartPolicy": "Never", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + }, + "status": { + "conditions": [ + { + "type": "Failed", + "status": "True", + "reason": "BackoffLimitExceeded", + "lastProbeTime": "2021-11-16T01:11:31Z", + "lastTransitionTime": "2021-11-16T01:11:31Z" + } + ], + "startTime": "2021-11-16T01:10:24Z", + "failed": 2 + } + } + ], + "pods": [ + { + "metadata": { + "name": "dice-roller-cronjob-1637025000-gstc4", + "generateName": "dice-roller-cronjob-1637025000-", + "namespace": "default", + "uid": "3b0f2b65-5ae2-441a-beda-bdc92bcafaf0", + "resourceVersion": "1361029179", + "creationTimestamp": "2021-11-16T01:10:24Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "controller-uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a", + "job-name": "dice-roller-cronjob-1637025000" + }, + "annotations": {}, + "ownerReferences": [ + { + "apiVersion": "batch/v1", + "kind": "Job", + "name": "dice-roller-cronjob-1637025000", + "uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "volumes": [], + "containers": [ + { + "command": ["/bin/sh", "-c", "date; echo Rolling a die!"], + "image": "busybox", + "imagePullPolicy": "IfNotPresent", + "name": "busybox", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File" + } + ], + "restartPolicy": "Never", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "default", + "serviceAccount": "default", + "nodeName": "minikube", + "securityContext": {}, + "schedulerName": "default-scheduler", + "tolerations": [ + { + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 300 + }, + { + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 300 + } + ], + "priority": 0, + "enableServiceLinks": true + }, + "status": { + "phase": "Failed", + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2021-11-18T19:10:13Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2021-11-18T19:11:02Z", + "message": "containers with unready status: [busybox]", + "reason": "ContainersNotReady", + "status": "False", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2021-11-18T19:11:02Z", + "message": "containers with unready status: [busybox]", + "reason": "ContainersNotReady", + "status": "False", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2021-11-18T19:10:08Z", + "status": "True", + "type": "PodScheduled" + } + ], + "hostIP": "192.168.49.2", + "podIP": "172.17.0.25", + "podIPs": [{ "ip": "172.17.0.25" }], + "startTime": "2021-11-18T19:10:08Z", + "containerStatuses": [ + { + "name": "busybox", + "state": { + "terminated": { + "exitCode": 1, + "finishedAt": "2021-11-18T19:11:01Z", + "reason": "Error", + "startedAt": "2021-11-18T19:10:17Z", + "containerID": "docker://1062343e1bb3625e36717bc15617c1bbca976366c3c9dd87035c71f33d015615" + } + }, + "lastState": {}, + "ready": false, + "restartCount": 0, + "image": "busybox:latest", + "imageID": "docker-pullable://busybox@sha256:e7157b6d7ebbe2cce5eaa8cfe8aa4fa82d173999b9f90a9ec42e57323546c353", + "containerID": "docker://1062343e1bb3625e36717bc15617c1bbca976366c3c9dd87035c71f33d015615", + "started": false + } + ], + "qosClass": "Burstable" + } + }, + { + "metadata": { + "name": "dice-roller-cronjob-1637025000-p4mlc", + "generateName": "dice-roller-cronjob-1637025000-", + "namespace": "default", + "uid": "acddd5d2-ac7f-473b-a9d8-17a89f99ac39", + "resourceVersion": "1361174579", + "creationTimestamp": "2021-11-16T02:10:22Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "controller-uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a", + "job-name": "dice-roller-cronjob-1637025000" + }, + "annotations": {}, + "ownerReferences": [ + { + "apiVersion": "batch/v1", + "kind": "Job", + "name": "dice-roller-cronjob-1637025000", + "uid": "69d5d242-a9a4-47b8-b9c7-c536ae8f151a", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "volumes": [], + "containers": [ + { + "command": ["/bin/sh", "-c", "date; echo Rolling a die!"], + "image": "busybox", + "imagePullPolicy": "IfNotPresent", + "name": "busybox", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File" + } + ], + "restartPolicy": "Never", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "default", + "serviceAccount": "default", + "nodeName": "minikube", + "securityContext": {}, + "schedulerName": "default-scheduler", + "tolerations": [ + { + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 300 + }, + { + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 300 + } + ], + "priority": 0, + "enableServiceLinks": true + }, + "status": { + "phase": "Failed", + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2021-11-18T19:10:13Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2021-11-18T19:11:02Z", + "message": "containers with unready status: [busybox]", + "reason": "ContainersNotReady", + "status": "False", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2021-11-18T19:11:02Z", + "message": "containers with unready status: [busybox]", + "reason": "ContainersNotReady", + "status": "False", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2021-11-18T19:10:08Z", + "status": "True", + "type": "PodScheduled" + } + ], + "hostIP": "192.168.49.2", + "podIP": "172.17.0.25", + "podIPs": [{ "ip": "172.17.0.25" }], + "startTime": "2021-11-16T02:10:22Z", + "containerStatuses": [ + { + "name": "busybox", + "state": { + "terminated": { + "exitCode": 1, + "finishedAt": "2021-11-18T19:11:01Z", + "reason": "Error", + "startedAt": "2021-11-18T19:10:17Z", + "containerID": "docker://2659c4d0f8a68f2b49863c18738322f1686d5b87275428e5e641fd9fd9e06739" + } + }, + "lastState": {}, + "ready": true, + "restartCount": 0, + "image": "busybox:latest", + "imageID": "docker-pullable://busybox@sha256:e7157b6d7ebbe2cce5eaa8cfe8aa4fa82d173999b9f90a9ec42e57323546c353", + "containerID": "docker://2659c4d0f8a68f2b49863c18738322f1686d5b87275428e5e641fd9fd9e06739", + "started": true + } + ], + "qosClass": "Burstable" + } + } + ] +} diff --git a/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.test.tsx b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.test.tsx new file mode 100644 index 0000000000..2434631fd0 --- /dev/null +++ b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.test.tsx @@ -0,0 +1,50 @@ +/* + * 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 { render } from '@testing-library/react'; +import { CronJobsAccordions } from './CronJobsAccordions'; +import * as oneCronJobsFixture from '../../__fixtures__/1-cronjobs.json'; +import * as twoCronJobsFixture from '../../__fixtures__/2-cronjobs.json'; +import { wrapInTestApp } from '@backstage/test-utils'; +import { kubernetesProviders } from '../../hooks/test-utils'; + +describe('CronJobsAccordions', () => { + it('should render 1 active cronjobs', async () => { + const wrapper = kubernetesProviders(oneCronJobsFixture, []); + + const { getByText } = render( + wrapper(wrapInTestApp()), + ); + + expect(getByText('dice-roller-cronjob')).toBeInTheDocument(); + expect(getByText('CronJob')).toBeInTheDocument(); + expect(getByText('namespace: default')).toBeInTheDocument(); + expect(getByText('Active')).toBeInTheDocument(); + }); + + it('should render 1 suspended cronjobs', async () => { + const wrapper = kubernetesProviders(twoCronJobsFixture, []); + + const { getByText } = render( + wrapper(wrapInTestApp()), + ); + + expect(getByText('dice-roller-cronjob')).toBeInTheDocument(); + expect(getByText('CronJob')).toBeInTheDocument(); + expect(getByText('namespace: default')).toBeInTheDocument(); + expect(getByText('Suspended')).toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx index 992fa6c469..b136e2273c 100644 --- a/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx +++ b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx @@ -20,6 +20,7 @@ import { AccordionSummary, Divider, Grid, + Typography, } from '@material-ui/core'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import { V1CronJob, V1Job } from '@kubernetes/client-node'; @@ -75,11 +76,14 @@ const CronJobSummary = ({ cronJob }: CronJobSummaryProps) => { )} - Schedule:{' '} - {cronJob.spec?.schedule - ? `${cronJob.spec.schedule} ( - ${cronstrue.toString(cronJob.spec.schedule)})` - : '???'} + + Schedule:{' '} + {cronJob.spec?.schedule + ? `${cronJob.spec.schedule} (${cronstrue.toString( + cronJob.spec.schedule, + )})` + : '???'} + diff --git a/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsDrawer.test.tsx b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsDrawer.test.tsx new file mode 100644 index 0000000000..b16e83ee50 --- /dev/null +++ b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsDrawer.test.tsx @@ -0,0 +1,38 @@ +/* + * 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 * as oneCronJobsFixture from '../../__fixtures__/1-cronjobs.json'; +import { renderInTestApp } from '@backstage/test-utils'; +import { CronJobDrawer } from './CronJobsDrawer'; + +describe('CronJobDrawer', () => { + it('should render cronJob drawer', async () => { + const { getByText, getAllByText } = await renderInTestApp( + , + ); + + expect(getAllByText('dice-roller-cronjob')).toHaveLength(2); + expect(getAllByText('CronJob')).toHaveLength(2); + expect(getByText('YAML')).toBeInTheDocument(); + expect(getByText('Schedule')).toBeInTheDocument(); + expect(getByText('30 5 * * *')).toBeInTheDocument(); + expect(getByText('Starting Deadline Seconds')).toBeInTheDocument(); + expect(getByText('Last Schedule Time')).toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.test.tsx b/plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.test.tsx new file mode 100644 index 0000000000..093d616319 --- /dev/null +++ b/plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.test.tsx @@ -0,0 +1,42 @@ +/* + * 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 { render } from '@testing-library/react'; +import { JobsAccordions } from './JobsAccordions'; +import * as oneCronJobsFixture from '../../__fixtures__/1-cronjobs.json'; +import { wrapInTestApp } from '@backstage/test-utils'; +import { kubernetesProviders } from '../../hooks/test-utils'; +import { V1Job, ObjectSerializer } from '@kubernetes/client-node'; + +describe('JobsAccordions', () => { + it('should render 2 jobs', async () => { + const wrapper = kubernetesProviders(oneCronJobsFixture, []); + + const jobs: V1Job[] = oneCronJobsFixture.jobs.map( + job => ObjectSerializer.deserialize(job, 'V1Job') as V1Job, + ); + + const { getByText } = render( + wrapper(wrapInTestApp()), + ); + + expect(getByText('dice-roller-cronjob-1637028600')).toBeInTheDocument(); + expect(getByText('Running')).toBeInTheDocument(); + + expect(getByText('dice-roller-cronjob-1637025000')).toBeInTheDocument(); + expect(getByText('Succeeded')).toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.tsx b/plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.tsx index 5c95dfbb6d..715be4c88a 100644 --- a/plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.tsx +++ b/plugins/kubernetes/src/components/JobsAccordions/JobsAccordions.tsx @@ -76,9 +76,11 @@ const JobSummary = ({ job }: JobSummaryProps) => { {job.status?.active && Running} {job.status?.failed && Failed} - Start time: {job.status?.startTime} + Start time: {job.status?.startTime?.toString()} {job.status?.completionTime && ( - Completion time: {job.status.completionTime} + + Completion time: {job.status.completionTime.toString()} + )} diff --git a/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.test.tsx b/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.test.tsx new file mode 100644 index 0000000000..dc20e603c1 --- /dev/null +++ b/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.test.tsx @@ -0,0 +1,35 @@ +/* + * 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 * as oneCronJobsFixture from '../../__fixtures__/1-cronjobs.json'; +import { renderInTestApp } from '@backstage/test-utils'; +import { JobDrawer } from './JobsDrawer'; + +describe('JobDrawer', () => { + it('should render job drawer', async () => { + const { getByText, getAllByText } = await renderInTestApp( + , + ); + + expect(getAllByText('dice-roller-cronjob-1637025000')).toHaveLength(2); + expect(getAllByText('Job')).toHaveLength(2); + expect(getByText('YAML')).toBeInTheDocument(); + expect(getByText('Parallelism')).toBeInTheDocument(); + expect(getByText('Completions')).toBeInTheDocument(); + expect(getByText('Back Off Limit')).toBeInTheDocument(); + expect(getByText('Start Time')).toBeInTheDocument(); + }); +}); From 86ed770308d110d964f654f25fbacd51ff5a533a Mon Sep 17 00:00:00 2001 From: Andrew Tran Date: Tue, 23 Nov 2021 14:55:15 -0600 Subject: [PATCH 3/6] add changeset Signed-off-by: Andrew Tran --- .changeset/weak-rivers-perform.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/weak-rivers-perform.md diff --git a/.changeset/weak-rivers-perform.md b/.changeset/weak-rivers-perform.md new file mode 100644 index 0000000000..42ff814cbe --- /dev/null +++ b/.changeset/weak-rivers-perform.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes': minor +--- + +Added accordions to display information on jobs and cronjobs in the kubernetes plugin. Updated the PodsTable with fewer default columns and the ability to pass in additional ones depending on the use case. From a73df1532f1f874c2a88cb4d858142212d27cdb4 Mon Sep 17 00:00:00 2001 From: Andrew Tran Date: Wed, 24 Nov 2021 09:51:05 -0600 Subject: [PATCH 4/6] fix spellings and update changeset Signed-off-by: Andrew Tran --- .changeset/weak-rivers-perform.md | 4 ++-- .../src/components/CronJobsAccordions/CronJobsAccordions.tsx | 2 +- .../kubernetes/src/components/JobsAccordions/JobsDrawer.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.changeset/weak-rivers-perform.md b/.changeset/weak-rivers-perform.md index 42ff814cbe..92f96ab6e0 100644 --- a/.changeset/weak-rivers-perform.md +++ b/.changeset/weak-rivers-perform.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-kubernetes': minor +'@backstage/plugin-kubernetes': patch --- -Added accordions to display information on jobs and cronjobs in the kubernetes plugin. Updated the PodsTable with fewer default columns and the ability to pass in additional ones depending on the use case. +Added accordions to display information on Jobs and CronJobs in the kubernetes plugin. Updated the PodsTable with fewer default columns and the ability to pass in additional ones depending on the use case. diff --git a/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx index b136e2273c..2581678928 100644 --- a/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx +++ b/plugins/kubernetes/src/components/CronJobsAccordions/CronJobsAccordions.tsx @@ -82,7 +82,7 @@ const CronJobSummary = ({ cronJob }: CronJobSummaryProps) => { ? `${cronJob.spec.schedule} (${cronstrue.toString( cronJob.spec.schedule, )})` - : '???'} + : 'N/A'} diff --git a/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.tsx b/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.tsx index d81e099acc..a7217725b2 100644 --- a/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.tsx +++ b/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.tsx @@ -34,7 +34,7 @@ export const JobDrawer = ({ return { parallelism: jobObj.spec?.parallelism ?? '???', completions: jobObj.spec?.completions ?? '???', - backOffLimit: jobObj.spec?.backoffLimit ?? '???', + backoffLimit: jobObj.spec?.backoffLimit ?? '???', startTime: jobObj.status?.startTime ?? '???', }; }} From 487647524ae656d0fb288548ca267a7a1d7e79b4 Mon Sep 17 00:00:00 2001 From: Andrew Tran Date: Mon, 29 Nov 2021 11:17:57 -0600 Subject: [PATCH 5/6] update PodsTable tests Signed-off-by: Andrew Tran --- .../src/components/Pods/PodsTable.test.tsx | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/plugins/kubernetes/src/components/Pods/PodsTable.test.tsx b/plugins/kubernetes/src/components/Pods/PodsTable.test.tsx index 7c6c7e97f7..868b74bbc9 100644 --- a/plugins/kubernetes/src/components/Pods/PodsTable.test.tsx +++ b/plugins/kubernetes/src/components/Pods/PodsTable.test.tsx @@ -18,8 +18,25 @@ import React from 'react'; import { render } from '@testing-library/react'; import * as pod from './__fixtures__/pod.json'; import * as crashingPod from './__fixtures__/crashing-pod.json'; +import { TableColumn } from '@backstage/core-components'; import { wrapInTestApp } from '@backstage/test-utils'; +import { V1Pod } from '@kubernetes/client-node'; import { PodsTable } from './PodsTable'; +import { containersReady, totalRestarts } from '../../utils/pod'; + +const extraColumns: TableColumn[] = [ + { + title: 'containers ready', + align: 'center', + render: containersReady, + }, + { + title: 'total restarts', + align: 'center', + render: totalRestarts, + type: 'numeric', + }, +]; describe('PodsTable', () => { it('should render pod', async () => { @@ -27,6 +44,24 @@ describe('PodsTable', () => { wrapInTestApp(), ); + // titles + expect(getByText('name')).toBeInTheDocument(); + expect(getByText('phase')).toBeInTheDocument(); + expect(getByText('status')).toBeInTheDocument(); + + // values + expect(getByText('dice-roller-6c8646bfd-2m5hv')).toBeInTheDocument(); + expect(getByText('Running')).toBeInTheDocument(); + expect(getByText('OK')).toBeInTheDocument(); + }); + + it('should render pod with extra columns', async () => { + const { getByText } = render( + wrapInTestApp( + , + ), + ); + // titles expect(getByText('name')).toBeInTheDocument(); expect(getByText('phase')).toBeInTheDocument(); @@ -41,9 +76,12 @@ describe('PodsTable', () => { expect(getByText('0')).toBeInTheDocument(); expect(getByText('OK')).toBeInTheDocument(); }); - it('should render crashing pod', async () => { + + it('should render crashing pod with extra columns', async () => { const { getByText, getAllByText } = render( - wrapInTestApp(), + wrapInTestApp( + , + ), ); // titles From bccebd0884dac31489ab53288c0d1b3249f36ae3 Mon Sep 17 00:00:00 2001 From: Andrew Tran Date: Mon, 29 Nov 2021 12:14:45 -0600 Subject: [PATCH 6/6] fix typo in test Signed-off-by: Andrew Tran --- .../src/components/JobsAccordions/JobsDrawer.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.test.tsx b/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.test.tsx index dc20e603c1..48a4e6fe7e 100644 --- a/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.test.tsx +++ b/plugins/kubernetes/src/components/JobsAccordions/JobsDrawer.test.tsx @@ -29,7 +29,7 @@ describe('JobDrawer', () => { expect(getByText('YAML')).toBeInTheDocument(); expect(getByText('Parallelism')).toBeInTheDocument(); expect(getByText('Completions')).toBeInTheDocument(); - expect(getByText('Back Off Limit')).toBeInTheDocument(); + expect(getByText('Backoff Limit')).toBeInTheDocument(); expect(getByText('Start Time')).toBeInTheDocument(); }); });