1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

chore: Improve linting within @k8slens/core

- Turning on @typescript-eslint/recommended-requiring-type-checking
- Turning off @typescript-eslint/no-unnecessary-type-assertion (due too many false positives)
- Making @typescript-eslint/no-explicit-any an error (except in tests)

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-05-01 13:48:32 -04:00
parent 6184c2f03b
commit fa44b795d4
734 changed files with 32216 additions and 4566 deletions

View File

@ -95,7 +95,7 @@ The properties of the `clusterPages` array objects are defined as follows:
- `id` is a string that identifies the page.
- `components` matches the `PageComponents` interface for which there is one field, `Page`.
- `Page` is of type ` React.ComponentType<any>`.
- `Page` is of type ` React.ComponentType<Record<string, never>>`.
It offers flexibility in defining the appearance and behavior of your page.
`ExamplePage` in the example above can be defined in `page.tsx`:
@ -304,7 +304,7 @@ The properties of the `globalPages` array objects are defined as follows:
- `id` is a string that identifies the page.
- `components` matches the `PageComponents` interface for which there is one field, `Page`.
- `Page` is of type `React.ComponentType<any>`.
- `Page` is of type `React.ComponentType<Record<string, never>>`.
It offers flexibility in defining the appearance and behavior of your page.
`HelpPage` in the example above can be defined in `page.tsx`:

