@terasky/backstage-plugin-vcf-automation 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/VcfAutomationClient.esm.js +148 -0
- package/dist/api/VcfAutomationClient.esm.js.map +1 -1
- package/dist/components/VCFAutomationCCINamespaceDetails.esm.js +16 -2
- package/dist/components/VCFAutomationCCINamespaceDetails.esm.js.map +1 -1
- package/dist/components/VCFAutomationCCINamespaceOverview.esm.js +35 -7
- package/dist/components/VCFAutomationCCINamespaceOverview.esm.js.map +1 -1
- package/dist/components/VCFAutomationCCIResourceDetails.esm.js +400 -19
- package/dist/components/VCFAutomationCCIResourceDetails.esm.js.map +1 -1
- package/dist/components/VCFAutomationCCIResourceOverview.esm.js +506 -106
- package/dist/components/VCFAutomationCCIResourceOverview.esm.js.map +1 -1
- package/dist/components/VCFAutomationVMPowerManagement.esm.js +262 -0
- package/dist/components/VCFAutomationVMPowerManagement.esm.js.map +1 -0
- package/dist/index.d.ts +83 -1
- package/dist/index.esm.js +1 -0
- package/dist/index.esm.js.map +1 -1
- package/package.json +3 -2
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
|
-
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
2
|
+
import { useState, useMemo, useCallback } from 'react';
|
|
3
|
+
import { useEntity, catalogApiRef } from '@backstage/plugin-catalog-react';
|
|
4
4
|
import { useApi } from '@backstage/core-plugin-api';
|
|
5
|
+
import { usePermission } from '@backstage/plugin-permission-react';
|
|
5
6
|
import { vcfAutomationApiRef } from '../api/VcfAutomationClient.esm.js';
|
|
7
|
+
import { supervisorResourceEditPermission } from '@terasky/backstage-plugin-vcf-automation-common';
|
|
8
|
+
import Editor from '@monaco-editor/react';
|
|
6
9
|
import { InfoCard, Progress, ResponseErrorPanel, StructuredMetadataTable, CodeSnippet, StatusPending, StatusError, StatusOK } from '@backstage/core-components';
|
|
7
|
-
import { Typography, Grid, Box, Chip, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core';
|
|
10
|
+
import { Typography, Grid, Box, Chip, Button, Accordion, AccordionSummary, AccordionDetails, Dialog, DialogTitle, DialogContent, DialogActions, Snackbar } from '@material-ui/core';
|
|
11
|
+
import { Alert } from '@material-ui/lab';
|
|
8
12
|
import { makeStyles } from '@material-ui/core/styles';
|
|
9
13
|
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
|
10
|
-
import
|
|
14
|
+
import EditIcon from '@material-ui/icons/Edit';
|
|
15
|
+
import yaml__default from 'js-yaml';
|
|
11
16
|
import useAsync from 'react-use/lib/useAsync';
|
|
17
|
+
import { VCFAutomationVMPowerManagement } from './VCFAutomationVMPowerManagement.esm.js';
|
|
12
18
|
|
|
13
19
|
const useStyles = makeStyles((theme) => ({
|
|
14
20
|
statusChip: {
|
|
@@ -29,15 +35,67 @@ const useStyles = makeStyles((theme) => ({
|
|
|
29
35
|
dependencyChip: {
|
|
30
36
|
margin: theme.spacing(0.25),
|
|
31
37
|
cursor: "pointer"
|
|
38
|
+
},
|
|
39
|
+
editButton: {
|
|
40
|
+
marginTop: theme.spacing(1)
|
|
41
|
+
},
|
|
42
|
+
monacoEditor: {
|
|
43
|
+
flex: 1,
|
|
44
|
+
minHeight: "500px",
|
|
45
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
46
|
+
borderRadius: theme.shape.borderRadius
|
|
47
|
+
},
|
|
48
|
+
validationStatus: {
|
|
49
|
+
padding: theme.spacing(1),
|
|
50
|
+
borderTop: `1px solid ${theme.palette.divider}`,
|
|
51
|
+
backgroundColor: theme.palette.background.paper,
|
|
52
|
+
flexShrink: 0
|
|
53
|
+
},
|
|
54
|
+
dialogContent: {
|
|
55
|
+
padding: theme.spacing(2),
|
|
56
|
+
height: "100%",
|
|
57
|
+
display: "flex",
|
|
58
|
+
flexDirection: "column",
|
|
59
|
+
overflow: "hidden"
|
|
60
|
+
},
|
|
61
|
+
editorContainer: {
|
|
62
|
+
flex: 1,
|
|
63
|
+
display: "flex",
|
|
64
|
+
flexDirection: "column",
|
|
65
|
+
minHeight: 0,
|
|
66
|
+
overflow: "hidden"
|
|
67
|
+
},
|
|
68
|
+
yamlValidationError: {
|
|
69
|
+
color: theme.palette.error.main,
|
|
70
|
+
marginTop: theme.spacing(1),
|
|
71
|
+
fontSize: "0.875rem"
|
|
32
72
|
}
|
|
33
73
|
}));
|
|
34
74
|
const VCFAutomationCCIResourceOverview = () => {
|
|
35
75
|
const classes = useStyles();
|
|
36
76
|
const { entity } = useEntity();
|
|
37
77
|
const api = useApi(vcfAutomationApiRef);
|
|
78
|
+
const catalogApi = useApi(catalogApiRef);
|
|
79
|
+
const { allowed: canEditResource } = usePermission({
|
|
80
|
+
permission: supervisorResourceEditPermission
|
|
81
|
+
});
|
|
82
|
+
const [editModalOpen, setEditModalOpen] = useState(false);
|
|
83
|
+
const [editingYaml, setEditingYaml] = useState("");
|
|
84
|
+
const [originalManifest, setOriginalManifest] = useState(null);
|
|
85
|
+
const [isLoadingManifest, setIsLoadingManifest] = useState(false);
|
|
86
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
87
|
+
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
|
|
88
|
+
const [yamlValidationError, setYamlValidationError] = useState("");
|
|
89
|
+
const [snackbar, setSnackbar] = useState({
|
|
90
|
+
open: false,
|
|
91
|
+
message: "",
|
|
92
|
+
severity: "success"
|
|
93
|
+
});
|
|
38
94
|
const deploymentId = entity.spec?.system;
|
|
39
95
|
const resourceId = entity.metadata.name;
|
|
40
96
|
const instanceName = entity.metadata.annotations?.["terasky.backstage.io/vcf-automation-instance"];
|
|
97
|
+
const isStandalone = entity.metadata.annotations?.["terasky.backstage.io/vcf-automation-resource-origin"] === "STANDALONE";
|
|
98
|
+
const vmOrganizationType = entity.metadata.annotations?.["terasky.backstage.io/vcf-automation-version"] === "9" && entity.metadata.tags?.some((tag) => tag.startsWith("vcf-automation:")) ? "all-apps" : "vm-apps";
|
|
41
99
|
const annotationData = useMemo(() => {
|
|
42
100
|
const resourceProperties = entity.metadata.annotations?.["terasky.backstage.io/vcf-automation-resource-properties"];
|
|
43
101
|
const resourceManifest = entity.metadata.annotations?.["terasky.backstage.io/vcf-automation-cci-resource-manifest"];
|
|
@@ -57,32 +115,214 @@ const VCFAutomationCCIResourceOverview = () => {
|
|
|
57
115
|
]);
|
|
58
116
|
const needsApiCall = !annotationData.resourceData || !annotationData.manifest || !annotationData.objectData;
|
|
59
117
|
const { value: apiResourceData, loading, error } = useAsync(async () => {
|
|
60
|
-
if (!needsApiCall || !
|
|
118
|
+
if (!needsApiCall || !resourceId) {
|
|
61
119
|
return null;
|
|
62
120
|
}
|
|
63
121
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
122
|
+
if (isStandalone) {
|
|
123
|
+
const response = await api.getSupervisorResource(resourceId, instanceName);
|
|
124
|
+
if (response) {
|
|
125
|
+
return {
|
|
126
|
+
id: response.id,
|
|
127
|
+
properties: {
|
|
128
|
+
manifest: {
|
|
129
|
+
apiVersion: response.apiVersion,
|
|
130
|
+
kind: response.kind,
|
|
131
|
+
metadata: response.metadata,
|
|
132
|
+
spec: response.spec
|
|
133
|
+
},
|
|
134
|
+
object: {
|
|
135
|
+
apiVersion: response.apiVersion,
|
|
136
|
+
kind: response.kind,
|
|
137
|
+
metadata: response.metadata,
|
|
138
|
+
spec: response.spec,
|
|
139
|
+
status: response.status
|
|
140
|
+
},
|
|
141
|
+
context: JSON.stringify({
|
|
142
|
+
namespace: response.metadata.namespace,
|
|
143
|
+
apiVersion: response.apiVersion,
|
|
144
|
+
kind: response.kind,
|
|
145
|
+
standalone: true
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
if (!deploymentId) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const response = await api.getDeploymentResources(deploymentId, instanceName);
|
|
155
|
+
let resources = null;
|
|
156
|
+
if (response) {
|
|
157
|
+
if (Array.isArray(response)) {
|
|
158
|
+
resources = response;
|
|
159
|
+
} else if (response.content && Array.isArray(response.content)) {
|
|
160
|
+
resources = response.content;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (resources) {
|
|
164
|
+
return resources.find((r) => r.id === resourceId);
|
|
71
165
|
}
|
|
72
|
-
}
|
|
73
|
-
if (resources) {
|
|
74
|
-
return resources.find((r) => r.id === resourceId);
|
|
75
166
|
}
|
|
76
167
|
return null;
|
|
77
168
|
} catch (apiError) {
|
|
78
169
|
console.error("Failed to fetch resource data:", apiError);
|
|
79
170
|
return null;
|
|
80
171
|
}
|
|
81
|
-
}, [needsApiCall, deploymentId, resourceId, instanceName]);
|
|
172
|
+
}, [needsApiCall, isStandalone, deploymentId, resourceId, instanceName]);
|
|
82
173
|
const resourceData = annotationData.resourceData || apiResourceData;
|
|
83
174
|
const manifest = annotationData.manifest || apiResourceData?.properties?.manifest;
|
|
84
175
|
const objectData = annotationData.objectData || apiResourceData?.properties?.object;
|
|
85
176
|
const resourceContext = annotationData.resourceContext || apiResourceData?.properties?.context;
|
|
177
|
+
const resourceKind = manifest?.kind || objectData?.kind;
|
|
178
|
+
const resourceName = manifest?.metadata?.name || objectData?.metadata?.name;
|
|
179
|
+
const namespaceName = manifest?.metadata?.namespace || objectData?.metadata?.namespace;
|
|
180
|
+
const apiVersion = manifest?.apiVersion || objectData?.apiVersion;
|
|
181
|
+
const vmName = resourceName;
|
|
182
|
+
const extractNamespaceUrnId = useCallback((endpoint) => {
|
|
183
|
+
const match = endpoint.match(/\/namespaces\/(urn:vcloud:namespace:[^\/]+)/);
|
|
184
|
+
return match ? match[1] : void 0;
|
|
185
|
+
}, []);
|
|
186
|
+
const findCCINamespaceParent = useCallback(async (currentEntity, depth = 0) => {
|
|
187
|
+
if (depth >= 3) return void 0;
|
|
188
|
+
if (currentEntity.spec?.type === "CCI.Supervisor.Namespace") {
|
|
189
|
+
const endpoint = currentEntity.metadata?.annotations?.["terasky.backstage.io/vcf-automation-cci-namespace-endpoint"];
|
|
190
|
+
if (endpoint) {
|
|
191
|
+
return extractNamespaceUrnId(endpoint);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const parentRef = currentEntity.spec?.subcomponentOf;
|
|
195
|
+
if (!parentRef) return void 0;
|
|
196
|
+
try {
|
|
197
|
+
const parentEntity = await catalogApi.getEntityByRef(parentRef);
|
|
198
|
+
if (parentEntity) {
|
|
199
|
+
return await findCCINamespaceParent(parentEntity, depth + 1);
|
|
200
|
+
}
|
|
201
|
+
} catch (error2) {
|
|
202
|
+
console.warn("Failed to fetch parent entity:", parentRef, error2);
|
|
203
|
+
}
|
|
204
|
+
return void 0;
|
|
205
|
+
}, [catalogApi, extractNamespaceUrnId]);
|
|
206
|
+
const { value: namespaceUrnId } = useAsync(async () => {
|
|
207
|
+
if (!namespaceName) return void 0;
|
|
208
|
+
if (isStandalone) {
|
|
209
|
+
let contextData = null;
|
|
210
|
+
try {
|
|
211
|
+
contextData = typeof resourceContext === "string" ? JSON.parse(resourceContext || "{}") : resourceContext;
|
|
212
|
+
} catch (error2) {
|
|
213
|
+
console.log("Debug - Resource context is not JSON, treating as string:", resourceContext);
|
|
214
|
+
contextData = null;
|
|
215
|
+
}
|
|
216
|
+
const urnId = contextData?.namespaceUrnId || namespaceName;
|
|
217
|
+
console.log("Debug - Standalone CCI Resource URN ID resolution:", {
|
|
218
|
+
namespaceName,
|
|
219
|
+
resourceContext,
|
|
220
|
+
contextData,
|
|
221
|
+
urnId
|
|
222
|
+
});
|
|
223
|
+
return urnId;
|
|
224
|
+
} else {
|
|
225
|
+
console.log("Debug - Looking for CCI Supervisor Namespace parent for deployment-managed resource");
|
|
226
|
+
const urnId = await findCCINamespaceParent(entity);
|
|
227
|
+
console.log("Debug - Deployment-managed CCI Resource URN ID resolution:", {
|
|
228
|
+
namespaceName,
|
|
229
|
+
urnId,
|
|
230
|
+
entityRef: entity.metadata.name
|
|
231
|
+
});
|
|
232
|
+
return urnId || namespaceName;
|
|
233
|
+
}
|
|
234
|
+
}, [namespaceName, resourceContext, isStandalone, entity, findCCINamespaceParent]);
|
|
235
|
+
const handleEditResource = useCallback(async () => {
|
|
236
|
+
if (!canEditResource || !namespaceName || !resourceName || !namespaceUrnId || !apiVersion || !resourceKind) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
setIsLoadingManifest(true);
|
|
240
|
+
setEditModalOpen(true);
|
|
241
|
+
try {
|
|
242
|
+
const manifestResponse = await api.getSupervisorResourceManifest(
|
|
243
|
+
namespaceUrnId,
|
|
244
|
+
namespaceName,
|
|
245
|
+
resourceName,
|
|
246
|
+
apiVersion,
|
|
247
|
+
resourceKind,
|
|
248
|
+
instanceName
|
|
249
|
+
);
|
|
250
|
+
setOriginalManifest(manifestResponse);
|
|
251
|
+
const yamlContent = yaml__default.dump(manifestResponse, {
|
|
252
|
+
indent: 2,
|
|
253
|
+
lineWidth: -1,
|
|
254
|
+
noRefs: true,
|
|
255
|
+
sortKeys: false
|
|
256
|
+
});
|
|
257
|
+
setEditingYaml(yamlContent);
|
|
258
|
+
setYamlValidationError("");
|
|
259
|
+
} catch (error2) {
|
|
260
|
+
setSnackbar({
|
|
261
|
+
open: true,
|
|
262
|
+
message: `Failed to fetch resource manifest: ${error2 instanceof Error ? error2.message : "Unknown error"}`,
|
|
263
|
+
severity: "error"
|
|
264
|
+
});
|
|
265
|
+
setEditModalOpen(false);
|
|
266
|
+
} finally {
|
|
267
|
+
setIsLoadingManifest(false);
|
|
268
|
+
}
|
|
269
|
+
}, [canEditResource, namespaceName, resourceName, namespaceUrnId, apiVersion, resourceKind, instanceName, api]);
|
|
270
|
+
const handleSaveResource = useCallback(async () => {
|
|
271
|
+
if (!originalManifest || !namespaceName || !resourceName || !namespaceUrnId || !apiVersion || !resourceKind) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
setIsSaving(true);
|
|
275
|
+
setConfirmDialogOpen(false);
|
|
276
|
+
try {
|
|
277
|
+
const updatedManifest = yaml__default.load(editingYaml);
|
|
278
|
+
await api.updateSupervisorResourceManifest(
|
|
279
|
+
namespaceUrnId,
|
|
280
|
+
namespaceName,
|
|
281
|
+
resourceName,
|
|
282
|
+
apiVersion,
|
|
283
|
+
resourceKind,
|
|
284
|
+
updatedManifest,
|
|
285
|
+
instanceName
|
|
286
|
+
);
|
|
287
|
+
setSnackbar({
|
|
288
|
+
open: true,
|
|
289
|
+
message: "Resource manifest updated successfully",
|
|
290
|
+
severity: "success"
|
|
291
|
+
});
|
|
292
|
+
setEditModalOpen(false);
|
|
293
|
+
setTimeout(() => window.location.reload(), 1e3);
|
|
294
|
+
} catch (error2) {
|
|
295
|
+
setSnackbar({
|
|
296
|
+
open: true,
|
|
297
|
+
message: `Failed to update resource manifest: ${error2 instanceof Error ? error2.message : "Unknown error"}`,
|
|
298
|
+
severity: "error"
|
|
299
|
+
});
|
|
300
|
+
} finally {
|
|
301
|
+
setIsSaving(false);
|
|
302
|
+
}
|
|
303
|
+
}, [originalManifest, namespaceName, resourceName, namespaceUrnId, apiVersion, resourceKind, editingYaml, instanceName, api]);
|
|
304
|
+
const handleCloseSnackbar = useCallback(() => {
|
|
305
|
+
setSnackbar((prev) => ({ ...prev, open: false }));
|
|
306
|
+
}, []);
|
|
307
|
+
const validateYaml = useCallback((yamlString) => {
|
|
308
|
+
try {
|
|
309
|
+
yaml__default.load(yamlString);
|
|
310
|
+
setYamlValidationError("");
|
|
311
|
+
return true;
|
|
312
|
+
} catch (error2) {
|
|
313
|
+
const errorMessage = error2 instanceof Error ? error2.message : "Invalid YAML syntax";
|
|
314
|
+
setYamlValidationError(errorMessage);
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
}, []);
|
|
318
|
+
const handleYamlChange = useCallback((value) => {
|
|
319
|
+
setEditingYaml(value);
|
|
320
|
+
if (value.trim()) {
|
|
321
|
+
validateYaml(value);
|
|
322
|
+
} else {
|
|
323
|
+
setYamlValidationError("");
|
|
324
|
+
}
|
|
325
|
+
}, [validateYaml]);
|
|
86
326
|
if (loading) {
|
|
87
327
|
return /* @__PURE__ */ jsx(InfoCard, { title: "CCI Supervisor Resource", children: /* @__PURE__ */ jsx(Progress, {}) });
|
|
88
328
|
}
|
|
@@ -123,7 +363,7 @@ const VCFAutomationCCIResourceOverview = () => {
|
|
|
123
363
|
const formatYaml = (data) => {
|
|
124
364
|
try {
|
|
125
365
|
const reorderedData = reorderKubernetesResource(data);
|
|
126
|
-
return
|
|
366
|
+
return yaml__default.dump(reorderedData, {
|
|
127
367
|
indent: 2,
|
|
128
368
|
lineWidth: -1,
|
|
129
369
|
noRefs: true,
|
|
@@ -156,109 +396,269 @@ const VCFAutomationCCIResourceOverview = () => {
|
|
|
156
396
|
return Object.keys(statusInfo).length > 0 ? statusInfo : null;
|
|
157
397
|
};
|
|
158
398
|
const objectStatus = getObjectStatus();
|
|
159
|
-
return /* @__PURE__ */
|
|
160
|
-
/* @__PURE__ */ jsxs(Grid, {
|
|
161
|
-
/* @__PURE__ */ jsx(
|
|
162
|
-
/* @__PURE__ */ jsx(StructuredMetadataTable, { metadata: basicInfo })
|
|
163
|
-
] }),
|
|
164
|
-
entity.spec?.dependsOn && Array.isArray(entity.spec.dependsOn) && entity.spec.dependsOn.length > 0 && /* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, children: [
|
|
165
|
-
/* @__PURE__ */ jsx(Typography, { variant: "h6", className: classes.sectionTitle, children: "Dependencies" }),
|
|
166
|
-
/* @__PURE__ */ jsx(Box, { children: entity.spec.dependsOn.filter((dep) => typeof dep === "string").map((dep, index) => /* @__PURE__ */ jsx(
|
|
399
|
+
return /* @__PURE__ */ jsxs(InfoCard, { title: "CCI Supervisor Resource Overview", children: [
|
|
400
|
+
/* @__PURE__ */ jsxs(Grid, { container: true, spacing: 3, children: [
|
|
401
|
+
isStandalone && /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(Box, { mb: 2, children: /* @__PURE__ */ jsx(
|
|
167
402
|
Chip,
|
|
168
403
|
{
|
|
169
|
-
label:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
/* @__PURE__ */
|
|
180
|
-
|
|
181
|
-
renderStatusIcon(condition.status),
|
|
404
|
+
label: "Standalone Resource",
|
|
405
|
+
color: "secondary",
|
|
406
|
+
variant: "outlined",
|
|
407
|
+
size: "small"
|
|
408
|
+
}
|
|
409
|
+
) }) }),
|
|
410
|
+
/* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, children: [
|
|
411
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", className: classes.sectionTitle, children: "Basic Information" }),
|
|
412
|
+
/* @__PURE__ */ jsx(StructuredMetadataTable, { metadata: basicInfo })
|
|
413
|
+
] }),
|
|
414
|
+
vmOrganizationType === "all-apps" && resourceKind === "VirtualMachine" && /* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, children: [
|
|
415
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", className: classes.sectionTitle, children: "Power Management" }),
|
|
182
416
|
/* @__PURE__ */ jsx(
|
|
183
|
-
|
|
417
|
+
VCFAutomationVMPowerManagement,
|
|
184
418
|
{
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
419
|
+
entity,
|
|
420
|
+
resourceId,
|
|
421
|
+
instanceName,
|
|
422
|
+
isStandalone,
|
|
423
|
+
vmName: isStandalone ? vmName : void 0,
|
|
424
|
+
namespaceName: isStandalone ? namespaceName : void 0,
|
|
425
|
+
namespaceUrnId: isStandalone ? namespaceUrnId : void 0
|
|
189
426
|
}
|
|
190
427
|
)
|
|
191
|
-
] },
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
428
|
+
] }),
|
|
429
|
+
canEditResource && vmOrganizationType === "all-apps" && resourceKind === "VirtualMachine" && resourceName && namespaceName && namespaceUrnId && apiVersion && /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsxs(Box, { mt: vmOrganizationType === "all-apps" ? 0 : 2, children: [
|
|
430
|
+
vmOrganizationType !== "all-apps" && /* @__PURE__ */ jsx(Typography, { variant: "h6", className: classes.sectionTitle, children: "Resource Management" }),
|
|
431
|
+
/* @__PURE__ */ jsx(
|
|
432
|
+
Button,
|
|
433
|
+
{
|
|
434
|
+
variant: "outlined",
|
|
435
|
+
color: "primary",
|
|
436
|
+
startIcon: /* @__PURE__ */ jsx(EditIcon, {}),
|
|
437
|
+
onClick: handleEditResource,
|
|
438
|
+
className: classes.editButton,
|
|
439
|
+
disabled: isLoadingManifest,
|
|
440
|
+
children: "Edit Resource Manifest"
|
|
441
|
+
}
|
|
442
|
+
)
|
|
443
|
+
] }) }),
|
|
444
|
+
canEditResource && (resourceKind !== "VirtualMachine" || vmOrganizationType !== "all-apps") && resourceName && namespaceName && namespaceUrnId && apiVersion && /* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, children: [
|
|
445
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", className: classes.sectionTitle, children: "Resource Management" }),
|
|
197
446
|
/* @__PURE__ */ jsx(
|
|
447
|
+
Button,
|
|
448
|
+
{
|
|
449
|
+
variant: "outlined",
|
|
450
|
+
color: "primary",
|
|
451
|
+
startIcon: /* @__PURE__ */ jsx(EditIcon, {}),
|
|
452
|
+
onClick: handleEditResource,
|
|
453
|
+
className: classes.editButton,
|
|
454
|
+
disabled: isLoadingManifest,
|
|
455
|
+
children: "Edit Resource Manifest"
|
|
456
|
+
}
|
|
457
|
+
)
|
|
458
|
+
] }),
|
|
459
|
+
entity.spec?.dependsOn && Array.isArray(entity.spec.dependsOn) && entity.spec.dependsOn.length > 0 && /* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, children: [
|
|
460
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", className: classes.sectionTitle, children: "Dependencies" }),
|
|
461
|
+
/* @__PURE__ */ jsx(Box, { children: entity.spec.dependsOn.filter((dep) => typeof dep === "string").map((dep, index) => /* @__PURE__ */ jsx(
|
|
198
462
|
Chip,
|
|
199
463
|
{
|
|
200
|
-
label:
|
|
464
|
+
label: dep,
|
|
201
465
|
size: "small",
|
|
202
|
-
className: classes.
|
|
203
|
-
color:
|
|
466
|
+
className: classes.dependencyChip,
|
|
467
|
+
color: "primary",
|
|
468
|
+
variant: "outlined"
|
|
469
|
+
},
|
|
470
|
+
index
|
|
471
|
+
)) })
|
|
472
|
+
] }),
|
|
473
|
+
resourceData.wait?.conditions && resourceData.wait.conditions.length > 0 && /* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, children: [
|
|
474
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", className: classes.sectionTitle, children: "Wait Conditions" }),
|
|
475
|
+
/* @__PURE__ */ jsx(Box, { children: resourceData.wait.conditions.map((condition, index) => /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", mb: 1, children: [
|
|
476
|
+
renderStatusIcon(condition.status),
|
|
477
|
+
/* @__PURE__ */ jsx(
|
|
478
|
+
Chip,
|
|
479
|
+
{
|
|
480
|
+
label: `${condition.type}: ${condition.status}`,
|
|
481
|
+
size: "small",
|
|
482
|
+
className: classes.conditionChip,
|
|
483
|
+
color: condition.status === "True" ? "primary" : "default"
|
|
484
|
+
}
|
|
485
|
+
)
|
|
486
|
+
] }, index)) })
|
|
487
|
+
] }),
|
|
488
|
+
objectData?.status?.conditions && objectData.status.conditions.length > 0 && /* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, children: [
|
|
489
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", className: classes.sectionTitle, children: "Resource Conditions" }),
|
|
490
|
+
/* @__PURE__ */ jsx(Box, { children: objectData.status.conditions.map((condition, index) => /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", mb: 1, children: [
|
|
491
|
+
renderStatusIcon(condition.status),
|
|
492
|
+
/* @__PURE__ */ jsx(
|
|
493
|
+
Chip,
|
|
494
|
+
{
|
|
495
|
+
label: `${condition.type}: ${condition.status}`,
|
|
496
|
+
size: "small",
|
|
497
|
+
className: classes.conditionChip,
|
|
498
|
+
color: condition.status === "True" ? "primary" : "default"
|
|
499
|
+
}
|
|
500
|
+
),
|
|
501
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", style: { marginLeft: 8 }, children: condition.lastTransitionTime })
|
|
502
|
+
] }, index)) })
|
|
503
|
+
] }),
|
|
504
|
+
objectStatus && /* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, children: [
|
|
505
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", className: classes.sectionTitle, children: "Resource Status" }),
|
|
506
|
+
/* @__PURE__ */ jsx(StructuredMetadataTable, { metadata: objectStatus })
|
|
507
|
+
] }),
|
|
508
|
+
manifest && /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsxs(Accordion, { className: classes.yamlContainer, children: [
|
|
509
|
+
/* @__PURE__ */ jsx(
|
|
510
|
+
AccordionSummary,
|
|
511
|
+
{
|
|
512
|
+
expandIcon: /* @__PURE__ */ jsx(ExpandMoreIcon, {}),
|
|
513
|
+
"aria-controls": "manifest-content",
|
|
514
|
+
id: "manifest-header",
|
|
515
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "h6", children: "Resource Manifest" })
|
|
204
516
|
}
|
|
205
517
|
),
|
|
206
|
-
/* @__PURE__ */ jsx(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
expandIcon: /* @__PURE__ */ jsx(ExpandMoreIcon, {}),
|
|
218
|
-
"aria-controls": "manifest-content",
|
|
219
|
-
id: "manifest-header",
|
|
220
|
-
children: /* @__PURE__ */ jsx(Typography, { variant: "h6", children: "Resource Manifest" })
|
|
221
|
-
}
|
|
222
|
-
),
|
|
223
|
-
/* @__PURE__ */ jsx(AccordionDetails, { children: /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(
|
|
224
|
-
CodeSnippet,
|
|
225
|
-
{
|
|
226
|
-
text: formatYaml(manifest),
|
|
227
|
-
language: "yaml",
|
|
228
|
-
showLineNumbers: true,
|
|
229
|
-
customStyle: {
|
|
230
|
-
fontSize: "12px",
|
|
231
|
-
maxHeight: "500px",
|
|
232
|
-
overflow: "auto"
|
|
518
|
+
/* @__PURE__ */ jsx(AccordionDetails, { children: /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(
|
|
519
|
+
CodeSnippet,
|
|
520
|
+
{
|
|
521
|
+
text: formatYaml(manifest),
|
|
522
|
+
language: "yaml",
|
|
523
|
+
showLineNumbers: true,
|
|
524
|
+
customStyle: {
|
|
525
|
+
fontSize: "12px",
|
|
526
|
+
maxHeight: "500px",
|
|
527
|
+
overflow: "auto"
|
|
528
|
+
}
|
|
233
529
|
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
children: /* @__PURE__ */ jsx(Typography, { variant: "h6", children: "Kubernetes Object" })
|
|
245
|
-
}
|
|
246
|
-
),
|
|
247
|
-
/* @__PURE__ */ jsx(AccordionDetails, { children: /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(
|
|
248
|
-
CodeSnippet,
|
|
249
|
-
{
|
|
250
|
-
text: formatYaml(objectData),
|
|
251
|
-
language: "yaml",
|
|
252
|
-
showLineNumbers: true,
|
|
253
|
-
customStyle: {
|
|
254
|
-
fontSize: "12px",
|
|
255
|
-
maxHeight: "500px",
|
|
256
|
-
overflow: "auto"
|
|
530
|
+
) }) })
|
|
531
|
+
] }) }),
|
|
532
|
+
objectData && /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsxs(Accordion, { className: classes.yamlContainer, children: [
|
|
533
|
+
/* @__PURE__ */ jsx(
|
|
534
|
+
AccordionSummary,
|
|
535
|
+
{
|
|
536
|
+
expandIcon: /* @__PURE__ */ jsx(ExpandMoreIcon, {}),
|
|
537
|
+
"aria-controls": "object-content",
|
|
538
|
+
id: "object-header",
|
|
539
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "h6", children: "Kubernetes Object" })
|
|
257
540
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
541
|
+
),
|
|
542
|
+
/* @__PURE__ */ jsx(AccordionDetails, { children: /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(
|
|
543
|
+
CodeSnippet,
|
|
544
|
+
{
|
|
545
|
+
text: formatYaml(objectData),
|
|
546
|
+
language: "yaml",
|
|
547
|
+
showLineNumbers: true,
|
|
548
|
+
customStyle: {
|
|
549
|
+
fontSize: "12px",
|
|
550
|
+
maxHeight: "500px",
|
|
551
|
+
overflow: "auto"
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
) }) })
|
|
555
|
+
] }) })
|
|
556
|
+
] }),
|
|
557
|
+
/* @__PURE__ */ jsxs(
|
|
558
|
+
Dialog,
|
|
559
|
+
{
|
|
560
|
+
open: editModalOpen,
|
|
561
|
+
onClose: () => setEditModalOpen(false),
|
|
562
|
+
maxWidth: "xl",
|
|
563
|
+
fullWidth: true,
|
|
564
|
+
PaperProps: {
|
|
565
|
+
style: {
|
|
566
|
+
height: "90vh",
|
|
567
|
+
maxHeight: "90vh"
|
|
568
|
+
}
|
|
569
|
+
},
|
|
570
|
+
children: [
|
|
571
|
+
/* @__PURE__ */ jsxs(DialogTitle, { children: [
|
|
572
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", children: "Edit Resource Manifest" }),
|
|
573
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "textSecondary", children: [
|
|
574
|
+
resourceName,
|
|
575
|
+
" (",
|
|
576
|
+
resourceKind,
|
|
577
|
+
")"
|
|
578
|
+
] })
|
|
579
|
+
] }),
|
|
580
|
+
/* @__PURE__ */ jsx(DialogContent, { className: classes.dialogContent, dividers: true, children: isLoadingManifest ? /* @__PURE__ */ jsx(Box, { display: "flex", justifyContent: "center", alignItems: "center", flex: 1, children: /* @__PURE__ */ jsx(Progress, {}) }) : /* @__PURE__ */ jsxs(Box, { className: classes.editorContainer, children: [
|
|
581
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle2", gutterBottom: true, children: "YAML Editor" }),
|
|
582
|
+
/* @__PURE__ */ jsx(Box, { className: classes.monacoEditor, children: /* @__PURE__ */ jsx(
|
|
583
|
+
Editor,
|
|
584
|
+
{
|
|
585
|
+
height: "100%",
|
|
586
|
+
defaultLanguage: "yaml",
|
|
587
|
+
value: editingYaml,
|
|
588
|
+
onChange: (value) => handleYamlChange(value || ""),
|
|
589
|
+
theme: "vs-dark",
|
|
590
|
+
options: {
|
|
591
|
+
minimap: { enabled: false },
|
|
592
|
+
scrollBeyondLastLine: false,
|
|
593
|
+
fontSize: 14,
|
|
594
|
+
lineNumbers: "on",
|
|
595
|
+
wordWrap: "off",
|
|
596
|
+
automaticLayout: true,
|
|
597
|
+
tabSize: 2,
|
|
598
|
+
insertSpaces: true,
|
|
599
|
+
folding: true,
|
|
600
|
+
renderWhitespace: "selection"
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
) }),
|
|
604
|
+
/* @__PURE__ */ jsx(Box, { className: classes.validationStatus, children: yamlValidationError ? /* @__PURE__ */ jsxs(Typography, { className: classes.yamlValidationError, children: [
|
|
605
|
+
"\u26A0\uFE0F YAML Validation Error: ",
|
|
606
|
+
yamlValidationError
|
|
607
|
+
] }) : editingYaml.trim() ? /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "textSecondary", children: "\u2705 YAML syntax is valid" }) : /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "textSecondary", children: "Enter YAML content above" }) })
|
|
608
|
+
] }) }),
|
|
609
|
+
/* @__PURE__ */ jsxs(DialogActions, { children: [
|
|
610
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => setEditModalOpen(false), color: "primary", children: "Cancel" }),
|
|
611
|
+
/* @__PURE__ */ jsx(
|
|
612
|
+
Button,
|
|
613
|
+
{
|
|
614
|
+
onClick: () => setConfirmDialogOpen(true),
|
|
615
|
+
color: "primary",
|
|
616
|
+
variant: "contained",
|
|
617
|
+
disabled: isLoadingManifest || !editingYaml.trim() || !!yamlValidationError,
|
|
618
|
+
children: "Save Changes"
|
|
619
|
+
}
|
|
620
|
+
)
|
|
621
|
+
] })
|
|
622
|
+
]
|
|
623
|
+
}
|
|
624
|
+
),
|
|
625
|
+
/* @__PURE__ */ jsxs(
|
|
626
|
+
Dialog,
|
|
627
|
+
{
|
|
628
|
+
open: confirmDialogOpen,
|
|
629
|
+
onClose: () => setConfirmDialogOpen(false),
|
|
630
|
+
maxWidth: "sm",
|
|
631
|
+
fullWidth: true,
|
|
632
|
+
children: [
|
|
633
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Confirm Changes" }),
|
|
634
|
+
/* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsx(Typography, { children: "Are you sure you want to apply these changes to the resource? This action will update the Kubernetes resource based on your modifications." }) }),
|
|
635
|
+
/* @__PURE__ */ jsxs(DialogActions, { children: [
|
|
636
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => setConfirmDialogOpen(false), color: "primary", children: "Cancel" }),
|
|
637
|
+
/* @__PURE__ */ jsx(
|
|
638
|
+
Button,
|
|
639
|
+
{
|
|
640
|
+
onClick: handleSaveResource,
|
|
641
|
+
color: "primary",
|
|
642
|
+
variant: "contained",
|
|
643
|
+
disabled: isSaving,
|
|
644
|
+
children: isSaving ? "Applying..." : "Apply Changes"
|
|
645
|
+
}
|
|
646
|
+
)
|
|
647
|
+
] })
|
|
648
|
+
]
|
|
649
|
+
}
|
|
650
|
+
),
|
|
651
|
+
/* @__PURE__ */ jsx(
|
|
652
|
+
Snackbar,
|
|
653
|
+
{
|
|
654
|
+
open: snackbar.open,
|
|
655
|
+
autoHideDuration: 6e3,
|
|
656
|
+
onClose: handleCloseSnackbar,
|
|
657
|
+
anchorOrigin: { vertical: "bottom", horizontal: "left" },
|
|
658
|
+
children: /* @__PURE__ */ jsx(Alert, { onClose: handleCloseSnackbar, severity: snackbar.severity, children: snackbar.message })
|
|
659
|
+
}
|
|
660
|
+
)
|
|
661
|
+
] });
|
|
262
662
|
};
|
|
263
663
|
|
|
264
664
|
export { VCFAutomationCCIResourceOverview };
|