From ba568e33cadd437c45ca6c90594571908209d2e6 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 28 Apr 2021 12:32:34 -0400 Subject: [PATCH] Fix settings saving and catalog view Signed-off-by: Sebastian Malton --- src/main/catalog-pusher.ts | 17 ++++----- src/main/catalog-sources/kubeconfig-sync.ts | 38 ++++++++++--------- src/renderer/api/catalog-entity-registry.ts | 22 +---------- .../+preferences/kubeconfig-syncs.tsx | 26 +++++++------ 4 files changed, 44 insertions(+), 59 deletions(-) diff --git a/src/main/catalog-pusher.ts b/src/main/catalog-pusher.ts index 73b9b9d580..8bc028cbd7 100644 --- a/src/main/catalog-pusher.ts +++ b/src/main/catalog-pusher.ts @@ -1,7 +1,8 @@ -import { autorun, toJS } from "mobx"; +import { reaction, toJS } from "mobx"; import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "../common/ipc"; import { CatalogEntityRegistry} from "../common/catalog"; import "../common/catalog-entities/kubernetes-cluster"; +import { Disposer } from "../common/utils"; export class CatalogPusher { static init(catalog: CatalogEntityRegistry) { @@ -11,22 +12,20 @@ export class CatalogPusher { private constructor(private catalog: CatalogEntityRegistry) {} init() { - const disposers: { (): void; }[] = []; + const disposers: Disposer[] = []; - disposers.push(autorun(() => { - this.broadcast(); + disposers.push(reaction(() => this.catalog.items, (items) => { + broadcastMessage("catalog:items", toJS(items, { recurseEverything: true })); + }, { + fireImmediately: true, })); const listener = subscribeToBroadcast("catalog:broadcast", () => { - this.broadcast(); + broadcastMessage("catalog:items", toJS(this.catalog.items, { recurseEverything: true })); }); disposers.push(() => unsubscribeFromBroadcast("catalog:broadcast", listener)); return disposers; } - - broadcast() { - broadcastMessage("catalog:items", toJS(this.catalog.items, { recurseEverything: true })); - } } diff --git a/src/main/catalog-sources/kubeconfig-sync.ts b/src/main/catalog-sources/kubeconfig-sync.ts index 2b451a6177..3d5af56be3 100644 --- a/src/main/catalog-sources/kubeconfig-sync.ts +++ b/src/main/catalog-sources/kubeconfig-sync.ts @@ -4,7 +4,7 @@ import { watch } from "chokidar"; import fs from "fs"; import * as uuid from "uuid"; import stream from "stream"; -import { ExtendedObservableMap, iter, Singleton } from "../../common/utils"; +import { Disposer, ExtendedObservableMap, iter, Singleton } from "../../common/utils"; import logger from "../logger"; import { KubeConfig } from "@kubernetes/client-node"; import { loadConfigFromString, splitConfig, validateKubeConfig } from "../../common/kube-helpers"; @@ -15,16 +15,12 @@ import { ClusterStore, UpdateClusterModel } from "../../common/cluster-store"; const logPrefix = "[KUBECONFIG-SYNC]:"; -type Disposer = () => void; - export class KubeconfigSyncManager extends Singleton { - protected sources = observable.map>, Disposer]>(); + protected sources = observable.map, Disposer]>(); protected syncing = false; protected syncListDisposer?: Disposer; - protected getSyncName(file: string): string { - return `lens:kube-sync:${file}`; - } + protected static readonly syncName = "lens:kube-sync"; @action startSync(port: number): void { @@ -36,10 +32,17 @@ export class KubeconfigSyncManager extends Singleton { logger.info(`${logPrefix} starting requested syncs`); + catalogEntityRegistry.addComputedSource(KubeconfigSyncManager.syncName, computed(() => ( + Array.from(iter.flatMap( + this.sources.values(), + ([entities]) => entities.get() + )) + ))); + // This must be done so that c&p-ed clusters are visible this.startNewSync(ClusterStore.storedKubeConfigFolder, port); - for (const [filePath] of UserStore.getInstance().syncKubeconfigEntries) { + for (const filePath of UserStore.getInstance().syncKubeconfigEntries.keys()) { this.startNewSync(filePath, port); } @@ -63,6 +66,7 @@ export class KubeconfigSyncManager extends Singleton { this.stopOldSync(filePath); } + catalogEntityRegistry.removeSource(KubeconfigSyncManager.syncName); this.syncing = false; } @@ -70,26 +74,24 @@ export class KubeconfigSyncManager extends Singleton { protected startNewSync(filePath: string, port: number): void { if (this.sources.has(filePath)) { // don't start a new sync if we already have one - return; + return void logger.debug(`${logPrefix} already syncing file/folder`, { filePath }); } - logger.info(`${logPrefix} starting sync of file`, { filePath }); - const changeSet = watchFileChanges(filePath, port); + this.sources.set(filePath, watchFileChanges(filePath, port)); - this.sources.set(filePath, changeSet); - catalogEntityRegistry.addComputedSource(this.getSyncName(filePath), changeSet[0]); + logger.info(`${logPrefix} starting sync of file/folder`, { filePath }); + logger.debug(`${logPrefix} ${this.sources.size} files/folders watched`, { files: Array.from(this.sources.keys()) }); } @action protected stopOldSync(filePath: string): void { - if (!this.sources.has(filePath)) { + if (!this.sources.delete(filePath)) { // already stopped - return; + return void logger.debug(`${logPrefix} no syncing file/folder to stop`, { filePath }); } - logger.info(`${logPrefix} stopping sync of file`, { filePath }); - this.sources.delete(filePath); - catalogEntityRegistry.removeSource(this.getSyncName(filePath)); + logger.info(`${logPrefix} stopping sync of file/folder`, { filePath }); + logger.debug(`${logPrefix} ${this.sources.size} files/folders watched`, { files: Array.from(this.sources.keys()) }); } } diff --git a/src/renderer/api/catalog-entity-registry.ts b/src/renderer/api/catalog-entity-registry.ts index ea0537bda3..b67e2d2b0f 100644 --- a/src/renderer/api/catalog-entity-registry.ts +++ b/src/renderer/api/catalog-entity-registry.ts @@ -16,27 +16,7 @@ export class CatalogEntityRegistry { } @action updateItems(items: (CatalogEntityData & CatalogEntityKindData)[]) { - this._items.forEach((item, index) => { - const foundIndex = items.findIndex((i) => i.apiVersion === item.apiVersion && i.kind === item.kind && i.metadata.uid === item.metadata.uid); - - if (foundIndex === -1) { - this._items.splice(index, 1); - } - }); - - items.forEach((data) => { - const item = this.categoryRegistry.getEntityForData(data); - - if (!item) return; // invalid data - - const index = this._items.findIndex((i) => i.apiVersion === item.apiVersion && i.kind === item.kind && i.metadata.uid === item.metadata.uid); - - if (index === -1) { - this._items.push(item); - } else { - this._items.splice(index, 1, item); - } - }); + this._items = items.map(data => this.categoryRegistry.getEntityForData(data)); } get items() { diff --git a/src/renderer/components/+preferences/kubeconfig-syncs.tsx b/src/renderer/components/+preferences/kubeconfig-syncs.tsx index 13ac8d6d46..6fbfd58e0b 100644 --- a/src/renderer/components/+preferences/kubeconfig-syncs.tsx +++ b/src/renderer/components/+preferences/kubeconfig-syncs.tsx @@ -7,7 +7,7 @@ import { Description, Folder, Delete, HelpOutline } from "@material-ui/icons"; import { action, computed, observable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import fse from "fs-extra"; -import { KubeconfigSyncEntry, UserStore } from "../../../common/user-store"; +import { KubeconfigSyncEntry, KubeconfigSyncValue, UserStore } from "../../../common/user-store"; import { Button } from "../button"; import { SubTitle } from "../layout/sub-title"; import { Spinner } from "../spinner"; @@ -18,37 +18,41 @@ interface SyncInfo { type: "file" | "folder" | "unknown"; } -interface Entry { +interface Entry extends Value { filePath: string; +} + +interface Value { + data: KubeconfigSyncValue; info: SyncInfo; } -async function getMapEntry(entry: KubeconfigSyncEntry): Promise<[string, SyncInfo]> { +async function getMapEntry({ filePath, ...data}: KubeconfigSyncEntry): Promise<[string, Value]> { try { // stat follows the stat(2) linux syscall spec, namely it follows symlinks - const stats = await fse.stat(entry.filePath); + const stats = await fse.stat(filePath); if (stats.isFile()) { - return [entry.filePath, { type: "file" }]; + return [filePath, { info: { type: "file" }, data }]; } if (stats.isDirectory()) { - return [entry.filePath, { type: "folder" }]; + return [filePath, { info: { type: "folder" }, data }]; } logger.warn("[KubeconfigSyncs]: unknown stat entry", { stats }); - return [entry.filePath, { type: "unknown" }]; + return [filePath, { info: { type: "unknown" }, data }]; } catch (error) { logger.warn(`[KubeconfigSyncs]: failed to stat entry: ${error}`, { error }); - return [entry.filePath, { type: "unknown" }]; + return [filePath, { info: { type: "unknown" }, data }]; } } @observer export class KubeconfigSyncs extends React.Component { - syncs = observable.map(); + syncs = observable.map(); @observable loaded = false; async componentDidMount() { @@ -63,7 +67,7 @@ export class KubeconfigSyncs extends React.Component { this.loaded = true; disposeOnUnmount(this, [ - reaction(() => Array.from(this.syncs.keys()), syncs => { + reaction(() => Array.from(this.syncs.entries(), ([filePath, { data }]) => [filePath, data]), syncs => { UserStore.getInstance().syncKubeconfigEntries.replace(syncs); }) ]); @@ -74,7 +78,7 @@ export class KubeconfigSyncs extends React.Component { return undefined; } - return Array.from(this.syncs.entries(), ([filePath, info]) => ({ filePath, info })); + return Array.from(this.syncs.entries(), ([filePath, value]) => ({ filePath, ...value })); } @action