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:
Sebastian Malton authored and GitHub committed 2022-12-15 17:07:19 +02:00
1 parent 7c5faaaf1d
commit 25f37ac1d1
364 files changed
+10035 -5246

No files matched your search

+15 -4
View File
@@ -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;
-45
View File
@@ -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;
}
}
+10 -6
View File
@@ -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,
+16 -17
View File
@@ -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();
+49 -12
View File
@@ -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 {