diff --git a/package.json b/package.json index bdfb91473e..d4581123b0 100644 --- a/package.json +++ b/package.json @@ -304,7 +304,7 @@ "ts-loader": "^7.0.5", "ts-node": "^8.10.2", "typeface-roboto": "^0.0.75", - "typescript": "^3.9.5", + "typescript": "^4.0.2", "url-loader": "^4.1.0", "webpack": "^4.43.0", "webpack-cli": "^3.3.11", diff --git a/src/common/base-store.ts b/src/common/base-store.ts index 0cc087eb9f..5013bffc72 100644 --- a/src/common/base-store.ts +++ b/src/common/base-store.ts @@ -6,7 +6,7 @@ import { action, observable, reaction, runInAction, toJS, when } from "mobx"; import Singleton from "./utils/singleton"; import { getAppVersion } from "./utils/app-version"; import logger from "../main/logger"; -import { broadcastIpc } from "./ipc"; +import { broadcastIpc, IpcBroadcastParams } from "./ipc"; import isEqual from "lodash/isEqual"; export interface BaseStoreParams extends ConfOptions { @@ -63,7 +63,7 @@ export class BaseStore extends Singleton { this.isLoaded = true; } - protected async save(model: T) { + protected async saveToFile(model: T) { logger.info(`[STORE]: SAVING ${this.name}`); // todo: update when fixed https://github.com/sindresorhus/conf/issues/114 Object.entries(model).forEach(([key, value]) => { @@ -115,8 +115,8 @@ export class BaseStore extends Singleton { protected async onModelChange(model: T) { if (ipcMain) { - this.save(model); // save config file - broadcastIpc({ channel: this.syncChannel, args: [model] }); // broadcast to renderer views + this.saveToFile(model); // save config file + this.syncToWebViews(model); // send update to renderer views } // send "update-request" to main-process if (ipcRenderer) { @@ -124,6 +124,30 @@ export class BaseStore extends Singleton { } } + protected async syncToWebViews(model: T) { + const msg: IpcBroadcastParams = { + channel: this.syncChannel, + args: [model], + } + broadcastIpc(msg); // send to all windows (BrowserWindow, webContents) + const frames = await this.getSubFrames(); + frames.forEach(frameId => { + broadcastIpc({ frameId, ...msg }); // send to all sub-frames (e.g. cluster-view managed in iframe) + }); + } + + // todo: refactor? + protected async getSubFrames(): Promise { + const subFrames: number[] = []; + const { clusterStore } = await import("./cluster-store"); + clusterStore.clustersList.forEach(cluster => { + if (cluster.frameId) { + subFrames.push(cluster.frameId) + } + }); + return subFrames; + } + @action protected fromStore(data: T) { this.data = data; diff --git a/src/common/cluster-ipc.ts b/src/common/cluster-ipc.ts index 5e1b86992b..e3024a69b0 100644 --- a/src/common/cluster-ipc.ts +++ b/src/common/cluster-ipc.ts @@ -3,7 +3,7 @@ import { ClusterId, clusterStore } from "./cluster-store"; import { tracker } from "./tracker"; export const clusterIpc = { - init: createIpcChannel({ + initView: createIpcChannel({ channel: "cluster:init", handle: async (clusterId: ClusterId, frameId: number) => { const cluster = clusterStore.getById(clusterId); diff --git a/src/main/feature.ts b/src/main/feature.ts index a62012af5e..96053c2a05 100644 --- a/src/main/feature.ts +++ b/src/main/feature.ts @@ -5,6 +5,7 @@ import { ResourceApplier } from "./resource-applier" import { CoreV1Api, KubeConfig, Watch } from "@kubernetes/client-node" import { Cluster } from "./cluster"; import logger from "./logger"; +import { isDevelopment } from "../common/vars"; export type FeatureStatusMap = Record export type FeatureMap = Record @@ -23,8 +24,8 @@ export interface FeatureStatus { } export abstract class Feature { - name: string; - latestVersion: string; + public name: string; + public latestVersion: string; abstract async upgrade(cluster: Cluster): Promise; @@ -35,6 +36,13 @@ export abstract class Feature { constructor(public config: any) { } + get folderPath() { + if (isDevelopment) { + return path.resolve(__static, "../src/features", this.name); + } + return path.resolve(__static, "../features", this.name); + } + async install(cluster: Cluster): Promise { const resources = this.renderTemplates(); try { @@ -70,9 +78,11 @@ export abstract class Feature { } protected renderTemplates(): string[] { + const folderPath = this.folderPath; const resources: string[] = []; - fs.readdirSync(this.manifestPath()).forEach(filename => { - const file = path.join(this.manifestPath(), filename); + logger.info(`[FEATURE]: render templates from ${folderPath}`); + fs.readdirSync(folderPath).forEach(filename => { + const file = path.join(folderPath, filename); const raw = fs.readFileSync(file); if (filename.endsWith('.hb')) { const template = hb.compile(raw.toString()); @@ -84,12 +94,4 @@ export abstract class Feature { return resources; } - - protected manifestPath() { - const devPath = path.join(__dirname, "..", 'src/features', this.name); - if (fs.existsSync(devPath)) { - return devPath; - } - return path.join(__dirname, "..", 'features', this.name); - } } diff --git a/src/main/kubectl.ts b/src/main/kubectl.ts index ee1c0efdc7..46e1c4ce24 100644 --- a/src/main/kubectl.ts +++ b/src/main/kubectl.ts @@ -273,7 +273,7 @@ export class Kubectl { zshScript += "test -f \"$OLD_ZDOTDIR/.zlogin\" && . \"$OLD_ZDOTDIR/.zlogin\"\n" zshScript += "test -f \"$OLD_ZDOTDIR/.zshrc\" && . \"$OLD_ZDOTDIR/.zshrc\"\n" - // voodoo to replace any previous occurences of kubectl path in the PATH + // voodoo to replace any previous occurrences of kubectl path in the PATH zshScript += `kubectlpath=\"${this.dirname}"\n` zshScript += `helmpath=\"${helmPath}"\n` zshScript += "p=\":$kubectlpath:\"\n" diff --git a/src/renderer/api/endpoints/pods.api.ts b/src/renderer/api/endpoints/pods.api.ts index 201f1e1751..c1394ab6db 100644 --- a/src/renderer/api/endpoints/pods.api.ts +++ b/src/renderer/api/endpoints/pods.api.ts @@ -291,7 +291,7 @@ export class Pod extends WorkloadKubeObject { return PodStatus.PENDING; } - // Returns pod phase or container error if occured + // Returns pod phase or container error if occurred getStatusMessage() { if (this.getReason() === PodStatus.EVICTED) return "Evicted"; if (this.getStatus() === PodStatus.RUNNING && this.metadata.deletionTimestamp) return "Terminating"; diff --git a/src/renderer/api/endpoints/resource-applier.api.ts b/src/renderer/api/endpoints/resource-applier.api.ts index 3cecb3de65..088c7ed8f2 100644 --- a/src/renderer/api/endpoints/resource-applier.api.ts +++ b/src/renderer/api/endpoints/resource-applier.api.ts @@ -18,7 +18,11 @@ export const resourceApplierApi = { .then(data => { const items = data.map(obj => { const api = apiManager.getApi(obj.metadata.selfLink); - return new api.objectConstructor(obj); + if (api) { + return new api.objectConstructor(obj); + } else { + return new KubeObject(obj) + } }); return items.length === 1 ? items[0] : items; }); diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx index 0c3044fc12..abdef9b7f4 100755 --- a/src/renderer/components/app.tsx +++ b/src/renderer/components/app.tsx @@ -38,10 +38,11 @@ import { webFrame } from "electron"; @observer export class App extends React.Component { static async init() { + const frameId = webFrame.routingId; const clusterId = getHostedClusterId(); - logger.info(`[APP]: Init dashboard, clusterId=${clusterId}`) + logger.info(`[APP]: Init dashboard, clusterId=${clusterId}, frameId=${frameId}`) await Terminal.preloadFonts() - await clusterIpc.init.invokeFromRenderer(clusterId, webFrame.routingId); + await clusterIpc.initView.invokeFromRenderer(clusterId, frameId); await getHostedCluster().whenInitialized; } diff --git a/static/RELEASE_NOTES.md b/static/RELEASE_NOTES.md index 16e86e9e6f..74ae294956 100644 --- a/static/RELEASE_NOTES.md +++ b/static/RELEASE_NOTES.md @@ -1,8 +1,12 @@ # What's new? -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! +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! + +## 3.5.3 (current version) +- Updated [EULA](https://k8slens.dev/licenses/eula.md) + +## 3.5.2 +- Fix application not opening properly in some cases by catching and logging error from shell sync. ## 3.5.1 (current version) - Fix kubernetes api requests to work with non-"namespaces" pathnames diff --git a/yarn.lock b/yarn.lock index 17eea67436..d8881d0f1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11177,10 +11177,10 @@ typeface-roboto@^0.0.75: resolved "https://registry.yarnpkg.com/typeface-roboto/-/typeface-roboto-0.0.75.tgz#98d5ba35ec234bbc7172374c8297277099cc712b" integrity sha512-VrR/IiH00Z1tFP4vDGfwZ1esNqTiDMchBEXYY9kilT6wRGgFoCAlgkEUMHb1E3mB0FsfZhv756IF0+R+SFPfdg== -typescript@^3.9.5: - version "3.9.7" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" - integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== +typescript@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== uglify-js@^3.1.4: version "3.9.4"