From 4642cb7ac23211407877bcd780682ce2e9478e77 Mon Sep 17 00:00:00 2001 From: Andres Mauricio Gomez P Date: Fri, 16 Feb 2024 13:28:32 -0500 Subject: [PATCH] Added support to fetch data for Daemon Sets and display an accordion in the same way as with Deployments Signed-off-by: Andres Mauricio Gomez P --- .changeset/two-planets-beam.md | 6 + plugins/kubernetes-common/api-report.md | 2 + plugins/kubernetes-common/src/types.ts | 1 + .../kubernetes-common/src/util/response.ts | 4 + .../src/__fixtures__/2-daemonsets.json | 581 ++++++++++++++++++ .../src/components/Cluster/Cluster.tsx | 6 + .../DaemonSetsAccordions.test.tsx | 33 + .../DaemonSetsAccordions.tsx | 147 +++++ .../DaemonSetsDrawer.test.tsx | 68 ++ .../DaemonSetsAccordions/DaemonSetsDrawer.tsx | 76 +++ .../components/DaemonSetsAccordions/index.ts | 16 + .../src/hooks/GroupedResponses.ts | 1 + 12 files changed, 941 insertions(+) create mode 100644 .changeset/two-planets-beam.md create mode 100644 plugins/kubernetes-react/src/__fixtures__/2-daemonsets.json create mode 100644 plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.test.tsx create mode 100644 plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.tsx create mode 100644 plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.test.tsx create mode 100644 plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.tsx create mode 100644 plugins/kubernetes-react/src/components/DaemonSetsAccordions/index.ts diff --git a/.changeset/two-planets-beam.md b/.changeset/two-planets-beam.md new file mode 100644 index 0000000000..d500d22853 --- /dev/null +++ b/.changeset/two-planets-beam.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-kubernetes-common': patch +'@backstage/plugin-kubernetes-react': patch +--- + +Add support to fetch data for Daemon Sets and display an accordion in the same way as with Deployments diff --git a/plugins/kubernetes-common/api-report.md b/plugins/kubernetes-common/api-report.md index c1d399b196..2f46ef90e5 100644 --- a/plugins/kubernetes-common/api-report.md +++ b/plugins/kubernetes-common/api-report.md @@ -275,6 +275,8 @@ export interface GroupedResponses extends DeploymentResources { // (undocumented) customResources: any[]; // (undocumented) + daemonSets: V1DaemonSet[]; + // (undocumented) ingresses: V1Ingress[]; // (undocumented) jobs: V1Job[]; diff --git a/plugins/kubernetes-common/src/types.ts b/plugins/kubernetes-common/src/types.ts index cd316af089..c4bb5fd32f 100644 --- a/plugins/kubernetes-common/src/types.ts +++ b/plugins/kubernetes-common/src/types.ts @@ -294,4 +294,5 @@ export interface GroupedResponses extends DeploymentResources { cronJobs: V1CronJob[]; customResources: any[]; statefulsets: V1StatefulSet[]; + daemonSets: V1DaemonSet[]; } diff --git a/plugins/kubernetes-common/src/util/response.ts b/plugins/kubernetes-common/src/util/response.ts index a86d90f780..b62fa162d1 100644 --- a/plugins/kubernetes-common/src/util/response.ts +++ b/plugins/kubernetes-common/src/util/response.ts @@ -58,6 +58,9 @@ export const groupResponses = ( case 'statefulsets': prev.statefulsets.push(...next.resources); break; + case 'daemonsets': + prev.daemonSets.push(...next.resources); + break; default: } return prev; @@ -74,6 +77,7 @@ export const groupResponses = ( cronJobs: [], customResources: [], statefulsets: [], + daemonSets: [], } as GroupedResponses, ); }; diff --git a/plugins/kubernetes-react/src/__fixtures__/2-daemonsets.json b/plugins/kubernetes-react/src/__fixtures__/2-daemonsets.json new file mode 100644 index 0000000000..1c88b9d8a7 --- /dev/null +++ b/plugins/kubernetes-react/src/__fixtures__/2-daemonsets.json @@ -0,0 +1,581 @@ +{ + "daemonSets": [ + { + "apiVersion": "apps/v1", + "kind": "DaemonSet", + "metadata": { + "annotations": { + "deprecated.daemonset.template.generation": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"DaemonSet\",\"metadata\":{\"annotations\":{},\"labels\":{\"backstage.io/kubernetes-id\":\"dice-roller\",\"k8s-app\":\"fluentd-logging\"},\"name\":\"fluentd-elasticsearch\",\"namespace\":\"default\"},\"spec\":{\"selector\":{\"matchLabels\":{\"name\":\"fluentd-elasticsearch\"}},\"template\":{\"metadata\":{\"labels\":{\"name\":\"fluentd-elasticsearch\"}},\"spec\":{\"containers\":[{\"image\":\"quay.io/fluentd_elasticsearch/fluentd:v2.5.2\",\"name\":\"fluentd-elasticsearch\",\"resources\":{\"limits\":{\"memory\":\"200Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"200Mi\"}}}],\"terminationGracePeriodSeconds\":30}}}}\n" + }, + "creationTimestamp": "2024-02-14T21:00:28Z", + "generation": 1, + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "k8s-app": "fluentd-logging" + }, + "name": "fluentd-elasticsearch", + "namespace": "default", + "resourceVersion": "1769498", + "uid": "2ba243f3-a733-4b63-9db9-c9b8ce303a23" + }, + "spec": { + "revisionHistoryLimit": 10, + "selector": { + "matchLabels": { + "name": "fluentd-elasticsearch" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "name": "fluentd-elasticsearch" + } + }, + "spec": { + "containers": [ + { + "image": "quay.io/fluentd_elasticsearch/fluentd:v2.5.2", + "imagePullPolicy": "IfNotPresent", + "name": "fluentd-elasticsearch", + "resources": { + "limits": { + "memory": "200Mi" + }, + "requests": { + "cpu": "100m", + "memory": "200Mi" + } + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File" + } + ], + "dnsPolicy": "ClusterFirst", + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": {}, + "terminationGracePeriodSeconds": 30 + } + }, + "updateStrategy": { + "rollingUpdate": { + "maxSurge": 0, + "maxUnavailable": 1 + }, + "type": "RollingUpdate" + } + }, + "status": { + "currentNumberScheduled": 1, + "desiredNumberScheduled": 1, + "numberAvailable": 1, + "numberMisscheduled": 0, + "numberReady": 1, + "observedGeneration": 1, + "updatedNumberScheduled": 1 + } + }, + { + "apiVersion": "apps/v1", + "kind": "DaemonSet", + "metadata": { + "annotations": { + "deprecated.daemonset.template.generation": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"DaemonSet\",\"metadata\":{\"annotations\":{},\"labels\":{\"backstage.io/kubernetes-id\":\"dice-roller\",\"k8s-app\":\"fluentd-logging\"},\"name\":\"fluentd-elasticsearch2\",\"namespace\":\"default\"},\"spec\":{\"selector\":{\"matchLabels\":{\"name\":\"fluentd-elasticsearch2\"}},\"template\":{\"metadata\":{\"labels\":{\"backstage.io/kubernetes-id\":\"dice-roller\",\"name\":\"fluentd-elasticsearch2\"}},\"spec\":{\"containers\":[{\"image\":\"quay.io/fluentd_elasticsearch/fluentd:v2.5.2\",\"name\":\"fluentd-elasticsearch\",\"resources\":{\"limits\":{\"memory\":\"200Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"200Mi\"}}}],\"terminationGracePeriodSeconds\":30}}}}\n" + }, + "creationTimestamp": "2024-02-16T18:11:26Z", + "generation": 1, + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "k8s-app": "fluentd-logging" + }, + "name": "fluentd-elasticsearch2", + "namespace": "default", + "resourceVersion": "1981736", + "uid": "b923d035-e9a4-4a40-9472-f0f3468f30df" + }, + "spec": { + "revisionHistoryLimit": 10, + "selector": { + "matchLabels": { + "name": "fluentd-elasticsearch2" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "name": "fluentd-elasticsearch2" + } + }, + "spec": { + "containers": [ + { + "image": "quay.io/fluentd_elasticsearch/fluentd:v2.5.2", + "imagePullPolicy": "IfNotPresent", + "name": "fluentd-elasticsearch", + "resources": { + "limits": { + "memory": "200Mi" + }, + "requests": { + "cpu": "100m", + "memory": "200Mi" + } + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File" + } + ], + "dnsPolicy": "ClusterFirst", + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": {}, + "terminationGracePeriodSeconds": 30 + } + }, + "updateStrategy": { + "rollingUpdate": { + "maxSurge": 0, + "maxUnavailable": 1 + }, + "type": "RollingUpdate" + } + }, + "status": { + "currentNumberScheduled": 1, + "desiredNumberScheduled": 1, + "numberAvailable": 1, + "numberMisscheduled": 0, + "numberReady": 1, + "observedGeneration": 1, + "updatedNumberScheduled": 1 + } + } + ], + "pods": [ + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "creationTimestamp": "2024-02-16T16:35:06Z", + "generateName": "fluentd-elasticsearch-", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "controller-revision-hash": "86978587c8", + "name": "fluentd-elasticsearch", + "pod-template-generation": "2" + }, + "name": "fluentd-elasticsearch-mmkpf", + "namespace": "default", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "DaemonSet", + "name": "fluentd-elasticsearch", + "uid": "2ba243f3-a733-4b63-9db9-c9b8ce303a23" + } + ], + "resourceVersion": "1970744", + "uid": "fc9f9070-1a3c-4e09-9cd6-8803a4ebfb0a" + }, + "spec": { + "affinity": { + "nodeAffinity": { + "requiredDuringSchedulingIgnoredDuringExecution": { + "nodeSelectorTerms": [ + { + "matchFields": [ + { + "key": "metadata.name", + "operator": "In", + "values": ["ucp-control-plane"] + } + ] + } + ] + } + } + }, + "containers": [ + { + "image": "quay.io/fluentd_elasticsearch/fluentd:v2.5.2", + "imagePullPolicy": "IfNotPresent", + "name": "fluentd-elasticsearch", + "resources": { + "limits": { + "memory": "200Mi" + }, + "requests": { + "cpu": "100m", + "memory": "200Mi" + } + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-8cwbv", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "nodeName": "ucp-control-plane", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": {}, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists" + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists" + }, + { + "effect": "NoSchedule", + "key": "node.kubernetes.io/disk-pressure", + "operator": "Exists" + }, + { + "effect": "NoSchedule", + "key": "node.kubernetes.io/memory-pressure", + "operator": "Exists" + }, + { + "effect": "NoSchedule", + "key": "node.kubernetes.io/pid-pressure", + "operator": "Exists" + }, + { + "effect": "NoSchedule", + "key": "node.kubernetes.io/unschedulable", + "operator": "Exists" + } + ], + "volumes": [ + { + "name": "kube-api-access-8cwbv", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-02-16T16:35:06Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-02-16T16:35:08Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-02-16T16:35:08Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-02-16T16:35:06Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "containerd://958e4ec14b6a3557724fabeaac9c8f8fe2ea797337a4397e700209f6e482e898", + "image": "quay.io/fluentd_elasticsearch/fluentd:v2.5.2", + "imageID": "sha256:a5bf47027e067e0376708cae10750ea154b13356dfc0209610f5ea0bc7c16fe0", + "lastState": {}, + "name": "fluentd-elasticsearch", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-02-16T16:35:07Z" + } + } + } + ], + "hostIP": "172.19.0.3", + "phase": "Running", + "podIP": "10.244.0.215", + "podIPs": [ + { + "ip": "10.244.0.215" + } + ], + "qosClass": "Burstable", + "startTime": "2024-02-16T16:35:06Z" + } + }, + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "creationTimestamp": "2024-02-16T18:11:26Z", + "generateName": "fluentd-elasticsearch2-", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "controller-revision-hash": "5979b599f6", + "name": "fluentd-elasticsearch2", + "pod-template-generation": "1" + }, + "name": "fluentd-elasticsearch2-7hwwg", + "namespace": "default", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "DaemonSet", + "name": "fluentd-elasticsearch2", + "uid": "b923d035-e9a4-4a40-9472-f0f3468f30df" + } + ], + "resourceVersion": "1981735", + "uid": "1b829903-2c3a-453f-a8f2-b6603eec0bbe" + }, + "spec": { + "affinity": { + "nodeAffinity": { + "requiredDuringSchedulingIgnoredDuringExecution": { + "nodeSelectorTerms": [ + { + "matchFields": [ + { + "key": "metadata.name", + "operator": "In", + "values": ["ucp-control-plane"] + } + ] + } + ] + } + } + }, + "containers": [ + { + "image": "quay.io/fluentd_elasticsearch/fluentd:v2.5.2", + "imagePullPolicy": "IfNotPresent", + "name": "fluentd-elasticsearch", + "resources": { + "limits": { + "memory": "200Mi" + }, + "requests": { + "cpu": "100m", + "memory": "200Mi" + } + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-rpkbp", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "nodeName": "ucp-control-plane", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": {}, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists" + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists" + }, + { + "effect": "NoSchedule", + "key": "node.kubernetes.io/disk-pressure", + "operator": "Exists" + }, + { + "effect": "NoSchedule", + "key": "node.kubernetes.io/memory-pressure", + "operator": "Exists" + }, + { + "effect": "NoSchedule", + "key": "node.kubernetes.io/pid-pressure", + "operator": "Exists" + }, + { + "effect": "NoSchedule", + "key": "node.kubernetes.io/unschedulable", + "operator": "Exists" + } + ], + "volumes": [ + { + "name": "kube-api-access-rpkbp", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-02-16T18:11:27Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-02-16T18:11:28Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-02-16T18:11:28Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-02-16T18:11:27Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "containerd://3372cb5a300c87e5b1530b32fea7af35dce92470fc7c1cea47c714fac283feb9", + "image": "quay.io/fluentd_elasticsearch/fluentd:v2.5.2", + "imageID": "sha256:a5bf47027e067e0376708cae10750ea154b13356dfc0209610f5ea0bc7c16fe0", + "lastState": {}, + "name": "fluentd-elasticsearch", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-02-16T18:11:28Z" + } + } + } + ], + "hostIP": "172.19.0.3", + "phase": "Running", + "podIP": "10.244.0.88", + "podIPs": [ + { + "ip": "10.244.0.88" + } + ], + "qosClass": "Burstable", + "startTime": "2024-02-16T18:11:27Z" + } + } + ] +} diff --git a/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx b/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx index f2108ac1b7..d713b9a873 100644 --- a/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx +++ b/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx @@ -34,6 +34,7 @@ import { IngressesAccordions } from '../IngressesAccordions'; import { ServicesAccordions } from '../ServicesAccordions'; import { CronJobsAccordions } from '../CronJobsAccordions'; import { CustomResources } from '../CustomResources'; +import { DaemonSetsAccordions } from '../DaemonSetsAccordions'; import { ClusterContext, GroupedResponsesContext, @@ -151,6 +152,11 @@ export const Cluster = ({ clusterObjects, podsWithErrors }: ClusterProps) => { ) : undefined} + {groupedResponses.daemonSets.length > 0 ? ( + + + + ) : undefined} {groupedResponses.statefulsets.length > 0 ? ( diff --git a/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.test.tsx b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.test.tsx new file mode 100644 index 0000000000..9f38abf897 --- /dev/null +++ b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.test.tsx @@ -0,0 +1,33 @@ +/* + * Copyright 2020 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 { screen } from '@testing-library/react'; +import { DaemonSetsAccordions } from './DaemonSetsAccordions'; +import * as oneDaemonsFixture from '../../__fixtures__/2-daemonsets.json'; +import { renderInTestApp } from '@backstage/test-utils'; +import { kubernetesProviders } from '../../hooks/test-utils'; + +describe('DaemonSetsAccordions', () => { + it('should render two daemonset', async () => { + const wrapper = kubernetesProviders(oneDaemonsFixture); + + await renderInTestApp(wrapper()); + + expect(screen.getByText('fluentd-elasticsearch')).toBeInTheDocument(); + expect(screen.getByText('fluentd-elasticsearch2')).toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.tsx b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.tsx new file mode 100644 index 0000000000..834a86e837 --- /dev/null +++ b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsAccordions.tsx @@ -0,0 +1,147 @@ +/* + * Copyright 2020 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, + Grid, +} from '@material-ui/core'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import { V1Pod, V1DaemonSet } from '@kubernetes/client-node'; +import { PodsTable } from '../Pods'; +import { DaemonSetDrawer } from './DaemonSetsDrawer'; +import { getOwnedResources } from '../../utils/owner'; +import { + GroupedResponsesContext, + PodNamesWithErrorsContext, +} from '../../hooks'; +import { StatusError, StatusOK } from '@backstage/core-components'; +import { READY_COLUMNS, RESOURCE_COLUMNS } from '../Pods/PodsTable'; + +type DaemonSetsAccordionsProps = { + children?: React.ReactNode; +}; + +type DaemonSetAccordionProps = { + daemonset: V1DaemonSet; + ownedPods: V1Pod[]; + children?: React.ReactNode; +}; + +type DaemonSetSummaryProps = { + daemonset: V1DaemonSet; + numberOfCurrentPods: number; + numberOfPodsWithErrors: number; + children?: React.ReactNode; +}; + +const DaemonSetSummary = ({ + daemonset, + numberOfCurrentPods, + numberOfPodsWithErrors, +}: DaemonSetSummaryProps) => { + return ( + + + + + + + {numberOfCurrentPods} pods + + + {numberOfPodsWithErrors > 0 ? ( + + {numberOfPodsWithErrors} pod + {numberOfPodsWithErrors > 1 ? 's' : ''} with errors + + ) : ( + No pods with errors + )} + + + + ); +}; + +const DaemonSetAccordion = ({ + daemonset, + ownedPods, +}: DaemonSetAccordionProps) => { + const podNamesWithErrors = useContext(PodNamesWithErrorsContext); + + const podsWithErrors = ownedPods.filter(p => + podNamesWithErrors.has(p.metadata?.name ?? ''), + ); + + return ( + + }> + + + + + + + ); +}; + +export const DaemonSetsAccordions = ({}: DaemonSetsAccordionsProps) => { + const groupedResponses = useContext(GroupedResponsesContext); + + return ( + + {groupedResponses.daemonSets.map((daemonset, i) => ( + + + + + + ))} + + ); +}; diff --git a/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.test.tsx b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.test.tsx new file mode 100644 index 0000000000..51e70a3b9b --- /dev/null +++ b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.test.tsx @@ -0,0 +1,68 @@ +/* + * Copyright 2020 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 daemonsets from '../../__fixtures__/2-daemonsets.json'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { DaemonSetDrawer } from './DaemonSetsDrawer'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; + +describe('DaemonsetsDrawer', () => { + it('should render daemonsets drawer', async () => { + const { getByText, getAllByText } = await renderInTestApp( + + + , + , + ); + expect(getAllByText('fluentd-elasticsearch')).toHaveLength(2); + expect(getAllByText('DaemonSet')).toHaveLength(2); + expect(getByText('YAML')).toBeInTheDocument(); + expect(getByText('Update Strategy Type')).toBeInTheDocument(); + expect(getByText('RollingUpdate')).toBeInTheDocument(); + expect(getByText('Min Ready Seconds')).toBeInTheDocument(); + expect(getByText('???')).toBeInTheDocument(); + expect(getByText('Min Ready Seconds')).toBeInTheDocument(); + expect(getByText('Revision History Limit')).toBeInTheDocument(); + expect(getByText('Current Number Scheduled')).toBeInTheDocument(); + expect(getByText('Desired Number Scheduled')).toBeInTheDocument(); + expect(getByText('Number Available')).toBeInTheDocument(); + expect(getByText('Number Misscheduled')).toBeInTheDocument(); + expect(getByText('Number Ready')).toBeInTheDocument(); + expect(getByText('namespace: default')).toBeInTheDocument(); + }); + + it('should render deployment drawer without namespace', async () => { + const daemonset = (daemonsets as any).daemonSets[0]; + const { queryByText } = await renderInTestApp( + + + , + , + ); + + expect(queryByText('namespace: default')).not.toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.tsx b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.tsx new file mode 100644 index 0000000000..e0f64ed679 --- /dev/null +++ b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/DaemonSetsDrawer.tsx @@ -0,0 +1,76 @@ +/* + * Copyright 2020 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 { V1DaemonSet } from '@kubernetes/client-node'; +import { KubernetesStructuredMetadataTableDrawer } from '../KubernetesDrawer'; +import { Typography, Grid, Chip } from '@material-ui/core'; + +export const DaemonSetDrawer = ({ + daemonset, + expanded, +}: { + daemonset: V1DaemonSet; + expanded?: boolean; +}) => { + const namespace = daemonset.metadata?.namespace; + return ( + { + return { + updateStrategyType: daemonsetObj.spec?.updateStrategy?.type ?? '???', + minReadySeconds: daemonsetObj.spec?.minReadySeconds ?? '???', + revisionHistoryLimit: + daemonsetObj.spec?.revisionHistoryLimit ?? '???', + currentNumberScheduled: + daemonsetObj.status?.currentNumberScheduled ?? '???', + desiredNumberScheduled: + daemonsetObj.status?.desiredNumberScheduled ?? '???', + numberAvailable: daemonsetObj.status?.numberAvailable ?? '???', + numberMisscheduled: daemonsetObj.status?.numberMisscheduled ?? '???', + numberReady: daemonsetObj.status?.numberReady ?? '???', + }; + }} + > + + + + {daemonset.metadata?.name ?? 'unknown object'} + + + + + DaemonSet + + + {namespace && ( + + + + )} + + + ); +}; diff --git a/plugins/kubernetes-react/src/components/DaemonSetsAccordions/index.ts b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/index.ts new file mode 100644 index 0000000000..ca25150cbd --- /dev/null +++ b/plugins/kubernetes-react/src/components/DaemonSetsAccordions/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2024 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 { DaemonSetsAccordions } from './DaemonSetsAccordions'; diff --git a/plugins/kubernetes-react/src/hooks/GroupedResponses.ts b/plugins/kubernetes-react/src/hooks/GroupedResponses.ts index 7a81a74337..10cd3a8abe 100644 --- a/plugins/kubernetes-react/src/hooks/GroupedResponses.ts +++ b/plugins/kubernetes-react/src/hooks/GroupedResponses.ts @@ -25,6 +25,7 @@ export const GroupedResponsesContext = React.createContext({ pods: [], replicaSets: [], deployments: [], + daemonSets: [], services: [], configMaps: [], horizontalPodAutoscalers: [],