diff --git a/package.json b/package.json index b549530f99..1631c98a87 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "kontena-lens", "productName": "Lens", "description": "Lens - The Kubernetes IDE", - "version": "4.0.3", + "version": "4.0.4", "main": "static/build/main.js", "copyright": "© 2020, Mirantis, Inc.", "license": "MIT", @@ -44,7 +44,7 @@ "typedocs-extensions-api": "yarn run typedoc --ignoreCompilerErrors --readme docs/extensions/typedoc-readme.md.tpl --name @k8slens/extensions --out docs/extensions/api --mode library --excludePrivate --hideBreadcrumbs --includes src/ src/extensions/extension-api.ts" }, "config": { - "bundledKubectlVersion": "1.17.11", + "bundledKubectlVersion": "1.17.15", "bundledHelmVersion": "3.3.4" }, "engines": { diff --git a/src/main/cluster-detectors/distribution-detector.ts b/src/main/cluster-detectors/distribution-detector.ts index be0cadb1bd..041a8b9158 100644 --- a/src/main/cluster-detectors/distribution-detector.ts +++ b/src/main/cluster-detectors/distribution-detector.ts @@ -56,12 +56,12 @@ export class DistributionDetector extends BaseClusterDetector { return { value: "docker-desktop", accuracy: 80}; } - if (this.isCustom()) { - return { value: "custom", accuracy: 10}; + if (this.isCustom() && await this.isOpenshift()) { + return { value: "openshift", accuracy: 90}; } - if (await this.isOpenshift()) { - return { value: "openshift", accuracy: 90}; + if (this.isCustom()) { + return { value: "custom", accuracy: 10}; } return { value: "unknown", accuracy: 10}; @@ -88,7 +88,7 @@ export class DistributionDetector extends BaseClusterDetector { } protected isAKS() { - return this.cluster.apiUrl.endsWith("azmk8s.io"); + return this.cluster.apiUrl.includes("azmk8s.io"); } protected isMirantis() { diff --git a/src/main/extension-filesystem.ts b/src/main/extension-filesystem.ts index c4ee622e1d..eddb7b747f 100644 --- a/src/main/extension-filesystem.ts +++ b/src/main/extension-filesystem.ts @@ -1,6 +1,6 @@ import { randomBytes } from "crypto"; import { SHA256 } from "crypto-js"; -import { app } from "electron"; +import { app, remote } from "electron"; import fse from "fs-extra"; import { action, observable, toJS } from "mobx"; import path from "path"; @@ -31,7 +31,7 @@ export class FilesystemProvisionerStore extends BaseStore { if (!this.registeredExtensions.has(extensionName)) { const salt = randomBytes(32).toString("hex"); const hashedName = SHA256(`${extensionName}/${salt}`).toString(); - const dirPath = path.resolve(app.getPath("userData"), "extension_data", hashedName); + const dirPath = path.resolve((app || remote.app).getPath("userData"), "extension_data", hashedName); this.registeredExtensions.set(extensionName, dirPath); } diff --git a/src/main/index.ts b/src/main/index.ts index 6f682efb6b..8da9be1a01 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -4,7 +4,7 @@ import "../common/system-ca"; import "../common/prometheus-providers"; import * as Mobx from "mobx"; import * as LensExtensions from "../extensions/core-api"; -import { app, dialog } from "electron"; +import { app, dialog, powerMonitor } from "electron"; import { appName } from "../common/vars"; import path from "path"; import { LensProxy } from "./lens-proxy"; @@ -59,6 +59,10 @@ app.on("ready", async () => { logger.info(`🚀 Starting Lens from "${workingDir}"`); await shellSync(); + powerMonitor.on("shutdown", () => { + app.exit(); + }); + const updater = new AppUpdater(); updater.start(); diff --git a/src/renderer/api/api-manager.ts b/src/renderer/api/api-manager.ts index 68d4773540..01e5ceb228 100644 --- a/src/renderer/api/api-manager.ts +++ b/src/renderer/api/api-manager.ts @@ -17,6 +17,10 @@ export class ApiManager { return Array.from(this.apis.values()).find(pathOrCallback ?? (() => true)); } + getApiByKind(kind: string, apiVersion: string) { + return Array.from(this.apis.values()).find((api) => api.kind === kind && api.apiVersion === apiVersion); + } + registerApi(apiBase: string, api: KubeApi) { if (!this.apis.has(apiBase)) { this.apis.set(apiBase, api); diff --git a/src/renderer/api/kube-api.ts b/src/renderer/api/kube-api.ts index e7934675c6..8a3a2517c2 100644 --- a/src/renderer/api/kube-api.ts +++ b/src/renderer/api/kube-api.ts @@ -79,6 +79,18 @@ export function forCluster(cluster: IKubeApiCluster, kubeC }); } +export function ensureObjectSelfLink(api: KubeApi, object: KubeJsonApiData) { + if (!object.metadata.selfLink) { + object.metadata.selfLink = createKubeApiURL({ + apiPrefix: api.apiPrefix, + apiVersion: api.apiVersionWithGroup, + resource: api.apiResource, + namespace: api.isNamespaced ? object.metadata.namespace : undefined, + name: object.metadata.name, + }); + } +} + export class KubeApi { static parseApi = parseKubeApi; @@ -260,7 +272,11 @@ export class KubeApi { const KubeObjectConstructor = this.objectConstructor; if (KubeObject.isJsonApiData(data)) { - return new KubeObjectConstructor(data); + const object = new KubeObjectConstructor(data); + + ensureObjectSelfLink(this, object); + + return object; } // process items list response @@ -270,11 +286,17 @@ export class KubeApi { this.setResourceVersion(namespace, metadata.resourceVersion); this.setResourceVersion("", metadata.resourceVersion); - return items.map(item => new KubeObjectConstructor({ - kind: this.kind, - apiVersion, - ...item, - })); + return items.map((item) => { + const object = new KubeObjectConstructor({ + kind: this.kind, + apiVersion, + ...item, + }); + + ensureObjectSelfLink(this, object); + + return object; + }); } // custom apis might return array for list response, e.g. users, groups, etc. diff --git a/src/renderer/api/kube-watch-api.ts b/src/renderer/api/kube-watch-api.ts index 58665a11a1..78ca25256e 100644 --- a/src/renderer/api/kube-watch-api.ts +++ b/src/renderer/api/kube-watch-api.ts @@ -5,7 +5,7 @@ import { stringify } from "querystring"; import { autobind, EventEmitter } from "../utils"; import { KubeJsonApiData } from "./kube-json-api"; import type { KubeObjectStore } from "../kube-object.store"; -import { KubeApi } from "./kube-api"; +import { ensureObjectSelfLink, KubeApi } from "./kube-api"; import { apiManager } from "./api-manager"; import { apiPrefix, isDevelopment } from "../../common/vars"; import { getHostedCluster } from "../../common/cluster-store"; @@ -158,12 +158,14 @@ export class KubeWatchApi { addListener(store: KubeObjectStore, callback: (evt: IKubeWatchEvent) => void) { const listener = (evt: IKubeWatchEvent) => { - const { selfLink, namespace, resourceVersion } = evt.object.metadata; - const api = apiManager.getApi(selfLink); + const { namespace, resourceVersion } = evt.object.metadata; + const api = apiManager.getApiByKind(evt.object.kind, evt.object.apiVersion); api.setResourceVersion(namespace, resourceVersion); api.setResourceVersion("", resourceVersion); + ensureObjectSelfLink(api, evt.object); + if (store == apiManager.getStore(api)) { callback(evt); } diff --git a/src/renderer/components/+custom-resources/crd-resources.tsx b/src/renderer/components/+custom-resources/crd-resources.tsx index a4a52ef867..cca7ac7015 100644 --- a/src/renderer/components/+custom-resources/crd-resources.tsx +++ b/src/renderer/components/+custom-resources/crd-resources.tsx @@ -93,7 +93,7 @@ export class CrdResources extends React.Component { isNamespaced && crdInstance.getNs(), ...extraColumns.map(column => ({ renderBoolean: true, - children: jsonPath.value(crdInstance, column.jsonPath.slice(1)), + children: JSON.stringify(jsonPath.value(crdInstance, column.jsonPath.slice(1))), })), crdInstance.getAge(), ]} diff --git a/src/renderer/kube-object.store.ts b/src/renderer/kube-object.store.ts index e23adf3566..bb2fffd819 100644 --- a/src/renderer/kube-object.store.ts +++ b/src/renderer/kube-object.store.ts @@ -195,10 +195,9 @@ export abstract class KubeObjectStore extends ItemSt const items = this.items.toJS(); for (const {type, object} of this.eventsBuffer.clear()) { - const { uid, selfLink } = object.metadata; - const index = items.findIndex(item => item.getId() === uid); + const index = items.findIndex(item => item.getId() === object.metadata?.uid); const item = items[index]; - const api = apiManager.getApi(selfLink); + const api = apiManager.getApiByKind(object.kind, object.apiVersion); switch (type) { case "ADDED": diff --git a/static/RELEASE_NOTES.md b/static/RELEASE_NOTES.md index 3457e012ec..9b97cea5e4 100644 --- a/static/RELEASE_NOTES.md +++ b/static/RELEASE_NOTES.md @@ -2,10 +2,17 @@ Here you can find description of changes we've built into each release. While we try our best to make each upgrade automatic and as smooth as possible, there may be some cases where you might need to do something to ensure the application works smoothly. So please read through the release highlights! -## 4.0.3 (current version) +## 4.0.4 (current version) We are aware some users are encountering issues and regressions from previous version. Many of these issues are something we have not seen as part of our automated or manual testing process. To make it worse, some of them are really difficult to reproduce. We want to ensure we are putting all our energy and effort trying to resolve these issues. We hope you are patient. Expect to see new patch releases still in the coming days! Fixes in this version: +- Fix errors on Kubernetes v1.20 +- Update bundled kubectl to v1.17.15 +- Fix: MacOS error on shutdown +- Fix: Kubernetes distribution detection +- Fix: error while displaying CRDs with column which type is an object +## 4.0.3 + - Fix: install in-tree extensions before others - Fix: bundle all dependencies in in-tree extensions - Fix: display error dialog if extensions couldn't be loaded