diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts
index 3812ef450d..7c8118da1f 100644
--- a/src/common/k8s-api/kube-object.store.ts
+++ b/src/common/k8s-api/kube-object.store.ts
@@ -286,7 +286,13 @@ export abstract class KubeObjectStore extends ItemStore
ensureObjectSelfLink(this.api, newItem);
- return this.items[index] = newItem;
+ if (index < 0) {
+ this.items.push(newItem);
+ } else {
+ this.items[index] = newItem;
+ }
+
+ return newItem;
}
async patch(item: T, patch: Patch): Promise {
diff --git a/src/common/k8s-api/kube-object.ts b/src/common/k8s-api/kube-object.ts
index 3011406fa4..bcbe6bf05f 100644
--- a/src/common/k8s-api/kube-object.ts
+++ b/src/common/k8s-api/kube-object.ts
@@ -192,7 +192,7 @@ export class KubeObject): Promise {
// use unified resource-applier api for updating all k8s objects
diff --git a/src/main/resource-applier.ts b/src/main/resource-applier.ts
index 9e1a345e58..a305ff1091 100644
--- a/src/main/resource-applier.ts
+++ b/src/main/resource-applier.ts
@@ -62,7 +62,7 @@ export class ResourceApplier {
args.push(
"--type", "json",
"--patch", JSON.stringify(patch),
- "-o", "yaml"
+ "-o", "json"
);
try {
@@ -93,18 +93,17 @@ export class ResourceApplier {
"-f", fileName,
];
- await fs.writeFile(fileName, content);
-
- logger.debug(`shooting manifests with with ${kubectlPath}`, { args });
+ logger.debug(`shooting manifests with ${kubectlPath}`, { args });
const execEnv = { ...process.env };
const httpsProxy = this.cluster.preferences?.httpsProxy;
if (httpsProxy) {
- execEnv["HTTPS_PROXY"] = httpsProxy;
+ execEnv.HTTPS_PROXY = httpsProxy;
}
try {
+ await fs.writeFile(fileName, content);
const { stdout } = await promiseExecFile(kubectlPath, args);
return stdout;
diff --git a/src/main/routes/resource-applier-route.ts b/src/main/routes/resource-applier-route.ts
index 03d2f1f4bf..0adf916188 100644
--- a/src/main/routes/resource-applier-route.ts
+++ b/src/main/routes/resource-applier-route.ts
@@ -40,7 +40,7 @@ export class ResourceApplierApiRoute {
const { response, cluster, payload } = request;
try {
- const resource = await new ResourceApplier(cluster).patch(payload.name, payload.kind, payload.ns, payload.patch);
+ const resource = await new ResourceApplier(cluster).patch(payload.name, payload.kind, payload.patch, payload.ns);
respondJson(response, resource, 200);
} catch (error) {
diff --git a/src/main/utils/http-responses.ts b/src/main/utils/http-responses.ts
index 36287806f1..5d57b6dd76 100644
--- a/src/main/utils/http-responses.ts
+++ b/src/main/utils/http-responses.ts
@@ -21,14 +21,37 @@
import type http from "http";
-export function respondJson(res: http.ServerResponse, content: any, status = 200) {
- respond(res, JSON.stringify(content), "application/json", status);
+/**
+ * Respond to a HTTP request with a body of JSON data
+ * @param res The HTTP response to write data to
+ * @param content The data or its JSON stringified version of it
+ * @param status [200] The status code to respond with
+ */
+export function respondJson(res: http.ServerResponse, content: Object | string, status = 200) {
+ const normalizedContent = typeof content === "object"
+ ? JSON.stringify(content)
+ : content;
+
+ respond(res, normalizedContent, "application/json", status);
}
+/**
+ * Respond to a HTTP request with a body of plain text data
+ * @param res The HTTP response to write data to
+ * @param content The string data to respond with
+ * @param status [200] The status code to respond with
+ */
export function respondText(res: http.ServerResponse, content: string, status = 200) {
respond(res, content, "text/plain", status);
}
+/**
+ * Respond to a HTTP request with a body of plain text data
+ * @param res The HTTP response to write data to
+ * @param content The string data to respond with
+ * @param contentType The HTTP Content-Type header value
+ * @param status [200] The status code to respond with
+ */
export function respond(res: http.ServerResponse, content: string, contentType: string, status = 200) {
res.setHeader("Content-Type", contentType);
res.statusCode = status;
diff --git a/src/renderer/components/+config-maps/config-map-details.tsx b/src/renderer/components/+config-maps/config-map-details.tsx
index abbd31c97a..6150aa1ad9 100644
--- a/src/renderer/components/+config-maps/config-map-details.tsx
+++ b/src/renderer/components/+config-maps/config-map-details.tsx
@@ -72,6 +72,8 @@ export class ConfigMapDetails extends React.Component {
<>ConfigMap {configMap.getName()} successfully updated.>
);
+ } catch (error) {
+ Notifications.error(`Failed to save config map: ${error}`);
} finally {
this.isSaving = false;
}
diff --git a/src/renderer/components/dock/create-resource.tsx b/src/renderer/components/dock/create-resource.tsx
index fef151da7d..c7de411e3c 100644
--- a/src/renderer/components/dock/create-resource.tsx
+++ b/src/renderer/components/dock/create-resource.tsx
@@ -34,9 +34,9 @@ import type { DockTab } from "./dock.store";
import { EditorPanel } from "./editor-panel";
import { InfoPanel } from "./info-panel";
import * as resourceApplierApi from "../../../common/k8s-api/endpoints/resource-applier.api";
-import type { JsonApiErrorParsed } from "../../../common/k8s-api/json-api";
import { Notifications } from "../notifications";
import { monacoModelsManager } from "./monaco-model-manager";
+import logger from "../../../common/logger";
interface Props {
className?: string;
@@ -95,7 +95,7 @@ export class CreateResource extends React.Component {
});
};
- create = async () => {
+ create = async (): Promise => {
if (this.error || !this.data.trim()) {
// do not save when field is empty or there is an error
return null;
@@ -103,31 +103,31 @@ export class CreateResource extends React.Component {
// skip empty documents if "---" pasted at the beginning or end
const resources = jsYaml.safeLoadAll(this.data).filter(Boolean);
- const createdResources: string[] = [];
- const errors: string[] = [];
- await Promise.all(
- resources.map(data => {
- return resourceApplierApi.update(data)
- .then(item => createdResources.push(item.metadata.name))
- .catch((err: JsonApiErrorParsed) => errors.push(err.toString()));
- })
- );
-
- if (errors.length) {
- errors.forEach(error => Notifications.error(error));
- if (!createdResources.length) throw errors[0];
+ if (resources.length === 0) {
+ return void logger.info("Nothing to create");
}
- const successMessage = (
-
- {createdResources.length === 1 ? "Resource" : "Resources"}{" "}
- {createdResources.join(", ")} successfully created
-
- );
- Notifications.ok(successMessage);
+ const createdResources: string[] = [];
- return successMessage;
+ for (const result of await Promise.allSettled(resources.map(resourceApplierApi.update))) {
+ if (result.status === "fulfilled") {
+ createdResources.push(result.value.metadata.name);
+ } else {
+ Notifications.error(result.reason.toString());
+ }
+ }
+
+ if (createdResources.length > 0) {
+ Notifications.ok((
+
+ {createdResources.length === 1 ? "Resource" : "Resources"}{" "}
+ {createdResources.join(", ")} successfully created
+
+ ));
+ }
+
+ return undefined;
};
renderControls(){
diff --git a/src/renderer/components/dock/edit-resource.tsx b/src/renderer/components/dock/edit-resource.tsx
index dec46af3fe..6cd3a3afdc 100644
--- a/src/renderer/components/dock/edit-resource.tsx
+++ b/src/renderer/components/dock/edit-resource.tsx
@@ -98,9 +98,11 @@ export class EditResource extends React.Component {
return null;
}
const store = editResourceStore.getStore(this.tabId);
- const currentEdit = yaml.safeLoad(this.draft);
+ const currentVersion = yaml.safeLoad(this.draft);
const firstVersion = yaml.safeLoad(editResourceStore.getData(this.tabId).firstDraft ?? this.draft);
- const patches = createPatch(firstVersion, currentEdit);
+ const patches = createPatch(firstVersion, currentVersion);
+
+ console.log({ currentVersion, firstVersion, patches });
const updatedResource = await store.patch(this.resource, patches);
return (
@@ -127,9 +129,9 @@ export class EditResource extends React.Component {
submittingMessage="Applying.."
controls={(
- Kind:
+ Kind:
Name:
- Namespace:
+ Namespace:
)}
/>
diff --git a/src/renderer/components/dock/editor-panel.tsx b/src/renderer/components/dock/editor-panel.tsx
index 31ccc5b2e2..bbf0b96e53 100644
--- a/src/renderer/components/dock/editor-panel.tsx
+++ b/src/renderer/components/dock/editor-panel.tsx
@@ -91,10 +91,7 @@ export class EditorPanel extends React.Component {
onChange = (value: string) => {
this.validate(value);
-
- if (this.props.onChange) {
- this.props.onChange(value, this.yamlError);
- }
+ this.props.onChange?.(value, this.yamlError);
};
render() {
diff --git a/src/renderer/components/kube-object-menu/kube-object-menu.tsx b/src/renderer/components/kube-object-menu/kube-object-menu.tsx
index 1848fa2906..ac2a99cc88 100644
--- a/src/renderer/components/kube-object-menu/kube-object-menu.tsx
+++ b/src/renderer/components/kube-object-menu/kube-object-menu.tsx
@@ -44,15 +44,11 @@ export class KubeObjectMenu extends React.Component