diff --git a/src/main/index.ts b/src/main/index.ts index 2e7ae172fdd6..ae3d74795880 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -41,7 +41,7 @@ import { InstalledExtension, ExtensionDiscovery } from "../extensions/extension- import type { LensExtensionId } from "../extensions/lens-extension"; import { installDeveloperTools } from "./developer-tools"; import { LensProtocolRouterMain } from "./protocol-handler"; -import { disposer, getAppVersion, getAppVersionFromProxyServer } from "../common/utils"; +import { disposer, getAppVersion, getAppVersionFromProxyServer, storedKubeConfigFolder } from "../common/utils"; import { bindBroadcastHandlers, ipcMainOn } from "../common/ipc"; import { startUpdateChecking } from "./app-updater"; import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; @@ -213,7 +213,7 @@ app.on("ready", async () => { ipcMainOn(IpcRendererNavigationEvents.LOADED, async () => { cleanup.push(pushCatalogToRenderer(catalogEntityRegistry)); - await ensureDir(ClusterStore.storedKubeConfigFolder); + await ensureDir(storedKubeConfigFolder()); KubeconfigSyncManager.getInstance().startSync(); startUpdateChecking(); LensProtocolRouterMain.getInstance().rendererLoaded = true; diff --git a/src/renderer/api/api-manager.ts b/src/renderer/api/api-manager.ts index 1e01bd4cfbdc..e11fd9a07b41 100644 --- a/src/renderer/api/api-manager.ts +++ b/src/renderer/api/api-manager.ts @@ -23,8 +23,9 @@ import type { KubeObjectStore } from "../kube-object.store"; import { action, observable, makeObservable } from "mobx"; import { autoBind, iter } from "../utils"; -import { KubeApi, parseKubeApi } from "./kube-api"; +import type { KubeApi } from "./kube-api"; import type { KubeObject } from "./kube-object"; +import { IKubeObjectRef, parseKubeApi, createKubeApiURL } from "./kube-api-parse"; export class ApiManager { private apis = observable.map>(); @@ -91,6 +92,45 @@ export class ApiManager { getStore>(api: string | KubeApi): S | undefined { return this.stores.get(this.resolveApi(api)?.apiBase) as S; } + + lookupApiLink(ref: IKubeObjectRef, parentObject: KubeObject): string { + const { + kind, apiVersion, name, + namespace = parentObject.getNs() + } = ref; + + if (!kind) return ""; + + // search in registered apis by 'kind' & 'apiVersion' + const api = this.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion); + + if (api) { + return api.getUrl({ namespace, name }); + } + + // lookup api by generated resource link + const apiPrefixes = ["/apis", "/api"]; + const resource = kind.endsWith("s") ? `${kind.toLowerCase()}es` : `${kind.toLowerCase()}s`; + + for (const apiPrefix of apiPrefixes) { + const apiLink = createKubeApiURL({ apiPrefix, apiVersion, name, namespace, resource }); + + if (this.getApi(apiLink)) { + return apiLink; + } + } + + // resolve by kind only (hpa's might use refs to older versions of resources for example) + const apiByKind = this.getApi(api => api.kind === kind); + + if (apiByKind) { + return apiByKind.getUrl({ name, namespace }); + } + + // otherwise generate link with default prefix + // resource still might exists in k8s, but api is not registered in the app + return createKubeApiURL({ apiVersion, name, namespace, resource }); + } } export const apiManager = new ApiManager(); diff --git a/src/renderer/api/endpoints/resource-applier.api.ts b/src/renderer/api/endpoints/resource-applier.api.ts index b10c10f7db53..b6a35d3a607e 100644 --- a/src/renderer/api/endpoints/resource-applier.api.ts +++ b/src/renderer/api/endpoints/resource-applier.api.ts @@ -20,35 +20,21 @@ */ import jsYaml from "js-yaml"; -import { KubeObject } from "../kube-object"; import type { KubeJsonApiData } from "../kube-json-api"; import { apiBase } from "../index"; -import { apiManager } from "../api-manager"; export const resourceApplierApi = { annotations: [ "kubectl.kubernetes.io/last-applied-configuration" ], - async update(resource: object | string): Promise { + async update(resource: object | string): Promise { if (typeof resource === "string") { resource = jsYaml.safeLoad(resource); } - return apiBase - .post("/stack", { data: resource }) - .then(data => { - const items = data.map(obj => { - const api = apiManager.getApiByKind(obj.kind, obj.apiVersion); + const [data = null] = await apiBase.post("/stack", { data: resource }); - if (api) { - return new api.objectConstructor(obj); - } else { - return new KubeObject(obj); - } - }); - - return items[0] as K ?? null; - }); + return data; } }; diff --git a/src/renderer/api/kube-api-parse.ts b/src/renderer/api/kube-api-parse.ts index 689a6b98585f..53d731203d81 100644 --- a/src/renderer/api/kube-api-parse.ts +++ b/src/renderer/api/kube-api-parse.ts @@ -21,9 +21,7 @@ // Parse kube-api path and get api-version, group, etc. -import type { KubeObject } from "./kube-object"; import { splitArray } from "../../common/utils"; -import { apiManager } from "./api-manager"; import { isDebugging } from "../../common/vars"; import logger from "../../main/logger"; import { inspect } from "util"; @@ -159,42 +157,3 @@ export function createKubeApiURL(ref: IKubeApiLinkRef): string { .filter(v => v) .join("/"); } - -export function lookupApiLink(ref: IKubeObjectRef, parentObject: KubeObject): string { - const { - kind, apiVersion, name, - namespace = parentObject.getNs() - } = ref; - - if (!kind) return ""; - - // search in registered apis by 'kind' & 'apiVersion' - const api = apiManager.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion); - - if (api) { - return api.getUrl({ namespace, name }); - } - - // lookup api by generated resource link - const apiPrefixes = ["/apis", "/api"]; - const resource = kind.endsWith("s") ? `${kind.toLowerCase()}es` : `${kind.toLowerCase()}s`; - - for (const apiPrefix of apiPrefixes) { - const apiLink = createKubeApiURL({ apiPrefix, apiVersion, name, namespace, resource }); - - if (apiManager.getApi(apiLink)) { - return apiLink; - } - } - - // resolve by kind only (hpa's might use refs to older versions of resources for example) - const apiByKind = apiManager.getApi(api => api.kind === kind); - - if (apiByKind) { - return apiByKind.getUrl({ name, namespace }); - } - - // otherwise generate link with default prefix - // resource still might exists in k8s, but api is not registered in the app - return createKubeApiURL({ apiVersion, name, namespace, resource }); -} diff --git a/src/renderer/api/kube-api.ts b/src/renderer/api/kube-api.ts index 20c4672727d0..5fe65a23575f 100644 --- a/src/renderer/api/kube-api.ts +++ b/src/renderer/api/kube-api.ts @@ -56,11 +56,6 @@ export interface IKubeApiOptions { checkPreferredVersion?: boolean; } -export interface KubeApiListOptions { - namespace?: string; - reqInit?: RequestInit; -} - export interface IKubeApiQueryParams { watch?: boolean | number; resourceVersion?: string; @@ -506,5 +501,3 @@ export class KubeApi { } } } - -export * from "./kube-api-parse"; diff --git a/src/renderer/api/kube-object.ts b/src/renderer/api/kube-object.ts index 97b6442a022b..c1e40eb2e56d 100644 --- a/src/renderer/api/kube-object.ts +++ b/src/renderer/api/kube-object.ts @@ -287,14 +287,14 @@ export class KubeObject(data: Partial): Promise { + async update(data: Partial): Promise { for (const field of KubeObject.nonEditableFields) { if (!_.isEqual(_.get(this, field), _.get(data, field))) { throw new Error(`Failed to update Kube Object: ${field} has been modified`); } } - return resourceApplierApi.update({ + return resourceApplierApi.update({ ...this.toPlainObject(), ...data, }); diff --git a/src/renderer/components/+cluster/cluster-issues.tsx b/src/renderer/components/+cluster/cluster-issues.tsx index 88ee6aae1490..a68950bc0efe 100644 --- a/src/renderer/components/+cluster/cluster-issues.tsx +++ b/src/renderer/components/+cluster/cluster-issues.tsx @@ -33,7 +33,7 @@ import { boundMethod, cssNames, prevDefault } from "../../utils"; import type { ItemObject } from "../../item.store"; import { Spinner } from "../spinner"; import { ThemeStore } from "../../theme.store"; -import { lookupApiLink } from "../../api/kube-api"; +import { apiManager } from "../../api/api-manager"; import { kubeSelectedUrlParam, showDetails } from "../kube-detail-params"; import { kubeWatchApi } from "../../api/kube-watch-api"; @@ -106,7 +106,7 @@ export class ClusterIssues extends React.Component { age: getAge(), message, kind, - selfLink: lookupApiLink(involvedObject, error), + selfLink: apiManager.lookupApiLink(involvedObject, error), }); }); diff --git a/src/renderer/components/+config-autoscalers/hpa-details.tsx b/src/renderer/components/+config-autoscalers/hpa-details.tsx index 24d5b8f96afd..4849c69fe0bd 100644 --- a/src/renderer/components/+config-autoscalers/hpa-details.tsx +++ b/src/renderer/components/+config-autoscalers/hpa-details.tsx @@ -30,7 +30,7 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import { cssNames } from "../../utils"; import { HorizontalPodAutoscaler, HpaMetricType, IHpaMetric } from "../../api/endpoints/hpa.api"; import { Table, TableCell, TableHead, TableRow } from "../table"; -import { lookupApiLink } from "../../api/kube-api"; +import { apiManager } from "../../api/api-manager"; import { KubeObjectMeta } from "../kube-object-meta"; import { getDetailsUrl } from "../kube-detail-params"; @@ -55,7 +55,7 @@ export class HpaDetails extends React.Component { case HpaMetricType.Object: const { target } = metric.object; const { kind, name } = target; - const objectUrl = getDetailsUrl(lookupApiLink(target, hpa)); + const objectUrl = getDetailsUrl(apiManager.lookupApiLink(target, hpa)); return ( <> @@ -108,7 +108,7 @@ export class HpaDetails extends React.Component { {scaleTargetRef && ( - + {scaleTargetRef.kind}/{scaleTargetRef.name} )} diff --git a/src/renderer/components/+events/event-details.tsx b/src/renderer/components/+events/event-details.tsx index c7b39023c0ed..496e249df39e 100644 --- a/src/renderer/components/+events/event-details.tsx +++ b/src/renderer/components/+events/event-details.tsx @@ -30,7 +30,7 @@ import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeEvent } from "../../api/endpoints/events.api"; import { KubeObjectMeta } from "../kube-object-meta"; import { Table, TableCell, TableHead, TableRow } from "../table"; -import { lookupApiLink } from "../../api/kube-api"; +import { apiManager } from "../../api/api-manager"; import { LocaleDate } from "../locale-date"; import { getDetailsUrl } from "../kube-detail-params"; @@ -82,7 +82,7 @@ export class EventDetails extends React.Component { - + {name} diff --git a/src/renderer/components/+events/events.tsx b/src/renderer/components/+events/events.tsx index 2e75a9404c09..ae9d71415d5a 100644 --- a/src/renderer/components/+events/events.tsx +++ b/src/renderer/components/+events/events.tsx @@ -35,7 +35,7 @@ import { Tooltip } from "../tooltip"; import { Link } from "react-router-dom"; import { cssNames, IClassName, stopPropagation } from "../../utils"; import { Icon } from "../icon"; -import { lookupApiLink } from "../../api/kube-api"; +import { apiManager } from "../../api/api-manager"; import { eventsURL } from "../../../common/routes"; import { getDetailsUrl } from "../kube-detail-params"; @@ -196,7 +196,7 @@ export class Events extends React.Component { ) }, event.getNs(), - + {involvedObject.kind}: {involvedObject.name} , event.getSource(), diff --git a/src/renderer/components/+network-endpoints/endpoint-subset-list.tsx b/src/renderer/components/+network-endpoints/endpoint-subset-list.tsx index fe32bbac4613..917d03e32023 100644 --- a/src/renderer/components/+network-endpoints/endpoint-subset-list.tsx +++ b/src/renderer/components/+network-endpoints/endpoint-subset-list.tsx @@ -26,7 +26,7 @@ import { observer } from "mobx-react"; import { EndpointSubset, Endpoint, EndpointAddress} from "../../api/endpoints"; import { Table, TableCell, TableHead, TableRow } from "../table"; import { boundMethod } from "../../utils"; -import { lookupApiLink } from "../../api/kube-api"; +import { apiManager } from "../../api/api-manager"; import { Link } from "react-router-dom"; import { getDetailsUrl } from "../kube-detail-params"; @@ -92,7 +92,7 @@ export class EndpointSubsetList extends React.Component { {address.hostname} { address.targetRef && ( - + {address.targetRef.name} )} diff --git a/src/renderer/components/+workloads-jobs/job-details.tsx b/src/renderer/components/+workloads-jobs/job-details.tsx index 64d77732bc22..9cf5a2e75d57 100644 --- a/src/renderer/components/+workloads-jobs/job-details.tsx +++ b/src/renderer/components/+workloads-jobs/job-details.tsx @@ -35,7 +35,7 @@ import { jobStore } from "./job.store"; import type { KubeObjectDetailsProps } from "../kube-object-details"; import { getMetricsForJobs, IPodMetrics, Job } from "../../api/endpoints"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; -import { lookupApiLink } from "../../api/kube-api"; +import { apiManager } from "../../api/api-manager"; import { KubeObjectMeta } from "../kube-object-meta"; import { makeObservable, observable } from "mobx"; import { podMetricTabs, PodCharts } from "../+workloads-pods/pod-charts"; @@ -117,7 +117,7 @@ export class JobDetails extends React.Component { { ownerRefs.map(ref => { const { name, kind } = ref; - const detailsUrl = getDetailsUrl(lookupApiLink(ref, job)); + const detailsUrl = getDetailsUrl(apiManager.lookupApiLink(ref, job)); return (

