From 4bc4fe3a24d4b3b39b4c7085166678952e4cbd9b Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Fri, 12 Feb 2021 16:24:02 +0200 Subject: [PATCH 1/7] Fix accessible namespaces reset on cluster disconnect (#2140) Signed-off-by: Jari Kolehmainen --- src/main/cluster.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 5cf5a58bd9..13c74a285e 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -400,7 +400,6 @@ export class Cluster implements ClusterModel, ClusterState { this.ready = false; this.activated = false; this.allowedNamespaces = []; - this.accessibleNamespaces = []; this.resourceAccessStatuses.clear(); this.pushState(); } From 557d96d4849e65e378b321f081a78af27d75080d Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Fri, 12 Feb 2021 16:37:41 +0200 Subject: [PATCH 2/7] Handle suspend/resume error from watch stream read (#2136) * handle suspend/resume error from stream read Signed-off-by: Jari Kolehmainen * add missing types Signed-off-by: Jari Kolehmainen --- package.json | 3 +- src/renderer/api/kube-api.ts | 23 +++++--- src/renderer/utils/readableStream.ts | 87 ++++++++++++++++++++++++++++ yarn.lock | 8 --- 4 files changed, 105 insertions(+), 16 deletions(-) create mode 100644 src/renderer/utils/readableStream.ts diff --git a/package.json b/package.json index f663accadc..05da6d14a5 100644 --- a/package.json +++ b/package.json @@ -222,7 +222,7 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-router": "^5.2.0", - "readable-web-to-node-stream": "^3.0.1", + "readable-stream": "^3.6.0", "request": "^2.88.2", "request-promise-native": "^1.0.8", "semver": "^7.3.2", @@ -277,6 +277,7 @@ "@types/react-router-dom": "^5.1.6", "@types/react-select": "^3.0.13", "@types/react-window": "^1.8.2", + "@types/readable-stream": "^2.3.9", "@types/request": "^2.48.5", "@types/request-promise-native": "^1.0.17", "@types/semver": "^7.2.0", diff --git a/src/renderer/api/kube-api.ts b/src/renderer/api/kube-api.ts index 97cfb0522b..a880cc2406 100644 --- a/src/renderer/api/kube-api.ts +++ b/src/renderer/api/kube-api.ts @@ -10,8 +10,8 @@ import { createKubeApiURL, parseKubeApi } from "./kube-api-parse"; import { KubeJsonApi, KubeJsonApiData, KubeJsonApiDataList } from "./kube-json-api"; import { IKubeObjectConstructor, KubeObject, KubeStatus } from "./kube-object"; import byline from "byline"; -import { ReadableWebToNodeStream } from "readable-web-to-node-stream"; import { IKubeWatchEvent } from "./kube-watch-api"; +import { ReadableWebToNodeStream } from "../utils/readableStream"; export interface IKubeApiOptions { /** @@ -373,7 +373,13 @@ export class KubeApi { opts.abortController = new AbortController(); } let errorReceived = false; + let timedRetry: NodeJS.Timeout; const { abortController, namespace, callback } = opts; + + abortController.signal.addEventListener("abort", () => { + clearTimeout(timedRetry); + }); + const watchUrl = this.getWatchUrl(namespace); const responsePromise = this.request.getResponse(watchUrl, null, { signal: abortController.signal @@ -387,14 +393,17 @@ export class KubeApi { } const nodeStream = new ReadableWebToNodeStream(response.body); - nodeStream.on("end", () => { - if (errorReceived) return; // kubernetes errors should be handled in a callback + ["end", "close", "error"].forEach((eventName) => { + nodeStream.on(eventName, () => { + if (errorReceived) return; // kubernetes errors should be handled in a callback - setTimeout(() => { // we did not get any kubernetes errors so let's retry - if (abortController.signal.aborted) return; + clearTimeout(timedRetry); + timedRetry = setTimeout(() => { // we did not get any kubernetes errors so let's retry + if (abortController.signal.aborted) return; - this.watch({...opts, namespace, callback}); - }, 1000); + this.watch({...opts, namespace, callback}); + }, 1000); + }); }); const stream = byline(nodeStream); diff --git a/src/renderer/utils/readableStream.ts b/src/renderer/utils/readableStream.ts new file mode 100644 index 0000000000..3b51106427 --- /dev/null +++ b/src/renderer/utils/readableStream.ts @@ -0,0 +1,87 @@ +import { Readable } from "readable-stream"; + +/** + * ReadableWebToNodeStream + * + * Copied from https://github.com/Borewit/readable-web-to-node-stream + * + * Adds read error handler + * + * */ +export class ReadableWebToNodeStream extends Readable { + + public bytesRead = 0; + public released = false; + + /** + * Default web API stream reader + * https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader + */ + private reader: ReadableStreamReader; + private pendingRead: Promise; + + /** + * + * @param stream Readable​Stream: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream + */ + constructor(stream: ReadableStream) { + super(); + this.reader = stream.getReader(); + } + + /** + * Implementation of readable._read(size). + * When readable._read() is called, if data is available from the resource, + * the implementation should begin pushing that data into the read queue + * https://nodejs.org/api/stream.html#stream_readable_read_size_1 + */ + public async _read() { + // Should start pushing data into the queue + // Read data from the underlying Web-API-readable-stream + if (this.released) { + this.push(null); // Signal EOF + + return; + } + + try { + this.pendingRead = this.reader.read(); + const data = await this.pendingRead; + + // clear the promise before pushing pushing new data to the queue and allow sequential calls to _read() + delete this.pendingRead; + + if (data.done || this.released) { + this.push(null); // Signal EOF + } else { + this.bytesRead += data.value.length; + this.push(data.value); // Push new data to the queue + } + } catch(error) { + this.push(null); // Signal EOF + } + } + + /** + * If there is no unresolved read call to Web-API Readable​Stream immediately returns; + * otherwise will wait until the read is resolved. + */ + public async waitForReadToComplete() { + if (this.pendingRead) { + await this.pendingRead; + } + } + + /** + * Close wrapper + */ + public async close(): Promise { + await this.syncAndRelease(); + } + + private async syncAndRelease() { + this.released = true; + await this.waitForReadToComplete(); + await this.reader.releaseLock(); + } +} diff --git a/yarn.lock b/yarn.lock index cd3384eba5..16ac2ca4f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11464,14 +11464,6 @@ readable-stream@~1.1.10: isarray "0.0.1" string_decoder "~0.10.x" -readable-web-to-node-stream@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.1.tgz#3f619b1bc5dd73a4cfe5c5f9b4f6faba55dff845" - integrity sha512-4zDC6CvjUyusN7V0QLsXVB7pJCD9+vtrM9bYDRv6uBQ+SKfx36rp5AFNPRgh9auKRul/a1iFZJYXcCbwRL+SaA== - dependencies: - "@types/readable-stream" "^2.3.9" - readable-stream "^3.6.0" - readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" From 4513a08b1fc0c00a0eb32f1421c2861c826e47f4 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 12 Feb 2021 10:59:58 -0500 Subject: [PATCH 3/7] don't use list all optimization when operating in accessible namespaces mode (#2142) Signed-off-by: Sebastian Malton --- src/renderer/kube-object.store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/kube-object.store.ts b/src/renderer/kube-object.store.ts index 943aae7ad0..023048cb73 100644 --- a/src/renderer/kube-object.store.ts +++ b/src/renderer/kube-object.store.ts @@ -111,7 +111,7 @@ export abstract class KubeObjectStore extends ItemSt const isLoadingAll = this.context.allNamespaces.every(ns => namespaces.includes(ns)); - if (isLoadingAll) { + if (isLoadingAll && this.context.cluster.accessibleNamespaces.length === 0) { this.loadedNamespaces = []; return api.list({}, this.query); From ebf91770986743d3c560c3087d7292c51b8d4a61 Mon Sep 17 00:00:00 2001 From: Lauri Nevala Date: Fri, 12 Feb 2021 19:04:12 +0200 Subject: [PATCH 4/7] Activate survey extension only on main renderer (#2145) Signed-off-by: Lauri Nevala --- extensions/survey/renderer.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/extensions/survey/renderer.tsx b/extensions/survey/renderer.tsx index e8b2e4758c..c740015634 100644 --- a/extensions/survey/renderer.tsx +++ b/extensions/survey/renderer.tsx @@ -15,7 +15,10 @@ export default class SurveyRendererExtension extends LensRendererExtension { } ]; async onActivate() { - await surveyPreferencesStore.loadExtension(this); - survey.start(); + // Activate extension only on main renderer + if (window.location.hostname === "localhost") { + await surveyPreferencesStore.loadExtension(this); + survey.start(); + } } } From f4a307492626e169a04a72dc83365592ffa4e952 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Sat, 13 Feb 2021 09:33:04 +0200 Subject: [PATCH 5/7] Fix events list default sort column (#2149) Signed-off-by: Jari Kolehmainen --- src/renderer/components/+events/events.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/+events/events.tsx b/src/renderer/components/+events/events.tsx index f8c48cfe09..f385555aaa 100644 --- a/src/renderer/components/+events/events.tsx +++ b/src/renderer/components/+events/events.tsx @@ -69,7 +69,7 @@ export class Events extends React.Component { tableProps={{ sortSyncWithUrl: false, sortByDefault: { - sortBy: columnId.type, + sortBy: columnId.age, orderBy: "desc", // show "Warning" events at the top }, }} @@ -108,7 +108,7 @@ export class Events extends React.Component { { title: "Involved Object", className: "object", sortBy: columnId.object, id: columnId.object }, { title: "Source", className: "source", id: columnId.source }, { title: "Count", className: "count", sortBy: columnId.count, id: columnId.count }, - { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + { title: "Last Seen", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(event: KubeEvent) => { const { involvedObject, type, message } = event; From 8d74f1c75906b9e0b474631f45c038d4e4c692c5 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 15 Feb 2021 00:25:34 -0500 Subject: [PATCH 6/7] Fix tailCount renderering in removeItemsDialog (#2148) Signed-off-by: Sebastian Malton --- src/renderer/components/item-object-list/item-list-layout.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/item-object-list/item-list-layout.tsx b/src/renderer/components/item-object-list/item-list-layout.tsx index dc0604017f..59bd18cd5e 100644 --- a/src/renderer/components/item-object-list/item-list-layout.tsx +++ b/src/renderer/components/item-object-list/item-list-layout.tsx @@ -282,8 +282,8 @@ export class ItemListLayout extends React.Component { const dialogCustomProps = customizeRemoveDialog ? customizeRemoveDialog(selectedItems) : {}; const selectedCount = selectedItems.length; const tailCount = selectedCount > visibleMaxNamesCount ? selectedCount - visibleMaxNamesCount : 0; - const tail = tailCount > 0 ? "and {tailCount} more" : null; - const message = selectedCount <= 1 ?

Remove item {selectedNames}?

:

Remove {selectedCount} items {selectedNames} {tail}?

; + const tail = tailCount > 0 ? <>, and {tailCount} more : null; + const message = selectedCount <= 1 ?

Remove item {selectedNames}?

:

Remove {selectedCount} items {selectedNames}{tail}?

; ConfirmDialog.open({ ok: removeSelectedItems, From 1e5d682b9bc60d5bfbc61ebecbe4e937fe6a2928 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Mon, 15 Feb 2021 11:29:04 +0200 Subject: [PATCH 7/7] Release v4.1.0-rc.2 (#2154) Signed-off-by: Jari Kolehmainen --- package.json | 2 +- static/RELEASE_NOTES.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 05da6d14a5..1e5c73a9b5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "kontena-lens", "productName": "Lens", "description": "Lens - The Kubernetes IDE", - "version": "4.1.0-rc.1", + "version": "4.1.0-rc.2", "main": "static/build/main.js", "copyright": "© 2020, Mirantis, Inc.", "license": "MIT", diff --git a/static/RELEASE_NOTES.md b/static/RELEASE_NOTES.md index 68363fba07..0fe2fa4c4d 100644 --- a/static/RELEASE_NOTES.md +++ b/static/RELEASE_NOTES.md @@ -2,7 +2,7 @@ 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.1.0-rc.1 (current version) +## 4.1.0-rc.2 (current version) - Change: list views default to a namespace (instead of listing resources from all namespaces) - Command palette