mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Make base store non Singleton (#6690)
* Remove Singleton from BaseStore to remove global shared state Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove more usages of Singleton Signed-off-by: Sebastian Malton <sebastian@malton.name> * Replace use of legacy global execHelm with injectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove last use of legacy global execHelm Signed-off-by: Sebastian Malton <sebastian@malton.name> * Extract BaseStore deps into constructor argument Signed-off-by: Sebastian Malton <sebastian@malton.name> * Introduce method to make store migrations injectable - Use it for ClusterStore Signed-off-by: Sebastian Malton <sebastian@malton.name> * Switch HotbarStore to injectable migrations Signed-off-by: Sebastian Malton <sebastian@malton.name> * Switch UserStore to injectable migrations Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move migration utils into common/utils/ Signed-off-by: Sebastian Malton <sebastian@malton.name> * Switch WeblinkStore to injectable migrations Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove dead code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type error in base-store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove tests that reference lastSeenVersion - That value is not used anywhere in code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove usage of legacy global .getInstance Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove usage of legacy global ClusterStore.getInstance Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add simple migrations dependency for stores without any preexisting migrations Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix messed up import Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add typing to transient injectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cleanup formatting Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix typing in tests to satisfy requirement to have cacheFile Signed-off-by: Sebastian Malton <sebastian@malton.name> * More consistent use of BaseStore.displayName Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add catching of error while starting main application Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move initializing sentry to runnable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove unneeded appPathsInjectionToken - Only had once impl, which was in common anyway Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add support for multiple "runAfter" runnables - Needed so that several dependencies can be declared Signed-off-by: Sebastian Malton <sebastian@malton.name> * Use multiple runAfter support to fix crash on renderer Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove traces Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add global override to fix tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix base store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix runManyFor tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix hotbar store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix user store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add global override for getConfigurationFileModel to fix tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove overrides for configuration stores - Now that there is an override for getConfiguration Signed-off-by: Sebastian Malton <sebastian@malton.name> * Overhaul FS fakes with full in-memory filesystem - This increases our confidence in fs related logic Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove use of global shared Electron.App Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add fake access support Signed-off-by: Sebastian Malton <sebastian@malton.name> * Handle copy as part of fake FS Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add ensureDir/Sync support to fake FS Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type error Signed-off-by: Sebastian Malton <sebastian@malton.name> * Use pathExistsSync instead of fsInjectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add createReadStream to fake FS Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add stat to fake FS Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove dead code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix test failures due to incomplete overrides Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully injectable-ize BaseStore so that ApplicationBuilder tests work Signed-off-by: Sebastian Malton <sebastian@malton.name> * Consolidate more bootstrapping into startFrame Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move initializing CatalogCategories to runnable in bootstrap Signed-off-by: Sebastian Malton <sebastian@malton.name> * Convert contextMenuOpen initializers into runnables Signed-off-by: Sebastian Malton <sebastian@malton.name> * Convert navigateForExtension init to runnable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Make cluster state sync fully injectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move init hotbar store into runnables Signed-off-by: Sebastian Malton <sebastian@malton.name> * Make LensTheme fully injectable and runnable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cleanup old code from missed from previous commit Signed-off-by: Sebastian Malton <sebastian@malton.name> * Manually split out terminal color names and fully type LensTheme Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix old imports Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove unnecessart awaits Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove dead code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully cherry pick injectablizing custom monaco themes Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix duplicate mock warning Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix incorrectly fully cherry picking new runnable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Complete cherry-pick of current cluster injcetablization Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix override file name Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix injecting before app paths are set up Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix injecting before app paths are set up Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix ordering of runnable and order of injection Signed-off-by: Sebastian Malton <sebastian@malton.name> * Convert all renderer runnables to late-inject style - To help fix issues around injection time Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix react-beautiful-dnd mocks Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update and fix WriteJson(Sync) to fix error in tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix HotbarStore.load being called twice is being buggy Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update listing-active-helm-repositories-in-preferences snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix sidebar-and-tab-navigation-tests - Move enabling extensions in tests to a proper location - Fix flushing promises Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove props from dnd mock to make snapshot diffs smaller Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix import Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests by overriding things that are no longer overriden by default - NOTE: They are overridden when using ApplicationBuilder Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix hotbar store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix cluster store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix extension-loader tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix extension-discovery tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix cluster-role-dialog tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix user store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix kubeconfig sync tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix sidebar and tab tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove unused code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix pick paths import type error and simplify signature Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type error in legacy ipc registration Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove another use of legacy requestOpenPathPicker Signed-off-by: Sebastian Malton <sebastian@malton.name> * Replace use of legacy global PathPicker.Pick Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix usage in light of changed prop names Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix catalog tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix more type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix test flakiness by removing side effects from userStore preferences Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix loading Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type error Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix crash Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cherry pick updated startFrameInjectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add tests to verify runMany behaviour in new possible incorrect configuration Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix init ordering during start frame Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix cluster state sync Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update snapshots after removing side-effects Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add override for technical test Signed-off-by: Sebastian Malton <sebastian@malton.name> * Correctly mark currentlyInClusterFrame as causedSideEffects Signed-off-by: Sebastian Malton <sebastian@malton.name> * Better formatting Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix behaviour regression Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add better logging Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix BaseStore sync Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update last snapshot Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add global override for randomBytes Signed-off-by: Sebastian Malton <sebastian@malton.name> * Make startMainApplication not an injection time side effect Signed-off-by: Sebastian Malton <sebastian@malton.name> * Choose better names for start-frame runnable tokens Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove duplication of code in RunManyFor Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add unit tests and fix handling empty runAfter array Signed-off-by: Sebastian Malton <sebastian@malton.name> * Replace use of mobx from runManyFor with custom barrier Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add missing test Signed-off-by: Sebastian Malton <sebastian@malton.name> Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
7c5faaaf1d
commit
25f37ac1d1
364 files changed
+10035
-5246
No files matched your search
@@ -22,6 +22,10 @@ import kubectlBinaryNameInjectable from "../kubectl/binary-name.injectable";
|
||||
import kubectlDownloadingNormalizedArchInjectable from "../kubectl/normalized-arch.injectable";
|
||||
import { apiResourceRecord, apiResources } from "../../common/rbac";
|
||||
import listApiResourcesInjectable from "../../common/cluster/list-api-resources.injectable";
|
||||
import pathExistsSyncInjectable from "../../common/fs/path-exists-sync.injectable";
|
||||
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
||||
import readJsonSyncInjectable from "../../common/fs/read-json-sync.injectable";
|
||||
import writeJsonSyncInjectable from "../../common/fs/write-json-sync.injectable";
|
||||
|
||||
console = new Console(process.stdout, process.stderr); // fix mockFS
|
||||
|
||||
@@ -56,6 +60,10 @@ describe("create clusters", () => {
|
||||
setupPrometheus: jest.fn(),
|
||||
ensureServer: jest.fn(),
|
||||
} as ClusterContextHandler));
|
||||
di.override(pathExistsInjectable, () => () => { throw new Error("tried call pathExists without override"); });
|
||||
di.override(pathExistsSyncInjectable, () => () => { throw new Error("tried call pathExistsSync without override"); });
|
||||
di.override(readJsonSyncInjectable, () => () => { throw new Error("tried call readJsonSync without override"); });
|
||||
di.override(writeJsonSyncInjectable, () => () => { throw new Error("tried call writeJsonSync without override"); });
|
||||
|
||||
createCluster = di.inject(createClusterInjectionToken);
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@ import normalizedPlatformInjectable from "../../common/vars/normalized-platform.
|
||||
import kubectlBinaryNameInjectable from "../kubectl/binary-name.injectable";
|
||||
import kubectlDownloadingNormalizedArchInjectable from "../kubectl/normalized-arch.injectable";
|
||||
import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable";
|
||||
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
||||
import readJsonSyncInjectable from "../../common/fs/read-json-sync.injectable";
|
||||
import writeJsonSyncInjectable from "../../common/fs/write-json-sync.injectable";
|
||||
import pathExistsSyncInjectable from "../../common/fs/path-exists-sync.injectable";
|
||||
|
||||
console = new Console(stdout, stderr);
|
||||
|
||||
@@ -73,6 +77,10 @@ describe("kube auth proxy tests", () => {
|
||||
|
||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
||||
di.override(directoryForTempInjectable, () => "some-directory-for-temp");
|
||||
di.override(pathExistsInjectable, () => () => { throw new Error("tried call pathExists without override"); });
|
||||
di.override(pathExistsSyncInjectable, () => () => { throw new Error("tried call pathExistsSync without override"); });
|
||||
di.override(readJsonSyncInjectable, () => () => { throw new Error("tried call readJsonSync without override"); });
|
||||
di.override(writeJsonSyncInjectable, () => () => { throw new Error("tried call writeJsonSync without override"); });
|
||||
|
||||
spawnMock = jest.fn();
|
||||
di.override(spawnInjectable, () => spawnMock);
|
||||
|
||||
@@ -25,8 +25,11 @@ import type { WriteFile } from "../../common/fs/write-file.injectable";
|
||||
import writeFileInjectable from "../../common/fs/write-file.injectable";
|
||||
import type { PathExists } from "../../common/fs/path-exists.injectable";
|
||||
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
||||
import type { DeleteFile } from "../../common/fs/delete-file.injectable";
|
||||
import deleteFileInjectable from "../../common/fs/delete-file.injectable";
|
||||
import type { RemovePath } from "../../common/fs/remove.injectable";
|
||||
import removePathInjectable from "../../common/fs/remove.injectable";
|
||||
import pathExistsSyncInjectable from "../../common/fs/path-exists-sync.injectable";
|
||||
import readJsonSyncInjectable from "../../common/fs/read-json-sync.injectable";
|
||||
import writeJsonSyncInjectable from "../../common/fs/write-json-sync.injectable";
|
||||
|
||||
const clusterServerUrl = "https://192.168.64.3:8443";
|
||||
|
||||
@@ -36,7 +39,7 @@ describe("kubeconfig manager tests", () => {
|
||||
let di: DiContainer;
|
||||
let loggerMock: jest.Mocked<Logger>;
|
||||
let readFileMock: AsyncFnMock<ReadFile>;
|
||||
let deleteFileMock: AsyncFnMock<DeleteFile>;
|
||||
let deleteFileMock: AsyncFnMock<RemovePath>;
|
||||
let writeFileMock: AsyncFnMock<WriteFile>;
|
||||
let pathExistsMock: AsyncFnMock<PathExists>;
|
||||
let kubeConfManager: KubeconfigManager;
|
||||
@@ -50,6 +53,9 @@ describe("kubeconfig manager tests", () => {
|
||||
di.override(kubectlBinaryNameInjectable, () => "kubectl");
|
||||
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
|
||||
di.override(normalizedPlatformInjectable, () => "darwin");
|
||||
di.override(pathExistsSyncInjectable, () => () => { throw new Error("tried call pathExistsSync without override"); });
|
||||
di.override(readJsonSyncInjectable, () => () => { throw new Error("tried call readJsonSync without override"); });
|
||||
di.override(writeJsonSyncInjectable, () => () => { throw new Error("tried call writeJsonSync without override"); });
|
||||
|
||||
readFileMock = asyncFn();
|
||||
di.override(readFileInjectable, () => readFileMock);
|
||||
@@ -58,7 +64,7 @@ describe("kubeconfig manager tests", () => {
|
||||
pathExistsMock = asyncFn();
|
||||
di.override(pathExistsInjectable, () => pathExistsMock);
|
||||
deleteFileMock = asyncFn();
|
||||
di.override(deleteFileInjectable, () => deleteFileMock);
|
||||
di.override(removePathInjectable, () => deleteFileMock);
|
||||
|
||||
loggerMock = {
|
||||
warn: jest.fn(),
|
||||
|
||||
@@ -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 { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../../common/base-store/disable-sync";
|
||||
|
||||
const shouldBaseStoreDisableSyncInIpcListenerInjectable = getInjectable({
|
||||
id: "should-base-store-disable-sync-in-ipc-listener",
|
||||
instantiate: () => false,
|
||||
injectionToken: shouldBaseStoreDisableSyncInIpcListenerInjectionToken,
|
||||
});
|
||||
|
||||
export default shouldBaseStoreDisableSyncInIpcListenerInjectable;
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 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 { baseStoreIpcChannelPrefixesInjectionToken } from "../../common/base-store/channel-prefix";
|
||||
|
||||
const baseStoreIpcChannelPrefixInjectable = getInjectable({
|
||||
id: "base-store-ipc-channel-prefix",
|
||||
instantiate: () => ({
|
||||
local: "store-sync-main",
|
||||
remote: "store-sync-renderer",
|
||||
}),
|
||||
injectionToken: baseStoreIpcChannelPrefixesInjectionToken,
|
||||
});
|
||||
|
||||
export default baseStoreIpcChannelPrefixInjectable;
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 { persistStateToConfigInjectionToken } from "../../common/base-store/save-to-file";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
|
||||
const persistStateToConfigInjectable = getInjectable({
|
||||
id: "persist-state-to-config",
|
||||
instantiate: (di) => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
return (config, state) => {
|
||||
logger.info(`[BASE-STORE]: saving ${config.path}...`);
|
||||
|
||||
for (const [key, value] of Object.entries(state)) {
|
||||
config.set(key, value);
|
||||
}
|
||||
};
|
||||
},
|
||||
injectionToken: persistStateToConfigInjectionToken,
|
||||
});
|
||||
|
||||
export default persistStateToConfigInjectable;
|
||||
@@ -22,14 +22,18 @@ import kubeconfigSyncsInjectable from "../../../common/user-store/kubeconfig-syn
|
||||
import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import type { AsyncFnMock } from "@async-fn/jest";
|
||||
import type { Stat } from "../../../common/fs/stat/stat.injectable";
|
||||
import type { Stat } from "../../../common/fs/stat.injectable";
|
||||
import asyncFn from "@async-fn/jest";
|
||||
import statInjectable from "../../../common/fs/stat/stat.injectable";
|
||||
import statInjectable from "../../../common/fs/stat.injectable";
|
||||
import type { Watcher } from "../../../common/fs/watch/watch.injectable";
|
||||
import watchInjectable from "../../../common/fs/watch/watch.injectable";
|
||||
import EventEmitter from "events";
|
||||
import type { ReadStream, Stats } from "fs";
|
||||
import createReadFileStreamInjectable from "../../../common/fs/create-read-file-stream.injectable";
|
||||
import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
|
||||
import pathExistsInjectable from "../../../common/fs/path-exists.injectable";
|
||||
import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable";
|
||||
import writeJsonSyncInjectable from "../../../common/fs/write-json-sync.injectable";
|
||||
|
||||
describe("kubeconfig-sync.source tests", () => {
|
||||
let computeKubeconfigDiff: ComputeKubeconfigDiff;
|
||||
@@ -43,6 +47,10 @@ describe("kubeconfig-sync.source tests", () => {
|
||||
|
||||
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
|
||||
di.override(directoryForTempInjectable, () => "/some-directory-for-temp");
|
||||
di.override(pathExistsInjectable, () => () => { throw new Error("tried call pathExists without override"); });
|
||||
di.override(pathExistsSyncInjectable, () => () => { throw new Error("tried call pathExistsSync without override"); });
|
||||
di.override(readJsonSyncInjectable, () => () => { throw new Error("tried call readJsonSync without override"); });
|
||||
di.override(writeJsonSyncInjectable, () => () => { throw new Error("tried call writeJsonSync without override"); });
|
||||
|
||||
clusters = new Map();
|
||||
di.override(getClusterByIdInjectable, () => id => clusters.get(id));
|
||||
|
||||
@@ -10,7 +10,7 @@ import path from "path";
|
||||
import { inspect } from "util";
|
||||
import type { CatalogEntity } from "../../../common/catalog";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import statInjectable from "../../../common/fs/stat/stat.injectable";
|
||||
import statInjectable from "../../../common/fs/stat.injectable";
|
||||
import type { Watcher } from "../../../common/fs/watch/watch.injectable";
|
||||
import watchInjectable from "../../../common/fs/watch/watch.injectable";
|
||||
import type { Disposer } from "../../../common/utils";
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { syncWeblinks } from "./weblinks";
|
||||
import weblinkStoreInjectable from "../../common/weblink-store.injectable";
|
||||
import weblinkStoreInjectable from "../../common/weblinks-store/weblink-store.injectable";
|
||||
import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable";
|
||||
|
||||
const syncWeblinksInjectable = getInjectable({
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { computed, observable, reaction } from "mobx";
|
||||
import type { WeblinkStore } from "../../common/weblink-store";
|
||||
import type { WeblinkStore } from "../../common/weblinks-store/weblink-store";
|
||||
import { WebLink } from "../../common/catalog-entities";
|
||||
import type { CatalogEntityRegistry } from "../catalog";
|
||||
import got from "got";
|
||||
|
||||
@@ -14,9 +14,9 @@ const startCatalogSyncInjectable = getInjectable({
|
||||
|
||||
return {
|
||||
id: "start-catalog-sync",
|
||||
run: async () => {
|
||||
run: () => {
|
||||
if (!catalogSyncToRenderer.started) {
|
||||
await catalogSyncToRenderer.start();
|
||||
catalogSyncToRenderer.start();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// Move embedded kubeconfig into separate file and add reference to it to cluster settings
|
||||
// convert file path cluster icons to their base64 encoded versions
|
||||
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import getCustomKubeConfigDirectoryInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
|
||||
import type { ClusterModel } from "../../../common/cluster-types";
|
||||
import readFileSyncInjectable from "../../../common/fs/read-file-sync.injectable";
|
||||
import { loadConfigFromString } from "../../../common/kube-helpers";
|
||||
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
|
||||
|
||||
interface Pre360ClusterModel extends ClusterModel {
|
||||
kubeConfig?: string;
|
||||
}
|
||||
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token";
|
||||
import readFileBufferSyncInjectable from "../../../common/fs/read-file-buffer-sync.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import writeFileSyncInjectable from "../../../common/fs/write-file-sync.injectable";
|
||||
|
||||
const v360Beta1ClusterStoreMigrationInjectable = getInjectable({
|
||||
id: "v3.6.0-beta.1-cluster-store-migration",
|
||||
instantiate: (di) => {
|
||||
const userDataPath = di.inject(directoryForUserDataInjectable);
|
||||
const getCustomKubeConfigDirectory = di.inject(getCustomKubeConfigDirectoryInjectable);
|
||||
const readFileSync = di.inject(readFileSyncInjectable);
|
||||
const readFileBufferSync = di.inject(readFileBufferSyncInjectable);
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const writeFileSync = di.inject(writeFileSyncInjectable);
|
||||
|
||||
return {
|
||||
version: "3.6.0-beta.1",
|
||||
run: (store) => {
|
||||
const storedClusters = (store.get("clusters") ?? []) as Pre360ClusterModel[];
|
||||
const migratedClusters: ClusterModel[] = [];
|
||||
|
||||
logger.info("Number of clusters to migrate: ", storedClusters.length);
|
||||
|
||||
for (const clusterModel of storedClusters) {
|
||||
/**
|
||||
* migrate kubeconfig
|
||||
*/
|
||||
try {
|
||||
const absPath = getCustomKubeConfigDirectory(clusterModel.id);
|
||||
|
||||
if (!clusterModel.kubeConfig) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// take the embedded kubeconfig and dump it into a file
|
||||
writeFileSync(absPath, clusterModel.kubeConfig);
|
||||
|
||||
clusterModel.kubeConfigPath = absPath;
|
||||
clusterModel.contextName = loadConfigFromString(readFileSync(absPath)).config.getCurrentContext();
|
||||
delete clusterModel.kubeConfig;
|
||||
|
||||
} catch (error) {
|
||||
logger.info(`Failed to migrate Kubeconfig for cluster "${clusterModel.id}", removing clusterModel...`, error);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* migrate cluster icon
|
||||
*/
|
||||
try {
|
||||
if (clusterModel.preferences?.icon) {
|
||||
logger.info(`migrating ${clusterModel.preferences.icon} for ${clusterModel.preferences.clusterName}`);
|
||||
const iconPath = clusterModel.preferences.icon.replace("store://", "");
|
||||
const fileData = readFileBufferSync(joinPaths(userDataPath, iconPath));
|
||||
|
||||
clusterModel.preferences.icon = `data:;base64,${fileData.toString("base64")}`;
|
||||
} else {
|
||||
delete clusterModel.preferences?.icon;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.info(`Failed to migrate cluster icon for cluster "${clusterModel.id}"`, error);
|
||||
delete clusterModel.preferences?.icon;
|
||||
}
|
||||
|
||||
migratedClusters.push(clusterModel);
|
||||
}
|
||||
|
||||
store.set("clusters", migratedClusters);
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: clusterStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v360Beta1ClusterStoreMigrationInjectable;
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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 directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token";
|
||||
import type { ClusterModel } from "../../../common/cluster-types";
|
||||
import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable";
|
||||
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
|
||||
import { isErrnoException } from "../../../common/utils";
|
||||
|
||||
interface Pre500WorkspaceStoreModel {
|
||||
workspaces: {
|
||||
id: string;
|
||||
name: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
const v500Beta10ClusterStoreMigrationInjectable = getInjectable({
|
||||
id: "v5.0.0-beta.10-cluster-store-migration",
|
||||
instantiate: (di) => {
|
||||
const userDataPath = di.inject(directoryForUserDataInjectable);
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const readJsonSync = di.inject(readJsonSyncInjectable);
|
||||
|
||||
return {
|
||||
version: "5.0.0-beta.10",
|
||||
run(store) {
|
||||
try {
|
||||
const workspaceData: Pre500WorkspaceStoreModel = readJsonSync(joinPaths(userDataPath, "lens-workspace-store.json"));
|
||||
const workspaces = new Map<string, string>(); // mapping from WorkspaceId to name
|
||||
|
||||
for (const { id, name } of workspaceData.workspaces) {
|
||||
workspaces.set(id, name);
|
||||
}
|
||||
|
||||
const clusters = (store.get("clusters") ?? []) as ClusterModel[];
|
||||
|
||||
for (const cluster of clusters) {
|
||||
if (cluster.workspace) {
|
||||
const workspace = workspaces.get(cluster.workspace);
|
||||
|
||||
if (workspace) {
|
||||
(cluster.labels ??= {}).workspace = workspace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
store.set("clusters", clusters);
|
||||
} catch (error) {
|
||||
if (isErrnoException(error) && !(error.code === "ENOENT" && error.path?.endsWith("lens-workspace-store.json"))) {
|
||||
// ignore lens-workspace-store.json being missing
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: clusterStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v500Beta10ClusterStoreMigrationInjectable;
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { ClusterModel, ClusterPreferences, ClusterPrometheusPreferences } from "../../../common/cluster-types";
|
||||
import { moveSync, removeSync } from "fs-extra";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import { isDefined } from "../../../common/utils";
|
||||
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
|
||||
|
||||
function mergePrometheusPreferences(left: ClusterPrometheusPreferences, right: ClusterPrometheusPreferences): ClusterPrometheusPreferences {
|
||||
if (left.prometheus && left.prometheusProvider) {
|
||||
return {
|
||||
prometheus: left.prometheus,
|
||||
prometheusProvider: left.prometheusProvider,
|
||||
};
|
||||
}
|
||||
|
||||
if (right.prometheus && right.prometheusProvider) {
|
||||
return {
|
||||
prometheus: right.prometheus,
|
||||
prometheusProvider: right.prometheusProvider,
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
function mergePreferences(left: ClusterPreferences, right: ClusterPreferences): ClusterPreferences {
|
||||
return {
|
||||
terminalCWD: left.terminalCWD || right.terminalCWD || undefined,
|
||||
clusterName: left.clusterName || right.clusterName || undefined,
|
||||
iconOrder: left.iconOrder || right.iconOrder || undefined,
|
||||
icon: left.icon || right.icon || undefined,
|
||||
httpsProxy: left.httpsProxy || right.httpsProxy || undefined,
|
||||
hiddenMetrics: mergeSet(left.hiddenMetrics ?? [], right.hiddenMetrics ?? []),
|
||||
...mergePrometheusPreferences(left, right),
|
||||
};
|
||||
}
|
||||
|
||||
function mergeLabels(left: Record<string, string>, right: Record<string, string>): Record<string, string> {
|
||||
return {
|
||||
...right,
|
||||
...left,
|
||||
};
|
||||
}
|
||||
|
||||
function mergeSet(...iterables: Iterable<string | undefined>[]): string[] {
|
||||
const res = new Set<string>();
|
||||
|
||||
for (const iterable of iterables) {
|
||||
for (const val of iterable) {
|
||||
if (val) {
|
||||
res.add(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...res];
|
||||
}
|
||||
|
||||
function mergeClusterModel(prev: ClusterModel, right: Omit<ClusterModel, "id">): ClusterModel {
|
||||
return {
|
||||
id: prev.id,
|
||||
kubeConfigPath: prev.kubeConfigPath,
|
||||
contextName: prev.contextName,
|
||||
preferences: mergePreferences(prev.preferences ?? {}, right.preferences ?? {}),
|
||||
metadata: prev.metadata,
|
||||
labels: mergeLabels(prev.labels ?? {}, right.labels ?? {}),
|
||||
accessibleNamespaces: mergeSet(prev.accessibleNamespaces ?? [], right.accessibleNamespaces ?? []),
|
||||
workspace: prev.workspace || right.workspace,
|
||||
workspaces: mergeSet([prev.workspace, right.workspace], prev.workspaces ?? [], right.workspaces ?? []),
|
||||
};
|
||||
}
|
||||
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token";
|
||||
import { generateNewIdFor } from "../../../common/utils/generate-new-id-for";
|
||||
|
||||
const v500Beta13ClusterStoreMigrationInjectable = getInjectable({
|
||||
id: "v5.0.0-beta.13-cluster-store-migration",
|
||||
instantiate: (di) => {
|
||||
const userDataPath = di.inject(directoryForUserDataInjectable);
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
const moveStorageFolder = ({ folder, newId, oldId }: { folder: string; newId: string; oldId: string }): void => {
|
||||
const oldPath = joinPaths(folder, `${oldId}.json`);
|
||||
const newPath = joinPaths(folder, `${newId}.json`);
|
||||
|
||||
try {
|
||||
moveSync(oldPath, newPath);
|
||||
} catch (error) {
|
||||
if (String(error).includes("dest already exists")) {
|
||||
logger.info(`Multiple old lens-local-storage files for newId=${newId}. Removing ${oldId}.json`);
|
||||
removeSync(oldPath);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
version: "5.0.0-beta.13",
|
||||
run(store) {
|
||||
const folder = joinPaths(userDataPath, "lens-local-storage");
|
||||
const oldClusters = (store.get("clusters") ?? []) as ClusterModel[];
|
||||
const clusters = new Map<string, ClusterModel>();
|
||||
|
||||
for (const { id: oldId, ...cluster } of oldClusters) {
|
||||
const newId = generateNewIdFor(cluster);
|
||||
const newCluster = clusters.get(newId);
|
||||
|
||||
if (newCluster) {
|
||||
logger.info(`Duplicate entries for ${newId}`, { oldId });
|
||||
clusters.set(newId, mergeClusterModel(newCluster, cluster));
|
||||
} else {
|
||||
logger.info(`First entry for ${newId}`, { oldId });
|
||||
clusters.set(newId, {
|
||||
...cluster,
|
||||
id: newId,
|
||||
workspaces: [cluster.workspace].filter(isDefined),
|
||||
});
|
||||
moveStorageFolder({ folder, newId, oldId });
|
||||
}
|
||||
}
|
||||
|
||||
store.set("clusters", [...clusters.values()]);
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: clusterStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v500Beta13ClusterStoreMigrationInjectable;
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// Fix embedded kubeconfig paths under snap config
|
||||
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import applicationInformationInjectable from "../../../common/vars/application-information.injectable";
|
||||
import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import isSnapPackageInjectable from "../../../common/vars/is-snap-package.injectable";
|
||||
import type { ClusterModel } from "../../../common/cluster-types";
|
||||
import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
|
||||
|
||||
const clusterStoreSnapMigrationInjectable = getInjectable({
|
||||
id: "cluster-store-snap-migration",
|
||||
instantiate: (di) => {
|
||||
const { version } = di.inject(applicationInformationInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const isSnapPackage = di.inject(isSnapPackageInjectable);
|
||||
const pathExistsSync = di.inject(pathExistsSyncInjectable);
|
||||
|
||||
return {
|
||||
version, // Run always after upgrade
|
||||
run(store) {
|
||||
if (!isSnapPackage) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Migrating embedded kubeconfig paths");
|
||||
const storedClusters = (store.get("clusters") || []) as ClusterModel[];
|
||||
|
||||
if (!storedClusters.length) return;
|
||||
|
||||
logger.info("Number of clusters to migrate: ", storedClusters.length);
|
||||
const migratedClusters = storedClusters
|
||||
.map(cluster => {
|
||||
/**
|
||||
* replace snap version with 'current' in kubeconfig path
|
||||
*/
|
||||
if (!pathExistsSync(cluster.kubeConfigPath)) {
|
||||
const kubeconfigPath = cluster.kubeConfigPath.replace(/\/snap\/kontena-lens\/[0-9]*\//, "/snap/kontena-lens/current/");
|
||||
|
||||
cluster.kubeConfigPath = kubeconfigPath;
|
||||
}
|
||||
|
||||
return cluster;
|
||||
});
|
||||
|
||||
|
||||
store.set("clusters", migratedClusters);
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: clusterStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default clusterStoreSnapMigrationInjectable;
|
||||
|
||||
@@ -8,4 +8,5 @@ import electronAppInjectable from "./electron-app.injectable";
|
||||
|
||||
export default getGlobalOverride(electronAppInjectable, () => ({
|
||||
getVersion: () => "6.0.0",
|
||||
} as Electron.App));
|
||||
setLoginItemSettings: () => {},
|
||||
} as Partial<Electron.App> as Electron.App));
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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 { Settings } from "electron";
|
||||
import electronAppInjectable from "../electron-app.injectable";
|
||||
|
||||
export type SetLoginItemSettings = (settings: Settings) => void;
|
||||
|
||||
const setLoginItemSettingsInjectable = getInjectable({
|
||||
id: "set-login-item-settings",
|
||||
instantiate: (di): SetLoginItemSettings => {
|
||||
const electronApp = di.inject(electronAppInjectable);
|
||||
|
||||
return (settings) => electronApp.setLoginItemSettings(settings);
|
||||
},
|
||||
});
|
||||
|
||||
export default setLoginItemSettingsInjectable;
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getGlobalOverrideForFunction } from "../../../common/test-utils/get-global-override-for-function";
|
||||
import showOpenDialogInjectable from "./show-open-dialog.injectable";
|
||||
|
||||
export default getGlobalOverrideForFunction(showOpenDialogInjectable);
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 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 { OpenDialogOptions } from "electron";
|
||||
import { dialog } from "electron";
|
||||
|
||||
export type ShowOpenDialog = (options: OpenDialogOptions) => Promise<Electron.OpenDialogReturnValue>;
|
||||
|
||||
const showOpenDialogInjectable = getInjectable({
|
||||
id: "show-open-dialog",
|
||||
instantiate: (): ShowOpenDialog => opts => dialog.showOpenDialog(opts),
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default showOpenDialogInjectable;
|
||||
+3
-6
@@ -7,11 +7,10 @@ import { setupIpcMainHandlers } from "./setup-ipc-main-handlers";
|
||||
import loggerInjectable from "../../../../common/logger.injectable";
|
||||
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
import operatingSystemThemeInjectable from "../../../theme/operating-system-theme.injectable";
|
||||
import catalogEntityRegistryInjectable from "../../../catalog/entity-registry.injectable";
|
||||
import askUserForFilePathsInjectable from "../../../ipc/ask-user-for-file-paths.injectable";
|
||||
import applicationMenuItemCompositeInjectable from "../../../../features/application-menu/main/application-menu-item-composite.injectable";
|
||||
import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.injectable";
|
||||
import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable";
|
||||
|
||||
const setupIpcMainHandlersInjectable = getInjectable({
|
||||
id: "setup-ipc-main-handlers",
|
||||
@@ -21,9 +20,8 @@ const setupIpcMainHandlersInjectable = getInjectable({
|
||||
const applicationMenuItemComposite = di.inject(applicationMenuItemCompositeInjectable);
|
||||
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
const clusterStore = di.inject(clusterStoreInjectable);
|
||||
const operatingSystemTheme = di.inject(operatingSystemThemeInjectable);
|
||||
const askUserForFilePaths = di.inject(askUserForFilePathsInjectable);
|
||||
const emitAppEvent = di.inject(emitAppEventInjectable);
|
||||
const getClusterById = di.inject(getClusterByIdInjectable);
|
||||
|
||||
return {
|
||||
id: "setup-ipc-main-handlers",
|
||||
@@ -34,9 +32,8 @@ const setupIpcMainHandlersInjectable = getInjectable({
|
||||
applicationMenuItemComposite,
|
||||
catalogEntityRegistry,
|
||||
clusterStore,
|
||||
operatingSystemTheme,
|
||||
askUserForFilePaths,
|
||||
emitAppEvent,
|
||||
getClusterById,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
+14
-23
@@ -5,54 +5,46 @@
|
||||
import type { IpcMainInvokeEvent } from "electron";
|
||||
import { BrowserWindow, Menu } from "electron";
|
||||
import { clusterFrameMap } from "../../../../common/cluster-frames";
|
||||
import { clusterActivateHandler, clusterSetFrameIdHandler, clusterDisconnectHandler } from "../../../../common/ipc/cluster";
|
||||
import { clusterActivateHandler, clusterSetFrameIdHandler, clusterDisconnectHandler, clusterStates } from "../../../../common/ipc/cluster";
|
||||
import type { ClusterId } from "../../../../common/cluster-types";
|
||||
import { ClusterStore } from "../../../../common/cluster-store/cluster-store";
|
||||
import type { ClusterStore } from "../../../../common/cluster-store/cluster-store";
|
||||
import { broadcastMainChannel, broadcastMessage, ipcMainHandle, ipcMainOn } from "../../../../common/ipc";
|
||||
import type { CatalogEntityRegistry } from "../../../catalog";
|
||||
import { pushCatalogToRenderer } from "../../../catalog-pusher";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window";
|
||||
import { handleWindowAction, onLocationChange } from "../../../ipc/window";
|
||||
import { openFilePickingDialogChannel } from "../../../../common/ipc/dialog";
|
||||
import { getNativeThemeChannel } from "../../../../common/ipc/native-theme";
|
||||
import type { Theme } from "../../../theme/operating-system-theme-state.injectable";
|
||||
import type { AskUserForFilePaths } from "../../../ipc/ask-user-for-file-paths.injectable";
|
||||
import type { ApplicationMenuItemTypes } from "../../../../features/application-menu/main/menu-items/application-menu-item-injection-token";
|
||||
import type { Composite } from "../../../../common/utils/composite/get-composite/get-composite";
|
||||
import { getApplicationMenuTemplate } from "../../../../features/application-menu/main/populate-application-menu.injectable";
|
||||
import type { MenuItemRoot } from "../../../../features/application-menu/main/application-menu-item-composite.injectable";
|
||||
import type { EmitAppEvent } from "../../../../common/app-event-bus/emit-event.injectable";
|
||||
|
||||
import type { GetClusterById } from "../../../../common/cluster-store/get-by-id.injectable";
|
||||
interface Dependencies {
|
||||
applicationMenuItemComposite: IComputedValue<Composite<ApplicationMenuItemTypes | MenuItemRoot>>;
|
||||
catalogEntityRegistry: CatalogEntityRegistry;
|
||||
clusterStore: ClusterStore;
|
||||
operatingSystemTheme: IComputedValue<Theme>;
|
||||
askUserForFilePaths: AskUserForFilePaths;
|
||||
emitAppEvent: EmitAppEvent;
|
||||
getClusterById: GetClusterById;
|
||||
}
|
||||
|
||||
export const setupIpcMainHandlers = ({
|
||||
applicationMenuItemComposite,
|
||||
catalogEntityRegistry,
|
||||
clusterStore,
|
||||
operatingSystemTheme,
|
||||
askUserForFilePaths,
|
||||
emitAppEvent,
|
||||
getClusterById,
|
||||
}: Dependencies) => {
|
||||
ipcMainHandle(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => {
|
||||
return ClusterStore.getInstance()
|
||||
.getById(clusterId)
|
||||
return getClusterById(clusterId)
|
||||
?.activate(force);
|
||||
});
|
||||
|
||||
ipcMainHandle(clusterSetFrameIdHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId) => {
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
const cluster = getClusterById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
clusterFrameMap.set(cluster.id, { frameId: event.frameId, processId: event.processId });
|
||||
cluster.pushState();
|
||||
|
||||
pushCatalogToRenderer(catalogEntityRegistry);
|
||||
}
|
||||
@@ -60,7 +52,7 @@ export const setupIpcMainHandlers = ({
|
||||
|
||||
ipcMainHandle(clusterDisconnectHandler, (event, clusterId: ClusterId) => {
|
||||
emitAppEvent({ name: "cluster", action: "stop" });
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
const cluster = getClusterById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
cluster.disconnect();
|
||||
@@ -72,8 +64,6 @@ export const setupIpcMainHandlers = ({
|
||||
|
||||
ipcMainOn(windowLocationChangedChannel, () => onLocationChange());
|
||||
|
||||
ipcMainHandle(openFilePickingDialogChannel, (event, opts) => askUserForFilePaths(opts));
|
||||
|
||||
ipcMainHandle(broadcastMainChannel, (event, channel, ...args) => broadcastMessage(channel, ...args));
|
||||
|
||||
ipcMainOn(windowOpenAppMenuAsContextMenuChannel, async (event) => {
|
||||
@@ -88,9 +78,10 @@ export const setupIpcMainHandlers = ({
|
||||
});
|
||||
});
|
||||
|
||||
ipcMainHandle(getNativeThemeChannel, () => {
|
||||
return operatingSystemTheme.get();
|
||||
});
|
||||
|
||||
clusterStore.provideInitialFromMain();
|
||||
ipcMainHandle(clusterStates, () => (
|
||||
clusterStore.clustersList.map(cluster => ({
|
||||
id: cluster.id,
|
||||
state: cluster.getState(),
|
||||
}))
|
||||
));
|
||||
};
|
||||
@@ -12,13 +12,6 @@ import readJsonFileInjectable from "../common/fs/read-json-file.injectable";
|
||||
import readFileInjectable from "../common/fs/read-file.injectable";
|
||||
import loggerInjectable from "../common/logger.injectable";
|
||||
import spawnInjectable from "./child-process/spawn.injectable";
|
||||
import extensionsStoreInjectable from "../extensions/extensions-store/extensions-store.injectable";
|
||||
import type { ExtensionsStore } from "../extensions/extensions-store/extensions-store";
|
||||
import fileSystemProvisionerStoreInjectable from "../extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable";
|
||||
import type { FileSystemProvisionerStore } from "../extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store";
|
||||
import userStoreInjectable from "../common/user-store/user-store.injectable";
|
||||
import type { UserStore } from "../common/user-store";
|
||||
import hotbarStoreInjectable from "../common/hotbars/store.injectable";
|
||||
import commandLineArgumentsInjectable from "./utils/command-line-arguments.injectable";
|
||||
import initializeExtensionsInjectable from "./start-main-application/runnables/initialize-extensions.injectable";
|
||||
import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.injectable";
|
||||
@@ -47,7 +40,6 @@ import clusterFramesInjectable from "../common/cluster-frames.injectable";
|
||||
import type { ClusterFrameInfo } from "../common/cluster-frames";
|
||||
import { observable, runInAction } from "mobx";
|
||||
import waitForElectronToBeReadyInjectable from "./electron-app/features/wait-for-electron-to-be-ready.injectable";
|
||||
import setupListenerForCurrentClusterFrameInjectable from "./start-main-application/lens-window/current-cluster-frame/setup-listener-for-current-cluster-frame.injectable";
|
||||
import setupRunnablesAfterWindowIsOpenedInjectable from "./electron-app/runnables/setup-runnables-after-window-is-opened.injectable";
|
||||
import broadcastMessageInjectable from "../common/ipc/broadcast-message.injectable";
|
||||
import getElectronThemeInjectable from "./electron-app/features/get-electron-theme.injectable";
|
||||
@@ -74,7 +66,6 @@ import rollbackHelmReleaseInjectable from "./helm/helm-service/rollback-helm-rel
|
||||
import waitUntilBundledExtensionsAreLoadedInjectable from "./start-main-application/lens-window/application-window/wait-until-bundled-extensions-are-loaded.injectable";
|
||||
import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx";
|
||||
import electronInjectable from "./utils/resolve-system-proxy/electron.injectable";
|
||||
import type { HotbarStore } from "../common/hotbars/store";
|
||||
import focusApplicationInjectable from "./electron-app/features/focus-application.injectable";
|
||||
import kubectlDownloadingNormalizedArchInjectable from "./kubectl/normalized-arch.injectable";
|
||||
import initializeClusterManagerInjectable from "./cluster/initialize-manager.injectable";
|
||||
@@ -121,19 +112,6 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
|
||||
di.override(waitUntilBundledExtensionsAreLoadedInjectable, () => async () => {});
|
||||
di.override(getRandomIdInjectable, () => () => "some-irrelevant-random-id");
|
||||
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
|
||||
di.override(hotbarStoreInjectable, () => ({
|
||||
load: () => {},
|
||||
getActive: () => ({ name: "some-hotbar", items: [] }),
|
||||
getDisplayIndex: () => "0",
|
||||
}) as unknown as HotbarStore);
|
||||
|
||||
di.override(userStoreInjectable, () => ({
|
||||
startMainReactions: () => {},
|
||||
extensionRegistryUrl: { customUrl: "some-custom-url" },
|
||||
load: () => {},
|
||||
}) as Partial<UserStore> as UserStore);
|
||||
di.override(extensionsStoreInjectable, () => ({ isEnabled: (opts) => (void opts, false) }) as ExtensionsStore);
|
||||
di.override(fileSystemProvisionerStoreInjectable, () => ({}) as FileSystemProvisionerStore);
|
||||
|
||||
overrideOperatingSystem(di);
|
||||
overrideRunnablesHavingSideEffects(di);
|
||||
@@ -199,7 +177,6 @@ const overrideRunnablesHavingSideEffects = (di: DiContainer) => {
|
||||
setupShellInjectable,
|
||||
setupSyncingOfWeblinksInjectable,
|
||||
setupSystemCaInjectable,
|
||||
setupListenerForCurrentClusterFrameInjectable,
|
||||
setupRunnablesAfterWindowIsOpenedInjectable,
|
||||
startCatalogSyncInjectable,
|
||||
startKubeConfigSyncInjectable,
|
||||
|
||||
@@ -41,10 +41,17 @@ describe("Helm Service tests", () => {
|
||||
getActiveHelmRepositoriesMock.mockReturnValue(
|
||||
Promise.resolve({
|
||||
callWasSuccessful: true,
|
||||
|
||||
response: [
|
||||
{ name: "stable", url: "stableurl" },
|
||||
{ name: "experiment", url: "experimenturl" },
|
||||
{
|
||||
name: "stable",
|
||||
url: "stableurl",
|
||||
cacheFilePath: "/some-cache-file-path-for-stable",
|
||||
},
|
||||
{
|
||||
name: "experiment",
|
||||
url: "experimenturl",
|
||||
cacheFilePath: "/some-cache-file-path-for-experiment",
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
@@ -152,7 +159,11 @@ describe("Helm Service tests", () => {
|
||||
getActiveHelmRepositoriesMock.mockReturnValue(
|
||||
Promise.resolve({
|
||||
callWasSuccessful: true,
|
||||
response: [{ name: "bitnami", url: "bitnamiurl" }],
|
||||
response: [{
|
||||
name: "bitnami",
|
||||
url: "bitnamiurl",
|
||||
cacheFilePath: "/some-cache-file-path-for-bitnami",
|
||||
}],
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import httpsProxyConfigurationInjectable from "../../../common/user-store/https-proxy.injectable";
|
||||
|
||||
const execHelmEnvInjectable = getInjectable({
|
||||
id: "exec-helm-env",
|
||||
instantiate: (di) => {
|
||||
const userStore = di.inject(userStoreInjectable);
|
||||
const httpsProxyConfiguration = di.inject(httpsProxyConfigurationInjectable);
|
||||
|
||||
return computed(() => {
|
||||
const {
|
||||
HTTPS_PROXY = userStore.httpsProxy,
|
||||
HTTPS_PROXY = httpsProxyConfiguration.get(),
|
||||
...env
|
||||
} = process.env;
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { promiseExecFile } from "../../common/utils/promise-exec";
|
||||
import type { ObjectEncodingOptions } from "fs";
|
||||
import type { ExecFileOptions, ExecFileOptionsWithStringEncoding } from "child_process";
|
||||
import { UserStore } from "../../common/user-store";
|
||||
import { isChildProcessError } from "../../common/utils";
|
||||
import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
import helmBinaryPathInjectable from "./helm-binary-path.injectable";
|
||||
|
||||
/**
|
||||
* ExecFile the bundled helm CLI
|
||||
* @returns STDOUT
|
||||
*/
|
||||
export async function execHelm(args: string[], { encoding, ...rest }: ObjectEncodingOptions & ExecFileOptions = {}): Promise<string> {
|
||||
const options: ExecFileOptionsWithStringEncoding = {
|
||||
encoding: encoding ?? "utf-8",
|
||||
...rest,
|
||||
};
|
||||
const di = getLegacyGlobalDiForExtensionApi();
|
||||
const helmBinaryPath = di.inject(helmBinaryPathInjectable);
|
||||
|
||||
try {
|
||||
const opts = { ...options };
|
||||
|
||||
opts.env ??= { ...process.env };
|
||||
|
||||
if (!opts.env.HTTPS_PROXY && UserStore.getInstance().httpsProxy) {
|
||||
opts.env.HTTPS_PROXY = UserStore.getInstance().httpsProxy;
|
||||
}
|
||||
|
||||
const { stdout } = await promiseExecFile(helmBinaryPath, args, opts);
|
||||
|
||||
return stdout;
|
||||
} catch (error) {
|
||||
if (isChildProcessError(error, "string")) {
|
||||
throw error.stderr || error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -7,16 +7,20 @@ import type { HelmRepo } from "../../common/helm/helm-repo";
|
||||
import { HelmChartManager } from "./helm-chart-manager";
|
||||
import helmChartManagerCacheInjectable from "./helm-chart-manager-cache.injectable";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import execHelmInjectable from "./exec-helm/exec-helm.injectable";
|
||||
import readFileInjectable from "../../common/fs/read-file.injectable";
|
||||
import statInjectable from "../../common/fs/stat.injectable";
|
||||
|
||||
const helmChartManagerInjectable = getInjectable({
|
||||
id: "helm-chart-manager",
|
||||
|
||||
instantiate: (di, repo: HelmRepo) => {
|
||||
const cache = di.inject(helmChartManagerCacheInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
return new HelmChartManager(repo, { cache, logger });
|
||||
},
|
||||
instantiate: (di, repo: HelmRepo) => new HelmChartManager({
|
||||
cache: di.inject(helmChartManagerCacheInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
execHelm: di.inject(execHelmInjectable),
|
||||
readFile: di.inject(readFileInjectable),
|
||||
stat: di.inject(statInjectable),
|
||||
}, repo),
|
||||
|
||||
lifecycle: lifecycleEnum.keyedSingleton({
|
||||
getInstanceKey: (di, repo: HelmRepo) => repo.name,
|
||||
|
||||
@@ -3,16 +3,15 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import fs from "fs";
|
||||
import * as yaml from "js-yaml";
|
||||
import { iter, put, sortBySemverVersion } from "../../common/utils";
|
||||
import { execHelm } from "./exec";
|
||||
import type { SetRequired } from "type-fest";
|
||||
import { assert } from "console";
|
||||
import type { HelmRepo } from "../../common/helm/helm-repo";
|
||||
import type { HelmChartManagerCache } from "./helm-chart-manager-cache.injectable";
|
||||
import type { Logger } from "../../common/logger";
|
||||
import type { RepoHelmChartList } from "../../common/k8s-api/endpoints/helm-charts.api/request-charts.injectable";
|
||||
import type { ExecHelm } from "./exec-helm/exec-helm.injectable";
|
||||
import type { ReadFile } from "../../common/fs/read-file.injectable";
|
||||
import type { Stat } from "../../common/fs/stat.injectable";
|
||||
|
||||
export interface HelmCacheFile {
|
||||
apiVersion: string;
|
||||
@@ -20,18 +19,18 @@ export interface HelmCacheFile {
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
cache: HelmChartManagerCache;
|
||||
logger: Logger;
|
||||
readonly cache: HelmChartManagerCache;
|
||||
readonly logger: Logger;
|
||||
execHelm: ExecHelm;
|
||||
readFile: ReadFile;
|
||||
stat: Stat;
|
||||
}
|
||||
|
||||
export class HelmChartManager {
|
||||
protected readonly repo: SetRequired<HelmRepo, "cacheFilePath">;
|
||||
|
||||
constructor(repo: HelmRepo, private dependencies: Dependencies) {
|
||||
assert(repo.cacheFilePath, "CacheFilePath must be provided on the helm repo");
|
||||
|
||||
this.repo = repo as SetRequired<HelmRepo, "cacheFilePath">;
|
||||
}
|
||||
constructor(
|
||||
private readonly dependencies: Dependencies,
|
||||
protected readonly repo: HelmRepo,
|
||||
) {}
|
||||
|
||||
public async chartVersions(name: string) {
|
||||
const charts = await this.charts();
|
||||
@@ -56,7 +55,7 @@ export class HelmChartManager {
|
||||
args.push("--version", version);
|
||||
}
|
||||
|
||||
return execHelm(args);
|
||||
return this.dependencies.execHelm(args);
|
||||
}
|
||||
|
||||
public async getReadme(name: string, version?: string) {
|
||||
@@ -68,8 +67,8 @@ export class HelmChartManager {
|
||||
}
|
||||
|
||||
protected async updateYamlCache() {
|
||||
const cacheFile = await fs.promises.readFile(this.repo.cacheFilePath, "utf-8");
|
||||
const cacheFileStats = await fs.promises.stat(this.repo.cacheFilePath);
|
||||
const cacheFile = await this.dependencies.readFile(this.repo.cacheFilePath);
|
||||
const cacheFileStats = await this.dependencies.stat(this.repo.cacheFilePath);
|
||||
const data = yaml.load(cacheFile) as string | number | HelmCacheFile;
|
||||
|
||||
if (!data || typeof data !== "object" || typeof data.entries !== "object") {
|
||||
@@ -94,7 +93,7 @@ export class HelmChartManager {
|
||||
if (!cacheEntry) {
|
||||
cacheEntry = await this.updateYamlCache();
|
||||
} else {
|
||||
const newStats = await fs.promises.stat(this.repo.cacheFilePath);
|
||||
const newStats = await this.dependencies.stat(this.repo.cacheFilePath);
|
||||
|
||||
if (cacheEntry.mtimeMs < newStats.mtimeMs) {
|
||||
cacheEntry = await this.updateYamlCache();
|
||||
|
||||
@@ -7,9 +7,12 @@ import tempy from "tempy";
|
||||
import fse from "fs-extra";
|
||||
import * as yaml from "js-yaml";
|
||||
import { toCamelCase } from "../../common/utils/camelCase";
|
||||
import { execHelm } from "./exec";
|
||||
import type { JsonValue } from "type-fest";
|
||||
import { isObject, json } from "../../common/utils";
|
||||
import { asLegacyGlobalFunctionForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
||||
import execHelmInjectable from "./exec-helm/exec-helm.injectable";
|
||||
|
||||
const execHelm = asLegacyGlobalFunctionForExtensionApi(execHelmInjectable);
|
||||
|
||||
export async function listReleases(pathToKubeconfig: string, namespace?: string): Promise<Record<string, any>[]> {
|
||||
const args = [
|
||||
@@ -26,7 +29,13 @@ export async function listReleases(pathToKubeconfig: string, namespace?: string)
|
||||
|
||||
args.push("--kubeconfig", pathToKubeconfig);
|
||||
|
||||
const output = json.parse(await execHelm(args));
|
||||
const result = await execHelm(args);
|
||||
|
||||
if (!result.callWasSuccessful) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
const output = json.parse(result.response);
|
||||
|
||||
if (!Array.isArray(output) || output.length == 0) {
|
||||
return [];
|
||||
@@ -60,7 +69,13 @@ export async function installChart(chart: string, values: JsonValue, name: strin
|
||||
}
|
||||
|
||||
try {
|
||||
const output = await execHelm(args);
|
||||
const result = await execHelm(args);
|
||||
|
||||
if (!result.callWasSuccessful) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
const output = result.response;
|
||||
const releaseName = output.split("\n")[0].split(" ")[1].trim();
|
||||
|
||||
return {
|
||||
@@ -75,13 +90,19 @@ export async function installChart(chart: string, values: JsonValue, name: strin
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteRelease(name: string, namespace: string, kubeconfigPath: string) {
|
||||
return execHelm([
|
||||
export async function deleteRelease(name: string, namespace: string, kubeconfigPath: string): Promise<string> {
|
||||
const result = await execHelm([
|
||||
"delete",
|
||||
name,
|
||||
"--namespace", namespace,
|
||||
"--kubeconfig", kubeconfigPath,
|
||||
]);
|
||||
|
||||
if (result.callWasSuccessful) {
|
||||
return result.response;
|
||||
}
|
||||
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
interface GetValuesOptions {
|
||||
@@ -90,7 +111,7 @@ interface GetValuesOptions {
|
||||
kubeconfigPath: string;
|
||||
}
|
||||
|
||||
export async function getValues(name: string, { namespace, all = false, kubeconfigPath }: GetValuesOptions) {
|
||||
export async function getValues(name: string, { namespace, all = false, kubeconfigPath }: GetValuesOptions): Promise<string> {
|
||||
const args = [
|
||||
"get",
|
||||
"values",
|
||||
@@ -107,25 +128,41 @@ export async function getValues(name: string, { namespace, all = false, kubeconf
|
||||
"--kubeconfig", kubeconfigPath,
|
||||
);
|
||||
|
||||
return execHelm(args);
|
||||
const result = await execHelm(args);
|
||||
|
||||
if (result.callWasSuccessful) {
|
||||
return result.response;
|
||||
}
|
||||
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
export async function getHistory(name: string, namespace: string, kubeconfigPath: string) {
|
||||
return json.parse(await execHelm([
|
||||
export async function getHistory(name: string, namespace: string, kubeconfigPath: string): Promise<JsonValue> {
|
||||
const result = await execHelm([
|
||||
"history",
|
||||
name,
|
||||
"--output", "json",
|
||||
"--namespace", namespace,
|
||||
"--kubeconfig", kubeconfigPath,
|
||||
]));
|
||||
]);
|
||||
|
||||
if (result.callWasSuccessful) {
|
||||
return json.parse(result.response);
|
||||
}
|
||||
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
export async function rollback(name: string, namespace: string, revision: number, kubeconfigPath: string) {
|
||||
await execHelm([
|
||||
export async function rollback(name: string, namespace: string, revision: number, kubeconfigPath: string): Promise<void> {
|
||||
const result = await execHelm([
|
||||
"rollback",
|
||||
name,
|
||||
`${revision}`,
|
||||
"--namespace", namespace,
|
||||
"--kubeconfig", kubeconfigPath,
|
||||
]);
|
||||
|
||||
if (!result.callWasSuccessful) {
|
||||
throw result.error;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import loggerInjectable from "../../../common/logger.injectable";
|
||||
import tempy from "tempy";
|
||||
import getHelmReleaseInjectable from "./get-helm-release.injectable";
|
||||
import writeFileInjectable from "../../../common/fs/write-file.injectable";
|
||||
import removePathInjectable from "../../../common/fs/remove-path.injectable";
|
||||
import removePathInjectable from "../../../common/fs/remove.injectable";
|
||||
import execHelmInjectable from "../exec-helm/exec-helm.injectable";
|
||||
|
||||
export interface UpdateChartArgs {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// Cleans up a store that had the state related data stored
|
||||
import { getEmptyHotbar } from "../../../common/hotbars/types";
|
||||
import catalogCatalogEntityInjectable from "../../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { hotbarStoreMigrationInjectionToken } from "../../../common/hotbars/migrations-token";
|
||||
|
||||
const v500Alpha0HotbarStoreMigrationInjectable = getInjectable({
|
||||
id: "v5.0.0-alpha.0-hotbar-store-migration",
|
||||
instantiate: (di) => {
|
||||
const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable);
|
||||
|
||||
return {
|
||||
version: "5.0.0-alpha.0",
|
||||
run(store) {
|
||||
const hotbar = getEmptyHotbar("default");
|
||||
|
||||
const { metadata: { uid, name, source }} = catalogCatalogEntity;
|
||||
|
||||
hotbar.items[0] = { entity: { uid, name, source }};
|
||||
|
||||
store.set("hotbars", [hotbar]);
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: hotbarStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v500Alpha0HotbarStoreMigrationInjectable;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// Cleans up a store that had the state related data stored
|
||||
import type { Hotbar } from "../../../common/hotbars/types";
|
||||
import * as uuid from "uuid";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { hotbarStoreMigrationInjectionToken } from "../../../common/hotbars/migrations-token";
|
||||
|
||||
const v500Alpha2HotbarStoreMigrationInjectable = getInjectable({
|
||||
id: "v5.0.0-alpha.2-hotbar-store-migration",
|
||||
instantiate: () => ({
|
||||
version: "5.0.0-alpha.2",
|
||||
run(store) {
|
||||
const rawHotbars = store.get("hotbars");
|
||||
const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars : [];
|
||||
|
||||
store.set("hotbars", hotbars.map(({ id, ...rest }) => ({
|
||||
id: id || uuid.v4(),
|
||||
...rest,
|
||||
})));
|
||||
},
|
||||
}),
|
||||
injectionToken: hotbarStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v500Alpha2HotbarStoreMigrationInjectable;
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import * as uuid from "uuid";
|
||||
import type { ClusterStoreModel } from "../../../common/cluster-store/cluster-store";
|
||||
import type { Hotbar, HotbarItem } from "../../../common/hotbars/types";
|
||||
import { defaultHotbarCells, getEmptyHotbar } from "../../../common/hotbars/types";
|
||||
import { getLegacyGlobalDiForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import catalogCatalogEntityInjectable from "../../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
|
||||
import { isDefined, isErrnoException } from "../../../common/utils";
|
||||
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { hotbarStoreMigrationInjectionToken } from "../../../common/hotbars/migrations-token";
|
||||
import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import { generateNewIdFor } from "../../../common/utils/generate-new-id-for";
|
||||
|
||||
interface Pre500WorkspaceStoreModel {
|
||||
workspaces: {
|
||||
id: string;
|
||||
name: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
interface PartialHotbar {
|
||||
id: string;
|
||||
name: string;
|
||||
items: (null | HotbarItem)[];
|
||||
}
|
||||
|
||||
const v500Beta10HotbarStoreMigrationInjectable = getInjectable({
|
||||
id: "v5.0.0-beta.10-hotbar-store-migration",
|
||||
instantiate: (di) => {
|
||||
const userDataPath = di.inject(directoryForUserDataInjectable);
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const readJsonSync = di.inject(readJsonSyncInjectable);
|
||||
const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
return {
|
||||
version: "5.0.0-beta.10",
|
||||
run(store) {
|
||||
const rawHotbars = store.get("hotbars");
|
||||
const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars.filter(h => h && typeof h === "object") : [];
|
||||
|
||||
|
||||
// Hotbars might be empty, if some of the previous migrations weren't run
|
||||
if (hotbars.length === 0) {
|
||||
const hotbar = getEmptyHotbar("default");
|
||||
const { metadata: { uid, name, source }} = catalogCatalogEntity;
|
||||
|
||||
hotbar.items[0] = { entity: { uid, name, source }};
|
||||
|
||||
hotbars.push(hotbar);
|
||||
}
|
||||
|
||||
try {
|
||||
const workspaceStoreData: Pre500WorkspaceStoreModel = readJsonSync(joinPaths(userDataPath, "lens-workspace-store.json"));
|
||||
const { clusters = [] }: ClusterStoreModel = readJsonSync(joinPaths(userDataPath, "lens-cluster-store.json"));
|
||||
const workspaceHotbars = new Map<string, PartialHotbar>(); // mapping from WorkspaceId to HotBar
|
||||
|
||||
for (const { id, name } of workspaceStoreData.workspaces) {
|
||||
logger.info(`Creating new hotbar for ${name}`);
|
||||
workspaceHotbars.set(id, {
|
||||
id: uuid.v4(), // don't use the old IDs as they aren't necessarily UUIDs
|
||||
items: [],
|
||||
name: `Workspace: ${name}`,
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
// grab the default named hotbar or the first.
|
||||
const defaultHotbarIndex = Math.max(0, hotbars.findIndex(hotbar => hotbar.name === "default"));
|
||||
const [{ name, id, items }] = hotbars.splice(defaultHotbarIndex, 1);
|
||||
|
||||
workspaceHotbars.set("default", {
|
||||
name,
|
||||
id,
|
||||
items: items.filter(isDefined),
|
||||
});
|
||||
}
|
||||
|
||||
for (const cluster of clusters) {
|
||||
const uid = generateNewIdFor(cluster);
|
||||
|
||||
for (const workspaceId of cluster.workspaces ?? [cluster.workspace].filter(isDefined)) {
|
||||
const workspaceHotbar = workspaceHotbars.get(workspaceId);
|
||||
|
||||
if (!workspaceHotbar) {
|
||||
logger.info(`Cluster ${uid} has unknown workspace ID, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.info(`Adding cluster ${uid} to ${workspaceHotbar.name}`);
|
||||
|
||||
if (workspaceHotbar?.items.length < defaultHotbarCells) {
|
||||
workspaceHotbar.items.push({
|
||||
entity: {
|
||||
uid: generateNewIdFor(cluster),
|
||||
name: cluster.preferences?.clusterName || cluster.contextName,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const hotbar of workspaceHotbars.values()) {
|
||||
if (hotbar.items.length === 0) {
|
||||
logger.info(`Skipping ${hotbar.name} due to it being empty`);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (hotbar.items.length < defaultHotbarCells) {
|
||||
hotbar.items.push(null);
|
||||
}
|
||||
|
||||
hotbars.push(hotbar as Hotbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finally, make sure that the catalog entity hotbar item is in place.
|
||||
* Just in case something else removed it.
|
||||
*
|
||||
* if every hotbar has elements that all not the `catalog-entity` item
|
||||
*/
|
||||
if (hotbars.every(hotbar => hotbar.items.every(item => item?.entity?.uid !== "catalog-entity"))) {
|
||||
// note, we will add a new whole hotbar here called "default" if that was previously removed
|
||||
const di = getLegacyGlobalDiForExtensionApi();
|
||||
const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable);
|
||||
|
||||
const defaultHotbar = hotbars.find(hotbar => hotbar.name === "default");
|
||||
const { metadata: { uid, name, source }} = catalogCatalogEntity;
|
||||
|
||||
if (defaultHotbar) {
|
||||
const freeIndex = defaultHotbar.items.findIndex(i => i === null);
|
||||
|
||||
if (freeIndex === -1) {
|
||||
// making a new hotbar is less destructive if the first hotbar
|
||||
// called "default" is full than overriding a hotbar item
|
||||
const hotbar = getEmptyHotbar("initial");
|
||||
|
||||
hotbar.items[0] = { entity: { uid, name, source }};
|
||||
hotbars.unshift(hotbar);
|
||||
} else {
|
||||
defaultHotbar.items[freeIndex] = { entity: { uid, name, source }};
|
||||
}
|
||||
} else {
|
||||
const hotbar = getEmptyHotbar("default");
|
||||
|
||||
hotbar.items[0] = { entity: { uid, name, source }};
|
||||
hotbars.unshift(hotbar);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// ignore files being missing
|
||||
if (isErrnoException(error) && error.code !== "ENOENT") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
store.set("hotbars", hotbars);
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: hotbarStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v500Beta10HotbarStoreMigrationInjectable;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { Hotbar } from "../../../common/hotbars/types";
|
||||
import catalogEntityRegistryInjectable from "../../catalog/entity-registry.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { hotbarStoreMigrationInjectionToken } from "../../../common/hotbars/migrations-token";
|
||||
|
||||
const v500Beta5HotbarStoreMigrationInjectable = getInjectable({
|
||||
id: "v500-beta5-hotbar-store-migration",
|
||||
instantiate: (di) => {
|
||||
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
|
||||
return {
|
||||
version: "5.0.0-beta.5",
|
||||
run(store) {
|
||||
const rawHotbars = store.get("hotbars");
|
||||
const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars : [];
|
||||
|
||||
for (const hotbar of hotbars) {
|
||||
for (let i = 0; i < hotbar.items.length; i += 1) {
|
||||
const item = hotbar.items[i];
|
||||
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const entity = catalogEntityRegistry.findById(item.entity.uid);
|
||||
|
||||
if (!entity) {
|
||||
// Clear disabled item
|
||||
hotbar.items[i] = null;
|
||||
} else {
|
||||
// Save additional data
|
||||
item.entity = {
|
||||
...item.entity,
|
||||
name: entity.metadata.name,
|
||||
source: entity.metadata.source,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
store.set("hotbars", hotbars);
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: hotbarStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v500Beta5HotbarStoreMigrationInjectable;
|
||||
|
||||
+10
-1
@@ -7,14 +7,23 @@
|
||||
|
||||
import * as Mobx from "mobx";
|
||||
import { spawn } from "node-pty";
|
||||
import process from "process";
|
||||
import * as LensExtensionsCommonApi from "../extensions/common-api";
|
||||
import * as LensExtensionsMainApi from "../extensions/main-api";
|
||||
import { getDi } from "./getDi";
|
||||
import startMainApplicationInjectable from "./start-main-application/start-main-application.injectable";
|
||||
|
||||
const di = getDi();
|
||||
const startMainApplication = di.inject(startMainApplicationInjectable);
|
||||
|
||||
void di.inject(startMainApplicationInjectable);
|
||||
(async () => {
|
||||
try {
|
||||
await startMainApplication();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* Exports for virtual package "@k8slens/extensions" for main-process.
|
||||
|
||||
@@ -3,34 +3,41 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { OpenDialogOptions } from "electron";
|
||||
import { dialog } from "electron";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.injectable";
|
||||
import type { RequestChannelHandler } from "../utils/channel/channel-listeners/listener-tokens";
|
||||
import type { openPathPickingDialogChannel } from "../../features/path-picking-dialog/common/channel";
|
||||
import showOpenDialogInjectable from "../electron-app/features/show-open-dialog.injectable";
|
||||
|
||||
// TODO: Replace leaking electron with abstraction
|
||||
export type AskUserForFilePaths = (
|
||||
dialogOptions: OpenDialogOptions
|
||||
) => Promise<{ canceled: boolean; filePaths: string[] }>;
|
||||
export type AskUserForFilePaths = RequestChannelHandler<typeof openPathPickingDialogChannel>;
|
||||
|
||||
const askUserForFilePathsInjectable = getInjectable({
|
||||
id: "ask-user-for-file-paths",
|
||||
|
||||
instantiate: (di): AskUserForFilePaths => {
|
||||
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
||||
const showOpenDialog = di.inject(showOpenDialogInjectable);
|
||||
|
||||
return async (dialogOptions) => {
|
||||
await showApplicationWindow();
|
||||
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog(
|
||||
const { canceled, filePaths } = await showOpenDialog(
|
||||
dialogOptions,
|
||||
);
|
||||
|
||||
return { canceled, filePaths };
|
||||
if (canceled) {
|
||||
return {
|
||||
canceled,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
canceled: false,
|
||||
paths: filePaths,
|
||||
};
|
||||
};
|
||||
},
|
||||
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default askUserForFilePathsInjectable;
|
||||
@@ -11,9 +11,9 @@ import loggerInjectable from "../../common/logger.injectable";
|
||||
import lensProxyPortInjectable from "../lens-proxy/lens-proxy-port.injectable";
|
||||
import joinPathsInjectable from "../../common/path/join-paths.injectable";
|
||||
import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable";
|
||||
import deleteFileInjectable from "../../common/fs/delete-file.injectable";
|
||||
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
||||
import writeFileInjectable from "../../common/fs/write-file.injectable";
|
||||
import removePathInjectable from "../../common/fs/remove.injectable";
|
||||
|
||||
export interface KubeConfigManagerInstantiationParameter {
|
||||
cluster: Cluster;
|
||||
@@ -31,7 +31,7 @@ const createKubeconfigManagerInjectable = getInjectable({
|
||||
lensProxyPort: di.inject(lensProxyPortInjectable),
|
||||
joinPaths: di.inject(joinPathsInjectable),
|
||||
getDirnameOfPath: di.inject(getDirnameOfPathInjectable),
|
||||
deleteFile: di.inject(deleteFileInjectable),
|
||||
removePath: di.inject(removePathInjectable),
|
||||
pathExists: di.inject(pathExistsInjectable),
|
||||
writeFile: di.inject(writeFileInjectable),
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ import type { Logger } from "../../common/logger";
|
||||
import type { JoinPaths } from "../../common/path/join-paths.injectable";
|
||||
import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable";
|
||||
import type { PathExists } from "../../common/fs/path-exists.injectable";
|
||||
import type { DeleteFile } from "../../common/fs/delete-file.injectable";
|
||||
import type { RemovePath } from "../../common/fs/remove.injectable";
|
||||
import type { WriteFile } from "../../common/fs/write-file.injectable";
|
||||
|
||||
export interface KubeconfigManagerDependencies {
|
||||
@@ -23,7 +23,7 @@ export interface KubeconfigManagerDependencies {
|
||||
joinPaths: JoinPaths;
|
||||
getDirnameOfPath: GetDirnameOfPath;
|
||||
pathExists: PathExists;
|
||||
deleteFile: DeleteFile;
|
||||
removePath: RemovePath;
|
||||
writeFile: WriteFile;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export class KubeconfigManager {
|
||||
this.dependencies.logger.info(`[KUBECONFIG-MANAGER]: Deleting temporary kubeconfig: ${this.tempFilePath}`);
|
||||
|
||||
try {
|
||||
await this.dependencies.deleteFile(this.tempFilePath);
|
||||
await this.dependencies.removePath(this.tempFilePath);
|
||||
} catch (error) {
|
||||
if (isErrnoException(error) && error.code !== "ENOENT") {
|
||||
throw error;
|
||||
|
||||
@@ -19,6 +19,10 @@ import type { ObservableMap } from "mobx";
|
||||
import extensionInstancesInjectable from "../../../extensions/extension-loader/extension-instances.injectable";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import broadcastMessageInjectable from "../../../common/ipc/broadcast-message.injectable";
|
||||
import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
|
||||
import pathExistsInjectable from "../../../common/fs/path-exists.injectable";
|
||||
import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable";
|
||||
import writeJsonSyncInjectable from "../../../common/fs/write-json-sync.injectable";
|
||||
|
||||
function throwIfDefined(val: any): void {
|
||||
if (val != null) {
|
||||
@@ -35,6 +39,11 @@ describe("protocol router tests", () => {
|
||||
beforeEach(async () => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
di.override(pathExistsInjectable, () => () => { throw new Error("tried call pathExists without override"); });
|
||||
di.override(pathExistsSyncInjectable, () => () => { throw new Error("tried call pathExistsSync without override"); });
|
||||
di.override(readJsonSyncInjectable, () => () => { throw new Error("tried call readJsonSync without override"); });
|
||||
di.override(writeJsonSyncInjectable, () => () => { throw new Error("tried call writeJsonSync without override"); });
|
||||
|
||||
enabledExtensions = new Set();
|
||||
|
||||
di.override(extensionsStoreInjectable, () => ({
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import deleteFileInjectable from "../../common/fs/delete-file.injectable";
|
||||
import removePathInjectable from "../../common/fs/remove.injectable";
|
||||
import execFileInjectable from "../../common/fs/exec-file.injectable";
|
||||
import writeFileInjectable from "../../common/fs/write-file.injectable";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
@@ -19,7 +19,7 @@ const createResourceApplierInjectable = getInjectable({
|
||||
id: "create-resource-applier",
|
||||
instantiate: (di): CreateResourceApplier => {
|
||||
const deps: ResourceApplierDependencies = {
|
||||
deleteFile: di.inject(deleteFileInjectable),
|
||||
deleteFile: di.inject(removePathInjectable),
|
||||
emitAppEvent: di.inject(emitAppEventInjectable),
|
||||
execFile: di.inject(execFileInjectable),
|
||||
joinPaths: di.inject(joinPathsInjectable),
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { KubernetesObject } from "@kubernetes/client-node";
|
||||
import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injectable";
|
||||
import type { Logger } from "../../common/logger";
|
||||
import type { WriteFile } from "../../common/fs/write-file.injectable";
|
||||
import type { DeleteFile } from "../../common/fs/delete-file.injectable";
|
||||
import type { RemovePath } from "../../common/fs/remove.injectable";
|
||||
import type { ExecFile } from "../../common/fs/exec-file.injectable";
|
||||
import type { JoinPaths } from "../../common/path/join-paths.injectable";
|
||||
import type { AsyncResult } from "../../common/utils/async-result";
|
||||
@@ -19,7 +19,7 @@ import type { AsyncResult } from "../../common/utils/async-result";
|
||||
export interface ResourceApplierDependencies {
|
||||
emitAppEvent: EmitAppEvent;
|
||||
writeFile: WriteFile;
|
||||
deleteFile: DeleteFile;
|
||||
deleteFile: RemovePath;
|
||||
execFile: ExecFile;
|
||||
joinPaths: JoinPaths;
|
||||
readonly logger: Logger;
|
||||
|
||||
@@ -23,7 +23,7 @@ import userShellSettingInjectable from "../../../common/user-store/shell-setting
|
||||
import appNameInjectable from "../../../common/vars/app-name.injectable";
|
||||
import buildVersionInjectable from "../../vars/build-version/build-version.injectable";
|
||||
import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable";
|
||||
import statInjectable from "../../../common/fs/stat/stat.injectable";
|
||||
import statInjectable from "../../../common/fs/stat.injectable";
|
||||
|
||||
export interface OpenLocalShellSessionArgs {
|
||||
websocket: WebSocket;
|
||||
|
||||
@@ -7,6 +7,11 @@ import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import { WebSocket } from "ws";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
|
||||
import pathExistsInjectable from "../../../common/fs/path-exists.injectable";
|
||||
import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable";
|
||||
import statInjectable from "../../../common/fs/stat.injectable";
|
||||
import writeJsonSyncInjectable from "../../../common/fs/write-json-sync.injectable";
|
||||
import platformInjectable from "../../../common/vars/platform.injectable";
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import createKubectlInjectable from "../../kubectl/create-kubectl.injectable";
|
||||
@@ -29,6 +34,11 @@ describe("technical unit tests for local shell sessions", () => {
|
||||
di.override(buildVersionInjectable, () => ({
|
||||
get: () => "1.1.1",
|
||||
}));
|
||||
di.override(pathExistsInjectable, () => () => { throw new Error("tried call pathExists without override"); });
|
||||
di.override(pathExistsSyncInjectable, () => () => { throw new Error("tried call pathExistsSync without override"); });
|
||||
di.override(readJsonSyncInjectable, () => () => { throw new Error("tried call readJsonSync without override"); });
|
||||
di.override(writeJsonSyncInjectable, () => () => { throw new Error("tried call writeJsonSync without override"); });
|
||||
di.override(statInjectable, () => () => { throw new Error("tried call stat without override"); });
|
||||
});
|
||||
|
||||
describe("when on windows", () => {
|
||||
|
||||
@@ -18,7 +18,7 @@ import userShellSettingInjectable from "../../../common/user-store/shell-setting
|
||||
import appNameInjectable from "../../../common/vars/app-name.injectable";
|
||||
import buildVersionInjectable from "../../vars/build-version/build-version.injectable";
|
||||
import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable";
|
||||
import statInjectable from "../../../common/fs/stat/stat.injectable";
|
||||
import statInjectable from "../../../common/fs/stat.injectable";
|
||||
|
||||
export interface NodeShellSessionArgs {
|
||||
websocket: WebSocket;
|
||||
|
||||
@@ -17,7 +17,7 @@ import type { ComputeShellEnvironment } from "../../features/shell-sync/main/com
|
||||
import type { SpawnPty } from "./spawn-pty.injectable";
|
||||
import type { InitializableState } from "../../common/initializable-state/create";
|
||||
import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injectable";
|
||||
import type { Stat } from "../../common/fs/stat/stat.injectable";
|
||||
import type { Stat } from "../../common/fs/stat.injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
|
||||
export class ShellOpenError extends Error {
|
||||
|
||||
+3
-2
@@ -12,8 +12,8 @@ import openLinkInBrowserInjectable from "../../../../common/utils/open-link-in-b
|
||||
import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path.injectable";
|
||||
import lensResourcesDirInjectable from "../../../../common/vars/lens-resources-dir.injectable";
|
||||
import isLinuxInjectable from "../../../../common/vars/is-linux.injectable";
|
||||
import fsInjectable from "../../../../common/fs/fs.injectable";
|
||||
import applicationInformationInjectable from "../../../../common/vars/application-information.injectable";
|
||||
import pathExistsSyncInjectable from "../../../../common/fs/path-exists-sync.injectable";
|
||||
|
||||
|
||||
export type ElectronWindowTitleBarStyle = "hiddenInset" | "hidden" | "default" | "customButtonsOnHover";
|
||||
@@ -55,6 +55,7 @@ const createElectronWindowInjectable = getInjectable({
|
||||
const lensResourcesDir = di.inject(lensResourcesDirInjectable);
|
||||
const isLinux = di.inject(isLinuxInjectable);
|
||||
const applicationInformation = di.inject(applicationInformationInjectable);
|
||||
const pathExistsSync = di.inject(pathExistsSyncInjectable);
|
||||
|
||||
return (configuration) => {
|
||||
const applicationWindowState = di.inject(
|
||||
@@ -93,7 +94,7 @@ const createElectronWindowInjectable = getInjectable({
|
||||
const iconFileName = [
|
||||
getAbsolutePath(lensResourcesDir, `../${applicationInformation.name}.png`),
|
||||
`/usr/share/icons/hicolor/512x512/apps/${applicationInformation.name}.png`,
|
||||
].find(di.inject(fsInjectable).existsSync);
|
||||
].find(pathExistsSync);
|
||||
|
||||
if (iconFileName != null) {
|
||||
try {
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { currentClusterMessageChannel } from "../../../../common/cluster/current-cluster-channel";
|
||||
import { getMessageChannelListenerInjectable } from "../../../../common/utils/channel/message-channel-listener-injection-token";
|
||||
import currentClusterFrameClusterIdStateInjectable from "./current-cluster-frame-cluster-id-state.injectable";
|
||||
|
||||
const currentVisibileClusterListenerInjectable = getMessageChannelListenerInjectable({
|
||||
id: "current-visibile-cluster",
|
||||
channel: currentClusterMessageChannel,
|
||||
handler: (di) => {
|
||||
const currentClusterFrameState = di.inject(currentClusterFrameClusterIdStateInjectable);
|
||||
|
||||
return clusterId => currentClusterFrameState.set(clusterId);
|
||||
},
|
||||
});
|
||||
|
||||
export default currentVisibileClusterListenerInjectable;
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { ipcMainOn } from "../../../../common/ipc";
|
||||
import { IpcRendererNavigationEvents } from "../../../../renderer/navigation/events";
|
||||
import type { ClusterId } from "../../../../common/cluster-types";
|
||||
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../../runnable-tokens/on-load-of-application-injection-token";
|
||||
import currentClusterFrameClusterIdStateInjectable from "./current-cluster-frame-cluster-id-state.injectable";
|
||||
|
||||
const setupListenerForCurrentClusterFrameInjectable = getInjectable({
|
||||
id: "setup-listener-for-current-cluster-frame",
|
||||
|
||||
instantiate: (di) => ({
|
||||
id: "setup-listener-for-current-cluster-frame",
|
||||
run: () => {
|
||||
const currentClusterFrameState = di.inject(currentClusterFrameClusterIdStateInjectable);
|
||||
|
||||
ipcMainOn(
|
||||
IpcRendererNavigationEvents.CLUSTER_VIEW_CURRENT_ID,
|
||||
(event, clusterId: ClusterId) => {
|
||||
currentClusterFrameState.set(clusterId);
|
||||
},
|
||||
);
|
||||
},
|
||||
}),
|
||||
|
||||
causesSideEffects: true,
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default setupListenerForCurrentClusterFrameInjectable;
|
||||
@@ -8,6 +8,8 @@ import clusterFramesInjectable from "../../../common/cluster-frames.injectable";
|
||||
import showApplicationWindowInjectable from "./show-application-window.injectable";
|
||||
import getCurrentApplicationWindowInjectable from "./application-window/get-current-application-window.injectable";
|
||||
import assert from "assert";
|
||||
import type { NavigateForExtensionArgs } from "../../../features/extensions/navigate/common/channel";
|
||||
import { navigateForExtensionChannel } from "../../../features/extensions/navigate/common/channel";
|
||||
|
||||
export type NavigateForExtension = (
|
||||
extId: string,
|
||||
@@ -42,9 +44,9 @@ const navigateForExtensionInjectable = getInjectable({
|
||||
);
|
||||
|
||||
applicationWindow.send({
|
||||
channel: "extension:navigate",
|
||||
channel: navigateForExtensionChannel.id,
|
||||
frameInfo,
|
||||
data: [extId, pageId, params],
|
||||
data: { extId, pageId, params } as NavigateForExtensionArgs,
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* 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 setupSyncingOfGeneralCatalogEntitiesInjectable from "./setup-syncing-of-general-catalog-entities.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../runnable-tokens/on-load-of-application-injection-token";
|
||||
import hotbarStoreInjectable from "../../../common/hotbars/store.injectable";
|
||||
|
||||
const setupHotbarStoreInjectable = getInjectable({
|
||||
id: "setup-hotbar-store",
|
||||
|
||||
instantiate: (di) => ({
|
||||
id: "setup-hotbar-store",
|
||||
run: () => {
|
||||
const hotbarStore = di.inject(hotbarStoreInjectable);
|
||||
|
||||
hotbarStore.load();
|
||||
},
|
||||
|
||||
runAfter: di.inject(setupSyncingOfGeneralCatalogEntitiesInjectable),
|
||||
}),
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default setupHotbarStoreInjectable;
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* 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 userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../runnable-tokens/on-load-of-application-injection-token";
|
||||
|
||||
const setupReactionsInUserStoreInjectable = getInjectable({
|
||||
id: "setup-reactions-in-user-store",
|
||||
|
||||
instantiate: (di) => {
|
||||
const userStore = di.inject(userStoreInjectable);
|
||||
|
||||
return {
|
||||
id: "setup-reactions-in-user-store",
|
||||
run: () => {
|
||||
userStore.startMainReactions();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default setupReactionsInUserStoreInjectable;
|
||||
@@ -29,19 +29,21 @@ const startMainApplicationInjectable = getInjectable({
|
||||
const showInitialWindowRunnablePhase = runMany(showInitialWindowRunnablePhaseInjectionToken);
|
||||
const afterApplicationIsLoaded = runMany(afterApplicationIsLoadedInjectionToken);
|
||||
|
||||
// Stuff happening before application is ready needs to be synchronous because of
|
||||
// https://github.com/electron/electron/issues/21370
|
||||
appPathsRunnablePhase();
|
||||
beforeElectronIsReady();
|
||||
return () => {
|
||||
// Stuff happening before application is ready needs to be synchronous because of
|
||||
// https://github.com/electron/electron/issues/21370
|
||||
appPathsRunnablePhase();
|
||||
beforeElectronIsReady();
|
||||
|
||||
return (async () => {
|
||||
await waitForElectronToBeReady();
|
||||
await beforeApplicationIsLoading();
|
||||
await showLoadingRunnablePhase();
|
||||
await onLoadOfApplication();
|
||||
await showInitialWindowRunnablePhase();
|
||||
await afterApplicationIsLoaded();
|
||||
})();
|
||||
return (async () => {
|
||||
await waitForElectronToBeReady();
|
||||
await beforeApplicationIsLoading();
|
||||
await showLoadingRunnablePhase();
|
||||
await onLoadOfApplication();
|
||||
await showInitialWindowRunnablePhase();
|
||||
await afterApplicationIsLoaded();
|
||||
})();
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* 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 { reaction } from "mobx";
|
||||
import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
|
||||
import { setNativeThemeChannel } from "../../../common/ipc/native-theme";
|
||||
import operatingSystemThemeInjectable from "../operating-system-theme.injectable";
|
||||
import broadcastMessageInjectable from "../../../common/ipc/broadcast-message.injectable";
|
||||
|
||||
const broadcastThemeChangeInjectable = getInjectable({
|
||||
id: "broadcast-theme-change",
|
||||
|
||||
instantiate: (di) => {
|
||||
const currentTheme = di.inject(operatingSystemThemeInjectable);
|
||||
const broadcastMessage = di.inject(broadcastMessageInjectable);
|
||||
|
||||
return getStartableStoppable("broadcast-theme-change", () =>
|
||||
reaction(() => currentTheme.get(), (theme) => {
|
||||
broadcastMessage(setNativeThemeChannel, theme);
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default broadcastThemeChangeInjectable;
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* 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 { onLoadOfApplicationInjectionToken } from "../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
import broadcastThemeChangeInjectable from "./broadcast-theme-change.injectable";
|
||||
|
||||
const startBroadcastingThemeChangeInjectable = getInjectable({
|
||||
id: "start-broadcasting-theme-change",
|
||||
|
||||
instantiate: (di) => {
|
||||
const broadcastThemeChange = di.inject(broadcastThemeChangeInjectable);
|
||||
|
||||
return {
|
||||
id: "start-broadcasting-theme-change",
|
||||
run: async () => {
|
||||
await broadcastThemeChange.start();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default startBroadcastingThemeChangeInjectable;
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 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 broadcastThemeChangeInjectable from "./broadcast-theme-change.injectable";
|
||||
import { beforeQuitOfBackEndInjectionToken } from "../../start-main-application/runnable-tokens/before-quit-of-back-end-injection-token";
|
||||
|
||||
const stopBroadcastingThemeChangeInjectable = getInjectable({
|
||||
id: "stop-broadcasting-theme-change",
|
||||
|
||||
instantiate: (di) => {
|
||||
const broadcastThemeChange = di.inject(broadcastThemeChangeInjectable);
|
||||
|
||||
return {
|
||||
id: "stop-broadcasting-theme-change",
|
||||
run: () => void broadcastThemeChange.stop(),
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeQuitOfBackEndInjectionToken,
|
||||
});
|
||||
|
||||
export default stopBroadcastingThemeChangeInjectable;
|
||||
+2
-2
@@ -14,8 +14,8 @@ const startSyncingThemeFromOperatingSystemInjectable = getInjectable({
|
||||
|
||||
return {
|
||||
id: "start-syncing-theme-from-operating-system",
|
||||
run: async () => {
|
||||
await syncTheme.start();
|
||||
run: () => {
|
||||
syncTheme.start();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -15,8 +15,8 @@ const startReactiveTrayMenuIconInjectable = getInjectable({
|
||||
|
||||
return {
|
||||
id: "start-reactive-tray-menu-icon",
|
||||
run: async () => {
|
||||
await reactiveTrayMenuIcon.start();
|
||||
run: () => {
|
||||
reactiveTrayMenuIcon.start();
|
||||
},
|
||||
|
||||
runAfter: di.inject(startTrayInjectable),
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// Switch representation of hiddenTableColumns in store
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { userStoreMigrationInjectionToken } from "../../../common/user-store/migrations-token";
|
||||
|
||||
interface PreV500Alpha3UserPreferencesModel {
|
||||
hiddenTableColumns?: Record<string, string[]>;
|
||||
}
|
||||
|
||||
const v500Alpha3UserStoreMigrationInjectable = getInjectable({
|
||||
id: "v5.0.0-alpha.3-user-store-migration",
|
||||
instantiate: () => ({
|
||||
version: "5.0.0-alpha.3",
|
||||
run(store) {
|
||||
const preferences = (store.get("preferences") ?? {}) as PreV500Alpha3UserPreferencesModel;
|
||||
const oldHiddenTableColumns = preferences.hiddenTableColumns;
|
||||
|
||||
if (!oldHiddenTableColumns) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.set("preferences", {
|
||||
...preferences,
|
||||
hiddenTableColumns: Object.entries(oldHiddenTableColumns),
|
||||
});
|
||||
},
|
||||
}),
|
||||
injectionToken: userStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v500Alpha3UserStoreMigrationInjectable;
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { ClusterStoreModel } from "../../../common/cluster-store/cluster-store";
|
||||
import type { KubeconfigSyncEntry, UserPreferencesModel } from "../../../common/user-store";
|
||||
import { isErrnoException } from "../../../common/utils";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
|
||||
import isLogicalChildPathInjectable from "../../../common/path/is-logical-child-path.injectable";
|
||||
import getDirnameOfPathInjectable from "../../../common/path/get-dirname.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { userStoreMigrationInjectionToken } from "../../../common/user-store/migrations-token";
|
||||
import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable";
|
||||
import homeDirectoryPathInjectable from "../../../common/os/home-directory-path.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
|
||||
|
||||
const v503Beta1UserStoreMigrationInjectable = getInjectable({
|
||||
id: "v5.0.3-beta.1-user-store-migration",
|
||||
instantiate: (di) => {
|
||||
const userDataPath = di.inject(directoryForUserDataInjectable);
|
||||
const kubeConfigsPath = di.inject(directoryForKubeConfigsInjectable);
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const isLogicalChildPath = di.inject(isLogicalChildPathInjectable);
|
||||
const getDirnameOfPath = di.inject(getDirnameOfPathInjectable);
|
||||
const readJsonSync = di.inject(readJsonSyncInjectable);
|
||||
const homeDirectoryPath = di.inject(homeDirectoryPathInjectable);
|
||||
const pathExistsSync = di.inject(pathExistsSyncInjectable);
|
||||
|
||||
return {
|
||||
version: "5.0.3-beta.1",
|
||||
run(store) {
|
||||
try {
|
||||
const { syncKubeconfigEntries = [], ...preferences } = (store.get("preferences") ?? {}) as UserPreferencesModel;
|
||||
const { clusters = [] }: ClusterStoreModel = readJsonSync(joinPaths(userDataPath, "lens-cluster-store.json"), "utf-8") ?? {};
|
||||
const extensionDataDir = joinPaths(userDataPath, "extension_data");
|
||||
const syncPaths = new Set(syncKubeconfigEntries.map(s => s.filePath));
|
||||
|
||||
syncPaths.add(joinPaths(homeDirectoryPath, ".kube"));
|
||||
|
||||
for (const cluster of clusters) {
|
||||
if (!cluster.kubeConfigPath) {
|
||||
continue;
|
||||
}
|
||||
const dirOfKubeconfig = getDirnameOfPath(cluster.kubeConfigPath);
|
||||
|
||||
if (dirOfKubeconfig === kubeConfigsPath) {
|
||||
logger.info(`Skipping ${cluster.id} because kubeConfigPath is under the stored KubeConfig folder`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (syncPaths.has(cluster.kubeConfigPath) || syncPaths.has(dirOfKubeconfig)) {
|
||||
logger.info(`Skipping ${cluster.id} because kubeConfigPath is already being synced`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isLogicalChildPath(extensionDataDir, cluster.kubeConfigPath)) {
|
||||
logger.info(`Skipping ${cluster.id} because kubeConfigPath is placed under an extension_data folder`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pathExistsSync(cluster.kubeConfigPath)) {
|
||||
logger.info(`Skipping ${cluster.id} because kubeConfigPath no longer exists`);
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.info(`Adding ${cluster.kubeConfigPath} from ${cluster.id} to sync paths`);
|
||||
syncPaths.add(cluster.kubeConfigPath);
|
||||
}
|
||||
|
||||
const updatedSyncEntries: KubeconfigSyncEntry[] = [...syncPaths].map(filePath => ({ filePath }));
|
||||
|
||||
logger.info("Final list of synced paths", updatedSyncEntries);
|
||||
store.set("preferences", { ...preferences, syncKubeconfigEntries: updatedSyncEntries });
|
||||
} catch (error) {
|
||||
if (isErrnoException(error) && error.code !== "ENOENT") {
|
||||
// ignore files being missing
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: userStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v503Beta1UserStoreMigrationInjectable;
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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 { reaction } from "mobx";
|
||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||
import setLoginItemSettingsInjectable from "../electron-app/features/set-login-item-settings.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
|
||||
const setupSyncOpenAtLoginWithOsInjectable = getInjectable({
|
||||
id: "setup-sync-open-at-login-with-os",
|
||||
instantiate: (di) => {
|
||||
const setLoginItemSettings = di.inject(setLoginItemSettingsInjectable);
|
||||
const userStore = di.inject(userStoreInjectable);
|
||||
|
||||
return {
|
||||
id: "setup-sync-open-at-login-with-os",
|
||||
run: () => {
|
||||
reaction(() => userStore.openAtLogin, openAtLogin => {
|
||||
setLoginItemSettings({
|
||||
openAtLogin,
|
||||
openAsHidden: true,
|
||||
args: ["--hidden"],
|
||||
});
|
||||
}, {
|
||||
fireImmediately: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default setupSyncOpenAtLoginWithOsInjectable;
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
|
||||
export const lensWebsiteWeblinkId = "lens-website-link";
|
||||
export const lensWebsiteLinkName = "Lens Website";
|
||||
|
||||
export const lensDocumentationWeblinkId = "lens-documentation-link";
|
||||
export const lensDocumentationWeblinkName = "Lens Documentation";
|
||||
|
||||
export const lensSlackWeblinkId = "lens-slack-link";
|
||||
export const lensSlackWeblinkName = "Lens Community Slack";
|
||||
|
||||
export const lensTwitterWeblinkId = "lens-twitter-link";
|
||||
export const lensTwitterWeblinkName = "Lens on Twitter";
|
||||
|
||||
export const lensBlogWeblinkId = "lens-blog-link";
|
||||
export const lensBlogWeblinkName = "Lens Official Blog";
|
||||
|
||||
export const kubernetesDocumentationWeblinkId = "kubernetes-documentation-link";
|
||||
export const kubernetesDocumentationWeblinkName = "Kubernetes Documentation";
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { docsUrl, slackUrl } from "../../../common/vars";
|
||||
import type { WeblinkData } from "../../../common/weblinks-store/weblink-store";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { weblinkStoreMigrationInjectionToken } from "../../../common/weblinks-store/migration-token";
|
||||
import * as links from "../links";
|
||||
|
||||
const v514WeblinkStoreMigrationInjectable = getInjectable({
|
||||
id: "v5.1.4-weblink-store-migration",
|
||||
instantiate: () => ({
|
||||
version: "5.1.4",
|
||||
run(store) {
|
||||
const weblinksRaw = store.get("weblinks");
|
||||
const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[];
|
||||
|
||||
weblinks.push(
|
||||
{ id: "https://k8slens.dev", name: links.lensWebsiteLinkName, url: "https://k8slens.dev" },
|
||||
{ id: docsUrl, name: links.lensDocumentationWeblinkName, url: docsUrl },
|
||||
{ id: slackUrl, name: links.lensSlackWeblinkName, url: slackUrl },
|
||||
{ id: "https://twitter.com/k8slens", name: links.lensTwitterWeblinkName, url: "https://twitter.com/k8slens" },
|
||||
{ id: "https://medium.com/k8slens", name: links.lensBlogWeblinkName, url: "https://medium.com/k8slens" },
|
||||
{ id: "https://kubernetes.io/docs/home/", name: links.kubernetesDocumentationWeblinkName, url: "https://kubernetes.io/docs/home/" },
|
||||
);
|
||||
|
||||
store.set("weblinks", weblinks);
|
||||
},
|
||||
}),
|
||||
injectionToken: weblinkStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v514WeblinkStoreMigrationInjectable;
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { WeblinkData } from "../../../common/weblinks-store/weblink-store";
|
||||
import * as links from "../links";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { weblinkStoreMigrationInjectionToken } from "../../../common/weblinks-store/migration-token";
|
||||
|
||||
const v545Beta1WeblinkStoreMigrationInjectable = getInjectable({
|
||||
id: "v5.4.5-beta.1-weblink-store-migration",
|
||||
instantiate: () => ({
|
||||
version: "5.4.5-beta.1 || >=5.5.0-alpha.0",
|
||||
run(store) {
|
||||
const weblinksRaw = store.get("weblinks");
|
||||
const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[];
|
||||
|
||||
const lensWebsiteLink = weblinks.find(weblink => weblink.name === links.lensWebsiteLinkName);
|
||||
|
||||
if (lensWebsiteLink) {
|
||||
lensWebsiteLink.id = links.lensWebsiteWeblinkId;
|
||||
}
|
||||
|
||||
const lensDocumentationWeblinkLink = weblinks.find(weblink => weblink.name === links.lensDocumentationWeblinkName);
|
||||
|
||||
if (lensDocumentationWeblinkLink) {
|
||||
lensDocumentationWeblinkLink.id = links.lensDocumentationWeblinkId;
|
||||
}
|
||||
|
||||
const lensSlackWeblinkLink = weblinks.find(weblink => weblink.name === links.lensSlackWeblinkName);
|
||||
|
||||
if (lensSlackWeblinkLink) {
|
||||
lensSlackWeblinkLink.id = links.lensSlackWeblinkId;
|
||||
}
|
||||
|
||||
const lensTwitterWeblinkLink = weblinks.find(weblink => weblink.name === links.lensTwitterWeblinkName);
|
||||
|
||||
if (lensTwitterWeblinkLink) {
|
||||
lensTwitterWeblinkLink.id = links.lensTwitterWeblinkId;
|
||||
}
|
||||
|
||||
const lensBlogWeblinkLink = weblinks.find(weblink => weblink.name === links.lensBlogWeblinkName);
|
||||
|
||||
if (lensBlogWeblinkLink) {
|
||||
lensBlogWeblinkLink.id = links.lensBlogWeblinkId;
|
||||
}
|
||||
|
||||
const kubernetesDocumentationWeblinkLink = weblinks.find(weblink => weblink.name === links.kubernetesDocumentationWeblinkName);
|
||||
|
||||
if (kubernetesDocumentationWeblinkLink) {
|
||||
kubernetesDocumentationWeblinkLink.id = links.kubernetesDocumentationWeblinkId;
|
||||
}
|
||||
|
||||
store.set("weblinks", weblinks);
|
||||
},
|
||||
}),
|
||||
injectionToken: weblinkStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v545Beta1WeblinkStoreMigrationInjectable;
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { docsUrl, slackUrl } from "../../../common/vars";
|
||||
import type { WeblinkData } from "../../../common/weblinks-store/weblink-store";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { weblinkStoreMigrationInjectionToken } from "../../../common/weblinks-store/migration-token";
|
||||
import applicationInformationInjectable from "../../../common/vars/application-information.injectable";
|
||||
import { lensDocumentationWeblinkId, lensSlackWeblinkId } from "../links";
|
||||
|
||||
const currentVersionWeblinkStoreMigrationInjectable = getInjectable({
|
||||
id: "current-version-weblink-store-migration",
|
||||
instantiate: (di) => {
|
||||
const { version } = di.inject(applicationInformationInjectable);
|
||||
|
||||
return {
|
||||
version, // Run always after upgrade
|
||||
run(store) {
|
||||
const weblinksRaw = store.get("weblinks");
|
||||
const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[];
|
||||
const slackWeblink = weblinks.find(weblink => weblink.id === lensSlackWeblinkId);
|
||||
|
||||
if (slackWeblink) {
|
||||
slackWeblink.url = slackUrl;
|
||||
}
|
||||
|
||||
const docsWeblink = weblinks.find(weblink => weblink.id === lensDocumentationWeblinkId);
|
||||
|
||||
if (docsWeblink) {
|
||||
docsWeblink.url = docsUrl;
|
||||
}
|
||||
|
||||
store.set("weblinks", weblinks);
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: weblinkStoreMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default currentVersionWeblinkStoreMigrationInjectable;
|
||||
Reference in new issue
Block a user