27983
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@ module.exports = {
"**/static/**/*",
"**/site/**/*",
"**/build/webpack/**/*",
"**/webpack/**/*",
],
settings: {
react: {
@ -106,6 +107,7 @@ module.exports = {
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:react/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
@ -116,8 +118,8 @@ module.exports = {
"react-hooks",
],
parserOptions: {
ecmaVersion: 2018,
sourceType: "module",
project: true,
tsconfigRootDir: "./tsconfig.json",
},
rules: {
"no-constant-condition": ["error", {
@ -126,19 +128,14 @@ module.exports = {
"header/header": [2, "../../license-header"],
"react/prop-types": "off",
"no-invalid-this": "off",
"@typescript-eslint/no-invalid-this": ["error"],
"@typescript-eslint/no-invalid-this": "error",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/interface-name-prefix": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-unnecessary-type-assertion": "off",
"no-restricted-imports": ["error", {
"paths": [
{
@ -186,6 +183,11 @@ module.exports = {
"named": "never",
"asyncArrow": "always",
}],
"@typescript-eslint/restrict-template-expressions": ["error", {
"allowNumber": true,
"allowBoolean": true,
"allowNever": true,
}],
"@typescript-eslint/naming-convention": ["error",
{
"selector": "interface",
@ -217,7 +219,7 @@ module.exports = {
"ignoreRestSiblings": true,
},
],
"comman-dangle": "off",
"comma-dangle": "off",
"@typescript-eslint/comma-dangle": ["error", "always-multiline"],
"comma-spacing": "off",
"@typescript-eslint/comma-spacing": "error",
@ -296,5 +298,19 @@ module.exports = {
}],
},
},
{
files: [
"**/*.test.ts",
"**/*.test.tsx",
],
plugins: [
"jest",
],
rules: {
"@typescript-eslint/unbound-method": "off",
"jest/unbound-method": "error",
"@typescript-eslint/no-unsafe-assignment": "off",
},
},
],
};

View File

@ -5,9 +5,7 @@
import type { DiContainer } from "@ogre-tools/injectable";
import kubectlApplyAllInjectable from "../../main/kubectl/kubectl-apply-all.injectable";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
import type { KubernetesCluster } from "../catalog-entities";
import readDirectoryInjectable from "../fs/read-directory.injectable";
import readFileInjectable from "../fs/read-file.injectable";
import { KubernetesCluster } from "../catalog-entities";
import createResourceStackInjectable from "../k8s/create-resource-stack.injectable";
import appPathsStateInjectable from "../app-paths/app-paths-state.injectable";
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
@ -16,14 +14,23 @@ describe("create resource stack tests", () => {
let di: DiContainer;
let cluster: KubernetesCluster;
beforeEach(async () => {
beforeEach(() => {
di = getDiForUnitTesting();
cluster = {
getId: () => "test-cluster",
} as any;
cluster = new KubernetesCluster({
metadata: {
labels: {},
name: "some-name",
uid: "test-cluster",
},
spec: {
kubeconfigContext: "some-context",
kubeconfigPath: "/some-kubeconfig-path",
},
status: {
phase: "some-phase",
},
});
di.override(readDirectoryInjectable, () => () => Promise.resolve(["file1"]) as any);
di.override(readFileInjectable, () => () => Promise.resolve("filecontents"));
di.override(appPathsStateInjectable, () => ({
get: () => ({}),
}));
@ -34,8 +41,8 @@ describe("create resource stack tests", () => {
describe("kubectlApplyFolder", () => {
it("returns response", async () => {
di.override(kubectlApplyAllInjectable, () => () => Promise.resolve({
callWasSuccessful: true as const,
response: "success",
isOk: true as const,
value: "success",
}));
const createResourceStack = di.inject(createResourceStackInjectable);
@ -48,7 +55,7 @@ describe("create resource stack tests", () => {
it("throws on error", async () => {
di.override(kubectlApplyAllInjectable, () => () => Promise.resolve({
callWasSuccessful: false as const,
isOk: false as const,
error: "No permissions",
}));

View File

@ -4,6 +4,7 @@
*/
import { KubeConfig } from "@kubernetes/client-node";
import assert from "assert";
import { validateKubeConfig, loadConfigFromString } from "../kube-helpers";
const kubeconfig = `
@ -63,7 +64,7 @@ interface Kubeconfig {
}];
kind: string;
"current-context": string;
preferences: {};
preferences: object;
}
let mockKubeConfig: Kubeconfig;
@ -78,30 +79,33 @@ describe("kube helpers", () => {
describe("with default validation options", () => {
describe("with valid kubeconfig", () => {
it("does not return an error", () => {
expect(validateKubeConfig(kc, "valid")).toBeDefined();
expect(validateKubeConfig(kc, "valid").isOk).toBe(true);
});
});
describe("with invalid context object", () => {
it("returns an error", () => {
expect(validateKubeConfig(kc, "invalid").error?.toString()).toEqual(
expect.stringContaining("No valid context object provided in kubeconfig for context 'invalid'"),
);
const result = validateKubeConfig(kc, "invalid");
assert(result.isOk === false);
expect(result.error).toBe("No valid context object provided in kubeconfig for context 'invalid'");
});
});
describe("with invalid cluster object", () => {
it("returns an error", () => {
expect(validateKubeConfig(kc, "invalidCluster").error?.toString()).toEqual(
expect.stringContaining("No valid cluster object provided in kubeconfig for context 'invalidCluster'"),
);
const result = validateKubeConfig(kc, "invalidCluster");
assert(result.isOk === false);
expect(result.error).toBe("No valid cluster object provided in kubeconfig for context 'invalidCluster'");
});
});
describe("with invalid user object", () => {
it("returns an error", () => {
expect(validateKubeConfig(kc, "invalidUser").error?.toString()).toEqual(
expect.stringContaining("No valid user object provided in kubeconfig for context 'invalidUser'"),
);
const result = validateKubeConfig(kc, "invalidUser");
assert(result.isOk === false);
expect(result.error).toBe("No valid user object provided in kubeconfig for context 'invalidUser'");
});
});
});
@ -115,13 +119,15 @@ describe("kube helpers", () => {
describe("Check logger.error() output", () => {
it("invalid yaml string", () => {
const invalidYAMLString = "fancy foo config";
const result = loadConfigFromString(invalidYAMLString);
expect(loadConfigFromString(invalidYAMLString).error).toBeInstanceOf(Error);
expect(result.isOk).toBe(false);
});
it("empty contexts", () => {
const emptyContexts = `apiVersion: v1\ncontexts: []`;
const result = loadConfigFromString(emptyContexts);
expect(loadConfigFromString(emptyContexts).error).toBeUndefined();
expect(result.isOk).toBe(true);
});
});
@ -151,18 +157,20 @@ describe("kube helpers", () => {
};
});
it("single context is ok", async () => {
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
it("single context is ok", () => {
const result = loadConfigFromString(JSON.stringify(mockKubeConfig));
expect(config.getCurrentContext()).toBe("minikube");
assert(result.isOk === true);
expect(result.value.getCurrentContext()).toBe("minikube");
});
it("multiple context is ok", async () => {
it("multiple context is ok", () => {
mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "cluster-2" }, name: "cluster-2" });
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
const result = loadConfigFromString(JSON.stringify(mockKubeConfig));
expect(config.getCurrentContext()).toBe("minikube");
expect(config.contexts.length).toBe(2);
assert(result.isOk === true);
expect(result.value.getCurrentContext()).toBe("minikube");
expect(result.value.contexts.length).toBe(2);
});
});
@ -192,43 +200,47 @@ describe("kube helpers", () => {
};
});
it("empty name in context causes it to be removed", async () => {
it("empty name in context causes it to be removed", () => {
mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "cluster-2" }, name: "" });
expect(mockKubeConfig.contexts.length).toBe(2);
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
const result = loadConfigFromString(JSON.stringify(mockKubeConfig));
expect(config.getCurrentContext()).toBe("minikube");
expect(config.contexts.length).toBe(1);
assert(result.isOk === true);
expect(result.value.getCurrentContext()).toBe("minikube");
expect(result.value.contexts.length).toBe(1);
});
it("empty cluster in context causes it to be removed", async () => {
it("empty cluster in context causes it to be removed", () => {
mockKubeConfig.contexts.push({ context: { cluster: "", user: "cluster-2" }, name: "cluster-2" });
expect(mockKubeConfig.contexts.length).toBe(2);
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
const result = loadConfigFromString(JSON.stringify(mockKubeConfig));
expect(config.getCurrentContext()).toBe("minikube");
expect(config.contexts.length).toBe(1);
assert(result.isOk === true);
expect(result.value.getCurrentContext()).toBe("minikube");
expect(result.value.contexts.length).toBe(1);
});
it("empty user in context causes it to be removed", async () => {
it("empty user in context causes it to be removed", () => {
mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "" }, name: "cluster-2" });
expect(mockKubeConfig.contexts.length).toBe(2);
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
const result = loadConfigFromString(JSON.stringify(mockKubeConfig));
expect(config.getCurrentContext()).toBe("minikube");
expect(config.contexts.length).toBe(1);
assert(result.isOk === true);
expect(result.value.getCurrentContext()).toBe("minikube");
expect(result.value.contexts.length).toBe(1);
});
it("invalid context in between valid contexts is removed", async () => {
it("invalid context in between valid contexts is removed", () => {
mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "" }, name: "cluster-2" });
mockKubeConfig.contexts.push({ context: { cluster: "cluster-3", user: "cluster-3" }, name: "cluster-3" });
expect(mockKubeConfig.contexts.length).toBe(3);
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
const result = loadConfigFromString(JSON.stringify(mockKubeConfig));
expect(config.getCurrentContext()).toBe("minikube");
expect(config.contexts.length).toBe(2);
expect(config.contexts[0].name).toBe("minikube");
expect(config.contexts[1].name).toBe("cluster-3");
assert(result.isOk === true);
expect(result.value.getCurrentContext()).toBe("minikube");
expect(result.value.contexts.length).toBe(2);
expect(result.value.contexts[0].name).toBe("minikube");
expect(result.value.contexts[1].name).toBe("cluster-3");
});
});
});

View File

@ -23,7 +23,7 @@ describe("user store tests", () => {
let resetTheme: ResetTheme;
let di: DiContainer;
beforeEach(async () => {
beforeEach(() => {
di = getDiForUnitTesting();
di.override(writeFileInjectable, () => () => Promise.resolve());
@ -55,7 +55,7 @@ describe("user store tests", () => {
expect(state.colorTheme).toBe("light");
});
it("correctly resets theme to default value", async () => {
it("correctly resets theme to default value", () => {
state.colorTheme = "some other theme";
resetTheme();
expect(state.colorTheme).toBe(defaultThemeId);

View File

@ -10,5 +10,5 @@ export interface AppEvent {
name: string;
action: string;
destination?: string;
params?: Record<string, any>;
params?: Record<string, unknown>;
}

View File

@ -38,7 +38,7 @@ describe("app-paths", () => {
sessionData: "/some-irrelevant-user-data", // By default this points to userData
};
builder.beforeApplicationStart(({ mainDi }) => {
await builder.beforeApplicationStart(({ mainDi }) => {
mainDi.override(
getElectronAppPathInjectable,
() =>
@ -120,7 +120,7 @@ describe("app-paths", () => {
let windowDi: DiContainer;
beforeEach(async () => {
builder.beforeApplicationStart(({ mainDi }) => {
await builder.beforeApplicationStart(({ mainDi }) => {
mainDi.override(
directoryForIntegrationTestingInjectable,
() => "/some-integration-testing-app-data",

View File

@ -4,6 +4,12 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
interface NonWebpackRequire {
resolve(name: string): string;
}
declare const __non_webpack_require__: NonWebpackRequire;
const pathToNpmCliInjectable = getInjectable({
id: "path-to-npm-cli",
instantiate: () => __non_webpack_require__.resolve("npm"),

View File

@ -19,7 +19,7 @@ export class GeneralEntity extends CatalogEntity<CatalogEntityMetadata, CatalogE
public readonly apiVersion = "entity.k8slens.dev/v1alpha1";
public readonly kind = "General";
async onRun(context: CatalogEntityActionContext) {
onRun(context: CatalogEntityActionContext) {
context.navigate(this.spec.path);
}
}

View File

@ -55,8 +55,7 @@ export interface KubernetesClusterMetadata extends CatalogEntityMetadata {
*/
export type KubernetesClusterStatusPhase = "connected" | "connecting" | "disconnected" | "deleting";
export interface KubernetesClusterStatus extends CatalogEntityStatus {
}
export type KubernetesClusterStatus = CatalogEntityStatus;
export function isKubernetesCluster(item: unknown): item is KubernetesCluster {
return item instanceof KubernetesCluster;
@ -89,18 +88,10 @@ export class KubernetesCluster<
await requestClusterDeactivation(this.getId());
}
async onRun(context: CatalogEntityActionContext) {
onRun(context: CatalogEntityActionContext) {
context.navigate(`/cluster/${this.getId()}`);
}
onDetailsOpen(): void {
//
}
onSettingsOpen(): void {
//
}
onContextMenuOpen(context: CatalogEntityContextMenuContext) {
if (!this.metadata.source || this.metadata.source === "local") {
context.menuItems.push({
@ -120,8 +111,8 @@ export class KubernetesCluster<
title: "Disconnect",
icon: "link_off",
onClick: () => {
this.disconnect();
broadcastMessage(
void this.disconnect();
void broadcastMessage(
IpcRendererNavigationEvents.NAVIGATE_IN_APP,
"/catalog",
);

View File

@ -26,7 +26,7 @@ export class WebLink extends CatalogEntity<CatalogEntityMetadata, WebLinkStatus,
public readonly apiVersion = WebLink.apiVersion;
public readonly kind = WebLink.kind;
async onRun() {
onRun() {
window.open(this.spec.url, "_blank");
}

View File

@ -89,7 +89,7 @@ export interface CatalogCategorySpec {
/**
* If the filter return a thruthy value, the menu item is displayed
*/
export type AddMenuFilter = (menu: CatalogEntityAddMenu) => any;
export type AddMenuFilter = (menu: CatalogEntityAddMenu) => unknown;
export interface CatalogCategoryEvents {
/**
@ -291,7 +291,7 @@ export interface CatalogEntitySettingsMenu {
group?: string;
title: string;
components: {
View: React.ComponentType<any>;
View: React.ComponentType<Record<string, never>>;
};
}
@ -323,7 +323,7 @@ export interface CatalogEntityAddMenuContext {
menuItems: CatalogEntityAddMenu[];
}
export type CatalogEntitySpec = Record<string, any>;
export type CatalogEntitySpec = Record<string, unknown>;
export interface CatalogEntityData<
@ -409,7 +409,17 @@ export abstract class CatalogEntity<
return this.status.enabled ?? true;
}
public onRun?(context: CatalogEntityActionContext): void | Promise<void>;
public onContextMenuOpen?(context: CatalogEntityContextMenuContext): void | Promise<void>;
public onSettingsOpen?(context: CatalogEntitySettingsContext): void | Promise<void>;
public onRun(context: CatalogEntityActionContext): void | Promise<void> {
void context;
}
public onContextMenuOpen(context: CatalogEntityContextMenuContext): void | Promise<void> {
void context;
}
/**
* @deprecated This has never been used
*/
public onSettingsOpen(context: CatalogEntitySettingsContext): void | Promise<void> {
void context;
}
}

View File

@ -3,13 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { action, computed, observable, makeObservable } from "mobx";
import { action, computed, observable, makeObservable, runInAction } from "mobx";
import { once } from "lodash";
import { iter, getOrInsertMap, strictSet } from "@k8slens/utilities";
import type { Disposer } from "@k8slens/utilities";
import type { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity";
export type CategoryFilter = (category: CatalogCategory) => any;
export type CategoryFilter = (category: CatalogCategory) => unknown;
export class CatalogCategoryRegistry {
protected readonly categories = observable.set<CatalogCategory>();
@ -22,16 +22,18 @@ export class CatalogCategoryRegistry {
makeObservable(this);
}
@action add(category: CatalogCategory): Disposer {
add(category: CatalogCategory): Disposer {
return runInAction(() => {
const byGroup = getOrInsertMap(this.groupKinds, category.spec.group);
this.categories.add(category);
strictSet(byGroup, category.spec.names.kind, category);
return () => {
return action(() => {
this.categories.delete(category);
byGroup.delete(category.spec.names.kind);
};
});
});
}
getById(id: string) {

View File

@ -54,7 +54,7 @@ export function getShortName(entity: CatalogEntity): string {
}
export function getIconColourHash(entity: CatalogEntity): string {
return `${entity.metadata.name}-${entity.metadata.source}`;
return `${entity.metadata.name}-${entity.metadata.source ?? ""}`;
}
export function getIconBackground(entity: CatalogEntity): string | undefined {

View File

@ -14,7 +14,7 @@ const visitEntityContextMenuInjectable = getInjectable({
const categoryRegistry = di.inject(catalogCategoryRegistryInjectable);
return (entity, context) => {
entity.onContextMenuOpen?.(context);
void entity.onContextMenuOpen(context);
categoryRegistry.getCategoryForEntity(entity)?.emit("contextMenuOpen", entity, context);
};
},

View File

@ -3,12 +3,12 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import Joi from "joi";
import { z } from "zod";
/**
* JSON serializable metadata type
*/
export type ClusterMetadata = Record<string, string | number | boolean | object>;
export type ClusterMetadata = Partial<Record<string, Metadata>>;
/**
* Metadata for cluster's prometheus settings
@ -29,31 +29,55 @@ export type ClusterId = string;
*/
export type UpdateClusterModel = Omit<ClusterModel, "id">;
export type Literal = z.infer<typeof literalSchema>;
export const literalSchema = z.string().or(z.number()).or(z.boolean());
export type Metadata = Literal | { [key: string]: Metadata } | Metadata[];
export const metadataSchema: z.ZodType<Metadata> = z.lazy(() => z.union([literalSchema, z.array(metadataSchema), z.record(metadataSchema)]));
export const prometheusPreferencesSchema = z.object({
namespace: z.string(),
service: z.string(),
prefix: z.string(),
port: z.number(),
});
export const prometheusProviderPreferencesSchema = z.object({
type: z.string(),
});
export const preferencesSchema = z.object({
terminalCWD: z.string().min(1).optional(),
clusterName: z.string().min(1).optional(),
httpsProxy: z.string().min(1).optional(),
nodeShellImage: z.string().min(1).optional(),
imagePullSecret: z.string().min(1).optional(),
defaultNamespace: z.string().min(1).optional(),
iconOrder: z.number().optional(),
icon: z.nullable(z.string()).optional(),
hiddenMetrics: z.array(z.string()).optional(),
prometheus: prometheusPreferencesSchema.optional(),
prometheusProvider: prometheusProviderPreferencesSchema.optional(),
});
/**
* A type validator for `UpdateClusterModel` so that only expected types are present
*/
export const updateClusterModelChecker = Joi.object<UpdateClusterModel>({
kubeConfigPath: Joi.string()
.required()
.min(1),
contextName: Joi.string()
.required()
.min(1),
preferences: Joi.object(),
metadata: Joi.object(),
accessibleNamespaces: Joi.array()
.items(Joi.string()),
labels: Joi.object().pattern(Joi.string(), Joi.string()),
export const updateClusterModelSchema = z.object({
kubeConfigPath: z.string().min(1),
contextName: z.string().min(1),
preferences: preferencesSchema.optional(),
metadata: z.record(metadataSchema).optional(),
accessibleNamespaces: z.array(z.string()).optional(),
labels: z.record(z.string()).optional(),
});
/**
* A type validator for just the `id` fields of `ClusterModel`. The rest is
* covered by `updateClusterModelChecker`
*/
export const clusterModelIdChecker = Joi.object<Pick<ClusterModel, "id">>({
id: Joi.string()
.required()
.min(1),
export const clusterModelIdSchema = z.object({
id: z.string().min(1),
});
/**
@ -85,7 +109,7 @@ export interface ClusterModel {
}
/**
* This data is retreived from the kubeconfig file before calling the cluster constructor.
* This data is retrieved from the kubeconfig file before calling the cluster constructor.
*
* That is done to remove the external dependency on the construction of Cluster instances.
*/

View File

@ -6,10 +6,12 @@
import { computed, observable, toJS, runInAction } from "mobx";
import type { KubeApiResource } from "../rbac";
import type { ClusterState, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel } from "../cluster-types";
import { ClusterMetadataKey, clusterModelIdChecker, updateClusterModelChecker } from "../cluster-types";
import { ClusterMetadataKey, clusterModelIdSchema, updateClusterModelSchema } from "../cluster-types";
import type { IObservableValue } from "mobx";
import { replaceObservableObject } from "../utils/replace-observable-object";
import { pick } from "lodash";
import type { ZodError } from "zod";
import type { Result } from "@k8slens/utilities";
export class Cluster {
/**
@ -20,12 +22,12 @@ export class Cluster {
/**
* Kubeconfig context name
*/
readonly contextName = observable.box() as IObservableValue<string>;
readonly contextName: IObservableValue<string>;
/**
* Path to kubeconfig
*/
readonly kubeConfigPath = observable.box() as IObservableValue<string>;
readonly kubeConfigPath: IObservableValue<string>;
/**
* Describes if we can detect that cluster is online
@ -117,15 +119,58 @@ export class Cluster {
*/
readonly prometheusPreferences = computed(() => pick(toJS(this.preferences), "prometheus", "prometheusProvider") as ClusterPrometheusPreferences);
constructor({ id, ...model }: ClusterModel) {
const { error } = clusterModelIdChecker.validate({ id });
static create({ id, ...model }: ClusterModel): Result<Cluster, ZodError<unknown>> {
const clusterIdResult = clusterModelIdSchema.safeParse({ id });
if (error) {
throw error;
if (!clusterIdResult.success) {
return {
isOk: false,
error: clusterIdResult.error,
};
}
const updateModelResult = updateClusterModelSchema.safeParse(model);
if (!updateModelResult.success) {
return {
isOk: false,
error: updateModelResult.error,
};
}
return {
isOk: true,
value: new Cluster(clusterIdResult.data.id, updateModelResult.data),
};
}
static createForTestingOnly({ id, ...model }: ClusterModel): Cluster {
const clusterIdResult = clusterModelIdSchema.parse({ id });
const updateModelResult = updateClusterModelSchema.parse(model);
return new Cluster(clusterIdResult.id, updateModelResult);
}
private constructor(id: string, model: UpdateClusterModel) {
this.id = id;
this.updateModel(model);
this.kubeConfigPath = observable.box(model.kubeConfigPath);
this.contextName = observable.box(model.contextName);
if (model.preferences) {
replaceObservableObject(this.preferences, model.preferences);
}
if (model.metadata) {
replaceObservableObject(this.metadata, model.metadata);
}
if (model.accessibleNamespaces) {
this.accessibleNamespaces.replace(model.accessibleNamespaces);
}
if (model.labels) {
replaceObservableObject(this.labels, model.labels);
}
}
/**
@ -133,13 +178,14 @@ export class Cluster {
*
* @param model
*/
updateModel(model: UpdateClusterModel) {
// Note: do not assign ID as that should never be updated
updateModel(model: UpdateClusterModel): Result<Cluster, ZodError<unknown>> {
const updateModelResult = updateClusterModelSchema.safeParse(model);
const { error } = updateClusterModelChecker.validate(model, { allowUnknown: true });
if (error) {
throw error;
if (!updateModelResult.success) {
return {
isOk: false,
error: updateModelResult.error,
};
}
runInAction(() => {
@ -162,6 +208,11 @@ export class Cluster {
replaceObservableObject(this.labels, model.labels);
}
});
return {
isOk: true,
value: this,
};
}
toJSON(): ClusterModel {

View File

@ -31,7 +31,7 @@ const createCanIInjectable = getInjectable({
return body.status?.allowed ?? false;
} catch (error) {
logger.error(`[AUTHORIZATION-REVIEW]: failed to create access review: ${error}`, { resourceAttributes });
logger.error(`[AUTHORIZATION-REVIEW]: failed to create access review: ${String(error)}`, { resourceAttributes });
return false;
}

View File

@ -32,7 +32,7 @@ const createRequestNamespaceListPermissionsInjectable = getInjectable({
});
if (!status || status.incomplete) {
logger.warn(`[AUTHORIZATION-NAMESPACE-REVIEW]: allowing all resources in namespace="${namespace}" due to incomplete SelfSubjectRulesReview: ${status?.evaluationError}`);
logger.warn(`[AUTHORIZATION-NAMESPACE-REVIEW]: allowing all resources in namespace="${namespace}" due to incomplete SelfSubjectRulesReview: ${status?.evaluationError ?? ""}`);
return () => true;
}

View File

@ -6,27 +6,17 @@ import type { KubeConfig } from "@kubernetes/client-node";
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import type { Cluster } from "./cluster";
import loadConfigFromFileInjectable from "../kube-helpers/load-config-from-file.injectable";
import type { ConfigResult } from "../kube-helpers";
import type { AsyncResult } from "@k8slens/utilities";
import type { ZodError } from "zod";
export interface LoadKubeconfig {
(fullResult?: false): Promise<KubeConfig>;
(fullResult: true): Promise<ConfigResult>;
}
export type LoadKubeconfig = () => AsyncResult<KubeConfig, ZodError<unknown>>;
const loadKubeconfigInjectable = getInjectable({
id: "load-kubeconfig",
instantiate: (di, cluster) => {
const loadConfigFromFile = di.inject(loadConfigFromFileInjectable);
return (async (fullResult = false) => {
const result = await loadConfigFromFile(cluster.kubeConfigPath.get());
if (fullResult) {
return result;
}
return result.config;
}) as LoadKubeconfig;
return () => loadConfigFromFile(cluster.kubeConfigPath.get());
},
lifecycle: lifecycleEnum.keyedSingleton({
getInstanceKey: (di, cluster: Cluster) => cluster.id,

View File

@ -193,7 +193,7 @@ describe("requestNamespaceListPermissions", () => {
status,
spec: {},
},
response: null as unknown as IncomingMessage,
value: null as unknown as IncomingMessage,
});
});

View File

@ -25,26 +25,26 @@ const downloadBinaryInjectable = getInjectable({
result = await fetch(url, opts as RequestInit);
} catch (error) {
return {
callWasSuccessful: false,
isOk: false,
error: String(error),
};
}
if (result.status < 200 || 300 <= result.status) {
return {
callWasSuccessful: false,
isOk: false,
error: result.statusText,
};
}
try {
return {
callWasSuccessful: true,
response: await result.buffer(),
isOk: true,
value: await result.buffer(),
};
} catch (error) {
return {
callWasSuccessful: false,
isOk: false,
error: String(error),
};
}

View File

@ -19,26 +19,26 @@ export const downloadJsonWith = (fetch: Fetch): DownloadJson => async (url, opts
result = await fetch(url, opts as RequestInit);
} catch (error) {
return {
callWasSuccessful: false,
isOk: false,
error: String(error),
};
}
if (result.status < 200 || 300 <= result.status) {
return {
callWasSuccessful: false,
isOk: false,
error: result.statusText,
};
}
try {
return {
callWasSuccessful: true,
response: await result.json(),
isOk: true,
value: await result.json(),
};
} catch (error) {
return {
callWasSuccessful: false,
isOk: false,
error: String(error),
};
}

View File

@ -18,9 +18,11 @@ type RequiredKeys<T> = Exclude<
type ObjectContainingNoRequired<T> = T extends void
? never
: RequiredKeys<T> extends []
? any
: never;
: (
RequiredKeys<T> extends []
? Record<string, never>
: never
);
type ObjectContainsNoRequired<T> = T extends ObjectContainingNoRequired<T>
? true
@ -30,10 +32,12 @@ type ObjectContainsNoRequired<T> = T extends ObjectContainingNoRequired<T>
// - Navigating to route without parameters, with parameters
// - Navigating to route with required parameters, without parameters
type Parameters<TParameters> = TParameters extends void
? {}
: ObjectContainsNoRequired<TParameters> extends true
? { parameters?: undefined }
: (
ObjectContainsNoRequired<TParameters> extends true
? { parameters?: TParameters }
: { parameters: TParameters };
: { parameters: TParameters }
);
export type NavigateToRouteOptions<TRoute> = Parameters<
InferParametersFrom<TRoute>

View File

@ -9,7 +9,7 @@ export interface NavigateToUrlOptions {
forceRootFrame?: boolean;
}
export type NavigateToUrl = (url: string, options?: NavigateToUrlOptions) => void;
export type NavigateToUrl = (url: string, options?: NavigateToUrlOptions) => void | Promise<void>;
export const navigateToUrlInjectionToken = getInjectionToken<NavigateToUrl>(
{ id: "navigate-to-url-injection-token" },

View File

@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import type { ExecFileException, ExecFileOptions } from "child_process";
import { execFile } from "child_process";
import type { AsyncResult } from "@k8slens/utilities";
import { isArray } from "@k8slens/utilities";
export type ExecFileError = ExecFileException & { stderr: string };
@ -21,7 +22,7 @@ const execFileInjectable = getInjectable({
instantiate: (): ExecFile => {
return (filePath: string, argsOrOptions?: string[] | ExecFileOptions, maybeOptions?: ExecFileOptions) => {
const { args, options } = (() => {
if (Array.isArray(argsOrOptions)) {
if (isArray(argsOrOptions)) {
return {
args: argsOrOptions,
options: maybeOptions ?? {},
@ -38,13 +39,13 @@ const execFileInjectable = getInjectable({
execFile(filePath, args, options, (error, stdout, stderr) => {
if (error) {
resolve({
callWasSuccessful: false,
isOk: false,
error: Object.assign(error, { stderr }),
});
} else {
resolve({
callWasSuccessful: true,
response: stdout,
isOk: true,
value: stdout,
});
}
});

View File

@ -6,6 +6,7 @@
import { getGlobalOverride } from "@k8slens/test-utils";
import extractTarInjectable from "./extract-tar.injectable";
// eslint-disable-next-line @typescript-eslint/require-await
export default getGlobalOverride(extractTarInjectable, () => async () => {
throw new Error("tried to extract a tar file without override");
});

View File

@ -3,9 +3,12 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { WriteFileOptions } from "fs";
import type { ReadOptions } from "fs-extra";
import fse from "fs-extra";
export type FileSystemFunctions = ReturnType<(typeof fsInjectable)["instantiate"]>;
/**
* NOTE: Add corresponding override of this injectable in `src/test-utils/override-fs-with-fakes.ts`
*/
@ -40,13 +43,13 @@ const fsInjectable = getInjectable({
return {
readFile,
readJson: readJson as (file: string, options?: ReadOptions | BufferEncoding) => Promise<any>,
readJson: readJson as (file: string, options?: ReadOptions | BufferEncoding) => Promise<unknown>,
writeFile,
writeJson,
writeJson: writeJson as (file: string, value: unknown, options?: string | WriteFileOptions) => Promise<void>,
pathExists,
readdir,
readFileSync,
readJsonSync,
readJsonSync: readJsonSync as (file: string, options?: ReadOptions | BufferEncoding) => unknown,
writeFileSync,
writeJsonSync,
pathExistsSync,

View File

@ -3,10 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { JsonValue } from "type-fest";
import fsInjectable from "./fs.injectable";
export type ReadJson = (filePath: string) => Promise<JsonValue>;
export type ReadJson = (filePath: string) => Promise<unknown>;
const readJsonFileInjectable = getInjectable({
id: "read-json-file",

View File

@ -9,7 +9,7 @@ import type { Stats } from "fs-extra";
import { lowerFirst } from "lodash/fp";
import statInjectable from "./stat.injectable";
export type ValidateDirectory = (path: string) => AsyncResult<undefined>;
export type ValidateDirectory = (path: string) => AsyncResult<undefined, string>;
function getUserReadableFileType(stats: Stats): string {
if (stats.isFile()) {
@ -46,13 +46,13 @@ const validateDirectoryInjectable = getInjectable({
const stats = await stat(path);
if (stats.isDirectory()) {
return { callWasSuccessful: true, response: undefined };
return { isOk: true, value: undefined };
}
return { callWasSuccessful: false, error: `the provided path is ${getUserReadableFileType(stats)} and not a directory.` };
return { isOk: false, error: `the provided path is ${getUserReadableFileType(stats)} and not a directory.` };
} catch (error) {
if (!isErrnoException(error)) {
return { callWasSuccessful: false, error: "of an unknown error, please try again." };
return { isOk: false, error: "of an unknown error, please try again." };
}
const humanReadableErrors: Record<string, string> = {
@ -67,7 +67,7 @@ const validateDirectoryInjectable = getInjectable({
? humanReadableErrors[error.code]
: lowerFirst(String(error));
return { callWasSuccessful: false, error: humanReadableError };
return { isOk: false, error: humanReadableError };
}
};
},

View File

@ -12,6 +12,7 @@ import readJsonSyncInjectable from "../fs/read-json-sync.injectable";
import writeJsonSyncInjectable from "../fs/write-json-sync.injectable";
import { get, has, set } from "lodash";
import semver from "semver";
import type { Options as ConfOptions } from "conf/dist/source/types";
const MIGRATION_KEY = `__internal__.migrations.version`;
@ -43,16 +44,16 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => {
const readJsonSync = di.inject(readJsonSyncInjectable);
const writeJsonSync = di.inject(writeJsonSyncInjectable);
return (options) => {
return <T extends object>(options: ConfOptions<T>) => {
assert(options.cwd, "Missing options.cwd");
assert(options.configName, "Missing options.configName");
assert(options.projectVersion, "Missing options.projectVersion");
const configFilePath = path.posix.join(options.cwd, `${options.configName}.json`);
let store: object = {};
let store: Partial<Record<string, unknown>> = {};
try {
store = readJsonSync(configFilePath);
store = readJsonSync(configFilePath) as Partial<Record<string, unknown>>;
} catch {
// ignore
}
@ -62,12 +63,12 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => {
return store;
},
path: configFilePath,
get: (key: string) => get(store, key),
get: (key: string) => get(store, key) as unknown,
set: (key: string, value: unknown) => {
let currentState: Partial<Record<string, unknown>>;
try {
currentState = readJsonSync(configFilePath);
currentState = readJsonSync(configFilePath) as Partial<Record<string, unknown>>;
} catch {
currentState = {};
}
@ -76,13 +77,13 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => {
...currentState,
[key]: value,
});
store = readJsonSync(configFilePath);
store = readJsonSync(configFilePath) as Partial<Record<string, unknown>>;
},
delete: (key: string) => {
let currentState: Partial<Record<string, unknown>>;
try {
currentState = readJsonSync(configFilePath);
currentState = readJsonSync(configFilePath) as Partial<Record<string, unknown>>;
} catch {
currentState = {};
}
@ -90,20 +91,20 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => {
delete currentState[key];
writeJsonSync(configFilePath, currentState);
store = readJsonSync(configFilePath);
store = readJsonSync(configFilePath) as Partial<Record<string, unknown>>;
},
has: (key: string) => has(store, key),
clear: () => {
writeJsonSync(configFilePath, {});
store = readJsonSync(configFilePath);
store = readJsonSync(configFilePath) as Partial<Record<string, unknown>>;
},
} as Partial<Config> as Config<any>;
} as Partial<Config> as Config<T>;
// Migrate
{
const migrations = options.migrations ?? [];
const versionToMigrate = options.projectVersion;
let previousMigratedVersion = get(store, MIGRATION_KEY) || "0.0.0";
let previousMigratedVersion = String(get(store, MIGRATION_KEY)) || "0.0.0";
const newerVersions = Object.entries(migrations)
.filter(([candidateVersion]) => _shouldPerformMigration(candidateVersion, previousMigratedVersion, versionToMigrate));
@ -118,7 +119,7 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => {
}
catch (error) {
store = storeBackup;
throw new Error(`Something went wrong during the migration! Changes applied to the store until this failed migration will be restored. ${error}`);
throw new Error(`Something went wrong during the migration! Changes applied to the store until this failed migration will be restored. ${String(error)}`);
}
}

View File

@ -8,5 +8,5 @@ import { getRequestChannel } from "@k8slens/messaging";
export const getActiveHelmRepositoriesChannel = getRequestChannel<
void,
AsyncResult<HelmRepo[]>
AsyncResult<HelmRepo[], string>
>("get-helm-active-list-repositories");

View File

@ -4,6 +4,7 @@
*/
import type { Runnable } from "@k8slens/run-many";
import { isArray } from "@k8slens/utilities";
import type { DiContainerForInjection, Injectable, InjectionToken } from "@ogre-tools/injectable";
import { getInjectionToken, getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
@ -67,7 +68,7 @@ export const getInjectablesForInitializable = <T>({
runAfter: rest.runAfter,
}),
injectionToken: (() => {
if (rest.runAfter && !Array.isArray(rest.runAfter)) {
if (rest.runAfter && !isArray(rest.runAfter)) {
return rest.runAfter.injectionToken;
}

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import { broadcastMessage } from "./ipc";
export type BroadcastMessage = (channel: string, ...args: any[]) => Promise<void>;
export type BroadcastMessage = (channel: string, ...args: unknown[]) => Promise<void>;
const broadcastMessageInjectable = getInjectable({
id: "broadcast-message",

View File

@ -10,6 +10,7 @@
import { ipcMain, ipcRenderer, webContents } from "electron";
import { toJS } from "../utils/toJS";
import type { Disposer } from "@k8slens/utilities";
import { isArray } from "@k8slens/utilities";
import { getLegacyGlobalDiForExtensionApi } from "@k8slens/legacy-global-di";
import ipcRendererInjectable from "../../renderer/utils/channel/ipc-renderer.injectable";
import { loggerInjectionToken } from "@k8slens/logger";
@ -18,19 +19,21 @@ import clusterFramesInjectable from "../cluster-frames.injectable";
export const broadcastMainChannel = "ipc:broadcast-main";
export function ipcMainHandle(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) {
export function ipcMainHandle(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: unknown[]) => unknown) {
const di = getLegacyGlobalDiForExtensionApi();
const ipcMain = di.inject(ipcMainInjectionToken);
ipcMain.handle(channel, async (event, ...args) => {
return sanitizePayload(await listener(event, ...args));
ipcMain.handle(channel, async (event, ...args: unknown[]) => {
return toJS(await listener(event, ...args));
});
}
export async function broadcastMessage(channel: string, ...args: any[]): Promise<void> {
export async function broadcastMessage(channel: string, ...args: unknown[]): Promise<void> {
if (ipcRenderer) {
return ipcRenderer.invoke(broadcastMainChannel, channel, ...args.map(sanitizePayload));
await ipcRenderer.invoke(broadcastMainChannel, channel, ...args.map(toJS));
return;
}
if (!webContents) {
@ -41,15 +44,23 @@ export async function broadcastMessage(channel: string, ...args: any[]): Promise
const logger = di.inject(loggerInjectionToken);
const clusterFrames = di.inject(clusterFramesInjectable);
ipcMain.listeners(channel).forEach((func) => func({
processId: undefined, frameId: undefined, sender: undefined, senderFrame: undefined,
}, ...args));
ipcMain.listeners(channel).forEach((func) => {
func(
{
processId: undefined,
frameId: undefined,
sender: undefined,
senderFrame: undefined,
},
...args,
);
});
const views = webContents.getAllWebContents();
if (!views || !Array.isArray(views) || views.length === 0) return;
if (!views || !isArray(views) || views.length === 0) return;
args = args.map(sanitizePayload);
args = args.map(toJS);
for (const view of views) {
let viewType = "unknown";
@ -82,7 +93,7 @@ export async function broadcastMessage(channel: string, ...args: any[]): Promise
}
}
export function ipcMainOn(channel: string, listener: (event: Electron.IpcMainEvent, ...args: any[]) => any): Disposer {
export function ipcMainOn(channel: string, listener: (event: Electron.IpcMainEvent, ...args: unknown[]) => unknown): Disposer {
const di = getLegacyGlobalDiForExtensionApi();
const ipcMain = di.inject(ipcMainInjectionToken);
@ -92,7 +103,7 @@ export function ipcMainOn(channel: string, listener: (event: Electron.IpcMainEve
return () => ipcMain.off(channel, listener);
}
export function ipcRendererOn(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer {
export function ipcRendererOn(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: unknown[]) => unknown): Disposer {
const di = getLegacyGlobalDiForExtensionApi();
const ipcRenderer = di.inject(ipcRendererInjectable);
@ -101,11 +112,3 @@ export function ipcRendererOn(channel: string, listener: (event: Electron.IpcRen
return () => ipcRenderer.off(channel, listener);
}
/**
* Sanitizing data for IPC-messaging before send.
* Removes possible observable values to avoid exceptions like "can't clone object".
*/
function sanitizePayload<T>(data: any): T {
return toJS(data);
}

View File

@ -57,11 +57,11 @@ export abstract class ItemStore<Item extends ItemObject> {
* @param order whether to sort from least to greatest (`"asc"` (default)) or vice-versa (`"desc"`)
*/
@action
protected sortItems(items: Item[] = this.items, sorting: ((item: Item) => any)[] = [this.defaultSorting], order?: "asc" | "desc"): Item[] {
protected sortItems(items: Item[] = this.items, sorting: ((item: Item) => unknown)[] = [this.defaultSorting], order?: "asc" | "desc"): Item[] {
return orderBy(items, sorting, order);
}
protected async createItem(...args: any[]): Promise<any>;
protected async createItem(...args: unknown[]): Promise<unknown>;
@action
protected async createItem(request: () => Promise<Item>) {
const newItem = await request();
@ -78,7 +78,7 @@ export abstract class ItemStore<Item extends ItemObject> {
}
}
protected async loadItems(...args: any[]): Promise<any>;
protected async loadItems(...args: unknown[]): Promise<unknown>;
/**
* Load items to this.items
* @param request Function to return the items to be loaded.
@ -87,7 +87,7 @@ export abstract class ItemStore<Item extends ItemObject> {
* @returns
*/
@action
protected async loadItems(request: () => Promise<Item[] | any>, sortItems = true, concurrency = false) {
protected async loadItems(request: () => Promise<Item[]>, sortItems = true, concurrency = false) {
if (this.isLoading) {
await when(() => !this.isLoading);
@ -142,7 +142,7 @@ export abstract class ItemStore<Item extends ItemObject> {
}
@action
protected async removeItem(item: Item, request: () => Promise<any>) {
protected async removeItem(item: Item, request: () => Promise<unknown>) {
await request();
this.items.remove(item);
this.selectedItemsIds.delete(item.getId());
@ -173,19 +173,19 @@ export abstract class ItemStore<Item extends ItemObject> {
@action
toggleSelectionAll(visibleItems: Item[] = this.items) {
const allSelected = visibleItems.every(this.isSelected);
const allSelected = visibleItems.every(item => this.isSelected(item));
if (allSelected) {
visibleItems.forEach(this.unselect);
} else {
visibleItems.forEach(this.select);
}
visibleItems.forEach((
allSelected
? (item) => this.unselect(item)
: (item) => this.select(item)
));
}
isSelectedAll(visibleItems: Item[] = this.items) {
if (!visibleItems.length) return false;
return visibleItems.every(this.isSelected);
return visibleItems.every((item) => this.isSelected(item));
}
@action
@ -202,7 +202,7 @@ export abstract class ItemStore<Item extends ItemObject> {
this.isLoading = false;
}
async removeSelectedItems?(): Promise<any>;
async removeSelectedItems?(): Promise<unknown>;
async removeItems?(items: Item[]): Promise<void>;

View File

@ -47,7 +47,7 @@ describe("ApiManager", () => {
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
di.override(storesAndApisCanBeCreatedInjectable, () => true);
di.override(hostedClusterInjectable, () => new Cluster({
di.override(hostedClusterInjectable, () => Cluster.createForTestingOnly({
contextName: "some-context-name",
id: "some-cluster-id",
kubeConfigPath: "/some-path-to-a-kubeconfig",

View File

@ -12,7 +12,6 @@ import fetchInjectable from "../../fetch/fetch.injectable";
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import { flushPromises } from "@k8slens/test-utils";
import setupAutoRegistrationInjectable from "../../../renderer/before-frame-starts/runnables/setup-auto-registration.injectable";
import { createMockResponseFromString } from "../../../test-utils/mock-responses";
import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable";
@ -30,7 +29,7 @@ describe("KubeApi", () => {
let apiManager: ApiManager;
let di: DiContainer;
beforeEach(async () => {
beforeEach(() => {
di = getDiForUnitTesting();
fetchMock = asyncFn();
@ -40,17 +39,13 @@ describe("KubeApi", () => {
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
di.override(storesAndApisCanBeCreatedInjectable, () => true);
di.override(hostedClusterInjectable, () => new Cluster({
di.override(hostedClusterInjectable, () => Cluster.createForTestingOnly({
contextName: "some-context-name",
id: "some-cluster-id",
kubeConfigPath: "/some-path-to-a-kubeconfig",
}));
apiManager = di.inject(apiManagerInjectable);
const setupAutoRegistration = di.inject(setupAutoRegistrationInjectable);
setupAutoRegistration.run();
});
describe("on first call to IngressApi.get()", () => {
@ -722,7 +717,7 @@ describe("KubeApi", () => {
],
},
});
horizontalPodAutoscalerApi.get({
void horizontalPodAutoscalerApi.get({
name: "foo",
namespace: "default",
});

View File

@ -14,7 +14,6 @@ import asyncFn from "@async-fn/jest";
import { flushPromises } from "@k8slens/test-utils";
import createKubeJsonApiInjectable from "../create-kube-json-api.injectable";
import type { KubeStatusData } from "@k8slens/kube-object";
import setupAutoRegistrationInjectable from "../../../renderer/before-frame-starts/runnables/setup-auto-registration.injectable";
import { createMockResponseFromString } from "../../../test-utils/mock-responses";
import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
import directoryForUserDataInjectable from "../../app-paths/directory-for-user-data/directory-for-user-data.injectable";
@ -40,7 +39,7 @@ describe("createKubeApiForRemoteCluster", () => {
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
di.override(storesAndApisCanBeCreatedInjectable, () => true);
di.override(hostedClusterInjectable, () => new Cluster({
di.override(hostedClusterInjectable, () => Cluster.createForTestingOnly({
contextName: "some-context-name",
id: "some-cluster-id",
kubeConfigPath: "/some-path-to-a-kubeconfig",
@ -144,7 +143,7 @@ describe("KubeApi", () => {
const createKubeJsonApi = di.inject(createKubeJsonApiInjectable);
di.override(hostedClusterInjectable, () => new Cluster({
di.override(hostedClusterInjectable, () => Cluster.createForTestingOnly({
contextName: "some-context-name",
id: "some-cluster-id",
kubeConfigPath: "/some-path-to-a-kubeconfig",
@ -154,10 +153,6 @@ describe("KubeApi", () => {
serverAddress: `http://127.0.0.1:9999`,
apiBase: "/api-kube",
}));
const setupAutoRegistration = di.inject(setupAutoRegistrationInjectable);
setupAutoRegistration.run();
});
describe("deleting pods (namespace scoped resource)", () => {

View File

@ -3,12 +3,12 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { KubeObject } from "@k8slens/kube-object";
import { isJsonApiData, isJsonApiDataList, isPartialJsonApiData } from "@k8slens/kube-object";
describe("KubeObject", () => {
describe("isJsonApiData", () => {
{
type TestCase = [any];
type TestCase = [unknown];
const tests: TestCase[] = [
[false],
[true],
@ -22,12 +22,12 @@ describe("KubeObject", () => {
];
it.each(tests)("should reject invalid value: %p", (input) => {
expect(KubeObject.isJsonApiData(input)).toBe(false);
expect(isJsonApiData(input)).toBe(false);
});
}
{
type TestCase = [string, any];
type TestCase = [string, unknown];
const tests: TestCase[] = [
["kind", { apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}],
["apiVersion", { kind: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}],
@ -38,12 +38,12 @@ describe("KubeObject", () => {
];
it.each(tests)("should reject with missing: %s", (missingField, input) => {
expect(KubeObject.isJsonApiData(input)).toBe(false);
expect(isJsonApiData(input)).toBe(false);
});
}
{
type TestCase = [string, any];
type TestCase = [string, unknown];
const tests: TestCase[] = [
["kind", { kind: 1, apiVersion: "", metadata: {}}],
["apiVersion", { apiVersion: 1, kind: "", metadata: {}}],
@ -65,20 +65,20 @@ describe("KubeObject", () => {
];
it.each(tests)("should reject with wrong type for field: %s", (missingField, input) => {
expect(KubeObject.isJsonApiData(input)).toBe(false);
expect(isJsonApiData(input)).toBe(false);
});
}
it("should accept valid KubeJsonApiData (ignoring other fields)", () => {
const valid = { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", annotations: { food: "" }}};
expect(KubeObject.isJsonApiData(valid)).toBe(true);
expect(isJsonApiData(valid)).toBe(true);
});
});
describe("isPartialJsonApiData", () => {
{
type TestCase = [any];
type TestCase = [unknown];
const tests: TestCase[] = [
[false],
[true],
@ -91,16 +91,16 @@ describe("KubeObject", () => {
];
it.each(tests)("should reject invalid value: %p", (input) => {
expect(KubeObject.isPartialJsonApiData(input)).toBe(false);
expect(isPartialJsonApiData(input)).toBe(false);
});
}
it("should accept {}", () => {
expect(KubeObject.isPartialJsonApiData({})).toBe(true);
expect(isPartialJsonApiData({})).toBe(true);
});
{
type TestCase = [string, any];
type TestCase = [string, unknown];
const tests: TestCase[] = [
["kind", { apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}],
["apiVersion", { kind: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}],
@ -108,12 +108,12 @@ describe("KubeObject", () => {
];
it.each(tests)("should not reject with missing top level field: %s", (missingField, input) => {
expect(KubeObject.isPartialJsonApiData(input)).toBe(true);
expect(isPartialJsonApiData(input)).toBe(true);
});
}
{
type TestCase = [string, any];
type TestCase = [string, unknown];
const tests: TestCase[] = [
["kind", { kind: 1, apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}],
["apiVersion", { apiVersion: 1, kind: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "" }}],
@ -135,23 +135,23 @@ describe("KubeObject", () => {
];
it.each(tests)("should reject with wrong type for field: %s", (missingField, input) => {
expect(KubeObject.isPartialJsonApiData(input)).toBe(false);
expect(isPartialJsonApiData(input)).toBe(false);
});
}
it("should accept valid Partial<KubeJsonApiData> (ignoring other fields)", () => {
const valid = { kind: "", apiVersion: "", metadata: { uid: "", name: "", resourceVersion: "", selfLink: "", annotations: { food: "" }}};
expect(KubeObject.isPartialJsonApiData(valid)).toBe(true);
expect(isPartialJsonApiData(valid)).toBe(true);
});
});
describe("isJsonApiDataList", () => {
function isAny(val: unknown): val is any {
function isSomething(val: unknown): val is unknown {
return true;
}
function isNotAny(val: unknown): val is any {
function isNotSomething(val: unknown): val is unknown {
return false;
}
@ -160,7 +160,7 @@ describe("KubeObject", () => {
}
{
type TestCase = [any];
type TestCase = [unknown];
const tests: TestCase[] = [
[false],
[true],
@ -174,12 +174,12 @@ describe("KubeObject", () => {
];
it.each(tests)("should reject invalid value: %p", (input) => {
expect(KubeObject.isJsonApiDataList(input, isAny)).toBe(false);
expect(isJsonApiDataList(input, isSomething)).toBe(false);
});
}
{
type TestCase = [string, any];
type TestCase = [string, unknown];
const tests: TestCase[] = [
["kind", { apiVersion: "", items: [], metadata: { resourceVersion: "", selfLink: "" }}],
["apiVersion", { kind: "", items: [], metadata: { resourceVersion: "", selfLink: "" }}],
@ -187,12 +187,12 @@ describe("KubeObject", () => {
];
it.each(tests)("should reject with missing: %s", (missingField, input) => {
expect(KubeObject.isJsonApiDataList(input, isAny)).toBe(false);
expect(isJsonApiDataList(input, isSomething)).toBe(false);
});
}
{
type TestCase = [string, any];
type TestCase = [string, unknown];
const tests: TestCase[] = [
["kind", { kind: 1, items: [], apiVersion: "", metadata: { resourceVersion: "", selfLink: "" }}],
["apiVersion", { kind: "", items: [], apiVersion: 1, metadata: { resourceVersion: "", selfLink: "" }}],
@ -206,14 +206,14 @@ describe("KubeObject", () => {
];
it.each(tests)("should reject with wrong type for field: %s", (missingField, input) => {
expect(KubeObject.isJsonApiDataList(input, isNotAny)).toBe(false);
expect(isJsonApiDataList(input, isNotSomething)).toBe(false);
});
}
it("should accept valid KubeJsonApiDataList (ignoring other fields)", () => {
const valid = { kind: "", items: [false], apiVersion: "", metadata: { resourceVersion: "", selfLink: "" }};
expect(KubeObject.isJsonApiDataList(valid, isBoolean)).toBe(true);
expect(isJsonApiDataList(valid, isBoolean)).toBe(true);
});
});
});

View File

@ -13,10 +13,10 @@ import { parseKubeApi, createKubeApiURL } from "@k8slens/kube-api";
import { getOrInsertWith, iter } from "@k8slens/utilities";
import type { CreateCustomResourceStore } from "./create-custom-resource-store.injectable";
export type RegisterableStore<Store> = Store extends KubeObjectStore<any, any, any>
export type RegisterableStore<Store> = Store extends KubeObjectStore
? Store
: never;
export type RegisterableApi<Api> = Api extends KubeApi<any, any>
export type RegisterableApi<Api> = Api extends KubeApi
? Api
: never;
export type KubeObjectStoreFrom<Api> = Api extends KubeApi<infer KubeObj, infer ApiData>

View File

@ -1,25 +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 EventEmitter from "events";
import type TypedEventEmitter from "typed-emitter";
import type { KubeApi } from "@k8slens/kube-api";
export interface LegacyAutoRegistration {
kubeApi: (api: KubeApi<any, any>) => void;
}
/**
* This is used to remove dependency cycles from auto registering of instances
*
* - Custom Resource Definitions get their own registered store (will need in the future)
* - All KubeApi's get auto registered (this should be changed in the future)
*/
const autoRegistrationEmitterInjectable = getInjectable({
id: "auto-registration-emitter",
instantiate: (): TypedEventEmitter<LegacyAutoRegistration> => new EventEmitter(),
});
export default autoRegistrationEmitterInjectable;

View File

@ -2,9 +2,24 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { DiContainerForInjection } from "@ogre-tools/injectable";
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
import type { KubeObjectStore } from "../kube-object.store";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const kubeObjectStoreInjectionToken = getInjectionToken<KubeObjectStore<any, any, any>>({
id: "kube-object-store-token",
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export interface KubeStoreInjectableParts<Store extends KubeObjectStore<any, any, any>> {
id: string;
instantiate: (di: DiContainerForInjection) => Store;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const getKubeStoreInjectable = <Store extends KubeObjectStore<any, any, any>>(parts: KubeStoreInjectableParts<Store>) => getInjectable({
id: parts.id,
instantiate: (di) => parts.instantiate(di),
injectionToken: kubeObjectStoreInjectionToken,
});

View File

@ -24,7 +24,7 @@ const createJsonApiInjectable = getInjectable({
return (config, reqInit) => {
if (!config.getRequestOptions) {
config.getRequestOptions = async () => {
config.getRequestOptions = () => {
const agent = new Agent({
ca: lensProxyCert.get().cert,
});

View File

@ -19,16 +19,16 @@ export interface CreateKubeApiForLocalClusterConfig {
}
export interface CreateKubeApiForCluster {
<Object extends KubeObject, Api extends KubeApi<Object>, Data extends KubeJsonApiDataFor<Object>>(
<Kube extends KubeObject, Api extends KubeApi<Kube>, Data extends KubeJsonApiDataFor<Kube>>(
cluster: CreateKubeApiForLocalClusterConfig,
kubeClass: KubeObjectConstructor<Object, Data>,
apiClass: KubeApiConstructor<Object, Api>,
kubeClass: KubeObjectConstructor<Kube, Data>,
apiClass: KubeApiConstructor<Kube, Api>,
): Api;
<Object extends KubeObject, Data extends KubeJsonApiDataFor<Object>>(
<Kube extends KubeObject, Data extends KubeJsonApiDataFor<Kube>>(
cluster: CreateKubeApiForLocalClusterConfig,
kubeClass: KubeObjectConstructor<Object, Data>,
apiClass?: KubeApiConstructor<Object, KubeApi<Object>>,
): KubeApi<Object>;
kubeClass: KubeObjectConstructor<Kube, Data>,
apiClass?: KubeApiConstructor<Kube, KubeApi<Kube>>,
): KubeApi<Kube>;
}
const createKubeApiForClusterInjectable = getInjectable({

View File

@ -33,19 +33,19 @@ export interface CreateKubeApiForRemoteClusterConfig {
agent?: Agent;
}
export type KubeApiConstructor<Object extends KubeObject, Api extends KubeApi<Object>> = new (apiOpts: KubeApiOptions<Object>) => Api;
export type KubeApiConstructor<Kube extends KubeObject, Api extends KubeApi<Kube>> = new (apiOpts: KubeApiOptions<Kube>) => Api;
export interface CreateKubeApiForRemoteCluster {
<Object extends KubeObject, Api extends KubeApi<Object>, Data extends KubeJsonApiDataFor<Object>>(
<Kube extends KubeObject, Api extends KubeApi<Kube>, Data extends KubeJsonApiDataFor<Kube>>(
config: CreateKubeApiForRemoteClusterConfig,
kubeClass: KubeObjectConstructor<Object, Data>,
apiClass: KubeApiConstructor<Object, Api>,
kubeClass: KubeObjectConstructor<Kube, Data>,
apiClass: KubeApiConstructor<Kube, Api>,
): Api;
<Object extends KubeObject, Data extends KubeJsonApiDataFor<Object>>(
<Kube extends KubeObject, Data extends KubeJsonApiDataFor<Kube>>(
config: CreateKubeApiForRemoteClusterConfig,
kubeClass: KubeObjectConstructor<Object, Data>,
apiClass?: KubeApiConstructor<Object, KubeApi<Object>>,
): KubeApi<Object>;
kubeClass: KubeObjectConstructor<Kube, Data>,
apiClass?: KubeApiConstructor<Kube, KubeApi<Kube>>,
): KubeApi<Kube>;
}
const createKubeApiForRemoteClusterInjectable = getInjectable({

View File

@ -24,7 +24,7 @@ const createKubeJsonApiInjectable = getInjectable({
return (config, reqInit) => {
if (!config.getRequestOptions) {
config.getRequestOptions = async () => {
config.getRequestOptions = () => {
const agent = new Agent({
ca: lensProxyCert.get().cert,
});

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { ClusterRoleBindingApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const clusterRoleBindingApiInjectable = getInjectable({
const clusterRoleBindingApiInjectable = getKubeApiInjectable({
id: "cluster-role-binding-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "clusterRoleBindingApi is only accessible in certain environments");
@ -20,8 +19,6 @@ const clusterRoleBindingApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default clusterRoleBindingApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { ClusterRoleApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const clusterRoleApiInjectable = getInjectable({
const clusterRoleApiInjectable = getKubeApiInjectable({
id: "cluster-role-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "clusterRoleApi is only available in certain environments");
@ -20,8 +19,6 @@ const clusterRoleApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default clusterRoleApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { ClusterApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const clusterApiInjectable = getInjectable({
const clusterApiInjectable = getKubeApiInjectable({
id: "cluster-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "clusterApi is only available in certain environments");
@ -20,8 +19,6 @@ const clusterApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default clusterApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { ComponentStatusApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
import { loggerInjectionToken } from "@k8slens/logger";
const componentStatusApiInjectable = getInjectable({
const componentStatusApiInjectable = getKubeApiInjectable({
id: "component-status-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "componentStatusApi is only available in certain environments");
@ -20,8 +19,6 @@ const componentStatusApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default componentStatusApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { ConfigMapApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const configMapApiInjectable = getInjectable({
const configMapApiInjectable = getKubeApiInjectable({
id: "config-map-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "configMapApi is only available in certain environments");
@ -20,8 +19,6 @@ const configMapApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default configMapApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { CronJobApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const cronJobApiInjectable = getInjectable({
const cronJobApiInjectable = getKubeApiInjectable({
id: "cron-job-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "cronJobApi is only available in certain environments");
@ -22,8 +21,6 @@ const cronJobApiInjectable = getInjectable({
checkPreferredVersion: true,
});
},
injectionToken: kubeApiInjectionToken,
});
export default cronJobApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { CustomResourceDefinitionApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
import { loggerInjectionToken } from "@k8slens/logger";
const customResourceDefinitionApiInjectable = getInjectable({
const customResourceDefinitionApiInjectable = getKubeApiInjectable({
id: "custom-resource-definition-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "customResourceDefinitionApi is only available in certain environments");
@ -20,8 +19,6 @@ const customResourceDefinitionApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default customResourceDefinitionApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { DaemonSetApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const daemonSetApiInjectable = getInjectable({
const daemonSetApiInjectable = getKubeApiInjectable({
id: "daemon-set-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "daemonSetApi is only available in certain environments");
@ -20,8 +19,6 @@ const daemonSetApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default daemonSetApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { DeploymentApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const deploymentApiInjectable = getInjectable({
const deploymentApiInjectable = getKubeApiInjectable({
id: "deployment-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "deploymentApi is only available in certain environments");
@ -20,8 +19,6 @@ const deploymentApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default deploymentApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { EndpointsApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const endpointsApiInjectable = getInjectable({
const endpointsApiInjectable = getKubeApiInjectable({
id: "endpoints-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "endpointsApi is only available in certain environments");
@ -20,8 +19,6 @@ const endpointsApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default endpointsApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { KubeEventApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const kubeEventApiInjectable = getInjectable({
const kubeEventApiInjectable = getKubeApiInjectable({
id: "kube-event-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "kubeEventApi is only available in certain environments");
@ -20,8 +19,6 @@ const kubeEventApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default kubeEventApiInjectable;

View File

@ -3,9 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { array } from "@k8slens/utilities";
import autoBind from "auto-bind";
import Joi from "joi";
import { z } from "zod";
export interface RawHelmChart {
apiVersion: string;
@ -31,132 +30,51 @@ export interface RawHelmChart {
annotations?: Record<string, string>;
}
const helmChartMaintainerValidator = Joi.object<HelmChartMaintainer>({
name: Joi
.string()
.required(),
email: Joi
.string()
.required(),
url: Joi
.string()
.optional(),
const helmChartMaintainerSchema = z.object({
name: z.string(),
email: z.string(),
url: z.string().optional(),
});
const helmChartDependencyValidator = Joi.object<HelmChartDependency, true, RawHelmChartDependency>({
name: Joi
.string()
.required(),
repository: Joi
.string()
.required(),
condition: Joi
.string()
.optional(),
version: Joi
.string()
.required(),
tags: Joi
.array()
.items(Joi.string())
.default(() => ([])),
const helmChartDependencySchema = z.object({
name: z.string(),
repository: z.string(),
condition: z.string().optional(),
version: z.string(),
tags: z.array(z.string()).default(() => []),
});
const helmChartValidator = Joi.object<HelmChart, true, RawHelmChart>({
apiVersion: Joi
.string()
.required(),
name: Joi
.string()
.required(),
version: Joi
.string()
.required(),
repo: Joi
.string()
.required(),
created: Joi
.string()
.required(),
digest: Joi
.string()
const helmChartValidator = z.object({
apiVersion: z.string(),
name: z.string(),
version: z.string(),
repo: z.string(),
created: z.string(),
digest: z.string().optional(),
kubeVersion: z.string()
.optional(),
kubeVersion: Joi
.string()
.optional(),
description: Joi
.string()
description: z.string()
.default(""),
home: Joi
.string()
home: z.string()
.optional(),
engine: Joi
.string()
engine: z.string()
.optional(),
icon: Joi
.string()
icon: z.string()
.optional(),
appVersion: Joi
.string()
appVersion: z.string()
.optional(),
tillerVersion: Joi
.string()
tillerVersion: z.string()
.optional(),
type: Joi
.string()
type: z.string()
.optional(),
deprecated: Joi
.boolean()
deprecated: z.boolean()
.default(false),
keywords: Joi
.array()
.items(Joi.string())
.options({
stripUnknown: {
arrays: true,
},
})
.default(() => ([])),
sources: Joi
.array()
.items(Joi.string())
.options({
stripUnknown: {
arrays: true,
},
})
.default(() => ([])),
urls: Joi
.array()
.items(Joi.string())
.options({
stripUnknown: {
arrays: true,
},
})
.default(() => ([])),
maintainers: Joi
.array()
.items(helmChartMaintainerValidator)
.options({
stripUnknown: {
arrays: true,
},
})
.default(() => ([])),
dependencies: Joi
.array()
.items(helmChartDependencyValidator)
.options({
stripUnknown: {
arrays: true,
},
})
.default(() => ([])),
annotations: Joi
.object({})
.pattern(/.*/, Joi.string())
.default(() => ({})),
keywords: z.array(z.string()).default(() => ([])),
sources: z.array(z.string()).default(() => ([])),
urls: z.array(z.string()).default(() => ([])),
maintainers: z.array(helmChartMaintainerSchema).default(() => ([])),
dependencies: z.array(helmChartDependencySchema).default(() => ([])),
annotations: z.record(z.string()).default(() => ({})),
});
export interface HelmChartCreateOpts {
@ -256,31 +174,17 @@ export class HelmChart implements HelmChartData {
static create(data: RawHelmChart): HelmChart;
static create(data: RawHelmChart, opts?: HelmChartCreateOpts): HelmChart | undefined;
static create(data: RawHelmChart, { onError = "throw" }: HelmChartCreateOpts = {}): HelmChart | undefined {
const result = helmChartValidator.validate(data, {
abortEarly: false,
});
const result = helmChartValidator.safeParse(data);
if (!result.error) {
return new HelmChart(result.value);
if (result.success) {
return new HelmChart(result.data);
}
const [actualErrors, unknownDetails] = array.bifurcate(result.error.details, ({ type }) => type === "object.unknown");
if (unknownDetails.length > 0) {
console.warn("HelmChart data has unexpected fields", { original: data, unknownFields: unknownDetails.flatMap(d => d.path) });
}
if (actualErrors.length === 0) {
return new HelmChart(result.value as unknown as HelmChartData);
}
const validationError = new Joi.ValidationError(actualErrors.map(er => er.message).join(". "), actualErrors, result.error._original);
if (onError === "throw") {
throw validationError;
throw result.error;
}
console.warn("[HELM-CHART]: failed to validate data", data, validationError);
console.warn("[HELM-CHART]: failed to validate data", data, result.error);
return undefined;
}
@ -289,16 +193,20 @@ export class HelmChart implements HelmChartData {
const digestPart = this.digest
? `+${this.digest}`
: "";
const version = this.getAppVersion();
const versionId = version
? `@${version}`
: "";
return `${this.repo}:${this.apiVersion}/${this.name}@${this.getAppVersion()}${digestPart}`;
return `${this.repo}:${this.apiVersion}/${this.name}${versionId}${digestPart}`;
}
getName(): string {
return this.name;
}
getFullName(seperator = "/"): string {
return [this.getRepository(), this.getName()].join(seperator);
getFullName(separator = "/"): string {
return [this.getRepository(), this.getName()].join(separator);
}
getDescription(): string {

View File

@ -9,7 +9,7 @@ import apiBaseInjectable from "../../api-base.injectable";
const requestReadmeEndpoint = urlBuilderFor("/v2/charts/:repo/:name/readme");
export type RequestHelmChartReadme = (repo: string, name: string, version?: string) => AsyncResult<string>;
export type RequestHelmChartReadme = (repo: string, name: string, version?: string) => AsyncResult<string, string>;
const requestHelmChartReadmeInjectable = getInjectable({
id: "request-helm-chart-readme",

View File

@ -9,7 +9,7 @@ import apiBaseInjectable from "../../api-base.injectable";
const requestValuesEndpoint = urlBuilderFor("/v2/charts/:repo/:name/values");
export type RequestHelmChartValues = (repo: string, name: string, version: string) => AsyncResult<string>;
export type RequestHelmChartValues = (repo: string, name: string, version: string) => AsyncResult<string, string>;
const requestHelmChartValuesInjectable = getInjectable({
id: "request-helm-chart-values",

View File

@ -38,10 +38,10 @@ const requestHelmReleaseUpdateInjectable = getInjectable({
},
});
} catch (e) {
return { callWasSuccessful: false, error: e };
return { isOk: false, error: e };
}
return { callWasSuccessful: true };
return { isOk: true };
};
},
});

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { HorizontalPodAutoscalerApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const horizontalPodAutoscalerApiInjectable = getInjectable({
const horizontalPodAutoscalerApiInjectable = getKubeApiInjectable({
id: "horizontal-pod-autoscaler-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "horizontalPodAutoscalerApi is only available in certain environments");
@ -20,8 +19,6 @@ const horizontalPodAutoscalerApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default horizontalPodAutoscalerApiInjectable;

View File

@ -2,20 +2,23 @@
* 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 { IngressClassApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import assert from "assert";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
const ingressClassApiInjectable = getInjectable({
const ingressClassApiInjectable = getKubeApiInjectable({
id: "ingress-class-api",
instantiate: (di) => new IngressClassApi({
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "ingressClassApi is only available in certain environments");
return new IngressClassApi({
logger: di.inject(loggerInjectionToken),
maybeKubeApi: di.inject(maybeKubeApiInjectable),
}),
injectionToken: kubeApiInjectionToken,
});
},
});
export default ingressClassApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { IngressApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const ingressApiInjectable = getInjectable({
const ingressApiInjectable = getKubeApiInjectable({
id: "ingress-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "ingressApi is only available in certain environments");
@ -20,8 +19,6 @@ const ingressApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default ingressApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { JobApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const jobApiInjectable = getInjectable({
const jobApiInjectable = getKubeApiInjectable({
id: "job-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "jobApi is only available in certain environments");
@ -22,8 +21,6 @@ const jobApiInjectable = getInjectable({
checkPreferredVersion: true,
});
},
injectionToken: kubeApiInjectionToken,
});
export default jobApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { LeaseApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const leaseApiInjectable = getInjectable({
const leaseApiInjectable = getKubeApiInjectable({
id: "lease-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "leaseApi is only available in certain environments");
@ -20,8 +19,6 @@ const leaseApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default leaseApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { LimitRangeApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const limitRangeApiInjectable = getInjectable({
const limitRangeApiInjectable = getKubeApiInjectable({
id: "limit-range-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "limitRangeApi is only available in certain environments");
@ -20,8 +19,6 @@ const limitRangeApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default limitRangeApiInjectable;

View File

@ -2,26 +2,23 @@
* 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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { MutatingWebhookConfigurationApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const mutatingWebhookConfigurationApiInjectable = getInjectable({
const mutatingWebhookConfigurationApiInjectable = getKubeApiInjectable({
id: "mutating-webhook-configuration",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "mutatingWebhookApi is only available in certain environments");
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "mutatingWebhookConfigurationApi is only available in certain environments");
return new MutatingWebhookConfigurationApi({
logger: di.inject(loggerInjectionToken),
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default mutatingWebhookConfigurationApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { NamespaceApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const namespaceApiInjectable = getInjectable({
const namespaceApiInjectable = getKubeApiInjectable({
id: "namespace-api",
instantiate: (di) => {
@ -21,8 +20,6 @@ const namespaceApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default namespaceApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { NetworkPolicyApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const networkPolicyApiInjectable = getInjectable({
const networkPolicyApiInjectable = getKubeApiInjectable({
id: "network-policy-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "networkPolicyApi is only available in certain environments");
@ -20,8 +19,6 @@ const networkPolicyApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default networkPolicyApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { NodeApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const nodeApiInjectable = getInjectable({
const nodeApiInjectable = getKubeApiInjectable({
id: "node-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "nodeApi is only available in certain environments");
@ -20,8 +19,6 @@ const nodeApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default nodeApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { PersistentVolumeClaimApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const persistentVolumeClaimApiInjectable = getInjectable({
const persistentVolumeClaimApiInjectable = getKubeApiInjectable({
id: "persistent-volume-claim-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "persistentVolumeClaimApi is only available in certain environments");
@ -20,8 +19,6 @@ const persistentVolumeClaimApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default persistentVolumeClaimApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { PersistentVolumeApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const persistentVolumeApiInjectable = getInjectable({
const persistentVolumeApiInjectable = getKubeApiInjectable({
id: "persistent-volume-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "persistentVolumeApi is only available in certain environments");
@ -20,8 +19,6 @@ const persistentVolumeApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default persistentVolumeApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { PodDisruptionBudgetApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const podDisruptionBudgetApiInjectable = getInjectable({
const podDisruptionBudgetApiInjectable = getKubeApiInjectable({
id: "pod-disruption-budget-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "podDisruptionBudgetApi is only available in certain environments");
@ -28,8 +27,6 @@ const podDisruptionBudgetApiInjectable = getInjectable({
},
});
},
injectionToken: kubeApiInjectionToken,
});
export default podDisruptionBudgetApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { PodMetricsApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const podMetricsApiInjectable = getInjectable({
const podMetricsApiInjectable = getKubeApiInjectable({
id: "pod-metrics-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "podMetricsApi is only available in certain environments");
@ -20,8 +19,6 @@ const podMetricsApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default podMetricsApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { PodSecurityPolicyApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const podSecurityPolicyApiInjectable = getInjectable({
const podSecurityPolicyApiInjectable = getKubeApiInjectable({
id: "pod-security-policy-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "podSecurityPolicyApi is only available in certain environments");
@ -20,8 +19,6 @@ const podSecurityPolicyApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default podSecurityPolicyApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { PodApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const podApiInjectable = getInjectable({
const podApiInjectable = getKubeApiInjectable({
id: "pod-api",
instantiate: (di) => {
@ -21,8 +20,6 @@ const podApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default podApiInjectable;

View File

@ -2,26 +2,23 @@
* 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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { PriorityClassApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const priorityClassApiInjectable = getInjectable({
const priorityClassApiInjectable = getKubeApiInjectable({
id: "priority-class-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "PriorityClassApi is only available in certain environments");
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "priorityClassApi is only available in certain environments");
return new PriorityClassApi({
logger: di.inject(loggerInjectionToken),
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default priorityClassApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { ReplicaSetApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const replicaSetApiInjectable = getInjectable({
const replicaSetApiInjectable = getKubeApiInjectable({
id: "replica-set-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "replicaSetApi is only available in certain environments");
@ -20,8 +19,6 @@ const replicaSetApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default replicaSetApiInjectable;

View File

@ -2,22 +2,23 @@
* 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 { ReplicationControllerApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import assert from "assert";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
const replicationControllerApiInjectable = getInjectable({
const replicationControllerApiInjectable = getKubeApiInjectable({
id: "replication-controller-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "replicationControllerApi is only available in certain environments");
return new ReplicationControllerApi({
logger: di.inject(loggerInjectionToken),
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default replicationControllerApiInjectable;

View File

@ -25,20 +25,20 @@ const requestKubeObjectPatchInjectable = getInjectable({
},
})) as Result<string, string>;
if (!result.callWasSuccessful) {
if (!result.isOk) {
return result;
}
try {
const response = JSON.parse(result.response) as KubeJsonApiData;
const response = JSON.parse(result.value) as KubeJsonApiData;
return {
callWasSuccessful: true,
isOk: true,
response,
};
} catch (error) {
return {
callWasSuccessful: false,
isOk: false,
error: String(error),
};
}

View File

@ -17,20 +17,20 @@ const requestKubeObjectCreationInjectable = getInjectable({
return async (data) => {
const result = await apiBase.post("/stack", { data }) as Result<string, string>;
if (!result.callWasSuccessful) {
if (!result.isOk) {
return result;
}
try {
const response = JSON.parse(result.response);
const response = JSON.parse(result.value) as KubeJsonApiData;
return {
callWasSuccessful: true,
isOk: true,
response,
};
} catch (error) {
return {
callWasSuccessful: false,
isOk: false,
error: String(error),
};
}

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { ResourceQuotaApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const resourceQuotaApiInjectable = getInjectable({
const resourceQuotaApiInjectable = getKubeApiInjectable({
id: "resource-quota-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "resourceQuotaApi is only available in certain environments");
@ -20,8 +19,6 @@ const resourceQuotaApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default resourceQuotaApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { RoleBindingApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const roleBindingApiInjectable = getInjectable({
const roleBindingApiInjectable = getKubeApiInjectable({
id: "role-binding-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "roleBindingApi is only available in certain environments");
@ -20,8 +19,6 @@ const roleBindingApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default roleBindingApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { RoleApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const roleApiInjectable = getInjectable({
const roleApiInjectable = getKubeApiInjectable({
id: "role-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "roleApi is only available in certain environments");
@ -20,8 +19,6 @@ const roleApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default roleApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { RuntimeClassApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const runtimeClassApiInjectable = getInjectable({
const runtimeClassApiInjectable = getKubeApiInjectable({
id: "runtime-class-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "RuntimeClassApi is only available in certain environments");
@ -20,8 +19,6 @@ const runtimeClassApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default runtimeClassApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { SecretApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const secretApiInjectable = getInjectable({
const secretApiInjectable = getKubeApiInjectable({
id: "secret-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "secretApi is only available in certain environments");
@ -20,8 +19,6 @@ const secretApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default secretApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { SelfSubjectRulesReviewApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const selfSubjectRulesReviewApiInjectable = getInjectable({
const selfSubjectRulesReviewApiInjectable = getKubeApiInjectable({
id: "self-subject-rules-review-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "selfSubjectRulesReviewApi is only available in certain environments");
@ -20,8 +19,6 @@ const selfSubjectRulesReviewApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default selfSubjectRulesReviewApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { ServiceAccountApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const serviceAccountApiInjectable = getInjectable({
const serviceAccountApiInjectable = getKubeApiInjectable({
id: "service-account-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "serviceAccountApi is only available in certain environments");
@ -20,8 +19,6 @@ const serviceAccountApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default serviceAccountApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { ServiceApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const serviceApiInjectable = getInjectable({
const serviceApiInjectable = getKubeApiInjectable({
id: "service-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "serviceApi is only available in certain environments");
@ -20,8 +19,6 @@ const serviceApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default serviceApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { StatefulSetApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const statefulSetApiInjectable = getInjectable({
const statefulSetApiInjectable = getKubeApiInjectable({
id: "stateful-set-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "statefulSetApi is only available in certain environments");
@ -20,8 +19,6 @@ const statefulSetApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default statefulSetApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { StorageClassApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const storageClassApiInjectable = getInjectable({
const storageClassApiInjectable = getKubeApiInjectable({
id: "storage-class-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "storageClassApi is only available in certain environments");
@ -20,8 +19,6 @@ const storageClassApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default storageClassApiInjectable;

View File

@ -2,26 +2,23 @@
* 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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { ValidatingWebhookConfigurationApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const validatingWebhookConfigurationApiInjectable = getInjectable({
id: "validating-webhook-configuration",
const validatingWebhookConfigurationApiInjectable = getKubeApiInjectable({
id: "validating-webhook-configuration-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "validatingWebhookApi is only available in certain environments");
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "validatingWebhookConfigurationApi is only available in certain environments");
return new ValidatingWebhookConfigurationApi({
logger: di.inject(loggerInjectionToken),
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default validatingWebhookConfigurationApiInjectable;

View File

@ -2,15 +2,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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { VerticalPodAutoscalerApi } from "@k8slens/kube-api";
import { kubeApiInjectionToken } from "@k8slens/kube-api-specifics";
import { getKubeApiInjectable } from "@k8slens/kube-api-specifics";
import { loggerInjectionToken } from "@k8slens/logger";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
const verticalPodAutoscalerApiInjectable = getInjectable({
const verticalPodAutoscalerApiInjectable = getKubeApiInjectable({
id: "vertical-pod-autoscaler-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "verticalPodAutoscalerApi is only available in certain environments");
@ -20,8 +19,6 @@ const verticalPodAutoscalerApiInjectable = getInjectable({
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default verticalPodAutoscalerApiInjectable;

View File

@ -97,6 +97,14 @@ export class KubeObjectStore<
private readonly loadedNamespaces = observable.box<string[]>();
static create<Api>(
dependencies: KubeObjectStoreDependencies,
api: Api,
opts?: KubeObjectStoreOptions,
): Api extends KubeApi<infer Kube, infer Data> ? KubeObjectStore<Kube, Api, Data> : never {
return new KubeObjectStore(dependencies, api as KubeApi, opts) as never;
}
constructor(
protected readonly dependencies: KubeObjectStoreDependencies,
public readonly api: A,
@ -297,7 +305,7 @@ export class KubeObjectStore<
return items;
}
protected resetOnError(error: any) {
protected resetOnError(error: unknown) {
if (error) this.reset();
}

View File

@ -16,8 +16,8 @@ import type { ReadFile } from "../fs/read-file.injectable";
import { hasTypedProperty, isObject } from "@k8slens/utilities";
export interface ResourceApplyingStack {
kubectlApplyFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise<string>;
kubectlDeleteFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise<string>;
kubectlApplyFolder(folderPath: string, templateContext?: unknown, extraArgs?: string[]): Promise<string>;
kubectlDeleteFolder(folderPath: string, templateContext?: unknown, extraArgs?: string[]): Promise<string>;
}
export interface ResourceStackDependencies {
@ -41,12 +41,12 @@ export class ResourceStack {
* @param folderPath folder path that is searched for files defining kubernetes resources.
* @param templateContext sets the template parameters that are to be applied to any templated kubernetes resources that are to be applied.
*/
async kubectlApplyFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise<string> {
async kubectlApplyFolder(folderPath: string, templateContext?: unknown, extraArgs?: string[]): Promise<string> {
const resources = await this.renderTemplates(folderPath, templateContext);
const result = await this.applyResources(resources, extraArgs);
if (result.callWasSuccessful) {
return result.response;
if (result.isOk) {
return result.value;
}
this.dependencies.logger.warn(`[RESOURCE-STACK]: failed to apply resources: ${result.error}`);
@ -59,12 +59,12 @@ export class ResourceStack {
* @param folderPath folder path that is searched for files defining kubernetes resources.
* @param templateContext sets the template parameters that are to be applied to any templated kubernetes resources that are to be applied.
*/
async kubectlDeleteFolder(folderPath: string, templateContext?: any, extraArgs?: string[]): Promise<string> {
async kubectlDeleteFolder(folderPath: string, templateContext?: unknown, extraArgs?: string[]): Promise<string> {
const resources = await this.renderTemplates(folderPath, templateContext);
const result = await this.deleteResources(resources, extraArgs);
if (result.callWasSuccessful) {
return result.response;
if (result.isOk) {
return result.value;
}
this.dependencies.logger.warn(`[RESOURCE-STACK]: failed to delete resources: ${result.error}`);
@ -100,7 +100,7 @@ export class ResourceStack {
return [];
}
protected async renderTemplates(folderPath: string, templateContext: any): Promise<string[]> {
protected async renderTemplates(folderPath: string, templateContext: unknown): Promise<string[]> {
const resources: string[] = [];
const di = getLegacyGlobalDiForExtensionApi();
const productName = di.inject(productNameInjectable);

View File

@ -7,64 +7,55 @@ import { KubeConfig } from "@kubernetes/client-node";
import yaml from "js-yaml";
import type { Cluster, Context, User } from "@kubernetes/client-node/dist/config_types";
import { newClusters, newContexts, newUsers } from "@kubernetes/client-node/dist/config_types";
import type { Result } from "@k8slens/utilities";
import { isDefined } from "@k8slens/utilities";
import Joi from "joi";
import type { PartialDeep } from "type-fest";
import z from "zod";
import type { ZodError } from "zod";
const clusterSchema = Joi.object({
name: Joi
const userSchema = z.object({
name: z.string()
.min(1),
});
const clusterSchema = z.object({
name: z
.string()
.min(1)
.required(),
cluster: Joi
.min(1),
cluster: z
.object({
server: Joi
server: z
.string()
.min(1)
.required(),
})
.required(),
});
const userSchema = Joi.object({
name: Joi.string()
.min(1)
.required(),
});
const contextSchema = Joi.object({
name: Joi.string()
.min(1)
.required(),
context: Joi.object({
cluster: Joi.string()
.min(1)
.required(),
user: Joi.string()
.min(1)
.required(),
.min(1),
}),
});
const kubeConfigSchema = Joi.object({
users: Joi
.array()
.items(userSchema)
const contextSchema = z.object({
name: z.string()
.min(1),
context: z.object({
cluster: z.string()
.min(1),
user: z.string()
.min(1),
}),
});
const kubeConfigSchema = z.object({
users: z
.array(userSchema)
.optional(),
clusters: Joi
.array()
.items(clusterSchema)
clusters: z
.array(clusterSchema)
.optional(),
contexts: Joi
.array()
.items(contextSchema)
contexts: z
.array(contextSchema)
.optional(),
"current-context": Joi
"current-context": z
.string()
.min(1)
.optional(),
})
.required();
});
interface KubeConfigOptions {
clusters: Cluster[];
@ -73,37 +64,25 @@ interface KubeConfigOptions {
currentContext?: string;
}
interface OptionsResult {
options: KubeConfigOptions;
error: Joi.ValidationError | undefined;
}
function loadToOptions(rawYaml: string): OptionsResult {
function loadToOptions(rawYaml: string): Result<KubeConfigOptions, ZodError<unknown>> {
const parsed = yaml.load(rawYaml);
const { error } = kubeConfigSchema.validate(parsed, {
abortEarly: false,
allowUnknown: true,
});
const { value } = kubeConfigSchema.validate(parsed, {
abortEarly: false,
allowUnknown: true,
stripUnknown: {
arrays: true,
},
});
const {
clusters: rawClusters,
users: rawUsers,
contexts: rawContexts,
"current-context": currentContext,
} = value ?? {};
const clusters = newClusters(rawClusters);
const users = newUsers(rawUsers);
const contexts = newContexts(rawContexts);
const configParseResult = kubeConfigSchema.safeParse(parsed);
if (configParseResult.success === false) {
return {
isOk: false,
error: configParseResult.error,
};
}
return {
options: { clusters, users, contexts, currentContext },
error,
isOk: true,
value: {
clusters: newClusters(configParseResult.data.clusters),
users: newUsers(configParseResult.data.users),
contexts: newContexts(configParseResult.data.contexts),
currentContext: configParseResult.data["current-context"],
},
};
}
@ -116,33 +95,32 @@ export function loadFromOptions(options: KubeConfigOptions): KubeConfig {
return kc;
}
export interface ConfigResult {
config: KubeConfig;
error: Joi.ValidationError | undefined;
}
export function loadConfigFromString(content: string): Result<KubeConfig, ZodError<unknown>> {
const loadResult = loadToOptions(content);
export function loadConfigFromString(content: string): ConfigResult {
const { options, error } = loadToOptions(content);
if (loadResult.isOk === false) {
return loadResult;
}
return {
config: loadFromOptions(options),
error,
isOk: true,
value: loadFromOptions(loadResult.value),
};
}
export function loadValidatedConfig(content: string, contextName: string): ValidateKubeConfigResult {
const { options, error } = loadToOptions(content);
export function loadValidatedConfig(content: string, contextName: string): Result<PartialKubeConfig, ZodError<unknown> | string> {
const result = loadToOptions(content);
if (error) {
return { error };
if (result.isOk === false) {
return result;
}
return validateKubeConfig(loadFromOptions(options), contextName);
return validateKubeConfig(loadFromOptions(result.value), contextName);
}
export interface SplitConfigEntry {
config: KubeConfig;
validationResult: ValidateKubeConfigResult;
validationResult: Result<PartialKubeConfig, ZodError<unknown> | string>;
}
/**
@ -212,8 +190,8 @@ export function dumpConfigYaml(kubeConfig: PartialDeep<KubeConfig>): string {
"client-certificate": user.certFile,
"client-key-data": user.keyData,
"client-key": user.keyFile,
"auth-provider": user.authProvider,
exec: user.exec,
"auth-provider": user.authProvider as unknown,
exec: user.exec as unknown,
token: user.token,
username: user.username,
password: user.password,
@ -233,26 +211,24 @@ export function dumpConfigYaml(kubeConfig: PartialDeep<KubeConfig>): string {
return yaml.dump(config, { skipInvalid: true });
}
export type ValidateKubeConfigResult = {
error: Error;
} | {
error?: undefined;
context: Context;
cluster: Cluster;
user: User;
};
export interface PartialKubeConfig {
readonly context: Context;
readonly cluster: Cluster;
readonly user: User;
}
/**
* Checks if `config` has valid `Context`, `User`, `Cluster`, and `exec` fields (if present when required)
*
* Note: This function returns an error instead of throwing it, returning `undefined` if the validation passes
*/
export function validateKubeConfig(config: KubeConfig, contextName: string): ValidateKubeConfigResult {
export function validateKubeConfig(config: KubeConfig, contextName: string): Result<PartialKubeConfig, string> {
const context = config.getContextObject(contextName);
if (!context) {
return {
error: new Error(`No valid context object provided in kubeconfig for context '${contextName}'`),
isOk: false,
error: `No valid context object provided in kubeconfig for context '${contextName}'`,
};
}
@ -260,7 +236,8 @@ export function validateKubeConfig(config: KubeConfig, contextName: string): Val
if (!cluster) {
return {
error: new Error(`No valid cluster object provided in kubeconfig for context '${contextName}'`),
isOk: false,
error: `No valid cluster object provided in kubeconfig for context '${contextName}'`,
};
}
@ -268,9 +245,13 @@ export function validateKubeConfig(config: KubeConfig, contextName: string): Val
if (!user) {
return {
error: new Error(`No valid user object provided in kubeconfig for context '${contextName}'`),
isOk: false,
error: `No valid user object provided in kubeconfig for context '${contextName}'`,
};
}
return { cluster, user, context };
return {
isOk: true,
value: { cluster, user, context },
};
}

View File

@ -2,13 +2,15 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { AsyncResult } from "@k8slens/utilities";
import type { KubeConfig } from "@kubernetes/client-node";
import { getInjectable } from "@ogre-tools/injectable";
import type { ZodError } from "zod";
import readFileInjectable from "../fs/read-file.injectable";
import type { ConfigResult } from "../kube-helpers";
import { loadConfigFromString } from "../kube-helpers";
import resolveTildeInjectable from "../path/resolve-tilde.injectable";
export type LoadConfigFromFile = (filePath: string) => Promise<ConfigResult>;
export type LoadConfigFromFile = (filePath: string) => AsyncResult<KubeConfig, ZodError<unknown>>;
const loadConfigFromFileInjectable = getInjectable({
id: "load-config-from-file",

Some files were not shown because too many files have changed in this diff Show More