Merge branch 'master' of https://github.com/backstage/backstage into marley/7678-pr-dashboard-backend
This commit is contained in:
@@ -160,6 +160,14 @@ by this plugin.
|
||||
Grab a copy of [oauth2-redirect.html](https://github.com/swagger-api/swagger-ui/blob/master/dist/oauth2-redirect.html)
|
||||
and put it in the `app/public/` directory in order to enable Swagger UI to complete this redirection.
|
||||
|
||||
This also may require you to adjust `Content Security Policy` header settings of your Backstage application, so that the script in `oauth2-redirect.html` can be executed. Since the script is static we can add the hash of it directly to our CSP policy, which we do by adding the following to the `csp` section of the app configuration:
|
||||
|
||||
```yaml
|
||||
script-src:
|
||||
- "'self'"
|
||||
- "'sha256-GeDavzSZ8O71Jggf/pQkKbt52dfZkrdNMQ3e+Ox+AkI='" # oauth2-redirect.html
|
||||
```
|
||||
|
||||
#### Configuring your OAuth2 Client
|
||||
|
||||
You'll need to make sure your OAuth2 client has been registered in your OAuth2 Authentication Server (AS)
|
||||
|
||||
@@ -22,4 +22,4 @@ export {
|
||||
AzurePullRequestsPage,
|
||||
} from './plugin';
|
||||
|
||||
export * from './components/AzurePullRequestsIcon';
|
||||
export { AzurePullRequestsIcon } from './components/AzurePullRequestsIcon';
|
||||
|
||||
+18
@@ -63,5 +63,23 @@ export interface Config {
|
||||
apiVersion: string;
|
||||
plural: string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* (Optional) API Version Overrides
|
||||
* If set, the specified api version will be used to make requests for the corresponding object.
|
||||
* If running a legacy Kubernetes version, you may use this to override the default api versions
|
||||
* that are not supported in your cluster.
|
||||
*/
|
||||
apiVersionOverrides?: {
|
||||
pods?: string;
|
||||
services?: string;
|
||||
configmaps?: string;
|
||||
deployments?: string;
|
||||
replicasets?: string;
|
||||
horizontalpodautoscalers?: string;
|
||||
cronjobs?: string;
|
||||
jobs?: string;
|
||||
ingresses?: string;
|
||||
} & { [pluralKind: string]: string };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -243,6 +243,10 @@ export class KubernetesBuilder {
|
||||
'kubernetes.objectTypes',
|
||||
) as KubernetesObjectTypes[];
|
||||
|
||||
const apiVersionOverrides = this.env.config.getOptionalConfig(
|
||||
'kubernetes.apiVersionOverrides',
|
||||
);
|
||||
|
||||
let objectTypesToFetch;
|
||||
|
||||
if (objectTypesToFetchStrings) {
|
||||
@@ -250,6 +254,17 @@ export class KubernetesBuilder {
|
||||
objectTypesToFetchStrings.includes(obj.objectType),
|
||||
);
|
||||
}
|
||||
|
||||
if (apiVersionOverrides) {
|
||||
objectTypesToFetch = objectTypesToFetch ?? DEFAULT_OBJECTS;
|
||||
|
||||
for (const obj of objectTypesToFetch) {
|
||||
if (apiVersionOverrides.has(obj.objectType)) {
|
||||
obj.apiVersion = apiVersionOverrides.getString(obj.objectType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return objectTypesToFetch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
</TestApiProvider>
|
||||
),
|
||||
})
|
||||
.addPage({
|
||||
path: '/fixture-3',
|
||||
title: 'Fixture 3',
|
||||
element: (
|
||||
<TestApiProvider
|
||||
apis={[[kubernetesApiRef, new MockKubernetesClient(fixture3)]]}
|
||||
>
|
||||
<EntityProvider entity={mockEntity}>
|
||||
<EntityKubernetesContent />
|
||||
</EntityProvider>
|
||||
</TestApiProvider>
|
||||
),
|
||||
})
|
||||
.addPage({
|
||||
path: '/fixture-4',
|
||||
title: 'Fixture 4',
|
||||
element: (
|
||||
<TestApiProvider
|
||||
apis={[[kubernetesApiRef, new MockKubernetesClient(fixture4)]]}
|
||||
>
|
||||
<EntityProvider entity={mockEntity}>
|
||||
<EntityKubernetesContent />
|
||||
</EntityProvider>
|
||||
</TestApiProvider>
|
||||
),
|
||||
})
|
||||
.registerPlugin(kubernetesPlugin)
|
||||
.render();
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import { DeploymentsAccordions } from '../DeploymentsAccordions';
|
||||
import { groupResponses } from '../../utils/response';
|
||||
import { IngressesAccordions } from '../IngressesAccordions';
|
||||
import { ServicesAccordions } from '../ServicesAccordions';
|
||||
import { CronJobsAccordions } from '../CronJobsAccordions';
|
||||
import { CustomResources } from '../CustomResources';
|
||||
import {
|
||||
ClusterContext,
|
||||
@@ -133,6 +134,9 @@ export const Cluster = ({ clusterObjects, podsWithErrors }: ClusterProps) => {
|
||||
<Grid item>
|
||||
<ServicesAccordions />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<CronJobsAccordions />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
@@ -0,0 +1,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(<CronJobsAccordions />)),
|
||||
);
|
||||
|
||||
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(<CronJobsAccordions />)),
|
||||
);
|
||||
|
||||
expect(getByText('dice-roller-cronjob')).toBeInTheDocument();
|
||||
expect(getByText('CronJob')).toBeInTheDocument();
|
||||
expect(getByText('namespace: default')).toBeInTheDocument();
|
||||
expect(getByText('Suspended')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import { V1CronJob, V1Job } from '@kubernetes/client-node';
|
||||
import { JobsAccordions } from '../JobsAccordions';
|
||||
import { CronJobDrawer } from './CronJobsDrawer';
|
||||
import { getOwnedResources } from '../../utils/owner';
|
||||
import { GroupedResponsesContext } from '../../hooks';
|
||||
import { StatusError, StatusOK } from '@backstage/core-components';
|
||||
import cronstrue from 'cronstrue';
|
||||
|
||||
type CronJobsAccordionsProps = {
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
type CronJobAccordionProps = {
|
||||
cronJob: V1CronJob;
|
||||
ownedJobs: V1Job[];
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
type CronJobSummaryProps = {
|
||||
cronJob: V1CronJob;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
const CronJobSummary = ({ cronJob }: CronJobSummaryProps) => {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid xs={3} item>
|
||||
<CronJobDrawer cronJob={cronJob} />
|
||||
</Grid>
|
||||
<Grid item xs={1}>
|
||||
<Divider style={{ height: '5em' }} orientation="vertical" />
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
xs={5}
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<Grid item>
|
||||
{cronJob.spec?.suspend ? (
|
||||
<StatusError>Suspended</StatusError>
|
||||
) : (
|
||||
<StatusOK>Active</StatusOK>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="body1">
|
||||
Schedule:{' '}
|
||||
{cronJob.spec?.schedule
|
||||
? `${cronJob.spec.schedule} (${cronstrue.toString(
|
||||
cronJob.spec.schedule,
|
||||
)})`
|
||||
: 'N/A'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const CronJobAccordion = ({ cronJob, ownedJobs }: CronJobAccordionProps) => {
|
||||
return (
|
||||
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<CronJobSummary cronJob={cronJob} />
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<JobsAccordions jobs={ownedJobs.reverse()} />
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export const CronJobsAccordions = ({}: CronJobsAccordionsProps) => {
|
||||
const groupedResponses = useContext(GroupedResponsesContext);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
{groupedResponses.cronJobs.map((cronJob, i) => (
|
||||
<Grid container item key={i} xs>
|
||||
<Grid item xs>
|
||||
<CronJobAccordion
|
||||
ownedJobs={getOwnedResources(cronJob, groupedResponses.jobs)}
|
||||
cronJob={cronJob}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,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(
|
||||
<CronJobDrawer
|
||||
cronJob={(oneCronJobsFixture as any).cronJobs[0]}
|
||||
expanded
|
||||
/>,
|
||||
);
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { V1CronJob } from '@kubernetes/client-node';
|
||||
import { KubernetesDrawer } from '../KubernetesDrawer/KubernetesDrawer';
|
||||
import { Typography, Grid, Chip } from '@material-ui/core';
|
||||
|
||||
export const CronJobDrawer = ({
|
||||
cronJob,
|
||||
expanded,
|
||||
}: {
|
||||
cronJob: V1CronJob;
|
||||
expanded?: boolean;
|
||||
}) => {
|
||||
const namespace = cronJob.metadata?.namespace;
|
||||
return (
|
||||
<KubernetesDrawer
|
||||
object={cronJob}
|
||||
expanded={expanded}
|
||||
kind="CronJob"
|
||||
renderObject={(cronJobObj: V1CronJob) => ({
|
||||
schedule: cronJobObj.spec?.schedule ?? '???',
|
||||
startingDeadlineSeconds:
|
||||
cronJobObj.spec?.startingDeadlineSeconds ?? '???',
|
||||
concurrencyPolicy: cronJobObj.spec?.concurrencyPolicy ?? '???',
|
||||
lastScheduleTime: cronJobObj.status?.lastScheduleTime ?? '???',
|
||||
})}
|
||||
>
|
||||
<Grid
|
||||
container
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
spacing={0}
|
||||
>
|
||||
<Grid item>
|
||||
<Typography variant="h5">
|
||||
{cronJob.metadata?.name ?? 'unknown object'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography color="textSecondary" variant="body1">
|
||||
CronJob
|
||||
</Typography>
|
||||
</Grid>
|
||||
{namespace && (
|
||||
<Grid item>
|
||||
<Chip size="small" label={`namespace: ${namespace}`} />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</KubernetesDrawer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { CronJobsAccordions } from './CronJobsAccordions';
|
||||
@@ -36,11 +36,12 @@ import {
|
||||
getOwnedPodsThroughReplicaSets,
|
||||
getMatchingHpa,
|
||||
} from '../../utils/owner';
|
||||
import { containersReady, totalRestarts } from '../../utils/pod';
|
||||
import {
|
||||
GroupedResponsesContext,
|
||||
PodNamesWithErrorsContext,
|
||||
} from '../../hooks';
|
||||
import { StatusError, StatusOK } from '@backstage/core-components';
|
||||
import { StatusError, StatusOK, TableColumn } from '@backstage/core-components';
|
||||
|
||||
type DeploymentsAccordionsProps = {
|
||||
children?: React.ReactNode;
|
||||
@@ -61,6 +62,20 @@ type DeploymentSummaryProps = {
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
const deploymentPodColumns: TableColumn<V1Pod>[] = [
|
||||
{
|
||||
title: 'containers ready',
|
||||
align: 'center',
|
||||
render: containersReady,
|
||||
},
|
||||
{
|
||||
title: 'total restarts',
|
||||
align: 'center',
|
||||
render: totalRestarts,
|
||||
type: 'numeric',
|
||||
},
|
||||
];
|
||||
|
||||
const DeploymentSummary = ({
|
||||
deployment,
|
||||
numberOfCurrentPods,
|
||||
@@ -161,7 +176,7 @@ const DeploymentAccordion = ({
|
||||
/>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<PodsTable pods={ownedPods} />
|
||||
<PodsTable pods={ownedPods} extraColumns={deploymentPodColumns} />
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,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(<JobsAccordions jobs={jobs} />)),
|
||||
);
|
||||
|
||||
expect(getByText('dice-roller-cronjob-1637028600')).toBeInTheDocument();
|
||||
expect(getByText('Running')).toBeInTheDocument();
|
||||
|
||||
expect(getByText('dice-roller-cronjob-1637025000')).toBeInTheDocument();
|
||||
expect(getByText('Succeeded')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React, { useContext } from 'react';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Divider,
|
||||
Grid,
|
||||
} from '@material-ui/core';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import { V1Job, V1Pod } from '@kubernetes/client-node';
|
||||
import { PodsTable } from '../Pods';
|
||||
import { JobDrawer } from './JobsDrawer';
|
||||
import { getOwnedResources } from '../../utils/owner';
|
||||
import { GroupedResponsesContext } from '../../hooks';
|
||||
import {
|
||||
StatusError,
|
||||
StatusOK,
|
||||
StatusPending,
|
||||
} from '@backstage/core-components';
|
||||
|
||||
type JobsAccordionsProps = {
|
||||
jobs: V1Job[];
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
type JobAccordionProps = {
|
||||
job: V1Job;
|
||||
ownedPods: V1Pod[];
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
type JobSummaryProps = {
|
||||
job: V1Job;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
const JobSummary = ({ job }: JobSummaryProps) => {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid xs={3} item>
|
||||
<JobDrawer job={job} />
|
||||
</Grid>
|
||||
<Grid item xs={1}>
|
||||
<Divider style={{ height: '5em' }} orientation="vertical" />
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
xs={8}
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<Grid item>
|
||||
{job.status?.succeeded && <StatusOK>Succeeded</StatusOK>}
|
||||
{job.status?.active && <StatusPending>Running</StatusPending>}
|
||||
{job.status?.failed && <StatusError>Failed</StatusError>}
|
||||
</Grid>
|
||||
<Grid item>Start time: {job.status?.startTime?.toString()}</Grid>
|
||||
{job.status?.completionTime && (
|
||||
<Grid item>
|
||||
Completion time: {job.status.completionTime.toString()}
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const JobAccordion = ({ job, ownedPods }: JobAccordionProps) => {
|
||||
return (
|
||||
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<JobSummary job={job} />
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<PodsTable pods={ownedPods} />
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export const JobsAccordions = ({ jobs }: JobsAccordionsProps) => {
|
||||
const groupedResponses = useContext(GroupedResponsesContext);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
>
|
||||
{jobs.map((job, i) => (
|
||||
<Grid container item key={i} xs>
|
||||
<Grid item xs>
|
||||
<JobAccordion
|
||||
ownedPods={getOwnedResources(job, groupedResponses.pods)}
|
||||
job={job}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,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(
|
||||
<JobDrawer job={(oneCronJobsFixture as any).jobs[0]} expanded />,
|
||||
);
|
||||
|
||||
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('Backoff Limit')).toBeInTheDocument();
|
||||
expect(getByText('Start Time')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { V1Job } from '@kubernetes/client-node';
|
||||
import { KubernetesDrawer } from '../KubernetesDrawer/KubernetesDrawer';
|
||||
import { Typography, Grid } from '@material-ui/core';
|
||||
|
||||
export const JobDrawer = ({
|
||||
job,
|
||||
expanded,
|
||||
}: {
|
||||
job: V1Job;
|
||||
expanded?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<KubernetesDrawer
|
||||
object={job}
|
||||
expanded={expanded}
|
||||
kind="Job"
|
||||
renderObject={(jobObj: V1Job) => {
|
||||
return {
|
||||
parallelism: jobObj.spec?.parallelism ?? '???',
|
||||
completions: jobObj.spec?.completions ?? '???',
|
||||
backoffLimit: jobObj.spec?.backoffLimit ?? '???',
|
||||
startTime: jobObj.status?.startTime ?? '???',
|
||||
};
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
container
|
||||
direction="column"
|
||||
justifyContent="flex-start"
|
||||
alignItems="flex-start"
|
||||
spacing={0}
|
||||
>
|
||||
<Grid item>
|
||||
<Typography variant="h6">
|
||||
{job.metadata?.name ?? 'unknown object'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography color="textSecondary" variant="body1">
|
||||
Job
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</KubernetesDrawer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { JobsAccordions } from './JobsAccordions';
|
||||
+40
-2
@@ -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<V1Pod>[] = [
|
||||
{
|
||||
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(<PodsTable pods={[pod as any]} />),
|
||||
);
|
||||
|
||||
// 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(
|
||||
<PodsTable pods={[pod as any]} extraColumns={extraColumns} />,
|
||||
),
|
||||
);
|
||||
|
||||
// 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(<PodsTable pods={[crashingPod as any]} />),
|
||||
wrapInTestApp(
|
||||
<PodsTable pods={[crashingPod as any]} extraColumns={extraColumns} />,
|
||||
),
|
||||
);
|
||||
|
||||
// titles
|
||||
|
||||
+6
-20
@@ -17,14 +17,10 @@
|
||||
import React from 'react';
|
||||
import { V1Pod } from '@kubernetes/client-node';
|
||||
import { PodDrawer } from './PodDrawer';
|
||||
import {
|
||||
containersReady,
|
||||
containerStatuses,
|
||||
totalRestarts,
|
||||
} from '../../utils/pod';
|
||||
import { containerStatuses } from '../../utils/pod';
|
||||
import { Table, TableColumn } from '@backstage/core-components';
|
||||
|
||||
const columns: TableColumn<V1Pod>[] = [
|
||||
const DEFAULT_COLUMNS: TableColumn<V1Pod>[] = [
|
||||
{
|
||||
title: 'name',
|
||||
highlight: true,
|
||||
@@ -34,29 +30,19 @@ const columns: TableColumn<V1Pod>[] = [
|
||||
title: 'phase',
|
||||
render: (pod: V1Pod) => pod.status?.phase ?? 'unknown',
|
||||
},
|
||||
{
|
||||
title: 'containers ready',
|
||||
align: 'center',
|
||||
render: containersReady,
|
||||
},
|
||||
{
|
||||
title: 'total restarts',
|
||||
align: 'center',
|
||||
render: totalRestarts,
|
||||
type: 'numeric',
|
||||
},
|
||||
{
|
||||
title: 'status',
|
||||
render: containerStatuses,
|
||||
},
|
||||
];
|
||||
|
||||
type DeploymentTablesProps = {
|
||||
type PodsTablesProps = {
|
||||
pods: V1Pod[];
|
||||
extraColumns?: TableColumn<V1Pod>[];
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const PodsTable = ({ pods }: DeploymentTablesProps) => {
|
||||
export const PodsTable = ({ pods, extraColumns = [] }: PodsTablesProps) => {
|
||||
const tableStyle = {
|
||||
minWidth: '0',
|
||||
width: '100%',
|
||||
@@ -67,7 +53,7 @@ export const PodsTable = ({ pods }: DeploymentTablesProps) => {
|
||||
<Table
|
||||
options={{ paging: true, search: false }}
|
||||
data={pods}
|
||||
columns={columns}
|
||||
columns={DEFAULT_COLUMNS.concat(extraColumns)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -24,5 +24,7 @@ export const GroupedResponsesContext = React.createContext<GroupedResponses>({
|
||||
configMaps: [],
|
||||
horizontalPodAutoscalers: [],
|
||||
ingresses: [],
|
||||
jobs: [],
|
||||
cronJobs: [],
|
||||
customResources: [],
|
||||
});
|
||||
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
V1Service,
|
||||
V1ConfigMap,
|
||||
ExtensionsV1beta1Ingress,
|
||||
V1Job,
|
||||
V1CronJob,
|
||||
} from '@kubernetes/client-node';
|
||||
|
||||
export interface DeploymentResources {
|
||||
@@ -35,6 +37,8 @@ export interface GroupedResponses extends DeploymentResources {
|
||||
services: V1Service[];
|
||||
configMaps: V1ConfigMap[];
|
||||
ingresses: ExtensionsV1beta1Ingress[];
|
||||
jobs: V1Job[];
|
||||
cronJobs: V1CronJob[];
|
||||
customResources: any[];
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ export const groupResponses = (
|
||||
case 'ingresses':
|
||||
prev.ingresses.push(...next.resources);
|
||||
break;
|
||||
case 'jobs':
|
||||
prev.jobs.push(...next.resources);
|
||||
break;
|
||||
case 'cronjobs':
|
||||
prev.cronJobs.push(...next.resources);
|
||||
break;
|
||||
case 'customresources':
|
||||
prev.customResources.push(...next.resources);
|
||||
break;
|
||||
@@ -60,6 +66,8 @@ export const groupResponses = (
|
||||
configMaps: [],
|
||||
horizontalPodAutoscalers: [],
|
||||
ingresses: [],
|
||||
jobs: [],
|
||||
cronJobs: [],
|
||||
customResources: [],
|
||||
} as GroupedResponses,
|
||||
);
|
||||
|
||||
@@ -24,6 +24,7 @@ import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { Schema } from 'jsonschema';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { SpawnOptionsWithoutStdio } from 'child_process';
|
||||
import { TemplateEntityV1beta2 } from '@backstage/catalog-model';
|
||||
import { UrlReader } from '@backstage/backend-common';
|
||||
import { Writable } from 'stream';
|
||||
@@ -304,11 +305,12 @@ export interface RouterOptions {
|
||||
// Warning: (ae-forgotten-export) The symbol "RunCommandOptions" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "runCommand" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export const runCommand: ({
|
||||
command,
|
||||
args,
|
||||
logStream,
|
||||
options,
|
||||
}: RunCommandOptions) => Promise<void>;
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import { SpawnOptionsWithoutStdio, spawn } from 'child_process';
|
||||
import { PassThrough, Writable } from 'stream';
|
||||
import { Logger } from 'winston';
|
||||
import { Git } from '@backstage/backend-common';
|
||||
@@ -22,18 +22,27 @@ import { Octokit } from '@octokit/rest';
|
||||
import { assertError } from '@backstage/errors';
|
||||
|
||||
export type RunCommandOptions = {
|
||||
/** command to run */
|
||||
command: string;
|
||||
/** arguments to pass the command */
|
||||
args: string[];
|
||||
/** options to pass to spawn */
|
||||
options?: SpawnOptionsWithoutStdio;
|
||||
/** stream to capture stdout and stderr output */
|
||||
logStream?: Writable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run a command in a sub-process, normally a shell command.
|
||||
*/
|
||||
export const runCommand = async ({
|
||||
command,
|
||||
args,
|
||||
logStream = new PassThrough(),
|
||||
options,
|
||||
}: RunCommandOptions) => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const process = spawn(command, args);
|
||||
const process = spawn(command, args, options);
|
||||
|
||||
process.stdout.on('data', stream => {
|
||||
logStream.write(stream);
|
||||
|
||||
Reference in New Issue
Block a user