mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Switch to overriding dependencies to fix test flakiness
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
91bb7109d5
commit
38ca06fc80
30
src/common/fs/create-read-file-stream.injectable.ts
Normal file
30
src/common/fs/create-read-file-stream.injectable.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import type { ReadStream } from "fs";
|
||||||
|
import fsInjectable from "./fs.injectable";
|
||||||
|
|
||||||
|
export interface CreateReadStreamOptions {
|
||||||
|
mode?: number;
|
||||||
|
end?: number | undefined;
|
||||||
|
flags?: string | undefined;
|
||||||
|
encoding?: BufferEncoding | undefined;
|
||||||
|
autoClose?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
emitClose?: boolean | undefined;
|
||||||
|
start?: number | undefined;
|
||||||
|
highWaterMark?: number | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CreateReadFileStream = (filePath: string, options?: CreateReadStreamOptions) => ReadStream;
|
||||||
|
|
||||||
|
const createReadFileStreamInjectable = getInjectable({
|
||||||
|
id: "create-read-file-stream",
|
||||||
|
instantiate: (di): CreateReadFileStream => di.inject(fsInjectable).createReadStream,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default createReadFileStreamInjectable;
|
||||||
@ -3,12 +3,14 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import type { Stats } from "fs";
|
||||||
import fsInjectable from "../fs.injectable";
|
import fsInjectable from "../fs.injectable";
|
||||||
|
|
||||||
|
export type Stat = (path: string) => Promise<Stats>;
|
||||||
|
|
||||||
const statInjectable = getInjectable({
|
const statInjectable = getInjectable({
|
||||||
id: "stat",
|
id: "stat",
|
||||||
|
instantiate: (di): Stat => di.inject(fsInjectable).stat,
|
||||||
instantiate: (di) => di.inject(fsInjectable).stat,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default statInjectable;
|
export default statInjectable;
|
||||||
|
|||||||
@ -3,15 +3,161 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import type { FSWatcher, WatchOptions } from "chokidar";
|
|
||||||
import { watch } from "chokidar";
|
import { watch } from "chokidar";
|
||||||
|
import type { Stats } from "fs";
|
||||||
|
import type TypedEventEmitter from "typed-emitter";
|
||||||
|
import type { SingleOrMany } from "../../utils";
|
||||||
|
|
||||||
export type Watch = (path: string, options?: WatchOptions) => FSWatcher;
|
export interface AlwaysStatWatcherEvents {
|
||||||
|
add: (path: string, stats: Stats) => void;
|
||||||
|
addDir: (path: string, stats: Stats) => void;
|
||||||
|
change: (path: string, stats: Stats) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MaybeStatWatcherEvents {
|
||||||
|
add: (path: string, stats?: Stats) => void;
|
||||||
|
addDir: (path: string, stats?: Stats) => void;
|
||||||
|
change: (path: string, stats?: Stats) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WatcherEvents<AlwaysStat extends boolean> = BaseWatcherEvents
|
||||||
|
& (
|
||||||
|
AlwaysStat extends true
|
||||||
|
? AlwaysStatWatcherEvents
|
||||||
|
: MaybeStatWatcherEvents
|
||||||
|
);
|
||||||
|
|
||||||
|
export interface BaseWatcherEvents {
|
||||||
|
error: (error: Error) => void;
|
||||||
|
ready: () => void;
|
||||||
|
unlink: (path: string) => void;
|
||||||
|
unlinkDir: (path: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Watcher<AlwaysStat extends boolean> extends TypedEventEmitter<WatcherEvents<AlwaysStat>> {
|
||||||
|
close: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WatcherOptions<AlwaysStat extends boolean> = {
|
||||||
|
/**
|
||||||
|
* Indicates whether the process should continue to run as long as files are being watched. If
|
||||||
|
* set to `false` when using `fsevents` to watch, no more events will be emitted after `ready`,
|
||||||
|
* even if the process continues to run.
|
||||||
|
*/
|
||||||
|
persistent?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ([anymatch](https://github.com/micromatch/anymatch)-compatible definition) Defines files/paths to
|
||||||
|
* be ignored. The whole relative or absolute path is tested, not just filename. If a function
|
||||||
|
* with two arguments is provided, it gets called twice per path - once with a single argument
|
||||||
|
* (the path), second time with two arguments (the path and the
|
||||||
|
* [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object of that path).
|
||||||
|
*/
|
||||||
|
ignored?: SingleOrMany<string | RegExp | ((path: string) => boolean)>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to `false` then `add`/`addDir` events are also emitted for matching paths while
|
||||||
|
* instantiating the watching as chokidar discovers these file paths (before the `ready` event).
|
||||||
|
*/
|
||||||
|
ignoreInitial?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When `false`, only the symlinks themselves will be watched for changes instead of following
|
||||||
|
* the link references and bubbling events through the link's path.
|
||||||
|
*/
|
||||||
|
followSymlinks?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base directory from which watch `paths` are to be derived. Paths emitted with events will
|
||||||
|
* be relative to this.
|
||||||
|
*/
|
||||||
|
cwd?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to true then the strings passed to .watch() and .add() are treated as literal path
|
||||||
|
* names, even if they look like globs. Default: false.
|
||||||
|
*/
|
||||||
|
disableGlobbing?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use fs.watchFile (backed by polling), or fs.watch. If polling leads to high CPU
|
||||||
|
* utilization, consider setting this to `false`. It is typically necessary to **set this to
|
||||||
|
* `true` to successfully watch files over a network**, and it may be necessary to successfully
|
||||||
|
* watch files in other non-standard situations. Setting to `true` explicitly on OS X overrides
|
||||||
|
* the `useFsEvents` default.
|
||||||
|
*/
|
||||||
|
usePolling?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use the `fsevents` watching interface if available. When set to `true` explicitly
|
||||||
|
* and `fsevents` is available this supercedes the `usePolling` setting. When set to `false` on
|
||||||
|
* OS X, `usePolling: true` becomes the default.
|
||||||
|
*/
|
||||||
|
useFsEvents?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, limits how many levels of subdirectories will be traversed.
|
||||||
|
*/
|
||||||
|
depth?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interval of file system polling.
|
||||||
|
*/
|
||||||
|
interval?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interval of file system polling for binary files. ([see list of binary extensions](https://gi
|
||||||
|
* thub.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json))
|
||||||
|
*/
|
||||||
|
binaryInterval?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether to watch files that don't have read permissions if possible. If watching
|
||||||
|
* fails due to `EPERM` or `EACCES` with this set to `true`, the errors will be suppressed
|
||||||
|
* silently.
|
||||||
|
*/
|
||||||
|
ignorePermissionErrors?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `true` if `useFsEvents` and `usePolling` are `false`). Automatically filters out artifacts
|
||||||
|
* that occur when using editors that use "atomic writes" instead of writing directly to the
|
||||||
|
* source file. If a file is re-added within 100 ms of being deleted, Chokidar emits a `change`
|
||||||
|
* event rather than `unlink` then `add`. If the default of 100 ms does not work well for you,
|
||||||
|
* you can override it by setting `atomic` to a custom value, in milliseconds.
|
||||||
|
*/
|
||||||
|
atomic?: boolean | number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* can be set to an object in order to adjust timing params:
|
||||||
|
*/
|
||||||
|
awaitWriteFinish?: AwaitWriteFinishOptions | boolean;
|
||||||
|
} & (AlwaysStat extends true
|
||||||
|
? {
|
||||||
|
alwaysStat: true;
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
alwaysStat?: false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export interface AwaitWriteFinishOptions {
|
||||||
|
/**
|
||||||
|
* Amount of time in milliseconds for a file size to remain constant before emitting its event.
|
||||||
|
*/
|
||||||
|
stabilityThreshold?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File size polling interval.
|
||||||
|
*/
|
||||||
|
pollInterval?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Watch = <AlwaysStat extends boolean = false>(path: string, options?: WatcherOptions<AlwaysStat>) => Watcher<AlwaysStat>;
|
||||||
|
|
||||||
// TODO: Introduce wrapper to allow simpler API
|
// TODO: Introduce wrapper to allow simpler API
|
||||||
const watchInjectable = getInjectable({
|
const watchInjectable = getInjectable({
|
||||||
id: "watch",
|
id: "watch",
|
||||||
instantiate: (): Watch => watch,
|
instantiate: () => watch as Watch,
|
||||||
causesSideEffects: true,
|
causesSideEffects: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -3,72 +3,49 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { observable, ObservableMap, when } from "mobx";
|
import { observable, ObservableMap } from "mobx";
|
||||||
import type { CatalogEntity } from "../../../common/catalog";
|
import type { CatalogEntity } from "../../../common/catalog";
|
||||||
import { loadFromOptions } from "../../../common/kube-helpers";
|
import { loadFromOptions } from "../../../common/kube-helpers";
|
||||||
import type { Cluster } from "../../../common/cluster/cluster";
|
import type { Cluster } from "../../../common/cluster/cluster";
|
||||||
import mockFs from "mock-fs";
|
|
||||||
import fs from "fs";
|
|
||||||
import clusterStoreInjectable from "../../../common/cluster-store/cluster-store.injectable";
|
|
||||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||||
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
|
||||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
import directoryForTempInjectable from "../../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
|
import directoryForTempInjectable from "../../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
|
||||||
import kubectlBinaryNameInjectable from "../../kubectl/binary-name.injectable";
|
import { iter, strictGet } from "../../../common/utils";
|
||||||
import kubectlDownloadingNormalizedArchInjectable from "../../kubectl/normalized-arch.injectable";
|
|
||||||
import normalizedPlatformInjectable from "../../../common/vars/normalized-platform.injectable";
|
|
||||||
import { iter } from "../../../common/utils";
|
|
||||||
import fsInjectable from "../../../common/fs/fs.injectable";
|
|
||||||
import type { ComputeKubeconfigDiff } from "../kubeconfig-sync/compute-diff.injectable";
|
import type { ComputeKubeconfigDiff } from "../kubeconfig-sync/compute-diff.injectable";
|
||||||
import computeKubeconfigDiffInjectable from "../kubeconfig-sync/compute-diff.injectable";
|
import computeKubeconfigDiffInjectable from "../kubeconfig-sync/compute-diff.injectable";
|
||||||
import watchInjectable from "../../../common/fs/watch/watch.injectable";
|
|
||||||
import type { ConfigToModels } from "../kubeconfig-sync/config-to-models.injectable";
|
import type { ConfigToModels } from "../kubeconfig-sync/config-to-models.injectable";
|
||||||
import configToModelsInjectable from "../kubeconfig-sync/config-to-models.injectable";
|
import configToModelsInjectable from "../kubeconfig-sync/config-to-models.injectable";
|
||||||
import kubeconfigSyncManagerInjectable from "../kubeconfig-sync/manager.injectable";
|
import kubeconfigSyncManagerInjectable from "../kubeconfig-sync/manager.injectable";
|
||||||
import type { KubeconfigSyncManager } from "../kubeconfig-sync/manager";
|
import type { KubeconfigSyncManager } from "../kubeconfig-sync/manager";
|
||||||
import type { KubeconfigSyncValue } from "../../../common/user-store";
|
import type { KubeconfigSyncValue } from "../../../common/user-store";
|
||||||
import kubeconfigSyncsInjectable from "../../../common/user-store/kubeconfig-syncs.injectable";
|
import kubeconfigSyncsInjectable from "../../../common/user-store/kubeconfig-syncs.injectable";
|
||||||
|
import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable";
|
||||||
console.log("This is a reminder that mockFS breaks things and needs to be removed");
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
jest.mock("electron", () => ({
|
import type { Stat } from "../../../common/fs/stat/stat.injectable";
|
||||||
app: {
|
import asyncFn from "@async-fn/jest";
|
||||||
getVersion: () => "99.99.99",
|
import statInjectable from "../../../common/fs/stat/stat.injectable";
|
||||||
getName: () => "lens",
|
import type { Watcher } from "../../../common/fs/watch/watch.injectable";
|
||||||
setName: jest.fn(),
|
import watchInjectable from "../../../common/fs/watch/watch.injectable";
|
||||||
setPath: jest.fn(),
|
import EventEmitter from "events";
|
||||||
getPath: () => "tmp",
|
import type { ReadStream, Stats } from "fs";
|
||||||
getLocale: () => "en",
|
import createReadFileStreamInjectable from "../../../common/fs/create-read-file-stream.injectable";
|
||||||
setLoginItemSettings: jest.fn(),
|
|
||||||
},
|
|
||||||
ipcMain: {
|
|
||||||
on: jest.fn(),
|
|
||||||
handle: jest.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe("kubeconfig-sync.source tests", () => {
|
describe("kubeconfig-sync.source tests", () => {
|
||||||
let computeKubeconfigDiff: ComputeKubeconfigDiff;
|
let computeKubeconfigDiff: ComputeKubeconfigDiff;
|
||||||
let configToModels: ConfigToModels;
|
let configToModels: ConfigToModels;
|
||||||
let manager: KubeconfigSyncManager;
|
|
||||||
let kubeconfigSyncs: ObservableMap<string, KubeconfigSyncValue>;
|
let kubeconfigSyncs: ObservableMap<string, KubeconfigSyncValue>;
|
||||||
|
let clusters: Map<string, Cluster>;
|
||||||
|
let di: DiContainer;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
mockFs();
|
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
|
||||||
|
di.override(directoryForTempInjectable, () => "/some-directory-for-temp");
|
||||||
|
|
||||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
clusters = new Map();
|
||||||
di.override(directoryForTempInjectable, () => "some-directory-for-temp");
|
di.override(getClusterByIdInjectable, () => id => clusters.get(id));
|
||||||
di.override(kubectlBinaryNameInjectable, () => "kubectl");
|
|
||||||
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
|
|
||||||
di.override(normalizedPlatformInjectable, () => "darwin");
|
|
||||||
|
|
||||||
di.permitSideEffects(fsInjectable);
|
|
||||||
di.permitSideEffects(watchInjectable);
|
|
||||||
di.unoverride(clusterStoreInjectable);
|
|
||||||
di.permitSideEffects(clusterStoreInjectable);
|
|
||||||
di.permitSideEffects(getConfigurationFileModelInjectable);
|
|
||||||
|
|
||||||
kubeconfigSyncs = observable.map();
|
kubeconfigSyncs = observable.map();
|
||||||
|
|
||||||
@ -76,11 +53,6 @@ describe("kubeconfig-sync.source tests", () => {
|
|||||||
|
|
||||||
computeKubeconfigDiff = di.inject(computeKubeconfigDiffInjectable);
|
computeKubeconfigDiff = di.inject(computeKubeconfigDiffInjectable);
|
||||||
configToModels = di.inject(configToModelsInjectable);
|
configToModels = di.inject(configToModelsInjectable);
|
||||||
manager = di.inject(kubeconfigSyncManagerInjectable);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
mockFs.restore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("configsToModels", () => {
|
describe("configsToModels", () => {
|
||||||
@ -162,8 +134,6 @@ describe("kubeconfig-sync.source tests", () => {
|
|||||||
const rootSource = new ObservableMap<string, [Cluster, CatalogEntity]>();
|
const rootSource = new ObservableMap<string, [Cluster, CatalogEntity]>();
|
||||||
const filePath = "/bar";
|
const filePath = "/bar";
|
||||||
|
|
||||||
fs.writeFileSync(filePath, contents);
|
|
||||||
|
|
||||||
computeKubeconfigDiff(contents, rootSource, filePath);
|
computeKubeconfigDiff(contents, rootSource, filePath);
|
||||||
|
|
||||||
expect(rootSource.size).toBe(1);
|
expect(rootSource.size).toBe(1);
|
||||||
@ -206,8 +176,6 @@ describe("kubeconfig-sync.source tests", () => {
|
|||||||
const rootSource = new ObservableMap<string, [Cluster, CatalogEntity]>();
|
const rootSource = new ObservableMap<string, [Cluster, CatalogEntity]>();
|
||||||
const filePath = "/bar";
|
const filePath = "/bar";
|
||||||
|
|
||||||
fs.writeFileSync(filePath, contents);
|
|
||||||
|
|
||||||
computeKubeconfigDiff(contents, rootSource, filePath);
|
computeKubeconfigDiff(contents, rootSource, filePath);
|
||||||
|
|
||||||
expect(rootSource.size).toBe(1);
|
expect(rootSource.size).toBe(1);
|
||||||
@ -260,8 +228,6 @@ describe("kubeconfig-sync.source tests", () => {
|
|||||||
const rootSource = new ObservableMap<string, [Cluster, CatalogEntity]>();
|
const rootSource = new ObservableMap<string, [Cluster, CatalogEntity]>();
|
||||||
const filePath = "/bar";
|
const filePath = "/bar";
|
||||||
|
|
||||||
fs.writeFileSync(filePath, contents);
|
|
||||||
|
|
||||||
computeKubeconfigDiff(contents, rootSource, filePath);
|
computeKubeconfigDiff(contents, rootSource, filePath);
|
||||||
|
|
||||||
expect(rootSource.size).toBe(2);
|
expect(rootSource.size).toBe(2);
|
||||||
@ -316,34 +282,47 @@ describe("kubeconfig-sync.source tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("given a config file at /foobar/config", () => {
|
describe("given a config file at /foobar/config", () => {
|
||||||
|
let manager: KubeconfigSyncManager;
|
||||||
|
let watchInstances: Map<string, Watcher<true>>;
|
||||||
|
let firstReadFoobarConfigSteam: ReadStream;
|
||||||
|
let secondReadFoobarConfigSteam: ReadStream;
|
||||||
|
let statMock: AsyncFnMock<Stat>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fs.mkdirSync("/foobar");
|
statMock = asyncFn();
|
||||||
fs.writeFileSync("/foobar/config", JSON.stringify({
|
di.override(statInjectable, () => statMock);
|
||||||
clusters: [{
|
|
||||||
name: "cluster-name",
|
watchInstances = new Map();
|
||||||
cluster: {
|
di.override(watchInjectable, () => (path) => {
|
||||||
server: "1.2.3.4",
|
const fakeWatchInstance = getFakeWatchInstance();
|
||||||
},
|
|
||||||
skipTLSVerify: false,
|
watchInstances.set(path, fakeWatchInstance);
|
||||||
}],
|
|
||||||
users: [{
|
return fakeWatchInstance;
|
||||||
name: "user-name",
|
});
|
||||||
}],
|
|
||||||
contexts: [{
|
di.override(createReadFileStreamInjectable, () => (filePath) => {
|
||||||
name: "context-name",
|
if (filePath !== "/foobar/config") {
|
||||||
context: {
|
throw new Error(`unexpected file path "${filePath}"`);
|
||||||
cluster: "cluster-name",
|
}
|
||||||
user: "user-name",
|
|
||||||
},
|
if (!firstReadFoobarConfigSteam) {
|
||||||
}, {
|
return firstReadFoobarConfigSteam = getFakeReadStream(filePath);
|
||||||
name: "context-the-second",
|
}
|
||||||
context: {
|
|
||||||
cluster: "missing-cluster",
|
if (!secondReadFoobarConfigSteam) {
|
||||||
user: "user-name",
|
return secondReadFoobarConfigSteam = getFakeReadStream(filePath);
|
||||||
},
|
}
|
||||||
}],
|
|
||||||
currentContext: "foobar",
|
return getFakeReadStream(filePath);
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
manager = di.inject(kubeconfigSyncManagerInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
(firstReadFoobarConfigSteam as any) = undefined;
|
||||||
|
(secondReadFoobarConfigSteam as any) = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not find any entities", () => {
|
it("should not find any entities", () => {
|
||||||
@ -364,30 +343,118 @@ describe("kubeconfig-sync.source tests", () => {
|
|||||||
kubeconfigSyncs.set("/foobar/config", {});
|
kubeconfigSyncs.set("/foobar/config", {});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should find a single entity", (done) => {
|
describe("when stat resolves as not a directory", () => {
|
||||||
when(() => manager.source.get().length === 1, () => done());
|
beforeEach(async () => {
|
||||||
});
|
await statMock.resolveSpecific(["/foobar/config"], {
|
||||||
|
isDirectory: () => false,
|
||||||
describe("when a folder sync target for /foobar is added", () => {
|
} as Stats);
|
||||||
beforeEach(() => {
|
|
||||||
kubeconfigSyncs.set("/foobar", {});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should still only find a single entity", (done) => {
|
describe("when the watch emits that the file is added", () => {
|
||||||
when(() => manager.source.get().length === 1, () => done());
|
beforeEach(() => {
|
||||||
|
strictGet(watchInstances, "/foobar/config").emit("add", "/foobar/config", {
|
||||||
|
size: foobarConfig.length,
|
||||||
|
} as Stats);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("starts to read the file", () => {
|
||||||
|
expect(firstReadFoobarConfigSteam).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when the data is read in", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
firstReadFoobarConfigSteam.emit("data", Buffer.from(foobarConfig));
|
||||||
|
firstReadFoobarConfigSteam.emit("end");
|
||||||
|
firstReadFoobarConfigSteam.emit("close");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should find a single entity", () => {
|
||||||
|
expect(manager.source.get().length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when a folder sync target for /foobar is added", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
kubeconfigSyncs.set("/foobar", {});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when stat resolves as not a directory", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await statMock.resolveSpecific(["/foobar"], {
|
||||||
|
isDirectory: () => true,
|
||||||
|
} as Stats);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when the watch emits that the file is added", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
strictGet(watchInstances, "/foobar").emit("add", "/foobar/config", {
|
||||||
|
size: foobarConfig.length,
|
||||||
|
} as Stats);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("starts to read the file", () => {
|
||||||
|
expect(secondReadFoobarConfigSteam).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when the data is read in", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
secondReadFoobarConfigSteam.emit("data", Buffer.from(foobarConfig));
|
||||||
|
secondReadFoobarConfigSteam.emit("end");
|
||||||
|
secondReadFoobarConfigSteam.emit("close");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should still only find a single entity", () => {
|
||||||
|
expect(manager.source.get().length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when a folder sync target for /foobar is added", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
kubeconfigSyncs.set("/foobar", {});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should find a single entity", (done) => {
|
|
||||||
when(() => manager.source.get().length === 1, () => done());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getFakeWatchInstance = (): Watcher<true> => {
|
||||||
|
return Object.assign(new EventEmitter(), {
|
||||||
|
close: jest.fn().mockImplementation(async () => {}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFakeReadStream = (path: string): ReadStream => {
|
||||||
|
return Object.assign(new EventEmitter(), {
|
||||||
|
path,
|
||||||
|
close: () => {},
|
||||||
|
push: () => true,
|
||||||
|
read: () => {},
|
||||||
|
}) as unknown as ReadStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
const foobarConfig = JSON.stringify({
|
||||||
|
clusters: [{
|
||||||
|
name: "cluster-name",
|
||||||
|
cluster: {
|
||||||
|
server: "1.2.3.4",
|
||||||
|
},
|
||||||
|
skipTLSVerify: false,
|
||||||
|
}],
|
||||||
|
users: [{
|
||||||
|
name: "user-name",
|
||||||
|
}],
|
||||||
|
contexts: [{
|
||||||
|
name: "context-name",
|
||||||
|
context: {
|
||||||
|
cluster: "cluster-name",
|
||||||
|
user: "user-name",
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "context-the-second",
|
||||||
|
context: {
|
||||||
|
cluster: "missing-cluster",
|
||||||
|
user: "user-name",
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
currentContext: "foobar",
|
||||||
|
});
|
||||||
|
|||||||
@ -12,8 +12,8 @@ import type { CatalogEntity } from "../../../common/catalog";
|
|||||||
import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable";
|
import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable";
|
||||||
import type { Cluster } from "../../../common/cluster/cluster";
|
import type { Cluster } from "../../../common/cluster/cluster";
|
||||||
import { loadConfigFromString } from "../../../common/kube-helpers";
|
import { loadConfigFromString } from "../../../common/kube-helpers";
|
||||||
|
import clustersThatAreBeingDeletedInjectable from "../../cluster/are-being-deleted.injectable";
|
||||||
import { catalogEntityFromCluster } from "../../cluster/manager";
|
import { catalogEntityFromCluster } from "../../cluster/manager";
|
||||||
import clusterManagerInjectable from "../../cluster/manager.injectable";
|
|
||||||
import createClusterInjectable from "../../create-cluster/create-cluster.injectable";
|
import createClusterInjectable from "../../create-cluster/create-cluster.injectable";
|
||||||
import configToModelsInjectable from "./config-to-models.injectable";
|
import configToModelsInjectable from "./config-to-models.injectable";
|
||||||
import kubeconfigSyncLoggerInjectable from "./logger.injectable";
|
import kubeconfigSyncLoggerInjectable from "./logger.injectable";
|
||||||
@ -25,7 +25,7 @@ const computeKubeconfigDiffInjectable = getInjectable({
|
|||||||
instantiate: (di): ComputeKubeconfigDiff => {
|
instantiate: (di): ComputeKubeconfigDiff => {
|
||||||
const directoryForKubeConfigs = di.inject(directoryForKubeConfigsInjectable);
|
const directoryForKubeConfigs = di.inject(directoryForKubeConfigsInjectable);
|
||||||
const createCluster = di.inject(createClusterInjectable);
|
const createCluster = di.inject(createClusterInjectable);
|
||||||
const clusterManager = di.inject(clusterManagerInjectable);
|
const clustersThatAreBeingDeleted = di.inject(clustersThatAreBeingDeletedInjectable);
|
||||||
const configToModels = di.inject(configToModelsInjectable);
|
const configToModels = di.inject(configToModelsInjectable);
|
||||||
const logger = di.inject(kubeconfigSyncLoggerInjectable);
|
const logger = di.inject(kubeconfigSyncLoggerInjectable);
|
||||||
const getClusterById = di.inject(getClusterByIdInjectable);
|
const getClusterById = di.inject(getClusterByIdInjectable);
|
||||||
@ -48,8 +48,8 @@ const computeKubeconfigDiffInjectable = getInjectable({
|
|||||||
|
|
||||||
// remove and disconnect clusters that were removed from the config
|
// remove and disconnect clusters that were removed from the config
|
||||||
if (!data) {
|
if (!data) {
|
||||||
// remove from the deleting set, so that if a new context of the same name is added, it isn't marked as deleting
|
// remove from the deleting set, so that if a new context of the same name is added, it isn't marked as deleting
|
||||||
clusterManager.deleting.delete(value[0].id);
|
clustersThatAreBeingDeleted.delete(value[0].id);
|
||||||
|
|
||||||
value[0].disconnect();
|
value[0].disconnect();
|
||||||
source.delete(contextName);
|
source.delete(contextName);
|
||||||
@ -68,7 +68,7 @@ const computeKubeconfigDiffInjectable = getInjectable({
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const [contextName, [model, configData]] of models) {
|
for (const [contextName, [model, configData]] of models) {
|
||||||
// add new clusters to the source
|
// add new clusters to the source
|
||||||
try {
|
try {
|
||||||
const clusterId = createHash("md5").update(`${filePath}:${contextName}`).digest("hex");
|
const clusterId = createHash("md5").update(`${filePath}:${contextName}`).digest("hex");
|
||||||
const cluster = getClusterById(clusterId) ?? createCluster({ ...model, id: clusterId }, configData);
|
const cluster = getClusterById(clusterId) ?? createCluster({ ...model, id: clusterId }, configData);
|
||||||
@ -93,6 +93,8 @@ const computeKubeconfigDiffInjectable = getInjectable({
|
|||||||
logger.warn(`Failed to compute diff: ${error}`, { filePath });
|
logger.warn(`Failed to compute diff: ${error}`, { filePath });
|
||||||
source.clear(); // clear source if we have failed so as to not show outdated information
|
source.clear(); // clear source if we have failed so as to not show outdated information
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug("Finished computing diff", { filePath });
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import type { ObservableMap } from "mobx";
|
|||||||
import type { Readable } from "stream";
|
import type { Readable } from "stream";
|
||||||
import type { CatalogEntity } from "../../../common/catalog";
|
import type { CatalogEntity } from "../../../common/catalog";
|
||||||
import type { Cluster } from "../../../common/cluster/cluster";
|
import type { Cluster } from "../../../common/cluster/cluster";
|
||||||
import fsInjectable from "../../../common/fs/fs.injectable";
|
import createReadFileStreamInjectable from "../../../common/fs/create-read-file-stream.injectable";
|
||||||
import type { Disposer } from "../../../common/utils";
|
import type { Disposer } from "../../../common/utils";
|
||||||
import { bytesToUnits, noop } from "../../../common/utils";
|
import { bytesToUnits, noop } from "../../../common/utils";
|
||||||
import computeKubeconfigDiffInjectable from "./compute-diff.injectable";
|
import computeKubeconfigDiffInjectable from "./compute-diff.injectable";
|
||||||
@ -28,7 +28,7 @@ const diffChangedKubeconfigInjectable = getInjectable({
|
|||||||
instantiate: (di): DiffChangedKubeconfig => {
|
instantiate: (di): DiffChangedKubeconfig => {
|
||||||
const computeKubeconfigDiff = di.inject(computeKubeconfigDiffInjectable);
|
const computeKubeconfigDiff = di.inject(computeKubeconfigDiffInjectable);
|
||||||
const logger = di.inject(kubeconfigSyncLoggerInjectable);
|
const logger = di.inject(kubeconfigSyncLoggerInjectable);
|
||||||
const { createReadStream } = di.inject(fsInjectable);
|
const createReadFileStream = di.inject(createReadFileStreamInjectable);
|
||||||
|
|
||||||
return ({ filePath, maxAllowedFileReadSize, source, stats }) => {
|
return ({ filePath, maxAllowedFileReadSize, source, stats }) => {
|
||||||
logger.debug(`file changed`, { filePath });
|
logger.debug(`file changed`, { filePath });
|
||||||
@ -40,7 +40,7 @@ const diffChangedKubeconfigInjectable = getInjectable({
|
|||||||
return noop;
|
return noop;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileReader = createReadStream(filePath, {
|
const fileReader = createReadFileStream(filePath, {
|
||||||
mode: constants.O_RDONLY,
|
mode: constants.O_RDONLY,
|
||||||
});
|
});
|
||||||
const readStream = fileReader as Readable;
|
const readStream = fileReader as Readable;
|
||||||
|
|||||||
@ -36,11 +36,13 @@ export class KubeconfigSyncManager {
|
|||||||
return (
|
return (
|
||||||
iter.pipeline(this.sources.values())
|
iter.pipeline(this.sources.values())
|
||||||
.flatMap(([entities]) => entities.get())
|
.flatMap(([entities]) => entities.get())
|
||||||
.filter(entity => (
|
.filter(entity => {
|
||||||
seenIds.has(entity.getId())
|
const alreadySeen = seenIds.has(entity.getId());
|
||||||
? false
|
|
||||||
: seenIds.add(entity.getId())
|
seenIds.add(entity.getId());
|
||||||
))
|
|
||||||
|
return !alreadySeen;
|
||||||
|
})
|
||||||
.collect(items => [...items])
|
.collect(items => [...items])
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,8 +3,6 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import type { FSWatcher } from "chokidar";
|
|
||||||
import type { Stats } from "fs";
|
|
||||||
import GlobToRegExp from "glob-to-regexp";
|
import GlobToRegExp from "glob-to-regexp";
|
||||||
import type { IComputedValue, ObservableMap } from "mobx";
|
import type { IComputedValue, ObservableMap } from "mobx";
|
||||||
import { computed, observable } from "mobx";
|
import { computed, observable } from "mobx";
|
||||||
@ -12,7 +10,8 @@ import path from "path";
|
|||||||
import { inspect } from "util";
|
import { inspect } from "util";
|
||||||
import type { CatalogEntity } from "../../../common/catalog";
|
import type { CatalogEntity } from "../../../common/catalog";
|
||||||
import type { Cluster } from "../../../common/cluster/cluster";
|
import type { Cluster } from "../../../common/cluster/cluster";
|
||||||
import fsInjectable from "../../../common/fs/fs.injectable";
|
import statInjectable from "../../../common/fs/stat/stat.injectable";
|
||||||
|
import type { Watcher } from "../../../common/fs/watch/watch.injectable";
|
||||||
import watchInjectable from "../../../common/fs/watch/watch.injectable";
|
import watchInjectable from "../../../common/fs/watch/watch.injectable";
|
||||||
import type { Disposer } from "../../../common/utils";
|
import type { Disposer } from "../../../common/utils";
|
||||||
import { getOrInsertWith, iter } from "../../../common/utils";
|
import { getOrInsertWith, iter } from "../../../common/utils";
|
||||||
@ -47,14 +46,14 @@ const watchKubeconfigFileChangesInjectable = getInjectable({
|
|||||||
instantiate: (di): WatchKubeconfigFileChanges => {
|
instantiate: (di): WatchKubeconfigFileChanges => {
|
||||||
const diffChangedKubeconfig = di.inject(diffChangedKubeconfigInjectable);
|
const diffChangedKubeconfig = di.inject(diffChangedKubeconfigInjectable);
|
||||||
const logger = di.inject(kubeconfigSyncLoggerInjectable);
|
const logger = di.inject(kubeconfigSyncLoggerInjectable);
|
||||||
const { stat } = di.inject(fsInjectable);
|
const stat = di.inject(statInjectable);
|
||||||
const watch = di.inject(watchInjectable);
|
const watch = di.inject(watchInjectable);
|
||||||
|
|
||||||
return (filePath) => {
|
return (filePath) => {
|
||||||
const rootSource = observable.map<string, ObservableMap<string, [Cluster, CatalogEntity]>>();
|
const rootSource = observable.map<string, ObservableMap<string, [Cluster, CatalogEntity]>>();
|
||||||
const derivedSource = computed(() => Array.from(iter.flatMap(rootSource.values(), from => iter.map(from.values(), child => child[1]))));
|
const derivedSource = computed(() => Array.from(iter.flatMap(rootSource.values(), from => iter.map(from.values(), child => child[1]))));
|
||||||
|
|
||||||
let watcher: FSWatcher;
|
let watcher: Watcher<true>;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
@ -65,7 +64,7 @@ const watchKubeconfigFileChangesInjectable = getInjectable({
|
|||||||
? folderSyncMaxAllowedFileReadSize
|
? folderSyncMaxAllowedFileReadSize
|
||||||
: fileSyncMaxAllowedFileReadSize;
|
: fileSyncMaxAllowedFileReadSize;
|
||||||
|
|
||||||
watcher = watch(filePath, {
|
watcher = watch<true>(filePath, {
|
||||||
followSymlinks: true,
|
followSymlinks: true,
|
||||||
depth: isFolderSync ? 0 : 1, // DIRs works with 0 but files need 1 (bug: https://github.com/paulmillr/chokidar/issues/1095)
|
depth: isFolderSync ? 0 : 1, // DIRs works with 0 but files need 1 (bug: https://github.com/paulmillr/chokidar/issues/1095)
|
||||||
disableGlobbing: true,
|
disableGlobbing: true,
|
||||||
@ -80,7 +79,7 @@ const watchKubeconfigFileChangesInjectable = getInjectable({
|
|||||||
});
|
});
|
||||||
|
|
||||||
watcher
|
watcher
|
||||||
.on("change", (childFilePath, stats: Stats): void => {
|
.on("change", (childFilePath, stats): void => {
|
||||||
const cleanup = cleanupFns.get(childFilePath);
|
const cleanup = cleanupFns.get(childFilePath);
|
||||||
|
|
||||||
if (!cleanup) {
|
if (!cleanup) {
|
||||||
@ -96,7 +95,7 @@ const watchKubeconfigFileChangesInjectable = getInjectable({
|
|||||||
maxAllowedFileReadSize,
|
maxAllowedFileReadSize,
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
.on("add", (childFilePath, stats: Stats): void => {
|
.on("add", (childFilePath, stats): void => {
|
||||||
if (isFolderSync) {
|
if (isFolderSync) {
|
||||||
const fileName = path.basename(childFilePath);
|
const fileName = path.basename(childFilePath);
|
||||||
|
|
||||||
|
|||||||
14
src/main/cluster/are-being-deleted.injectable.ts
Normal file
14
src/main/cluster/are-being-deleted.injectable.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { observable } from "mobx";
|
||||||
|
import type { ClusterId } from "../../common/cluster-types";
|
||||||
|
|
||||||
|
const clustersThatAreBeingDeletedInjectable = getInjectable({
|
||||||
|
id: "clusters-that-are-being-deleted",
|
||||||
|
instantiate: () => observable.set<ClusterId>(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clustersThatAreBeingDeletedInjectable;
|
||||||
@ -5,6 +5,7 @@
|
|||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import clusterStoreInjectable from "../../common/cluster-store/cluster-store.injectable";
|
import clusterStoreInjectable from "../../common/cluster-store/cluster-store.injectable";
|
||||||
import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable";
|
import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable";
|
||||||
|
import clustersThatAreBeingDeletedInjectable from "./are-being-deleted.injectable";
|
||||||
import { ClusterManager } from "./manager";
|
import { ClusterManager } from "./manager";
|
||||||
|
|
||||||
const clusterManagerInjectable = getInjectable({
|
const clusterManagerInjectable = getInjectable({
|
||||||
@ -13,6 +14,7 @@ const clusterManagerInjectable = getInjectable({
|
|||||||
instantiate: (di) => new ClusterManager({
|
instantiate: (di) => new ClusterManager({
|
||||||
store: di.inject(clusterStoreInjectable),
|
store: di.inject(clusterStoreInjectable),
|
||||||
catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable),
|
catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable),
|
||||||
|
clustersThatAreBeingDeleted: di.inject(clustersThatAreBeingDeletedInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import "../../common/ipc/cluster";
|
import "../../common/ipc/cluster";
|
||||||
import type http from "http";
|
import type http from "http";
|
||||||
|
import type { ObservableSet } from "mobx";
|
||||||
import { action, makeObservable, observable, observe, reaction, toJS } from "mobx";
|
import { action, makeObservable, observable, observe, reaction, toJS } from "mobx";
|
||||||
import type { Cluster } from "../../common/cluster/cluster";
|
import type { Cluster } from "../../common/cluster/cluster";
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
@ -23,16 +24,15 @@ const logPrefix = "[CLUSTER-MANAGER]:";
|
|||||||
const lensSpecificClusterStatuses: Set<string> = new Set(Object.values(LensKubernetesClusterStatus));
|
const lensSpecificClusterStatuses: Set<string> = new Set(Object.values(LensKubernetesClusterStatus));
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
store: ClusterStore;
|
readonly store: ClusterStore;
|
||||||
catalogEntityRegistry: CatalogEntityRegistry;
|
readonly catalogEntityRegistry: CatalogEntityRegistry;
|
||||||
|
readonly clustersThatAreBeingDeleted: ObservableSet<ClusterId>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ClusterManager {
|
export class ClusterManager {
|
||||||
deleting = observable.set<ClusterId>();
|
|
||||||
|
|
||||||
@observable visibleCluster: ClusterId | undefined = undefined;
|
@observable visibleCluster: ClusterId | undefined = undefined;
|
||||||
|
|
||||||
constructor(private dependencies: Dependencies) {
|
constructor(private readonly dependencies: Dependencies) {
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ export class ClusterManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
observe(this.deleting, change => {
|
observe(this.dependencies.clustersThatAreBeingDeleted, change => {
|
||||||
if (change.type === "add") {
|
if (change.type === "add") {
|
||||||
this.updateEntityStatus(this.dependencies.catalogEntityRegistry.findById(change.newValue) as KubernetesCluster);
|
this.updateEntityStatus(this.dependencies.catalogEntityRegistry.findById(change.newValue) as KubernetesCluster);
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ export class ClusterManager {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
protected updateEntityStatus(entity: KubernetesCluster, cluster?: Cluster) {
|
protected updateEntityStatus(entity: KubernetesCluster, cluster?: Cluster) {
|
||||||
if (this.deleting.has(entity.getId())) {
|
if (this.dependencies.clustersThatAreBeingDeleted.has(entity.getId())) {
|
||||||
entity.status.phase = LensKubernetesClusterStatus.DELETING;
|
entity.status.phase = LensKubernetesClusterStatus.DELETING;
|
||||||
entity.status.enabled = false;
|
entity.status.enabled = false;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { onLoadOfApplicationInjectionToken } from "../../../start-main-applicati
|
|||||||
import operatingSystemThemeInjectable from "../../../theme/operating-system-theme.injectable";
|
import operatingSystemThemeInjectable from "../../../theme/operating-system-theme.injectable";
|
||||||
import catalogEntityRegistryInjectable from "../../../catalog/entity-registry.injectable";
|
import catalogEntityRegistryInjectable from "../../../catalog/entity-registry.injectable";
|
||||||
import askUserForFilePathsInjectable from "../../../ipc/ask-user-for-file-paths.injectable";
|
import askUserForFilePathsInjectable from "../../../ipc/ask-user-for-file-paths.injectable";
|
||||||
|
import clustersThatAreBeingDeletedInjectable from "../../../cluster/are-being-deleted.injectable";
|
||||||
|
|
||||||
const setupIpcMainHandlersInjectable = getInjectable({
|
const setupIpcMainHandlersInjectable = getInjectable({
|
||||||
id: "setup-ipc-main-handlers",
|
id: "setup-ipc-main-handlers",
|
||||||
@ -32,6 +33,7 @@ const setupIpcMainHandlersInjectable = getInjectable({
|
|||||||
const clusterStore = di.inject(clusterStoreInjectable);
|
const clusterStore = di.inject(clusterStoreInjectable);
|
||||||
const operatingSystemTheme = di.inject(operatingSystemThemeInjectable);
|
const operatingSystemTheme = di.inject(operatingSystemThemeInjectable);
|
||||||
const askUserForFilePaths = di.inject(askUserForFilePathsInjectable);
|
const askUserForFilePaths = di.inject(askUserForFilePathsInjectable);
|
||||||
|
const clustersThatAreBeingDeleted = di.inject(clustersThatAreBeingDeletedInjectable);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
run: () => {
|
run: () => {
|
||||||
@ -46,6 +48,7 @@ const setupIpcMainHandlersInjectable = getInjectable({
|
|||||||
clusterStore,
|
clusterStore,
|
||||||
operatingSystemTheme,
|
operatingSystemTheme,
|
||||||
askUserForFilePaths,
|
askUserForFilePaths,
|
||||||
|
clustersThatAreBeingDeleted,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { pushCatalogToRenderer } from "../../../catalog-pusher";
|
|||||||
import type { ClusterManager } from "../../../cluster/manager";
|
import type { ClusterManager } from "../../../cluster/manager";
|
||||||
import { ResourceApplier } from "../../../resource-applier";
|
import { ResourceApplier } from "../../../resource-applier";
|
||||||
import { remove } from "fs-extra";
|
import { remove } from "fs-extra";
|
||||||
import type { IComputedValue } from "mobx";
|
import type { IComputedValue, ObservableSet } from "mobx";
|
||||||
import type { GetAbsolutePath } from "../../../../common/path/get-absolute-path.injectable";
|
import type { GetAbsolutePath } from "../../../../common/path/get-absolute-path.injectable";
|
||||||
import type { MenuItemOpts } from "../../../menu/application-menu-items.injectable";
|
import type { MenuItemOpts } from "../../../menu/application-menu-items.injectable";
|
||||||
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window";
|
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window";
|
||||||
@ -34,9 +34,20 @@ interface Dependencies {
|
|||||||
clusterStore: ClusterStore;
|
clusterStore: ClusterStore;
|
||||||
operatingSystemTheme: IComputedValue<Theme>;
|
operatingSystemTheme: IComputedValue<Theme>;
|
||||||
askUserForFilePaths: AskUserForFilePaths;
|
askUserForFilePaths: AskUserForFilePaths;
|
||||||
|
clustersThatAreBeingDeleted: ObservableSet<ClusterId>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setupIpcMainHandlers = ({ applicationMenuItems, directoryForLensLocalStorage, getAbsolutePath, clusterManager, catalogEntityRegistry, clusterStore, operatingSystemTheme, askUserForFilePaths }: Dependencies) => {
|
export const setupIpcMainHandlers = ({
|
||||||
|
applicationMenuItems,
|
||||||
|
directoryForLensLocalStorage,
|
||||||
|
getAbsolutePath,
|
||||||
|
clusterManager,
|
||||||
|
catalogEntityRegistry,
|
||||||
|
clusterStore,
|
||||||
|
operatingSystemTheme,
|
||||||
|
askUserForFilePaths,
|
||||||
|
clustersThatAreBeingDeleted,
|
||||||
|
}: Dependencies) => {
|
||||||
ipcMainHandle(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => {
|
ipcMainHandle(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => {
|
||||||
return ClusterStore.getInstance()
|
return ClusterStore.getInstance()
|
||||||
.getById(clusterId)
|
.getById(clusterId)
|
||||||
@ -101,11 +112,11 @@ export const setupIpcMainHandlers = ({ applicationMenuItems, directoryForLensLoc
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMainHandle(clusterSetDeletingHandler, (event, clusterId: string) => {
|
ipcMainHandle(clusterSetDeletingHandler, (event, clusterId: string) => {
|
||||||
clusterManager.deleting.add(clusterId);
|
clustersThatAreBeingDeleted.add(clusterId);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMainHandle(clusterClearDeletingHandler, (event, clusterId: string) => {
|
ipcMainHandle(clusterClearDeletingHandler, (event, clusterId: string) => {
|
||||||
clusterManager.deleting.delete(clusterId);
|
clustersThatAreBeingDeleted.delete(clusterId);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMainHandle(clusterKubectlApplyAllHandler, async (event, clusterId: ClusterId, resources: string[], extraArgs: string[]) => {
|
ipcMainHandle(clusterKubectlApplyAllHandler, async (event, clusterId: ClusterId, resources: string[], extraArgs: string[]) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user