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 <andmagom@outlook.com>
This commit is contained in:
Andres Mauricio Gomez P
2024-02-16 13:28:32 -05:00
parent 65f02483c0
commit 4642cb7ac2
12 changed files with 941 additions and 0 deletions
+6
View File
@@ -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
+2
View File
@@ -275,6 +275,8 @@ export interface GroupedResponses extends DeploymentResources {
// (undocumented)
customResources: any[];
// (undocumented)
daemonSets: V1DaemonSet[];
// (undocumented)
ingresses: V1Ingress[];
// (undocumented)
jobs: V1Job[];
+1
View File
@@ -294,4 +294,5 @@ export interface GroupedResponses extends DeploymentResources {
cronJobs: V1CronJob[];
customResources: any[];
statefulsets: V1StatefulSet[];
daemonSets: V1DaemonSet[];
}
@@ -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,
);
};
@@ -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"
}
}
]
}
@@ -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) => {
<DeploymentsAccordions />
</Grid>
) : undefined}
{groupedResponses.daemonSets.length > 0 ? (
<Grid item>
<DaemonSetsAccordions />
</Grid>
) : undefined}
{groupedResponses.statefulsets.length > 0 ? (
<Grid item>
<StatefulSetsAccordions />
@@ -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(<DaemonSetsAccordions />));
expect(screen.getByText('fluentd-elasticsearch')).toBeInTheDocument();
expect(screen.getByText('fluentd-elasticsearch2')).toBeInTheDocument();
});
});
@@ -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 (
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
spacing={0}
>
<Grid xs={4} item>
<DaemonSetDrawer daemonset={daemonset} />
</Grid>
<Grid
item
container
xs={4}
direction="column"
justifyContent="flex-start"
alignItems="flex-end"
spacing={0}
>
<Grid item>
<StatusOK>{numberOfCurrentPods} pods</StatusOK>
</Grid>
<Grid item>
{numberOfPodsWithErrors > 0 ? (
<StatusError>
{numberOfPodsWithErrors} pod
{numberOfPodsWithErrors > 1 ? 's' : ''} with errors
</StatusError>
) : (
<StatusOK>No pods with errors</StatusOK>
)}
</Grid>
</Grid>
</Grid>
);
};
const DaemonSetAccordion = ({
daemonset,
ownedPods,
}: DaemonSetAccordionProps) => {
const podNamesWithErrors = useContext(PodNamesWithErrorsContext);
const podsWithErrors = ownedPods.filter(p =>
podNamesWithErrors.has(p.metadata?.name ?? ''),
);
return (
<Accordion TransitionProps={{ unmountOnExit: true }} variant="outlined">
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<DaemonSetSummary
daemonset={daemonset}
numberOfCurrentPods={ownedPods.length}
numberOfPodsWithErrors={podsWithErrors.length}
/>
</AccordionSummary>
<AccordionDetails>
<PodsTable
pods={ownedPods}
extraColumns={[READY_COLUMNS, RESOURCE_COLUMNS]}
/>
</AccordionDetails>
</Accordion>
);
};
export const DaemonSetsAccordions = ({}: DaemonSetsAccordionsProps) => {
const groupedResponses = useContext(GroupedResponsesContext);
return (
<Grid
container
direction="column"
justifyContent="flex-start"
alignItems="flex-start"
>
{groupedResponses.daemonSets.map((daemonset, i) => (
<Grid container item key={i} xs>
<Grid item xs>
<DaemonSetAccordion
ownedPods={getOwnedResources(daemonset, groupedResponses.pods)}
daemonset={daemonset}
/>
</Grid>
</Grid>
))}
</Grid>
);
};
@@ -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(
<TestApiProvider apis={[[kubernetesClusterLinkFormatterApiRef, {}]]}>
<DaemonSetDrawer
daemonset={(daemonsets as any).daemonSets[0]}
expanded
/>
,
</TestApiProvider>,
);
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(
<TestApiProvider apis={[[kubernetesClusterLinkFormatterApiRef, {}]]}>
<DaemonSetDrawer
daemonset={{
...daemonset,
metadata: { ...daemonset.metadata, namespace: undefined },
}}
expanded
/>
,
</TestApiProvider>,
);
expect(queryByText('namespace: default')).not.toBeInTheDocument();
});
});
@@ -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 (
<KubernetesStructuredMetadataTableDrawer
object={daemonset}
expanded={expanded}
kind="DaemonSet"
renderObject={(daemonsetObj: V1DaemonSet) => {
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 ?? '???',
};
}}
>
<Grid
container
direction="column"
justifyContent="flex-start"
alignItems="flex-start"
spacing={0}
>
<Grid item>
<Typography variant="body1">
{daemonset.metadata?.name ?? 'unknown object'}
</Typography>
</Grid>
<Grid item>
<Typography color="textSecondary" variant="subtitle1">
DaemonSet
</Typography>
</Grid>
{namespace && (
<Grid item>
<Chip size="small" label={`namespace: ${namespace}`} />
</Grid>
)}
</Grid>
</KubernetesStructuredMetadataTableDrawer>
);
};
@@ -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';
@@ -25,6 +25,7 @@ export const GroupedResponsesContext = React.createContext<GroupedResponses>({
pods: [],
replicaSets: [],
deployments: [],
daemonSets: [],
services: [],
configMaps: [],
horizontalPodAutoscalers: [],