diff --git a/src/renderer/components/+workloads-pods/pods.tsx b/src/renderer/components/+workloads-pods/pods.tsx index a3de5764fdf6..46fce21e1631 100644 --- a/src/renderer/components/+workloads-pods/pods.tsx +++ b/src/renderer/components/+workloads-pods/pods.tsx @@ -35,7 +35,7 @@ import { cssNames, stopPropagation } from "../../utils"; import toPairs from "lodash/toPairs"; import startCase from "lodash/startCase"; import kebabCase from "lodash/kebabCase"; -import { lookupApiLink } from "../../api/kube-api"; +import { apiManager } from "../../api/api-manager"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { Badge } from "../badge"; import type { PodsRouteParams } from "../../../common/routes"; @@ -135,7 +135,7 @@ export class Pods extends React.Component { pod.getRestartsCount(), pod.getOwnerRefs().map(ref => { const { kind, name } = ref; - const detailsLink = getDetailsUrl(lookupApiLink(ref, pod)); + const detailsLink = getDetailsUrl(apiManager.lookupApiLink(ref, pod)); return ( diff --git a/src/renderer/components/dock/create-resource.tsx b/src/renderer/components/dock/create-resource.tsx index f3423a7771b9..c290e3f8bf03 100644 --- a/src/renderer/components/dock/create-resource.tsx +++ b/src/renderer/components/dock/create-resource.tsx @@ -105,7 +105,7 @@ export class CreateResource extends React.Component { await Promise.all( resources.map(data => { return resourceApplierApi.update(data) - .then(item => createdResources.push(item.getName())) + .then(item => createdResources.push(item.metadata.name)) .catch((err: JsonApiErrorParsed) => errors.push(err.toString())); }) ); diff --git a/src/renderer/components/kube-object-meta/kube-object-meta.tsx b/src/renderer/components/kube-object-meta/kube-object-meta.tsx index 41e93422dcfe..ad3cac599859 100644 --- a/src/renderer/components/kube-object-meta/kube-object-meta.tsx +++ b/src/renderer/components/kube-object-meta/kube-object-meta.tsx @@ -22,7 +22,7 @@ import React from "react"; import type { KubeMetaField, KubeObject } from "../../api/kube-object"; import { DrawerItem, DrawerItemLabels } from "../drawer"; -import { lookupApiLink } from "../../api/kube-api"; +import { apiManager } from "../../api/api-manager"; import { Link } from "react-router-dom"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { LocaleDate } from "../locale-date"; @@ -93,7 +93,7 @@ export class KubeObjectMeta extends React.Component { { ownerRefs.map(ref => { const { name, kind } = ref; - const ownerDetailsUrl = getDetailsUrl(lookupApiLink(ref, object)); + const ownerDetailsUrl = getDetailsUrl(apiManager.lookupApiLink(ref, object)); return (

diff --git a/src/renderer/kube-object.store.ts b/src/renderer/kube-object.store.ts index 14dbbc6152f1..fd5334565502 100644 --- a/src/renderer/kube-object.store.ts +++ b/src/renderer/kube-object.store.ts @@ -27,7 +27,8 @@ import { KubeObject, KubeStatus } from "./api/kube-object"; import type { IKubeWatchEvent } from "./api/kube-watch-api"; import { ItemStore } from "./item.store"; import { apiManager } from "./api/api-manager"; -import { ensureObjectSelfLink, IKubeApiQueryParams, KubeApi, parseKubeApi } from "./api/kube-api"; +import { ensureObjectSelfLink, IKubeApiQueryParams, KubeApi } from "./api/kube-api"; +import { parseKubeApi } from "./api/kube-api-parse"; import type { KubeJsonApiData } from "./api/kube-json-api"; import { Notifications } from "./components/notifications"; @@ -279,7 +280,8 @@ export abstract class KubeObjectStore extends ItemStore } async update(item: T, data: Partial): Promise { - const newItem = await item.update(data); + const rawItem = await item.update(data); + const newItem = new this.api.objectConstructor(rawItem); ensureObjectSelfLink(this.api, newItem);