mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
fix: Do not crash when opening details of a helm release
- Switch to using RequestChannel instead of LensProxy for better type safety Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
b7ac14df73
commit
40af0d31c5
30 files changed
+664
-417
No files matched your search
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { AsyncResult } from "@k8slens/utilities";
|
||||
import { isObject, json } from "@k8slens/utilities";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { HelmReleaseData } from "../../../features/helm-releases/common/channels";
|
||||
import execHelmInjectable from "../exec-helm/exec-helm.injectable";
|
||||
|
||||
export type GetHelmReleaseData = (
|
||||
name: string,
|
||||
namespace: string,
|
||||
kubeconfigPath: string,
|
||||
) => AsyncResult<HelmReleaseData, string>;
|
||||
|
||||
const getHelmReleaseDataInjectable = getInjectable({
|
||||
id: "get-helm-release-data",
|
||||
instantiate: (di): GetHelmReleaseData => {
|
||||
const execHelm = di.inject(execHelmInjectable);
|
||||
|
||||
return async (releaseName, namespace, proxyKubeconfigPath) => {
|
||||
const result = await execHelm([
|
||||
"status",
|
||||
releaseName,
|
||||
"--namespace",
|
||||
namespace,
|
||||
"--kubeconfig",
|
||||
proxyKubeconfigPath,
|
||||
"--output",
|
||||
"json",
|
||||
]);
|
||||
|
||||
if (!result.callWasSuccessful) {
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: `Failed to execute helm: ${result.error}`,
|
||||
};
|
||||
}
|
||||
|
||||
const parseResult = json.parse(result.response);
|
||||
|
||||
if (!parseResult.callWasSuccessful) {
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: `Failed to parse helm response: ${parseResult.error}`,
|
||||
};
|
||||
}
|
||||
|
||||
const release = parseResult.response;
|
||||
|
||||
if (!isObject(release) || Array.isArray(release)) {
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: `Helm response is not an object: ${JSON.stringify(release)}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
callWasSuccessful: true,
|
||||
response: release as unknown as HelmReleaseData,
|
||||
};
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default getHelmReleaseDataInjectable;
|
||||
+5
-4
@@ -4,12 +4,13 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { AsyncResult } from "@k8slens/utilities";
|
||||
import { isObject } from "@k8slens/utilities";
|
||||
import execHelmInjectable from "../../../exec-helm/exec-helm.injectable";
|
||||
import yaml from "js-yaml";
|
||||
import type { KubeJsonApiData, KubeJsonApiDataList } from "@k8slens/kube-object";
|
||||
|
||||
const callForHelmManifestInjectable = getInjectable({
|
||||
id: "call-for-helm-manifest",
|
||||
const requestHelmManifestInjectable = getInjectable({
|
||||
id: "request-helm-manifest",
|
||||
|
||||
instantiate: (di) => {
|
||||
const execHelm = di.inject(execHelmInjectable);
|
||||
@@ -37,11 +38,11 @@ const callForHelmManifestInjectable = getInjectable({
|
||||
callWasSuccessful: true,
|
||||
response: yaml
|
||||
.loadAll(result.response)
|
||||
.filter((manifest) => !!manifest) as KubeJsonApiData[],
|
||||
.filter(isObject) as unknown as KubeJsonApiData[],
|
||||
};
|
||||
};
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
export default callForHelmManifestInjectable;
|
||||
export default requestHelmManifestInjectable;
|
||||
+3
-3
@@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import callForHelmManifestInjectable from "./call-for-helm-manifest/call-for-helm-manifest.injectable";
|
||||
import requestHelmManifestInjectable from "./call-for-helm-manifest/call-for-helm-manifest.injectable";
|
||||
import type { KubeJsonApiData, KubeJsonApiDataList } from "@k8slens/kube-object";
|
||||
import type { AsyncResult } from "@k8slens/utilities";
|
||||
|
||||
@@ -17,10 +17,10 @@ const getHelmReleaseResourcesInjectable = getInjectable({
|
||||
id: "get-helm-release-resources",
|
||||
|
||||
instantiate: (di): GetHelmReleaseResources => {
|
||||
const callForHelmManifest = di.inject(callForHelmManifestInjectable);
|
||||
const requestHelmManifest = di.inject(requestHelmManifestInjectable);
|
||||
|
||||
return async (name, namespace, kubeconfigPath) => {
|
||||
const result = await callForHelmManifest(name, namespace, kubeconfigPath);
|
||||
const result = await requestHelmManifest(name, namespace, kubeconfigPath);
|
||||
|
||||
if (!result.callWasSuccessful) {
|
||||
return result;
|
||||
|
||||
@@ -4,47 +4,42 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import { loggerInjectionToken } from "@k8slens/logger";
|
||||
import kubeconfigManagerInjectable from "../../kubeconfig-manager/kubeconfig-manager.injectable";
|
||||
import { isObject, json } from "@k8slens/utilities";
|
||||
import execHelmInjectable from "../exec-helm/exec-helm.injectable";
|
||||
import type { AsyncResult } from "@k8slens/utilities";
|
||||
import getHelmReleaseResourcesInjectable from "./get-helm-release-resources/get-helm-release-resources.injectable";
|
||||
import type { HelmReleaseDataWithResources } from "../../../features/helm-releases/common/channels";
|
||||
import getHelmReleaseDataInjectable from "./get-helm-release-data.injectable";
|
||||
|
||||
export interface GetHelmReleaseArgs {
|
||||
cluster: Cluster;
|
||||
releaseName: string;
|
||||
namespace: string;
|
||||
}
|
||||
|
||||
export type GetHelmRelease = (args: GetHelmReleaseArgs) => AsyncResult<HelmReleaseDataWithResources, string>;
|
||||
|
||||
const getHelmReleaseInjectable = getInjectable({
|
||||
id: "get-helm-release",
|
||||
|
||||
instantiate: (di) => {
|
||||
const logger = di.inject(loggerInjectionToken);
|
||||
const execHelm = di.inject(execHelmInjectable);
|
||||
instantiate: (di): GetHelmRelease => {
|
||||
const getHelmReleaseData = di.inject(getHelmReleaseDataInjectable);
|
||||
const getHelmReleaseResources = di.inject(getHelmReleaseResourcesInjectable);
|
||||
|
||||
return async (cluster: Cluster, releaseName: string, namespace: string) => {
|
||||
return async ({ cluster, namespace, releaseName }) => {
|
||||
const proxyKubeconfigManager = di.inject(kubeconfigManagerInjectable, cluster);
|
||||
const proxyKubeconfigPath = await proxyKubeconfigManager.ensurePath();
|
||||
|
||||
logger.debug("Fetch release");
|
||||
|
||||
const result = await execHelm([
|
||||
"status",
|
||||
const releaseResult = await getHelmReleaseData(
|
||||
releaseName,
|
||||
"--namespace",
|
||||
namespace,
|
||||
"--kubeconfig",
|
||||
proxyKubeconfigPath,
|
||||
"--output",
|
||||
"json",
|
||||
]);
|
||||
);
|
||||
|
||||
if (!result.callWasSuccessful) {
|
||||
logger.warn(`Failed to exectute helm: ${result.error}`);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const release = json.parse(result.response);
|
||||
|
||||
if (!isObject(release) || Array.isArray(release)) {
|
||||
return undefined;
|
||||
if (!releaseResult.callWasSuccessful) {
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: `Failed to get helm release data: ${releaseResult.error}`,
|
||||
};
|
||||
}
|
||||
|
||||
const resourcesResult = await getHelmReleaseResources(
|
||||
@@ -54,14 +49,18 @@ const getHelmReleaseInjectable = getInjectable({
|
||||
);
|
||||
|
||||
if (!resourcesResult.callWasSuccessful) {
|
||||
logger.warn(`Failed to get helm release resources: ${resourcesResult.error}`);
|
||||
|
||||
return undefined;
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: `Failed to get helm release resources: ${resourcesResult.error}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...release,
|
||||
resources: resourcesResult.response,
|
||||
callWasSuccessful: true,
|
||||
response: {
|
||||
...releaseResult.response,
|
||||
resources: resourcesResult.response,
|
||||
},
|
||||
};
|
||||
};
|
||||
},
|
||||
|
||||
@@ -7,15 +7,19 @@ import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import { loggerInjectionToken } from "@k8slens/logger";
|
||||
import kubeconfigManagerInjectable from "../../kubeconfig-manager/kubeconfig-manager.injectable";
|
||||
import listHelmReleasesInjectable from "../list-helm-releases.injectable";
|
||||
import type { AsyncResult } from "@k8slens/utilities";
|
||||
import type { ListedHelmRelease } from "../../../features/helm-releases/common/channels";
|
||||
|
||||
export type ListClusterHelmReleases = (cluster: Cluster, namespace?: string) => AsyncResult<ListedHelmRelease[], string>;
|
||||
|
||||
const listClusterHelmReleasesInjectable = getInjectable({
|
||||
id: "list-cluster-helm-releases",
|
||||
|
||||
instantiate: (di) => {
|
||||
instantiate: (di): ListClusterHelmReleases => {
|
||||
const logger = di.inject(loggerInjectionToken);
|
||||
const listHelmReleases = di.inject(listHelmReleasesInjectable);
|
||||
|
||||
return async (cluster: Cluster, namespace?: string) => {
|
||||
return async (cluster, namespace) => {
|
||||
const proxyKubeconfigManager = di.inject(kubeconfigManagerInjectable, cluster);
|
||||
const proxyKubeconfigPath = await proxyKubeconfigManager.ensurePath();
|
||||
|
||||
|
||||
@@ -52,9 +52,15 @@ const updateHelmReleaseInjectable = getInjectable({
|
||||
throw result.error; // keep the same interface
|
||||
}
|
||||
|
||||
const releaseResult = await getHelmRelease({ cluster, releaseName, namespace });
|
||||
|
||||
if (!releaseResult.callWasSuccessful) {
|
||||
throw releaseResult.error; // keep the same interface
|
||||
}
|
||||
|
||||
return {
|
||||
log: result.response,
|
||||
release: await getHelmRelease(cluster, releaseName, namespace),
|
||||
release: releaseResult.response,
|
||||
};
|
||||
} finally {
|
||||
await removePath(valuesFilePath);
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import execHelmInjectable from "./exec-helm/exec-helm.injectable";
|
||||
import { toCamelCase, isObject } from "@k8slens/utilities";
|
||||
import type { AsyncResult } from "@k8slens/utilities";
|
||||
import { isObject } from "@k8slens/utilities";
|
||||
import type { ListedHelmRelease } from "../../features/helm-releases/common/channels";
|
||||
|
||||
export type ListHelmReleases = (pathToKubeconfig: string, namespace?: string) => Promise<Record<string, any>[]>;
|
||||
export type ListHelmReleases = (pathToKubeconfig: string, namespace?: string) => AsyncResult<ListedHelmRelease[], string>;
|
||||
|
||||
const listHelmReleasesInjectable = getInjectable({
|
||||
id: "list-helm-releases",
|
||||
@@ -33,16 +35,21 @@ const listHelmReleasesInjectable = getInjectable({
|
||||
const result = await execHelm(args);
|
||||
|
||||
if (!result.callWasSuccessful) {
|
||||
throw result.error;
|
||||
return {
|
||||
callWasSuccessful: false,
|
||||
error: `Failed to list helm releases: ${result.error}`,
|
||||
};
|
||||
}
|
||||
|
||||
const output = JSON.parse(result.response);
|
||||
const rawOutput = JSON.parse(result.response);
|
||||
const output = Array.isArray(rawOutput)
|
||||
? rawOutput.filter(isObject)
|
||||
: [];
|
||||
|
||||
if (!Array.isArray(output) || output.length == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return output.filter(isObject).map(toCamelCase);
|
||||
return {
|
||||
callWasSuccessful: true,
|
||||
response: output as unknown as ListedHelmRelease[],
|
||||
};
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
import { getRouteInjectable } from "../../../router/router.injectable";
|
||||
import { clusterRoute } from "../../../router/route";
|
||||
import getHelmReleaseInjectable from "../../../helm/helm-service/get-helm-release.injectable";
|
||||
|
||||
const getReleaseRouteInjectable = getRouteInjectable({
|
||||
id: "get-release-route",
|
||||
|
||||
instantiate: (di) => {
|
||||
const getHelmRelease = di.inject(getHelmReleaseInjectable);
|
||||
|
||||
return clusterRoute({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
|
||||
})(async ({ cluster, params }) => ({
|
||||
response: await getHelmRelease(
|
||||
cluster,
|
||||
params.release,
|
||||
params.namespace,
|
||||
),
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
export default getReleaseRouteInjectable;
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
import { getRouteInjectable } from "../../../router/router.injectable";
|
||||
import { clusterRoute } from "../../../router/route";
|
||||
import listClusterHelmReleasesInjectable from "../../../helm/helm-service/list-helm-releases.injectable";
|
||||
|
||||
const listReleasesRouteInjectable = getRouteInjectable({
|
||||
id: "list-releases-route",
|
||||
|
||||
instantiate: (di) => {
|
||||
const listHelmReleases = di.inject(listClusterHelmReleasesInjectable);
|
||||
|
||||
return clusterRoute({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/v2/releases/{namespace?}`,
|
||||
})(async ({ cluster, params }) => ({
|
||||
response: await listHelmReleases(cluster, params.namespace),
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
export default listReleasesRouteInjectable;
|
||||
Reference in new issue
Block a user