mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Mobx-6 migration (#2718)
* mobx-6 migration -- part 1
Signed-off-by: Roman <ixrock@gmail.com>
* mobx-6 migration -- part 2 (npx mobx-undecorate --keepDecorators)
Signed-off-by: Roman <ixrock@gmail.com>
* mobx-6 migration -- part 3 (more fixes)
Signed-off-by: Roman <ixrock@gmail.com>
* unwrap possible observables from IPC-messaging
Signed-off-by: Roman <ixrock@gmail.com>
* mobx-6 migration -- remove @autobind as class-decorator
Signed-off-by: Roman <ixrock@gmail.com>
* mobx-6: replacing @autobind() as method-decorator to @boundMethod
Signed-off-by: Roman <ixrock@gmail.com>
* mobx-6: use toJS()-wrapper since monkey-patching require(mobx).toJS doesn't work
Signed-off-by: Roman <ixrock@gmail.com>
* removed `@observable static`
Signed-off-by: Roman <ixrock@gmail.com>
* use {useDefineForClassFields: true} in tsconfig.json
Signed-off-by: Roman <ixrock@gmail.com>
* remove ExtendedObservableMap
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
* fix: removed makeObservable(this) from "terminal-tab.tsx"
Signed-off-by: Roman <ixrock@gmail.com>
* storage-helper refactoring
Signed-off-by: Roman <ixrock@gmail.com>
* normalize usages of #observable-value.toJSON() / attempt to catch the wind
Signed-off-by: Roman <ixrock@gmail.com>
* refactoring, more possible branch fixes + lint
Signed-off-by: Roman <ixrock@gmail.com>
* debugging cluster-view error -- part 1
Signed-off-by: Roman <ixrock@gmail.com>
* fix: refreshing cluster-view on ready
Signed-off-by: Roman <ixrock@gmail.com>
* fix: various app-crashes related to KubeObject.spec.* access from "undefined"
fix: config-map-details crash
Signed-off-by: Roman <ixrock@gmail.com>
* fix: namespace-store refactoring / saving selected-namespaces to external json-file
Signed-off-by: Roman <ixrock@gmail.com>
* fix: don't cache mobx.when(() => this.someObservable) cause might not work as expected due later call of makeObservable(this) in constructor
Signed-off-by: Roman <ixrock@gmail.com>
* fix: app-crash on editing k8s resource
Signed-off-by: Roman <ixrock@gmail.com>
* fix: restore "all namespaces" on page reload
Signed-off-by: Roman <ixrock@gmail.com>
* - fix: persist table-sort params and cluster-view's sidebar state to lens-local-storage
- new-feature: auto-open main-window's devtools in development-mode (yes/no/ugly?)
Signed-off-by: Roman <ixrock@gmail.com>
* fix: crd definition details -> crashing with <AceEditor mode="json"> (added missing mode-file in ace-editor.tsx)
Signed-off-by: Roman <ixrock@gmail.com>
* fix: crd definitions -> groups selector couldn't deselect last selected option
Signed-off-by: Roman <ixrock@gmail.com>
* refactoring: extensions-api exports clarification for "@k8slens/extensions"
Signed-off-by: Roman <ixrock@gmail.com>
* fix: various app-crashes related to kube-events (events page, some details page, overview, etc.)
Signed-off-by: Roman <ixrock@gmail.com>
* Reverted "use {useDefineForClassFields: true} in tsconfig.json" (various app-crash fixes)
This flag seems to be not possible to use with class-inheritance in some cases.
Example / demo:
`KubeObject` class has initial type definitions for the fields like: "metadata", "kind", etc.
and constructor() has Object.assign(this, data);
Meanwhile child class, e.g. KubeEvent inherited from KubeObject and has it's own extra type definitions for underlying resource, e.g. "involvedObject", "source", etc.
So calling super(data) doesn't work as expected for child class as it's own type definitions overwrites data from parent's constructor with `undefined` at later point.
Signed-off-by: Roman <ixrock@gmail.com>
* master-merge lint-fixes
Signed-off-by: Roman <ixrock@gmail.com>
* catalog.tsx / catalog-entities.store.ts refactoring & fixes
Signed-off-by: Roman <ixrock@gmail.com>
* fix: Catalog -> Browse all tab
Signed-off-by: Roman <ixrock@gmail.com>
* fix: CommandPalette doesn't appear from global menu by click/hotkey
Signed-off-by: Roman <ixrock@gmail.com>
* - Merging interfaces & classses to avoid overwriting fields from parent's super(data)-call with Object.assign(this, data). Otherwise use "declare" keyword at class field definition.
- Revamping {useDefineForClassFields: true} to avoid issues with non-observable class fields in some cases (from previous commit):
```
@observer
export class CommandContainer extends React.Component<CommandContainerProps> {
// without some defined initial value "commandComponent" is non-observable for some reasons
// when tsconfig.ts has {useDefineForClassFields:false}
@observable.ref commandComponent: React.ReactNode = null;
constructor(props: CommandContainerProps) {
super(props);
makeObservable(this);
}
```
Signed-off-by: Roman <ixrock@gmail.com>
* update KubeObject class type definition
Signed-off-by: Roman <ixrock@gmail.com>
* clean up / responding to comments
Signed-off-by: Roman <ixrock@gmail.com>
* fix: app-crash when navigating to catalog from active cluster-view, refactoring `catalog-entity-store`
Signed-off-by: Roman <ixrock@gmail.com>
* catalog-pusher clean up, replaced .observe_() to external observe() helper from "mobx"
Signed-off-by: Roman <ixrock@gmail.com>
* fix: catalog's items stale/non-observable (after connection to the cluster status still "disconnected"), lint-fixes
Signed-off-by: Roman <ixrock@gmail.com>
* fix: Catalog is empty after closing main-window and re-opening app from Tray
Signed-off-by: Roman <ixrock@gmail.com>
* fix: HotBar's icon context menu items non-observable (no "disconnect cluster", etc.)
Signed-off-by: Roman <ixrock@gmail.com>
* lint-fix/license check
Signed-off-by: Roman <ixrock@gmail.com>
* fix: redirect to catalog when disconnecting active cluster
Signed-off-by: Roman <ixrock@gmail.com>
* fix: refresh visibility of active cluster-view on switching from hotbar/catalog
Signed-off-by: Roman <ixrock@gmail.com>
* updated package.json for built-in extensions to use "*" version for packages served from main app
Signed-off-by: Roman <ixrock@gmail.com>
* - added missing makeObservable(this) to metrics-settings.tsx
- updated package-lock.json for built-in extensions
- lint fixes
Signed-off-by: Roman <ixrock@gmail.com>
* master-merge clean up fix, updated package-lock.json for built-in extensions after `make clean-extensions && make build-extensions`
Signed-off-by: Roman <ixrock@gmail.com>
* fix unit-tests
Signed-off-by: Roman <ixrock@gmail.com>
* master-merge fixes
Signed-off-by: Roman <ixrock@gmail.com>
* make lint happy
Signed-off-by: Roman <ixrock@gmail.com>
* reverted some changes, removed auto-opening devtools in dev-mode
Signed-off-by: Roman <ixrock@gmail.com>
* merge fixes
Signed-off-by: Roman <ixrock@gmail.com>
* master-merge conflict fixes:
- proper handling and navigating into catalog's active category via URL-builder
Signed-off-by: Roman <ixrock@gmail.com>
* reverting splitted params for catalog's page route to "/catalog/:group?/:kind?"
Signed-off-by: Roman <ixrock@gmail.com>
* clean-up: remove app's injecting dependencies from `extensions/kube-object-event-status/package.json`
Signed-off-by: Roman <ixrock@gmail.com>
* master-merge fix: added missing makeObservable(this) for extensions.tsx
Signed-off-by: Roman <ixrock@gmail.com>
* fix: catalog entity context menu stale/unobservable
Signed-off-by: Roman <ixrock@gmail.com>
Co-authored-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
0c45dd807e
commit
2c3b510997
@ -25,7 +25,7 @@ The following example code creates a store for the `appPreferences` guide exampl
|
||||
|
||||
``` typescript
|
||||
import { Store } from "@k8slens/extensions";
|
||||
import { observable, toJS } from "mobx";
|
||||
import { observable, makeObservable } from "mobx";
|
||||
|
||||
export type ExamplePreferencesModel = {
|
||||
enabled: boolean;
|
||||
@ -42,6 +42,7 @@ export class ExamplePreferencesStore extends Store.ExtensionStore<ExamplePrefere
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
protected fromStore({ enabled }: ExamplePreferencesModel): void {
|
||||
@ -49,11 +50,9 @@ export class ExamplePreferencesStore extends Store.ExtensionStore<ExamplePrefere
|
||||
}
|
||||
|
||||
toJSON(): ExamplePreferencesModel {
|
||||
return toJS({
|
||||
return {
|
||||
enabled: this.enabled
|
||||
}, {
|
||||
recurseEverything: true
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -73,7 +72,6 @@ The `enabled` field of the `ExamplePreferencesStore` is set to the value from th
|
||||
The `toJSON()` method is complementary to `fromStore()`.
|
||||
It is called when the store is being saved.
|
||||
`toJSON()` must provide a JSON serializable object, facilitating its storage in JSON format.
|
||||
The `toJS()` function from [`mobx`](https://mobx.js.org/README.html) is convenient for this purpose, and is used here.
|
||||
|
||||
Finally, `ExamplePreferencesStore` is created by calling `ExamplePreferencesStore.getInstanceOrCreate()`, and exported for use by other parts of the extension.
|
||||
Note that `ExamplePreferencesStore` is a singleton.
|
||||
|
||||
@ -1661,6 +1661,12 @@
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
||||
"dev": true
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"json-parse-better-errors": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
|
||||
@ -1715,6 +1721,15 @@
|
||||
"path-exists": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
@ -1881,6 +1896,27 @@
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"mobx": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/mobx/-/mobx-6.3.1.tgz",
|
||||
"integrity": "sha512-by0VQNBltxLKcQ0uIz2h2mnVv1ZV0ief8mRRw+0k7mL2vSS3806rZg42bt8Vy78szaODtij/5iWIGvc46ZC3Cw==",
|
||||
"dev": true
|
||||
},
|
||||
"mobx-react": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-7.2.0.tgz",
|
||||
"integrity": "sha512-KHUjZ3HBmZlNnPd1M82jcdVsQRDlfym38zJhZEs33VxyVQTvL77hODCArq6+C1P1k/6erEeo2R7rpE7ZeOL7dg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mobx-react-lite": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"mobx-react-lite": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.2.0.tgz",
|
||||
"integrity": "sha512-q5+UHIqYCOpBoFm/PElDuOhbcatvTllgRp3M1s+Hp5j0Z6XNgDbgqxawJ0ZAUEyKM8X1zs70PCuhAIzX1f4Q/g==",
|
||||
"dev": true
|
||||
},
|
||||
"move-concurrently": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||
@ -2290,6 +2326,16 @@
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
||||
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": true,
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
|
||||
@ -23,6 +23,5 @@
|
||||
"ts-loader": "^8.0.4",
|
||||
"typescript": "^4.0.3",
|
||||
"webpack": "^4.44.2"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
import React from "react";
|
||||
import { Component, Catalog, K8sApi } from "@k8slens/extensions";
|
||||
import { observer } from "mobx-react";
|
||||
import { computed, observable } from "mobx";
|
||||
import { computed, observable, makeObservable } from "mobx";
|
||||
import { MetricsFeature, MetricsConfiguration } from "./metrics-feature";
|
||||
|
||||
interface Props {
|
||||
@ -30,6 +30,11 @@ interface Props {
|
||||
|
||||
@observer
|
||||
export class MetricsSettings extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@observable featureStates = {
|
||||
prometheus: false,
|
||||
kubeStateMetrics: false,
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": true,
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": true,
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": true,
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
|
||||
@ -183,6 +183,8 @@
|
||||
"@kubernetes/client-node": "^0.12.0",
|
||||
"abort-controller": "^3.0.0",
|
||||
"array-move": "^3.0.0",
|
||||
"auto-bind": "^4.0.0",
|
||||
"autobind-decorator": "^2.4.0",
|
||||
"await-lock": "^2.1.0",
|
||||
"byline": "^5.0.0",
|
||||
"chalk": "^4.1.0",
|
||||
@ -207,9 +209,9 @@
|
||||
"mac-ca": "^1.0.4",
|
||||
"marked": "^2.0.3",
|
||||
"md5-file": "^5.0.0",
|
||||
"mobx": "^5.15.7",
|
||||
"mobx-observable-history": "^1.0.3",
|
||||
"mobx-react": "^6.2.2",
|
||||
"mobx": "^6.3.0",
|
||||
"mobx-observable-history": "^2.0.1",
|
||||
"mobx-react": "^7.1.0",
|
||||
"mock-fs": "^4.12.0",
|
||||
"moment": "^2.26.0",
|
||||
"moment-timezone": "^0.5.33",
|
||||
|
||||
@ -23,9 +23,8 @@ import path from "path";
|
||||
import Config from "conf";
|
||||
import type { Options as ConfOptions } from "conf/dist/source/types";
|
||||
import { app, ipcMain, IpcMainEvent, ipcRenderer, IpcRendererEvent, remote } from "electron";
|
||||
import { IReactionOptions, observable, reaction, runInAction, when } from "mobx";
|
||||
import Singleton from "./utils/singleton";
|
||||
import { getAppVersion } from "./utils/app-version";
|
||||
import { IReactionOptions, makeObservable, observable, reaction, runInAction, when } from "mobx";
|
||||
import { getAppVersion, Singleton, toJS, Disposer } from "./utils";
|
||||
import logger from "../main/logger";
|
||||
import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "./ipc";
|
||||
import isEqual from "lodash/isEqual";
|
||||
@ -41,13 +40,18 @@ export interface BaseStoreParams<T = any> extends ConfOptions<T> {
|
||||
*/
|
||||
export abstract class BaseStore<T = any> extends Singleton {
|
||||
protected storeConfig?: Config<T>;
|
||||
protected syncDisposers: Function[] = [];
|
||||
protected syncDisposers: Disposer[] = [];
|
||||
|
||||
whenLoaded = when(() => this.isLoaded);
|
||||
@observable isLoaded = false;
|
||||
|
||||
get whenLoaded() {
|
||||
return when(() => this.isLoaded);
|
||||
}
|
||||
|
||||
protected constructor(protected params: BaseStoreParams) {
|
||||
super();
|
||||
makeObservable(this);
|
||||
|
||||
this.params = {
|
||||
autoLoad: false,
|
||||
syncEnabled: true,
|
||||
@ -114,7 +118,11 @@ export abstract class BaseStore<T = any> extends Singleton {
|
||||
|
||||
enableSync() {
|
||||
this.syncDisposers.push(
|
||||
reaction(() => this.toJSON(), model => this.onModelChange(model), this.params.syncOptions),
|
||||
reaction(
|
||||
() => toJS(this.toJSON()), // unwrap possible observables and react to everything
|
||||
model => this.onModelChange(model),
|
||||
this.params.syncOptions,
|
||||
),
|
||||
);
|
||||
|
||||
if (ipcMain) {
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, computed, observable } from "mobx";
|
||||
import { action, computed, observable, makeObservable } from "mobx";
|
||||
import { Disposer, ExtendedMap } from "../utils";
|
||||
import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity";
|
||||
|
||||
@ -27,6 +27,10 @@ export class CatalogCategoryRegistry {
|
||||
protected categories = observable.set<CatalogCategory>();
|
||||
protected groupKinds = new ExtendedMap<string, ExtendedMap<string, CatalogCategory>>();
|
||||
|
||||
constructor() {
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@action add(category: CatalogCategory): Disposer {
|
||||
this.categories.add(category);
|
||||
this.updateGroupKinds(category);
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { EventEmitter } from "events";
|
||||
import { observable } from "mobx";
|
||||
import { observable, makeObservable } from "mobx";
|
||||
|
||||
type ExtractEntityMetadataType<Entity> = Entity extends CatalogEntity<infer Metadata> ? Metadata : never;
|
||||
type ExtractEntityStatusType<Entity> = Entity extends CatalogEntity<any, infer Status> ? Status : never;
|
||||
@ -56,6 +56,12 @@ export abstract class CatalogCategory extends EventEmitter {
|
||||
};
|
||||
abstract spec: CatalogCategorySpec;
|
||||
|
||||
static parseId(id = ""): { group?: string, kind?: string } {
|
||||
const [group, kind] = id.split("/") ?? [];
|
||||
|
||||
return { group, kind };
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return `${this.spec.group}/${this.spec.names.kind}`;
|
||||
}
|
||||
@ -147,6 +153,7 @@ export abstract class CatalogEntity<
|
||||
@observable spec: Spec;
|
||||
|
||||
constructor(data: CatalogEntityData<Metadata, Status, Spec>) {
|
||||
makeObservable(this);
|
||||
this.metadata = data.metadata;
|
||||
this.status = data.status;
|
||||
this.spec = data.spec;
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
import path from "path";
|
||||
import { app, ipcMain, ipcRenderer, remote, webFrame } from "electron";
|
||||
import { unlink } from "fs-extra";
|
||||
import { action, comparer, computed, observable, reaction, toJS } from "mobx";
|
||||
import { action, comparer, computed, observable, reaction, makeObservable } from "mobx";
|
||||
import { BaseStore } from "./base-store";
|
||||
import { Cluster, ClusterState } from "../main/cluster";
|
||||
import migrations from "../migrations/cluster-store";
|
||||
@ -33,7 +33,7 @@ import { saveToAppFiles } from "./utils/saveToAppFiles";
|
||||
import type { KubeConfig } from "@kubernetes/client-node";
|
||||
import { handleRequest, requestMain, subscribeToBroadcast, unsubscribeAllFromBroadcast } from "./ipc";
|
||||
import type { ResourceType } from "../renderer/components/cluster-settings/components/cluster-metrics-setting";
|
||||
import { disposer, noop } from "./utils";
|
||||
import { disposer, noop, toJS } from "./utils";
|
||||
|
||||
export interface ClusterIconUpload {
|
||||
clusterId: string;
|
||||
@ -148,6 +148,8 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
migrations,
|
||||
});
|
||||
|
||||
makeObservable(this);
|
||||
|
||||
this.pushStateToViewsAutomatically();
|
||||
}
|
||||
|
||||
@ -171,16 +173,16 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
});
|
||||
} else if (ipcMain) {
|
||||
handleRequest(ClusterStore.stateRequestChannel, (): clusterStateSync[] => {
|
||||
const states: clusterStateSync[] = [];
|
||||
const clusterStates: clusterStateSync[] = [];
|
||||
|
||||
this.clustersList.forEach((cluster) => {
|
||||
states.push({
|
||||
clusterStates.push({
|
||||
state: cluster.getState(),
|
||||
id: cluster.id
|
||||
});
|
||||
});
|
||||
|
||||
return states;
|
||||
return clusterStates;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -309,7 +311,7 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
|
||||
@action
|
||||
protected fromStore({ activeCluster, clusters = [] }: ClusterStoreModel = {}) {
|
||||
const currentClusters = this.clusters.toJS();
|
||||
const currentClusters = new Map(this.clusters);
|
||||
const newClusters = new Map<ClusterId, Cluster>();
|
||||
const removedClusters = new Map<ClusterId, Cluster>();
|
||||
|
||||
@ -345,8 +347,6 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
return toJS({
|
||||
activeCluster: this.activeCluster,
|
||||
clusters: this.clustersList.map(cluster => cluster.toJSON()),
|
||||
}, {
|
||||
recurseEverything: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
44
src/common/configure-packages.ts
Normal file
44
src/common/configure-packages.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import * as Mobx from "mobx";
|
||||
import * as Immer from "immer";
|
||||
|
||||
/**
|
||||
* Setup default configuration for external npm-packages
|
||||
*/
|
||||
export default function configurePackages() {
|
||||
// Docs: https://mobx.js.org/configuration.html
|
||||
Mobx.configure({
|
||||
enforceActions: "never",
|
||||
isolateGlobalState: true,
|
||||
|
||||
// TODO: enable later (read more: https://mobx.js.org/migrating-from-4-or-5.html)
|
||||
// computedRequiresReaction: true,
|
||||
// reactionRequiresObservable: true,
|
||||
// observableRequiresReaction: true,
|
||||
});
|
||||
|
||||
// Docs: https://immerjs.github.io/immer/
|
||||
// Required in `utils/storage-helper.ts`
|
||||
Immer.setAutoFreeze(false); // allow to merge mobx observables
|
||||
Immer.enableMapSet(); // allow to merge maps and sets
|
||||
}
|
||||
@ -19,11 +19,12 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, comparer, observable, toJS } from "mobx";
|
||||
import { action, comparer, observable, makeObservable } from "mobx";
|
||||
import { BaseStore } from "./base-store";
|
||||
import migrations from "../migrations/hotbar-store";
|
||||
import * as uuid from "uuid";
|
||||
import isNull from "lodash/isNull";
|
||||
import { toJS } from "./utils";
|
||||
import { CatalogEntity } from "./catalog";
|
||||
|
||||
export interface HotbarItem {
|
||||
@ -69,6 +70,7 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||
},
|
||||
migrations,
|
||||
});
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
get activeHotbarId() {
|
||||
@ -252,8 +254,6 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||
activeHotbarId: this.activeHotbarId
|
||||
};
|
||||
|
||||
return toJS(model, {
|
||||
recurseEverything: true,
|
||||
});
|
||||
return toJS(model);
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,29 +23,34 @@
|
||||
// https://www.electronjs.org/docs/api/ipc-main
|
||||
// https://www.electronjs.org/docs/api/ipc-renderer
|
||||
|
||||
import { ipcMain, ipcRenderer, webContents, remote } from "electron";
|
||||
import { toJS } from "mobx";
|
||||
import { ipcMain, ipcRenderer, remote, webContents } from "electron";
|
||||
import { toJS } from "../utils/toJS";
|
||||
import logger from "../../main/logger";
|
||||
import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames";
|
||||
import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames";
|
||||
|
||||
const subFramesChannel = "ipc:get-sub-frames";
|
||||
|
||||
export function handleRequest(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) {
|
||||
ipcMain.handle(channel, listener);
|
||||
ipcMain.handle(channel, async (event, ...args) => {
|
||||
const payload = await listener(event, ...args);
|
||||
|
||||
return sanitizePayload(payload);
|
||||
});
|
||||
}
|
||||
|
||||
export async function requestMain(channel: string, ...args: any[]) {
|
||||
return ipcRenderer.invoke(channel, ...args);
|
||||
return ipcRenderer.invoke(channel, ...args.map(sanitizePayload));
|
||||
}
|
||||
|
||||
function getSubFrames(): ClusterFrameInfo[] {
|
||||
return toJS(Array.from(clusterFrameMap.values()), { recurseEverything: true });
|
||||
return Array.from(clusterFrameMap.values());
|
||||
}
|
||||
|
||||
export function broadcastMessage(channel: string, ...args: any[]) {
|
||||
const views = (webContents || remote?.webContents)?.getAllWebContents();
|
||||
|
||||
if (!views) return;
|
||||
args = args.map(sanitizePayload);
|
||||
|
||||
ipcRenderer?.send(channel, ...args);
|
||||
ipcMain?.emit(channel, ...args);
|
||||
@ -98,7 +103,13 @@ export function unsubscribeAllFromBroadcast(channel: string) {
|
||||
}
|
||||
|
||||
export function bindBroadcastHandlers() {
|
||||
handleRequest(subFramesChannel, () => {
|
||||
return getSubFrames();
|
||||
});
|
||||
handleRequest(subFramesChannel, () => getSubFrames());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@ -19,9 +19,9 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, computed, observable,reaction } from "mobx";
|
||||
import { action, computed, observable, reaction, makeObservable } from "mobx";
|
||||
import { dockStore } from "../renderer/components/dock/dock.store";
|
||||
import { autobind } from "../renderer/utils";
|
||||
import { boundMethod } from "../renderer/utils";
|
||||
|
||||
export class SearchStore {
|
||||
/**
|
||||
@ -54,6 +54,7 @@ export class SearchStore {
|
||||
@observable activeOverlayIndex = -1;
|
||||
|
||||
constructor() {
|
||||
makeObservable(this);
|
||||
reaction(() => dockStore.selectedTabId, () => {
|
||||
searchStore.reset();
|
||||
});
|
||||
@ -128,12 +129,12 @@ export class SearchStore {
|
||||
return prev;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
public setNextOverlayActive(): void {
|
||||
this.activeOverlayIndex = this.getNextOverlay(true);
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
public setPrevOverlayActive(): void {
|
||||
this.activeOverlayIndex = this.getPrevOverlay(true);
|
||||
}
|
||||
@ -159,7 +160,7 @@ export class SearchStore {
|
||||
* @param line Index of the line where overlay is located
|
||||
* @param occurrence Number of the overlay within one line
|
||||
*/
|
||||
@autobind()
|
||||
@boundMethod
|
||||
public isActiveOverlay(line: number, occurrence: number): boolean {
|
||||
const firstLineIndex = this.occurrences.findIndex(item => item === line);
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ import type { ThemeId } from "../renderer/theme.store";
|
||||
import { app, remote } from "electron";
|
||||
import semver from "semver";
|
||||
import { readFile } from "fs-extra";
|
||||
import { action, computed, observable, reaction, toJS } from "mobx";
|
||||
import { action, computed, observable, reaction, makeObservable } from "mobx";
|
||||
import moment from "moment-timezone";
|
||||
import { BaseStore } from "./base-store";
|
||||
import migrations from "../migrations/user-store";
|
||||
@ -34,7 +34,7 @@ import logger from "../main/logger";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import { fileNameMigration } from "../migrations/user-store";
|
||||
import { ObservableToggleSet } from "../renderer/utils";
|
||||
import { ObservableToggleSet, toJS } from "../renderer/utils";
|
||||
|
||||
export interface UserStoreModel {
|
||||
kubeConfigPath: string;
|
||||
@ -73,6 +73,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
configName: "lens-user-store",
|
||||
migrations,
|
||||
});
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@observable lastSeenAppVersion = "0.0.0";
|
||||
@ -306,9 +307,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
},
|
||||
};
|
||||
|
||||
return toJS(model, {
|
||||
recurseEverything: true,
|
||||
});
|
||||
return toJS(model);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
src/common/utils/__tests__/toJS.test.ts
Normal file
45
src/common/utils/__tests__/toJS.test.ts
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { isObservable, observable } from "mobx";
|
||||
import { toJS } from "../toJS";
|
||||
|
||||
describe("utils/toJS(data: any)", () => {
|
||||
const y = { y: 2 };
|
||||
|
||||
const data = observable({ x: 1, y }, {}, {
|
||||
deep: false, // this will keep ref to "y"
|
||||
});
|
||||
const data2 = {
|
||||
x: 1, // partially observable
|
||||
y: observable(y),
|
||||
};
|
||||
|
||||
test("converts mobx-observable to corresponding js struct with links preserving", () => {
|
||||
expect(toJS(data).y).toBe(y);
|
||||
expect(isObservable(toJS(data).y)).toBeFalsy();
|
||||
});
|
||||
|
||||
test("converts partially observable js struct", () => {
|
||||
expect(toJS(data2).y).not.toBe(y);
|
||||
expect(toJS(data2).y).toEqual(y);
|
||||
expect(isObservable(toJS(data2).y)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@ -19,48 +19,22 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Decorator for binding class methods
|
||||
// Can be applied to class or single method as @autobind()
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
import {boundMethod, boundClass} from "autobind-decorator";
|
||||
import autoBindClass, { Options } from "auto-bind";
|
||||
import autoBindReactClass from "auto-bind/react";
|
||||
|
||||
export function autobind() {
|
||||
return function (target: Constructor | object, prop?: string, descriptor?: PropertyDescriptor) {
|
||||
if (target instanceof Function) return bindClass(target);
|
||||
else return bindMethod(target, prop, descriptor);
|
||||
};
|
||||
}
|
||||
|
||||
function bindClass<T extends Constructor>(constructor: T) {
|
||||
const proto = constructor.prototype;
|
||||
const descriptors = Object.getOwnPropertyDescriptors(proto);
|
||||
const skipMethod = (methodName: string) => {
|
||||
return methodName === "constructor"
|
||||
|| typeof descriptors[methodName].value !== "function";
|
||||
};
|
||||
|
||||
Object.keys(descriptors).forEach(prop => {
|
||||
if (skipMethod(prop)) return;
|
||||
const boundDescriptor = bindMethod(proto, prop, descriptors[prop]);
|
||||
|
||||
Object.defineProperty(proto, prop, boundDescriptor);
|
||||
});
|
||||
}
|
||||
|
||||
function bindMethod(target: object, prop?: string, descriptor?: PropertyDescriptor) {
|
||||
if (!descriptor || typeof descriptor.value !== "function") {
|
||||
throw new Error(`@autobind() must be used on class or method only`);
|
||||
// Automatically bind methods to their class instance
|
||||
export function autoBind<T extends object>(obj: T, opts?: Options): T {
|
||||
if ("componentWillUnmount" in obj) {
|
||||
return autoBindReactClass(obj as any, opts);
|
||||
}
|
||||
const { value: func, enumerable, configurable } = descriptor;
|
||||
const boundFunc = new WeakMap<object, Function>();
|
||||
|
||||
return Object.defineProperty(target, prop, {
|
||||
enumerable,
|
||||
configurable,
|
||||
get() {
|
||||
if (this === target) return func; // direct access from prototype
|
||||
if (!boundFunc.has(this)) boundFunc.set(this, func.bind(this));
|
||||
|
||||
return boundFunc.get(this);
|
||||
}
|
||||
});
|
||||
return autoBindClass(obj, opts);
|
||||
}
|
||||
|
||||
// Class/method decorators
|
||||
// Note: @boundClass doesn't work with mobx-6.x/@action decorator
|
||||
export {
|
||||
boundClass,
|
||||
boundMethod,
|
||||
};
|
||||
|
||||
@ -27,6 +27,7 @@ export * from "./app-version";
|
||||
export * from "./autobind";
|
||||
export * from "./base64";
|
||||
export * from "./camelCase";
|
||||
export * from "./toJS";
|
||||
export * from "./cloneJson";
|
||||
export * from "./debouncePromise";
|
||||
export * from "./defineGlobal";
|
||||
|
||||
39
src/common/utils/toJS.ts
Normal file
39
src/common/utils/toJS.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wrapper for mobx.toJS() to support partially observable objects as data-input (>= mobx6).
|
||||
* Otherwise, output result won't be recursively converted to corresponding plain JS-structure.
|
||||
*
|
||||
* @example
|
||||
* mobx.toJS({one: 1, two: observable.array([2])}); // "data.two" == ObservableArray<number>
|
||||
*/
|
||||
import * as mobx from "mobx";
|
||||
import { isObservable, observable } from "mobx";
|
||||
|
||||
export function toJS<T>(data: T): T {
|
||||
// make data observable for recursive toJS()-output
|
||||
if (typeof data === "object" && !isObservable(data)) {
|
||||
return mobx.toJS(observable.box(data).get());
|
||||
}
|
||||
|
||||
return mobx.toJS(data);
|
||||
}
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, ObservableSet } from "mobx";
|
||||
import { ObservableSet } from "mobx";
|
||||
|
||||
export class ToggleSet<T> extends Set<T> {
|
||||
public toggle(value: T): void {
|
||||
@ -31,7 +31,6 @@ export class ToggleSet<T> extends Set<T> {
|
||||
}
|
||||
|
||||
export class ObservableToggleSet<T> extends ObservableSet<T> {
|
||||
@action
|
||||
public toggle(value: T): void {
|
||||
if (!this.delete(value)) {
|
||||
// Set.prototype.delete returns false if `value` was not in the set
|
||||
|
||||
@ -19,7 +19,8 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Extension-api types generation bundle
|
||||
// Extensions-api types bundle (main + renderer)
|
||||
// Available for lens-extensions via NPM-package "@k8slens/extensions"
|
||||
|
||||
export * from "./core-api";
|
||||
export * from "./renderer-api";
|
||||
|
||||
@ -23,11 +23,11 @@ import { watch } from "chokidar";
|
||||
import { ipcRenderer } from "electron";
|
||||
import { EventEmitter } from "events";
|
||||
import fse from "fs-extra";
|
||||
import { observable, reaction, toJS, when } from "mobx";
|
||||
import { observable, reaction, when, makeObservable } from "mobx";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import { broadcastMessage, handleRequest, requestMain, subscribeToBroadcast } from "../common/ipc";
|
||||
import { Singleton } from "../common/utils";
|
||||
import { Singleton, toJS } from "../common/utils";
|
||||
import logger from "../main/logger";
|
||||
import { ExtensionInstallationStateStore } from "../renderer/components/+extensions/extension-install.store";
|
||||
import { extensionInstaller, PackageJson } from "./extension-installer";
|
||||
@ -86,13 +86,22 @@ export class ExtensionDiscovery extends Singleton {
|
||||
|
||||
// True if extensions have been loaded from the disk after app startup
|
||||
@observable isLoaded = false;
|
||||
whenLoaded = when(() => this.isLoaded);
|
||||
|
||||
get whenLoaded() {
|
||||
return when(() => this.isLoaded);
|
||||
}
|
||||
|
||||
// IPC channel to broadcast changes to extension-discovery from main
|
||||
protected static readonly extensionDiscoveryChannel = "extension-discovery:main";
|
||||
|
||||
public events = new EventEmitter();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
get localFolderPath(): string {
|
||||
return path.join(os.homedir(), ".k8slens", "extensions");
|
||||
}
|
||||
@ -374,7 +383,7 @@ export class ExtensionDiscovery extends Singleton {
|
||||
const userExtensions = await this.loadFromFolder(this.localFolderPath, bundledExtensions.map((extension) => extension.manifest.name));
|
||||
|
||||
for (const extension of userExtensions) {
|
||||
if (await fse.pathExists(extension.manifestPath) === false) {
|
||||
if ((await fse.pathExists(extension.manifestPath)) === false) {
|
||||
await this.installPackage(extension.absolutePath);
|
||||
}
|
||||
}
|
||||
@ -468,8 +477,6 @@ export class ExtensionDiscovery extends Singleton {
|
||||
toJSON(): ExtensionDiscoveryChannelMessage {
|
||||
return toJS({
|
||||
isLoaded: this.isLoaded
|
||||
}, {
|
||||
recurseEverything: true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -22,11 +22,11 @@
|
||||
import { app, ipcRenderer, remote } from "electron";
|
||||
import { EventEmitter } from "events";
|
||||
import { isEqual } from "lodash";
|
||||
import { action, computed, observable, reaction, toJS, when } from "mobx";
|
||||
import { action, computed, makeObservable, observable, reaction, when } from "mobx";
|
||||
import path from "path";
|
||||
import { getHostedCluster } from "../common/cluster-store";
|
||||
import { broadcastMessage, handleRequest, requestMain, subscribeToBroadcast } from "../common/ipc";
|
||||
import { Singleton } from "../common/utils";
|
||||
import { Singleton, toJS } from "../common/utils";
|
||||
import logger from "../main/logger";
|
||||
import type { InstalledExtension } from "./extension-discovery";
|
||||
import { ExtensionsStore } from "./extensions-store";
|
||||
@ -58,7 +58,16 @@ export class ExtensionLoader extends Singleton {
|
||||
private events = new EventEmitter();
|
||||
|
||||
@observable isLoaded = false;
|
||||
whenLoaded = when(() => this.isLoaded);
|
||||
|
||||
get whenLoaded() {
|
||||
return when(() => this.isLoaded);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@computed get userExtensions(): Map<LensExtensionId, InstalledExtension> {
|
||||
const extensions = this.toJSON();
|
||||
@ -75,7 +84,7 @@ export class ExtensionLoader extends Singleton {
|
||||
@computed get userExtensionsByName(): Map<string, LensExtension> {
|
||||
const extensions = new Map();
|
||||
|
||||
for (const [, val] of this.instances.toJS()) {
|
||||
for (const [, val] of this.instances.toJSON()) {
|
||||
if (val.isBundled) {
|
||||
continue;
|
||||
}
|
||||
@ -117,6 +126,11 @@ export class ExtensionLoader extends Singleton {
|
||||
|
||||
await Promise.all([this.whenLoaded, ExtensionsStore.getInstance().whenLoaded]);
|
||||
|
||||
// broadcasting extensions between main/renderer processes
|
||||
reaction(() => this.toJSON(), () => this.broadcastExtensions(), {
|
||||
fireImmediately: true,
|
||||
});
|
||||
|
||||
// save state on change `extension.isEnabled`
|
||||
reaction(() => this.storeState, extensionsState => {
|
||||
ExtensionsStore.getInstance().mergeState(extensionsState);
|
||||
@ -156,14 +170,10 @@ export class ExtensionLoader extends Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
protected async initMain() {
|
||||
protected async initMain() {
|
||||
this.isLoaded = true;
|
||||
this.loadOnMain();
|
||||
|
||||
reaction(() => this.toJSON(), () => {
|
||||
this.broadcastExtensions();
|
||||
});
|
||||
|
||||
handleRequest(ExtensionLoader.extensionsMainChannel, () => {
|
||||
return Array.from(this.toJSON());
|
||||
});
|
||||
@ -173,7 +183,7 @@ export class ExtensionLoader extends Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
protected async initRenderer() {
|
||||
protected async initRenderer() {
|
||||
const extensionListHandler = (extensions: [LensExtensionId, InstalledExtension][]) => {
|
||||
this.isLoaded = true;
|
||||
this.syncExtensions(extensions);
|
||||
@ -188,16 +198,20 @@ export class ExtensionLoader extends Singleton {
|
||||
});
|
||||
};
|
||||
|
||||
reaction(() => this.toJSON(), () => {
|
||||
this.broadcastExtensions(false);
|
||||
});
|
||||
|
||||
requestMain(ExtensionLoader.extensionsMainChannel).then(extensionListHandler);
|
||||
subscribeToBroadcast(ExtensionLoader.extensionsMainChannel, (_event, extensions: [LensExtensionId, InstalledExtension][]) => {
|
||||
extensionListHandler(extensions);
|
||||
});
|
||||
}
|
||||
|
||||
broadcastExtensions() {
|
||||
const channel = ipcRenderer
|
||||
? ExtensionLoader.extensionsRendererChannel
|
||||
: ExtensionLoader.extensionsMainChannel;
|
||||
|
||||
broadcastMessage(channel, Array.from(this.extensions));
|
||||
}
|
||||
|
||||
syncExtensions(extensions: [LensExtensionId, InstalledExtension][]) {
|
||||
extensions.forEach(([lensExtensionId, extension]) => {
|
||||
if (!isEqual(this.extensions.get(lensExtensionId), extension)) {
|
||||
@ -255,7 +269,7 @@ export class ExtensionLoader extends Singleton {
|
||||
const cluster = getHostedCluster();
|
||||
|
||||
this.autoInitExtensions(async (extension: LensRendererExtension) => {
|
||||
if (await extension.isEnabledForCluster(cluster) === false) {
|
||||
if ((await extension.isEnabledForCluster(cluster)) === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -334,13 +348,6 @@ export class ExtensionLoader extends Singleton {
|
||||
}
|
||||
|
||||
toJSON(): Map<LensExtensionId, InstalledExtension> {
|
||||
return toJS(this.extensions, {
|
||||
exportMapsAsObjects: false,
|
||||
recurseEverything: true,
|
||||
});
|
||||
}
|
||||
|
||||
broadcastExtensions(main = true) {
|
||||
broadcastMessage(main ? ExtensionLoader.extensionsMainChannel : ExtensionLoader.extensionsRendererChannel, Array.from(this.toJSON()));
|
||||
return toJS(this.extensions);
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,7 +21,8 @@
|
||||
|
||||
import type { LensExtensionId } from "./lens-extension";
|
||||
import { BaseStore } from "../common/base-store";
|
||||
import { action, computed, observable, toJS } from "mobx";
|
||||
import { action, computed, observable, makeObservable } from "mobx";
|
||||
import { toJS } from "../common/utils";
|
||||
|
||||
export interface LensExtensionsStoreModel {
|
||||
extensions: Record<LensExtensionId, LensExtensionState>;
|
||||
@ -37,6 +38,7 @@ export class ExtensionsStore extends BaseStore<LensExtensionsStoreModel> {
|
||||
super({
|
||||
configName: "lens-extensions",
|
||||
});
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@computed
|
||||
@ -68,9 +70,7 @@ export class ExtensionsStore extends BaseStore<LensExtensionsStoreModel> {
|
||||
|
||||
toJSON(): LensExtensionsStoreModel {
|
||||
return toJS({
|
||||
extensions: this.state.toJSON(),
|
||||
}, {
|
||||
recurseEverything: true
|
||||
extensions: Object.fromEntries(this.state),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import type { InstalledExtension } from "./extension-discovery";
|
||||
import { action, observable, reaction } from "mobx";
|
||||
import { action, observable, reaction, makeObservable } from "mobx";
|
||||
import { FilesystemProvisionerStore } from "../main/extension-filesystem";
|
||||
import logger from "../main/logger";
|
||||
import type { ProtocolHandlerRegistration } from "./registries";
|
||||
@ -52,6 +52,7 @@ export class LensExtension {
|
||||
[Disposers] = disposer();
|
||||
|
||||
constructor({ id, manifest, manifestPath, isBundled }: InstalledExtension) {
|
||||
makeObservable(this);
|
||||
this.id = id;
|
||||
this.manifest = manifest;
|
||||
this.manifestPath = manifestPath;
|
||||
|
||||
@ -75,15 +75,22 @@ describe("getPageUrl", () => {
|
||||
});
|
||||
|
||||
it("gets page url with custom params", () => {
|
||||
const params: PageParams<string> = { test1: "one", test2: "2" };
|
||||
const params: PageParams = { test1: "one", test2: "2" };
|
||||
const searchParams = new URLSearchParams(params);
|
||||
const pageUrl = getExtensionPageUrl({ extensionId: ext.name, pageId: "page-with-params", params });
|
||||
const pageUrl = getExtensionPageUrl({
|
||||
extensionId: ext.name,
|
||||
pageId: "page-with-params",
|
||||
params,
|
||||
});
|
||||
|
||||
expect(pageUrl).toBe(`/extension/foo-bar/page-with-params?${searchParams}`);
|
||||
});
|
||||
|
||||
it("gets page url with default custom params", () => {
|
||||
const defaultPageUrl = getExtensionPageUrl({ extensionId: ext.name, pageId: "page-with-params", });
|
||||
const defaultPageUrl = getExtensionPageUrl({
|
||||
extensionId: ext.name,
|
||||
pageId: "page-with-params",
|
||||
});
|
||||
|
||||
expect(defaultPageUrl).toBe(`/extension/foo-bar/page-with-params?test1=test1-default`);
|
||||
});
|
||||
|
||||
@ -20,12 +20,16 @@
|
||||
*/
|
||||
|
||||
// Base class for extensions-api registries
|
||||
import { action, observable } from "mobx";
|
||||
import { action, observable, makeObservable } from "mobx";
|
||||
import { LensExtension } from "../lens-extension";
|
||||
|
||||
export class BaseRegistry<T, I = T> {
|
||||
private items = observable.map<T, I>();
|
||||
|
||||
constructor() {
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
getItems(): I[] {
|
||||
return Array.from(this.items.values());
|
||||
}
|
||||
|
||||
@ -22,9 +22,9 @@
|
||||
// Extensions API -> Commands
|
||||
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
import { action, observable } from "mobx";
|
||||
import { LensExtension } from "../lens-extension";
|
||||
import { CatalogEntity } from "../../common/catalog";
|
||||
import { makeObservable, observable } from "mobx";
|
||||
import type { LensExtension } from "../lens-extension";
|
||||
import type { CatalogEntity } from "../../common/catalog";
|
||||
|
||||
export type CommandContext = {
|
||||
entity?: CatalogEntity;
|
||||
@ -39,9 +39,14 @@ export interface CommandRegistration {
|
||||
}
|
||||
|
||||
export class CommandRegistry extends BaseRegistry<CommandRegistration> {
|
||||
@observable activeEntity: CatalogEntity;
|
||||
@observable.ref activeEntity: CatalogEntity;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@action
|
||||
add(items: CommandRegistration | CommandRegistration[], extension?: LensExtension) {
|
||||
const itemArray = [items].flat();
|
||||
|
||||
|
||||
@ -23,9 +23,8 @@
|
||||
import type { IconProps } from "../../renderer/components/icon";
|
||||
import type React from "react";
|
||||
import type { PageTarget, RegisteredPage } from "./page-registry";
|
||||
import { action } from "mobx";
|
||||
import type { LensExtension } from "../lens-extension";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
import { LensExtension } from "../lens-extension";
|
||||
|
||||
export interface PageMenuRegistration {
|
||||
target?: PageTarget;
|
||||
@ -43,7 +42,6 @@ export interface PageMenuComponents {
|
||||
}
|
||||
|
||||
export class PageMenuRegistry<T extends PageMenuRegistration> extends BaseRegistry<T> {
|
||||
@action
|
||||
add(items: T[], ext: LensExtension) {
|
||||
const normalizedItems = items.map(menuItem => {
|
||||
menuItem.target = {
|
||||
|
||||
@ -24,9 +24,8 @@
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
import { LensExtension, sanitizeExtensionName } from "../lens-extension";
|
||||
import type { PageParam, PageParamInit } from "../../renderer/navigation/page-param";
|
||||
import { createPageParam } from "../../renderer/navigation/helpers";
|
||||
import { LensExtension, LensExtensionId, sanitizeExtensionName } from "../lens-extension";
|
||||
import { createPageParam, PageParam, PageParamInit, searchParamsOptions } from "../../renderer/navigation";
|
||||
|
||||
export interface PageRegistration {
|
||||
/**
|
||||
@ -34,21 +33,18 @@ export interface PageRegistration {
|
||||
* When not provided, first registered page without "id" would be used for page-menus without target.pageId for same extension
|
||||
*/
|
||||
id?: string;
|
||||
params?: PageParams<string | ExtensionPageParamInit>;
|
||||
params?: PageParams<string | Omit<PageParamInit<any>, "name" | "prefix">>;
|
||||
components: PageComponents;
|
||||
}
|
||||
|
||||
// exclude "name" field since provided as key in page.params
|
||||
export type ExtensionPageParamInit = Omit<PageParamInit, "name" | "isSystem">;
|
||||
|
||||
export interface PageComponents {
|
||||
Page: React.ComponentType<any>;
|
||||
}
|
||||
|
||||
export interface PageTarget<P = PageParams> {
|
||||
export interface PageTarget {
|
||||
extensionId?: string;
|
||||
pageId?: string;
|
||||
params?: P;
|
||||
params?: PageParams;
|
||||
}
|
||||
|
||||
export interface PageParams<V = any> {
|
||||
@ -83,13 +79,12 @@ export function getExtensionPageUrl(target: PageTarget): string {
|
||||
|
||||
if (registeredPage?.params) {
|
||||
Object.entries(registeredPage.params).forEach(([name, param]) => {
|
||||
const paramValue = param.stringify(targetParams[name]);
|
||||
pageUrl.searchParams.delete(name); // first off, clear existing value(s)
|
||||
|
||||
if (param.init.skipEmpty && param.isEmpty(paramValue)) {
|
||||
pageUrl.searchParams.delete(name);
|
||||
} else {
|
||||
pageUrl.searchParams.set(name, paramValue);
|
||||
}
|
||||
param.stringify(targetParams[name]).forEach(value => {
|
||||
if (searchParamsOptions.skipEmpty && !value) return;
|
||||
pageUrl.searchParams.append(name, value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -100,7 +95,7 @@ export class PageRegistry extends BaseRegistry<PageRegistration, RegisteredPage>
|
||||
protected getRegisteredItem(page: PageRegistration, ext: LensExtension): RegisteredPage {
|
||||
const { id: pageId } = page;
|
||||
const extensionId = ext.name;
|
||||
const params = this.normalizeParams(page.params);
|
||||
const params = this.normalizeParams(extensionId, page.params);
|
||||
const components = this.normalizeComponents(page.components, params);
|
||||
const url = getExtensionPageUrl({ extensionId, pageId });
|
||||
|
||||
@ -113,25 +108,48 @@ export class PageRegistry extends BaseRegistry<PageRegistration, RegisteredPage>
|
||||
if (params) {
|
||||
const { Page } = components;
|
||||
|
||||
// inject extension's page component props.params
|
||||
components.Page = observer((props: object) => React.createElement(Page, { params, ...props }));
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
protected normalizeParams(params?: PageParams<string | ExtensionPageParamInit>): PageParams<PageParam> | undefined {
|
||||
if (!params) {
|
||||
return undefined;
|
||||
}
|
||||
Object.entries(params).forEach(([name, value]) => {
|
||||
const paramInit: PageParamInit = typeof value === "object"
|
||||
? { name, ...value }
|
||||
: { name, defaultValue: value };
|
||||
protected normalizeParams(extensionId: LensExtensionId, params?: PageParams<string | Partial<PageParamInit>>): PageParams<PageParam> {
|
||||
if (!params) return undefined;
|
||||
const normalizedParams: PageParams<PageParam> = {};
|
||||
|
||||
params[paramInit.name] = createPageParam(paramInit);
|
||||
Object.entries(params).forEach(([paramName, paramValue]) => {
|
||||
const paramInit: PageParamInit = {
|
||||
name: paramName,
|
||||
prefix: `${extensionId}:`,
|
||||
defaultValue: paramValue,
|
||||
};
|
||||
|
||||
// handle non-string params
|
||||
if (typeof paramValue !== "string") {
|
||||
const { defaultValue: value, parse, stringify } = paramValue;
|
||||
|
||||
const notAStringValue = typeof value !== "string" || (
|
||||
Array.isArray(value) && !value.every(value => typeof value === "string")
|
||||
);
|
||||
|
||||
if (notAStringValue && !(parse || stringify)) {
|
||||
throw new Error(
|
||||
`PageRegistry: param's "${paramName}" initialization has failed:
|
||||
paramInit.parse() and paramInit.stringify() are required for non string | string[] "defaultValue"`
|
||||
);
|
||||
}
|
||||
|
||||
paramInit.defaultValue = value;
|
||||
paramInit.parse = parse;
|
||||
paramInit.stringify = stringify;
|
||||
}
|
||||
|
||||
normalizedParams[paramName] = createPageParam(paramInit);
|
||||
});
|
||||
|
||||
return params as PageParams<PageParam>;
|
||||
return normalizedParams;
|
||||
}
|
||||
|
||||
getByPageTarget(target: PageTarget): RegisteredPage | null {
|
||||
|
||||
@ -19,15 +19,13 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { PageParam, PageParamInit } from "../../renderer/navigation/page-param";
|
||||
import { navigation } from "../../renderer/navigation";
|
||||
import { navigation, PageParam, PageParamInit } from "../../renderer/navigation";
|
||||
|
||||
export type { PageParamInit, PageParam } from "../../renderer/navigation/page-param";
|
||||
export { navigate, isActiveRoute } from "../../renderer/navigation/helpers";
|
||||
export { hideDetails, showDetails, getDetailsUrl } from "../../renderer/components/kube-object/kube-object-details";
|
||||
export type { IURLParams } from "../../common/utils/buildUrl";
|
||||
|
||||
// exporting to extensions-api version of helper without `isSystem` flag
|
||||
export function createPageParam<V = string>(init: PageParamInit<V>) {
|
||||
export function createPageParam<V>(init: PageParamInit<V>) {
|
||||
return new PageParam<V>(init, navigation);
|
||||
}
|
||||
|
||||
@ -20,6 +20,11 @@
|
||||
*/
|
||||
|
||||
import fetchMock from "jest-fetch-mock";
|
||||
import configurePackages from "./common/configure-packages";
|
||||
|
||||
// setup default configuration for external npm-packages
|
||||
configurePackages();
|
||||
|
||||
// rewire global.fetch to call 'fetchMock'
|
||||
fetchMock.enableMocks();
|
||||
|
||||
|
||||
@ -19,13 +19,14 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { reaction, toJS } from "mobx";
|
||||
import { reaction } from "mobx";
|
||||
import { broadcastMessage } from "../common/ipc";
|
||||
import type { CatalogEntityRegistry} from "./catalog";
|
||||
import type { CatalogEntityRegistry } from "./catalog";
|
||||
import "../common/catalog-entities/kubernetes-cluster";
|
||||
import { toJS } from "../common/utils";
|
||||
|
||||
export function pushCatalogToRenderer(catalog: CatalogEntityRegistry) {
|
||||
return reaction(() => toJS(catalog.items, { recurseEverything: true }), (items) => {
|
||||
return reaction(() => toJS(catalog.items), (items) => {
|
||||
broadcastMessage("catalog:items", items);
|
||||
}, {
|
||||
fireImmediately: true,
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, observable, IComputedValue, computed, ObservableMap, runInAction } from "mobx";
|
||||
import { action, observable, IComputedValue, computed, ObservableMap, runInAction, makeObservable, observe } from "mobx";
|
||||
import type { CatalogEntity } from "../../common/catalog";
|
||||
import { catalogEntityRegistry } from "../../main/catalog";
|
||||
import { watch } from "chokidar";
|
||||
@ -45,6 +45,12 @@ export class KubeconfigSyncManager extends Singleton {
|
||||
|
||||
protected static readonly syncName = "lens:kube-sync";
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@action
|
||||
startSync(): void {
|
||||
if (this.syncing) {
|
||||
@ -69,7 +75,7 @@ export class KubeconfigSyncManager extends Singleton {
|
||||
this.startNewSync(filePath);
|
||||
}
|
||||
|
||||
this.syncListDisposer = UserStore.getInstance().syncKubeconfigEntries.observe(change => {
|
||||
this.syncListDisposer = observe(UserStore.getInstance().syncKubeconfigEntries, change => {
|
||||
switch (change.type) {
|
||||
case "add":
|
||||
this.startNewSync(change.name);
|
||||
|
||||
@ -19,14 +19,16 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, computed, observable, IComputedValue, IObservableArray } from "mobx";
|
||||
import { action, computed, IComputedValue, IObservableArray, makeObservable, observable } from "mobx";
|
||||
import { CatalogCategoryRegistry, catalogCategoryRegistry, CatalogEntity } from "../../common/catalog";
|
||||
import { iter } from "../../common/utils";
|
||||
|
||||
export class CatalogEntityRegistry {
|
||||
protected sources = observable.map<string, IComputedValue<CatalogEntity[]>>([], { deep: true });
|
||||
protected sources = observable.map<string, IComputedValue<CatalogEntity[]>>();
|
||||
|
||||
constructor(private categoryRegistry: CatalogCategoryRegistry) {}
|
||||
constructor(private categoryRegistry: CatalogCategoryRegistry) {
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@action addObservableSource(id: string, source: IObservableArray<CatalogEntity>) {
|
||||
this.sources.set(id, computed(() => source));
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
import "../common/cluster-ipc";
|
||||
import type http from "http";
|
||||
import { ipcMain } from "electron";
|
||||
import { action, autorun, reaction, toJS } from "mobx";
|
||||
import { action, autorun, makeObservable, reaction } from "mobx";
|
||||
import { ClusterStore, getClusterIdFromHost } from "../common/cluster-store";
|
||||
import type { Cluster } from "./cluster";
|
||||
import logger from "./logger";
|
||||
@ -32,38 +32,47 @@ import { catalogEntityRegistry } from "./catalog";
|
||||
import { KubernetesCluster, KubernetesClusterPrometheusMetrics } from "../common/catalog-entities/kubernetes-cluster";
|
||||
|
||||
export class ClusterManager extends Singleton {
|
||||
private store = ClusterStore.getInstance();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
makeObservable(this);
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
reaction(() => toJS(ClusterStore.getInstance().clustersList, { recurseEverything: true }), () => {
|
||||
this.updateCatalog(ClusterStore.getInstance().clustersList);
|
||||
}, { fireImmediately: true });
|
||||
private bindEvents() {
|
||||
// reacting to every cluster's state change and total amount of items
|
||||
reaction(
|
||||
() => this.store.clustersList.map(c => c.getState()),
|
||||
() => this.updateCatalog(this.store.clustersList),
|
||||
{ fireImmediately: true, }
|
||||
);
|
||||
|
||||
reaction(() => catalogEntityRegistry.getItemsForApiKind<KubernetesCluster>("entity.k8slens.dev/v1alpha1", "KubernetesCluster"), (entities) => {
|
||||
this.syncClustersFromCatalog(entities);
|
||||
});
|
||||
|
||||
|
||||
// auto-stop removed clusters
|
||||
autorun(() => {
|
||||
const removedClusters = Array.from(ClusterStore.getInstance().removedClusters.values());
|
||||
const removedClusters = Array.from(this.store.removedClusters.values());
|
||||
|
||||
if (removedClusters.length > 0) {
|
||||
const meta = removedClusters.map(cluster => cluster.getMeta());
|
||||
|
||||
logger.info(`[CLUSTER-MANAGER]: removing clusters`, meta);
|
||||
removedClusters.forEach(cluster => cluster.disconnect());
|
||||
ClusterStore.getInstance().removedClusters.clear();
|
||||
this.store.removedClusters.clear();
|
||||
}
|
||||
}, {
|
||||
delay: 250
|
||||
});
|
||||
|
||||
ipcMain.on("network:offline", () => { this.onNetworkOffline(); });
|
||||
ipcMain.on("network:online", () => { this.onNetworkOnline(); });
|
||||
ipcMain.on("network:offline", this.onNetworkOffline);
|
||||
ipcMain.on("network:online", this.onNetworkOnline);
|
||||
}
|
||||
|
||||
@action protected updateCatalog(clusters: Cluster[]) {
|
||||
@action
|
||||
protected updateCatalog(clusters: Cluster[]) {
|
||||
for (const cluster of clusters) {
|
||||
const index = catalogEntityRegistry.items.findIndex((entity) => entity.metadata.uid === cluster.id);
|
||||
|
||||
@ -94,10 +103,10 @@ export class ClusterManager extends Singleton {
|
||||
|
||||
@action syncClustersFromCatalog(entities: KubernetesCluster[]) {
|
||||
for (const entity of entities) {
|
||||
const cluster = ClusterStore.getInstance().getById(entity.metadata.uid);
|
||||
const cluster = this.store.getById(entity.metadata.uid);
|
||||
|
||||
if (!cluster) {
|
||||
ClusterStore.getInstance().addCluster({
|
||||
this.store.addCluster({
|
||||
id: entity.metadata.uid,
|
||||
preferences: {
|
||||
clusterName: entity.metadata.name
|
||||
@ -117,28 +126,28 @@ export class ClusterManager extends Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
protected onNetworkOffline() {
|
||||
protected onNetworkOffline = () => {
|
||||
logger.info("[CLUSTER-MANAGER]: network is offline");
|
||||
ClusterStore.getInstance().clustersList.forEach((cluster) => {
|
||||
this.store.clustersList.forEach((cluster) => {
|
||||
if (!cluster.disconnected) {
|
||||
cluster.online = false;
|
||||
cluster.accessible = false;
|
||||
cluster.refreshConnectionStatus().catch((e) => e);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
protected onNetworkOnline() {
|
||||
protected onNetworkOnline = () => {
|
||||
logger.info("[CLUSTER-MANAGER]: network is online");
|
||||
ClusterStore.getInstance().clustersList.forEach((cluster) => {
|
||||
this.store.clustersList.forEach((cluster) => {
|
||||
if (!cluster.disconnected) {
|
||||
cluster.refreshConnectionStatus().catch((e) => e);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
stop() {
|
||||
ClusterStore.getInstance().clusters.forEach((cluster: Cluster) => {
|
||||
this.store.clusters.forEach((cluster: Cluster) => {
|
||||
cluster.disconnect();
|
||||
});
|
||||
}
|
||||
@ -150,18 +159,18 @@ export class ClusterManager extends Singleton {
|
||||
if (req.headers.host.startsWith("127.0.0.1")) {
|
||||
const clusterId = req.url.split("/")[1];
|
||||
|
||||
cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
cluster = this.store.getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
// we need to swap path prefix so that request is proxied to kube api
|
||||
req.url = req.url.replace(`/${clusterId}`, apiKubePrefix);
|
||||
}
|
||||
} else if (req.headers["x-cluster-id"]) {
|
||||
cluster = ClusterStore.getInstance().getById(req.headers["x-cluster-id"].toString());
|
||||
cluster = this.store.getById(req.headers["x-cluster-id"].toString());
|
||||
} else {
|
||||
const clusterId = getClusterIdFromHost(req.headers.host);
|
||||
|
||||
cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
cluster = this.store.getById(clusterId);
|
||||
}
|
||||
|
||||
return cluster;
|
||||
@ -169,9 +178,7 @@ export class ClusterManager extends Singleton {
|
||||
}
|
||||
|
||||
export function catalogEntityFromCluster(cluster: Cluster) {
|
||||
return new KubernetesCluster(toJS({
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
kind: "KubernetesCluster",
|
||||
return new KubernetesCluster({
|
||||
metadata: {
|
||||
uid: cluster.id,
|
||||
name: cluster.name,
|
||||
@ -190,5 +197,5 @@ export function catalogEntityFromCluster(cluster: Cluster) {
|
||||
message: "",
|
||||
active: !cluster.disconnected
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
import { ipcMain } from "electron";
|
||||
import type { ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel } from "../common/cluster-store";
|
||||
import { action, comparer, computed, observable, reaction, toJS, when } from "mobx";
|
||||
import { action, comparer, computed, makeObservable, observable, reaction, when } from "mobx";
|
||||
import { broadcastMessage, ClusterListNamespaceForbiddenChannel } from "../common/ipc";
|
||||
import { ContextHandler } from "./context-handler";
|
||||
import { AuthorizationV1Api, CoreV1Api, HttpError, KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node";
|
||||
@ -33,6 +33,7 @@ import logger from "./logger";
|
||||
import { VersionDetector } from "./cluster-detectors/version-detector";
|
||||
import { detectorRegistry } from "./cluster-detectors/detector-registry";
|
||||
import plimit from "p-limit";
|
||||
import { toJS } from "../common/utils";
|
||||
|
||||
export enum ClusterStatus {
|
||||
AccessGranted = 2,
|
||||
@ -91,7 +92,9 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
protected activated = false;
|
||||
private resourceAccessStatuses: Map<KubeApiResource, boolean> = new Map();
|
||||
|
||||
whenReady = when(() => this.ready);
|
||||
get whenReady() {
|
||||
return when(() => this.ready);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kubeconfig context name
|
||||
@ -227,9 +230,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
@computed get prometheusPreferences(): ClusterPrometheusPreferences {
|
||||
const { prometheus, prometheusProvider } = this.preferences;
|
||||
|
||||
return toJS({ prometheus, prometheusProvider }, {
|
||||
recurseEverything: true,
|
||||
});
|
||||
return toJS({ prometheus, prometheusProvider });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,6 +241,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
}
|
||||
|
||||
constructor(model: ClusterModel) {
|
||||
makeObservable(this);
|
||||
this.id = model.id;
|
||||
this.updateModel(model);
|
||||
|
||||
@ -570,9 +572,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
accessibleNamespaces: this.accessibleNamespaces,
|
||||
};
|
||||
|
||||
return toJS(model, {
|
||||
recurseEverything: true
|
||||
});
|
||||
return toJS(model);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -592,9 +592,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
isGlobalWatchEnabled: this.isGlobalWatchEnabled,
|
||||
};
|
||||
|
||||
return toJS(state, {
|
||||
recurseEverything: true
|
||||
});
|
||||
return toJS(state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -23,23 +23,25 @@ import { randomBytes } from "crypto";
|
||||
import { SHA256 } from "crypto-js";
|
||||
import { app, remote } from "electron";
|
||||
import fse from "fs-extra";
|
||||
import { action, observable, toJS } from "mobx";
|
||||
import { action, makeObservable, observable } from "mobx";
|
||||
import path from "path";
|
||||
import { BaseStore } from "../common/base-store";
|
||||
import type { LensExtensionId } from "../extensions/lens-extension";
|
||||
import { toJS } from "../common/utils";
|
||||
|
||||
interface FSProvisionModel {
|
||||
extensions: Record<string, string>; // extension names to paths
|
||||
}
|
||||
|
||||
export class FilesystemProvisionerStore extends BaseStore<FSProvisionModel> {
|
||||
@observable registeredExtensions = observable.map<LensExtensionId, string>();
|
||||
registeredExtensions = observable.map<LensExtensionId, string>();
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
configName: "lens-filesystem-provisioner-store",
|
||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
||||
});
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,9 +73,7 @@ export class FilesystemProvisionerStore extends BaseStore<FSProvisionModel> {
|
||||
|
||||
toJSON(): FSProvisionModel {
|
||||
return toJS({
|
||||
extensions: this.registeredExtensions.toJSON(),
|
||||
}, {
|
||||
recurseEverything: true
|
||||
extensions: Object.fromEntries(this.registeredExtensions),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
import "../common/system-ca";
|
||||
import "../common/prometheus-providers";
|
||||
import * as Mobx from "mobx";
|
||||
import * as LensExtensions from "../extensions/core-api";
|
||||
import * as LensExtensionsCoreApi from "../extensions/core-api";
|
||||
import { app, autoUpdater, ipcMain, dialog, powerMonitor } from "electron";
|
||||
import { appName, isMac, productName } from "../common/vars";
|
||||
import path from "path";
|
||||
@ -55,6 +55,7 @@ import { HotbarStore } from "../common/hotbar-store";
|
||||
import { HelmRepoManager } from "./helm/helm-repo-manager";
|
||||
import { KubeconfigSyncManager } from "./catalog-sources";
|
||||
import { handleWsUpgrade } from "./proxy/ws-upgrade";
|
||||
import configurePackages from "../common/configure-packages";
|
||||
|
||||
const workingDir = path.join(app.getPath("appData"), appName);
|
||||
const cleanup = disposer();
|
||||
@ -77,6 +78,7 @@ if (process.env.LENS_DISABLE_GPU) {
|
||||
app.disableHardwareAcceleration();
|
||||
}
|
||||
|
||||
configurePackages();
|
||||
mangleProxyEnv();
|
||||
|
||||
if (app.commandLine.getSwitchValue("proxy-server") !== "") {
|
||||
@ -191,15 +193,11 @@ app.on("ready", async () => {
|
||||
cleanup.push(pushCatalogToRenderer(catalogEntityRegistry));
|
||||
KubeconfigSyncManager.getInstance().startSync();
|
||||
startUpdateChecking();
|
||||
LensProtocolRouterMain
|
||||
.getInstance()
|
||||
.rendererLoaded = true;
|
||||
LensProtocolRouterMain.getInstance().rendererLoaded = true;
|
||||
});
|
||||
|
||||
ExtensionLoader.getInstance().whenLoaded.then(() => {
|
||||
LensProtocolRouterMain
|
||||
.getInstance()
|
||||
.extensionsLoaded = true;
|
||||
LensProtocolRouterMain.getInstance().extensionsLoaded = true;
|
||||
});
|
||||
|
||||
logger.info("🧩 Initializing extensions");
|
||||
@ -272,12 +270,16 @@ app.on("open-url", (event, rawUrl) => {
|
||||
.catch(error => logger.error(`${LensProtocolRouterMain.LoggingPrefix}: an error occured`, { error, rawUrl }));
|
||||
});
|
||||
|
||||
// Extensions-api runtime exports
|
||||
export const LensExtensionsApi = {
|
||||
...LensExtensions,
|
||||
/**
|
||||
* Exports for virtual package "@k8slens/extensions" for main-process.
|
||||
* All exporting names available in global runtime scope:
|
||||
* e.g. global.Mobx, global.LensExtensions
|
||||
*/
|
||||
const LensExtensions = {
|
||||
...LensExtensionsCoreApi,
|
||||
};
|
||||
|
||||
export {
|
||||
Mobx,
|
||||
LensExtensionsApi as LensExtensions,
|
||||
LensExtensions,
|
||||
};
|
||||
|
||||
@ -24,7 +24,7 @@ import * as proto from "../../common/protocol-handler";
|
||||
import Url from "url-parse";
|
||||
import type { LensExtension } from "../../extensions/lens-extension";
|
||||
import { broadcastMessage } from "../../common/ipc";
|
||||
import { observable, when } from "mobx";
|
||||
import { observable, when, makeObservable } from "mobx";
|
||||
|
||||
export interface FallbackHandler {
|
||||
(name: string): Promise<boolean>;
|
||||
@ -36,6 +36,12 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
|
||||
@observable rendererLoaded = false;
|
||||
@observable extensionsLoaded = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the most specific registered handler, if it exists, and invoke it.
|
||||
*
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import type { ClusterId } from "../common/cluster-store";
|
||||
import { observable } from "mobx";
|
||||
import { makeObservable, observable } from "mobx";
|
||||
import { app, BrowserWindow, dialog, shell, webContents } from "electron";
|
||||
import windowStateKeeper from "electron-window-state";
|
||||
import { appEventBus } from "../common/event-bus";
|
||||
@ -44,6 +44,7 @@ export class WindowManager extends Singleton {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
makeObservable(this);
|
||||
this.bindEvents();
|
||||
this.initMenu();
|
||||
this.initTray();
|
||||
|
||||
@ -21,15 +21,19 @@
|
||||
|
||||
import type { KubeObjectStore } from "../kube-object.store";
|
||||
|
||||
import { action, observable } from "mobx";
|
||||
import { autobind } from "../utils";
|
||||
import { action, observable, makeObservable } from "mobx";
|
||||
import { autoBind } from "../utils";
|
||||
import { KubeApi, parseKubeApi } from "./kube-api";
|
||||
|
||||
@autobind()
|
||||
export class ApiManager {
|
||||
private apis = observable.map<string, KubeApi>();
|
||||
private stores = observable.map<string, KubeObjectStore>();
|
||||
|
||||
constructor() {
|
||||
makeObservable(this);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) {
|
||||
if (typeof pathOrCallback === "string") {
|
||||
return this.apis.get(pathOrCallback) || this.apis.get(parseKubeApi(pathOrCallback).apiBase);
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { computed, observable } from "mobx";
|
||||
import { computed, observable, makeObservable } from "mobx";
|
||||
import { subscribeToBroadcast } from "../../common/ipc";
|
||||
import { CatalogCategory, CatalogEntity, CatalogEntityData, catalogCategoryRegistry, CatalogCategoryRegistry, CatalogEntityKindData } from "../../common/catalog";
|
||||
import "../../common/catalog-entities";
|
||||
@ -29,7 +29,9 @@ export class CatalogEntityRegistry {
|
||||
protected rawItems = observable.array<CatalogEntityData & CatalogEntityKindData>([], { deep: true });
|
||||
@observable protected _activeEntity: CatalogEntity;
|
||||
|
||||
constructor(private categoryRegistry: CatalogCategoryRegistry) {}
|
||||
constructor(private categoryRegistry: CatalogCategoryRegistry) {
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
init() {
|
||||
subscribeToBroadcast("catalog:items", (ev, items: (CatalogEntityData & CatalogEntityKindData)[]) => {
|
||||
|
||||
@ -19,11 +19,9 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { autobind } from "../../utils";
|
||||
import { Role } from "./role.api";
|
||||
import { KubeApi } from "../kube-api";
|
||||
|
||||
@autobind()
|
||||
export class ClusterRole extends Role {
|
||||
static kind = "ClusterRole";
|
||||
static namespaced = false;
|
||||
|
||||
@ -71,10 +71,7 @@ export interface IClusterMetrics<T = IMetrics> {
|
||||
fsUsage: T;
|
||||
}
|
||||
|
||||
export class Cluster extends KubeObject {
|
||||
static kind = "Cluster";
|
||||
static apiBase = "/apis/cluster.k8s.io/v1alpha1/clusters";
|
||||
|
||||
export interface Cluster {
|
||||
spec: {
|
||||
clusterNetwork?: {
|
||||
serviceDomain?: string;
|
||||
@ -106,6 +103,11 @@ export class Cluster extends KubeObject {
|
||||
errorMessage?: string;
|
||||
errorReason?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class Cluster extends KubeObject {
|
||||
static kind = "Cluster";
|
||||
static apiBase = "/apis/cluster.k8s.io/v1alpha1/clusters";
|
||||
|
||||
getStatus() {
|
||||
if (this.metadata.deletionTimestamp) return ClusterStatus.REMOVING;
|
||||
|
||||
@ -28,13 +28,15 @@ export interface IComponentStatusCondition {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ComponentStatus {
|
||||
conditions: IComponentStatusCondition[];
|
||||
}
|
||||
|
||||
export class ComponentStatus extends KubeObject {
|
||||
static kind = "ComponentStatus";
|
||||
static namespaced = false;
|
||||
static apiBase = "/api/v1/componentstatuses";
|
||||
|
||||
conditions: IComponentStatusCondition[];
|
||||
|
||||
getTruthyConditions() {
|
||||
return this.conditions.filter(c => c.status === "True");
|
||||
}
|
||||
|
||||
@ -21,10 +21,15 @@
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
import { autobind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import { autoBind } from "../../../common/utils";
|
||||
|
||||
export interface ConfigMap {
|
||||
data: {
|
||||
[param: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class ConfigMap extends KubeObject {
|
||||
static kind = "ConfigMap";
|
||||
static namespaced = true;
|
||||
@ -32,12 +37,10 @@ export class ConfigMap extends KubeObject {
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
this.data = this.data || {};
|
||||
}
|
||||
autoBind(this);
|
||||
|
||||
data: {
|
||||
[param: string]: string;
|
||||
};
|
||||
this.data ??= {};
|
||||
}
|
||||
|
||||
getKeys(): string[] {
|
||||
return Object.keys(this.data);
|
||||
|
||||
@ -38,11 +38,7 @@ type AdditionalPrinterColumnsV1Beta = AdditionalPrinterColumnsCommon & {
|
||||
JSONPath: string;
|
||||
};
|
||||
|
||||
export class CustomResourceDefinition extends KubeObject {
|
||||
static kind = "CustomResourceDefinition";
|
||||
static namespaced = false;
|
||||
static apiBase = "/apis/apiextensions.k8s.io/v1/customresourcedefinitions";
|
||||
|
||||
export interface CustomResourceDefinition {
|
||||
spec: {
|
||||
group: string;
|
||||
version?: string; // deprecated in v1 api
|
||||
@ -84,6 +80,12 @@ export class CustomResourceDefinition extends KubeObject {
|
||||
};
|
||||
storedVersions: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export class CustomResourceDefinition extends KubeObject {
|
||||
static kind = "CustomResourceDefinition";
|
||||
static namespaced = false;
|
||||
static apiBase = "/apis/apiextensions.k8s.io/v1/customresourcedefinitions";
|
||||
|
||||
getResourceUrl() {
|
||||
return crdResourcesURL({
|
||||
|
||||
@ -23,8 +23,9 @@ import moment from "moment";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { IPodContainer } from "./pods.api";
|
||||
import { formatDuration } from "../../utils/formatDuration";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export class CronJobApi extends KubeApi<CronJob> {
|
||||
suspend(params: { namespace: string; name: string }) {
|
||||
@ -58,28 +59,7 @@ export class CronJobApi extends KubeApi<CronJob> {
|
||||
}
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class CronJob extends KubeObject {
|
||||
static kind = "CronJob";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/batch/v1beta1/cronjobs";
|
||||
|
||||
kind: string;
|
||||
apiVersion: string;
|
||||
metadata: {
|
||||
name: string;
|
||||
namespace: string;
|
||||
selfLink: string;
|
||||
uid: string;
|
||||
resourceVersion: string;
|
||||
creationTimestamp: string;
|
||||
labels: {
|
||||
[key: string]: string;
|
||||
};
|
||||
annotations: {
|
||||
[key: string]: string;
|
||||
};
|
||||
};
|
||||
export interface CronJob {
|
||||
spec: {
|
||||
schedule: string;
|
||||
concurrencyPolicy: string;
|
||||
@ -116,6 +96,17 @@ export class CronJob extends KubeObject {
|
||||
status: {
|
||||
lastScheduleTime?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class CronJob extends KubeObject {
|
||||
static kind = "CronJob";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/batch/v1beta1/cronjobs";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getSuspendFlag() {
|
||||
return this.spec.suspend.toString();
|
||||
|
||||
@ -22,16 +22,21 @@
|
||||
import get from "lodash/get";
|
||||
import type { IPodContainer } from "./pods.api";
|
||||
import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
@autobind()
|
||||
export class DaemonSet extends WorkloadKubeObject {
|
||||
static kind = "DaemonSet";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/apps/v1/daemonsets";
|
||||
|
||||
spec: {
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
declare spec: {
|
||||
selector: {
|
||||
matchLabels: {
|
||||
[name: string]: string;
|
||||
@ -73,7 +78,7 @@ export class DaemonSet extends WorkloadKubeObject {
|
||||
};
|
||||
revisionHistoryLimit: number;
|
||||
};
|
||||
status: {
|
||||
declare status: {
|
||||
currentNumberScheduled: number;
|
||||
numberMisscheduled: number;
|
||||
desiredNumberScheduled: number;
|
||||
|
||||
@ -22,8 +22,9 @@
|
||||
import moment from "moment";
|
||||
|
||||
import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export class DeploymentApi extends KubeApi<Deployment> {
|
||||
protected getScaleApiUrl(params: { namespace: string; name: string }) {
|
||||
@ -87,13 +88,17 @@ interface IContainerProbe {
|
||||
failureThreshold?: number;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class Deployment extends WorkloadKubeObject {
|
||||
static kind = "Deployment";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/apps/v1/deployments";
|
||||
|
||||
spec: {
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
declare spec: {
|
||||
replicas: number;
|
||||
selector: { matchLabels: { [app: string]: string } };
|
||||
template: {
|
||||
@ -172,7 +177,7 @@ export class Deployment extends WorkloadKubeObject {
|
||||
};
|
||||
};
|
||||
};
|
||||
status: {
|
||||
declare status: {
|
||||
observedGeneration: number;
|
||||
replicas: number;
|
||||
updatedReplicas: number;
|
||||
|
||||
@ -19,9 +19,10 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export interface IEndpointPort {
|
||||
name?: string;
|
||||
@ -121,13 +122,19 @@ export class EndpointSubset implements IEndpointSubset {
|
||||
}
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export interface Endpoint {
|
||||
subsets: IEndpointSubset[];
|
||||
}
|
||||
|
||||
export class Endpoint extends KubeObject {
|
||||
static kind = "Endpoints";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/endpoints";
|
||||
|
||||
subsets: IEndpointSubset[];
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getEndpointSubsets(): EndpointSubset[] {
|
||||
const subsets = this.subsets || [];
|
||||
|
||||
@ -22,15 +22,9 @@
|
||||
import moment from "moment";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { formatDuration } from "../../utils/formatDuration";
|
||||
import { autobind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
|
||||
@autobind()
|
||||
export class KubeEvent extends KubeObject {
|
||||
static kind = "Event";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/events";
|
||||
|
||||
export interface KubeEvent {
|
||||
involvedObject: {
|
||||
kind: string;
|
||||
namespace: string;
|
||||
@ -53,6 +47,12 @@ export class KubeEvent extends KubeObject {
|
||||
eventTime: null;
|
||||
reportingComponent: string;
|
||||
reportingInstance: string;
|
||||
}
|
||||
|
||||
export class KubeEvent extends KubeObject {
|
||||
static kind = "Event";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/events";
|
||||
|
||||
isWarning() {
|
||||
return this.type === "Warning";
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
import { compile } from "path-to-regexp";
|
||||
import { apiBase } from "../index";
|
||||
import { stringify } from "querystring";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
|
||||
export type RepoHelmChartList = Record<string, HelmChart[]>;
|
||||
export type HelmChartList = Record<string, RepoHelmChartList>;
|
||||
@ -83,16 +83,7 @@ export async function getChartValues(repo: string, name: string, version: string
|
||||
return apiBase.get<string>(`/v2/charts/${repo}/${name}/values?${stringify({ version })}`);
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class HelmChart {
|
||||
constructor(data: any) {
|
||||
Object.assign(this, data);
|
||||
}
|
||||
|
||||
static create(data: any) {
|
||||
return new HelmChart(data);
|
||||
}
|
||||
|
||||
export interface HelmChart {
|
||||
apiVersion: string;
|
||||
name: string;
|
||||
version: string;
|
||||
@ -114,6 +105,17 @@ export class HelmChart {
|
||||
appVersion?: string;
|
||||
deprecated?: boolean;
|
||||
tillerVersion?: string;
|
||||
}
|
||||
|
||||
export class HelmChart {
|
||||
constructor(data: HelmChart) {
|
||||
Object.assign(this, data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
static create(data: any) {
|
||||
return new HelmChart(data);
|
||||
}
|
||||
|
||||
getId() {
|
||||
return `${this.repo}:${this.apiVersion}/${this.name}@${this.getAppVersion()}+${this.digest}`;
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
import jsYaml from "js-yaml";
|
||||
import { compile } from "path-to-regexp";
|
||||
import { autobind, formatDuration } from "../../utils";
|
||||
import { autoBind, formatDuration } from "../../utils";
|
||||
import capitalize from "lodash/capitalize";
|
||||
import { apiBase } from "../index";
|
||||
import { helmChartStore } from "../../components/+apps-helm-charts/helm-chart.store";
|
||||
@ -155,16 +155,7 @@ export async function rollbackRelease(name: string, namespace: string, revision:
|
||||
});
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class HelmRelease implements ItemObject {
|
||||
constructor(data: any) {
|
||||
Object.assign(this, data);
|
||||
}
|
||||
|
||||
static create(data: any) {
|
||||
return new HelmRelease(data);
|
||||
}
|
||||
|
||||
export interface HelmRelease {
|
||||
appVersion: string;
|
||||
name: string;
|
||||
namespace: string;
|
||||
@ -172,6 +163,17 @@ export class HelmRelease implements ItemObject {
|
||||
status: string;
|
||||
updated: string;
|
||||
revision: string;
|
||||
}
|
||||
|
||||
export class HelmRelease implements ItemObject {
|
||||
constructor(data: any) {
|
||||
Object.assign(this, data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
static create(data: any) {
|
||||
return new HelmRelease(data);
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.namespace + this.name;
|
||||
|
||||
@ -59,11 +59,7 @@ export interface IHpaMetric {
|
||||
}>;
|
||||
}
|
||||
|
||||
export class HorizontalPodAutoscaler extends KubeObject {
|
||||
static kind = "HorizontalPodAutoscaler";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/autoscaling/v2beta1/horizontalpodautoscalers";
|
||||
|
||||
export interface HorizontalPodAutoscaler {
|
||||
spec: {
|
||||
scaleTargetRef: {
|
||||
kind: string;
|
||||
@ -86,6 +82,12 @@ export class HorizontalPodAutoscaler extends KubeObject {
|
||||
type: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
export class HorizontalPodAutoscaler extends KubeObject {
|
||||
static kind = "HorizontalPodAutoscaler";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/autoscaling/v2beta1/horizontalpodautoscalers";
|
||||
|
||||
getMaxPods() {
|
||||
return this.spec.maxReplicas || 0;
|
||||
|
||||
@ -20,9 +20,10 @@
|
||||
*/
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { IMetrics, metricsApi } from "./metrics.api";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export class IngressApi extends KubeApi<Ingress> {
|
||||
getMetrics(ingress: string, namespace: string): Promise<IIngressMetrics> {
|
||||
@ -82,12 +83,7 @@ export const getBackendServiceNamePort = (backend: IIngressBackend) => {
|
||||
return { serviceName, servicePort };
|
||||
};
|
||||
|
||||
@autobind()
|
||||
export class Ingress extends KubeObject {
|
||||
static kind = "Ingress";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/networking.k8s.io/v1/ingresses";
|
||||
|
||||
export interface Ingress {
|
||||
spec: {
|
||||
tls: {
|
||||
secretName: string;
|
||||
@ -117,6 +113,17 @@ export class Ingress extends KubeObject {
|
||||
ingress: ILoadBalancerIngress[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export class Ingress extends KubeObject {
|
||||
static kind = "Ingress";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/networking.k8s.io/v1/ingresses";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getRoutes() {
|
||||
const { spec: { tls, rules } } = this;
|
||||
|
||||
@ -20,19 +20,24 @@
|
||||
*/
|
||||
|
||||
import get from "lodash/get";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
|
||||
import type { IPodContainer } from "./pods.api";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { JsonApiParams } from "../json-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
@autobind()
|
||||
export class Job extends WorkloadKubeObject {
|
||||
static kind = "Job";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/batch/v1/jobs";
|
||||
|
||||
spec: {
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
declare spec: {
|
||||
parallelism?: number;
|
||||
completions?: number;
|
||||
backoffLimit?: number;
|
||||
@ -78,7 +83,7 @@ export class Job extends WorkloadKubeObject {
|
||||
serviceAccount?: string;
|
||||
schedulerName?: string;
|
||||
};
|
||||
status: {
|
||||
declare status: {
|
||||
conditions: {
|
||||
type: string;
|
||||
status: string;
|
||||
|
||||
@ -21,7 +21,8 @@
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export enum LimitType {
|
||||
CONTAINER = "Container",
|
||||
@ -50,15 +51,21 @@ export interface LimitRangeItem extends LimitRangeParts {
|
||||
type: string
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export interface LimitRange {
|
||||
spec: {
|
||||
limits: LimitRangeItem[];
|
||||
};
|
||||
}
|
||||
|
||||
export class LimitRange extends KubeObject {
|
||||
static kind = "LimitRange";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/limitranges";
|
||||
|
||||
spec: {
|
||||
limits: LimitRangeItem[];
|
||||
};
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getContainerLimits() {
|
||||
return this.spec.limits.filter(limit => limit.type === LimitType.CONTAINER);
|
||||
|
||||
@ -21,25 +21,32 @@
|
||||
|
||||
import { KubeApi } from "../kube-api";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export enum NamespaceStatus {
|
||||
ACTIVE = "Active",
|
||||
TERMINATING = "Terminating",
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export interface Namespace {
|
||||
status?: {
|
||||
phase: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class Namespace extends KubeObject {
|
||||
static kind = "Namespace";
|
||||
static namespaced = false;
|
||||
static apiBase = "/api/v1/namespaces";
|
||||
|
||||
status?: {
|
||||
phase: string;
|
||||
};
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
return this.status ? this.status.phase : "-";
|
||||
return this.status?.phase ?? "-";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,8 +20,9 @@
|
||||
*/
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export interface IPolicyIpBlock {
|
||||
cidr: string;
|
||||
@ -56,12 +57,7 @@ export interface IPolicyEgress {
|
||||
}[];
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class NetworkPolicy extends KubeObject {
|
||||
static kind = "NetworkPolicy";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/networking.k8s.io/v1/networkpolicies";
|
||||
|
||||
export interface NetworkPolicy {
|
||||
spec: {
|
||||
podSelector: {
|
||||
matchLabels: {
|
||||
@ -73,6 +69,17 @@ export class NetworkPolicy extends KubeObject {
|
||||
ingress: IPolicyIngress[];
|
||||
egress: IPolicyEgress[];
|
||||
};
|
||||
}
|
||||
|
||||
export class NetworkPolicy extends KubeObject {
|
||||
static kind = "NetworkPolicy";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/networking.k8s.io/v1/networkpolicies";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getMatchLabels(): string[] {
|
||||
if (!this.spec.podSelector || !this.spec.podSelector.matchLabels) return [];
|
||||
|
||||
@ -20,13 +20,14 @@
|
||||
*/
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { autobind, cpuUnitsToNumber, unitsToBytes } from "../../utils";
|
||||
import { autoBind, cpuUnitsToNumber, unitsToBytes } from "../../utils";
|
||||
import { IMetrics, metricsApi } from "./metrics.api";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export class NodesApi extends KubeApi<Node> {
|
||||
getMetrics(): Promise<INodeMetrics> {
|
||||
const opts = { category: "nodes"};
|
||||
const opts = { category: "nodes" };
|
||||
|
||||
return metricsApi.getMetrics({
|
||||
memoryUsage: opts,
|
||||
@ -49,12 +50,7 @@ export interface INodeMetrics<T = IMetrics> {
|
||||
fsSize: T;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class Node extends KubeObject {
|
||||
static kind = "Node";
|
||||
static namespaced = false;
|
||||
static apiBase = "/api/v1/nodes";
|
||||
|
||||
export interface Node {
|
||||
spec: {
|
||||
podCIDR: string;
|
||||
externalID: string;
|
||||
@ -105,6 +101,17 @@ export class Node extends KubeObject {
|
||||
sizeBytes: number;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
export class Node extends KubeObject {
|
||||
static kind = "Node";
|
||||
static namespaced = false;
|
||||
static apiBase = "/api/v1/nodes";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getNodeConditionText() {
|
||||
const { conditions } = this.status;
|
||||
|
||||
@ -20,10 +20,11 @@
|
||||
*/
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { IMetrics, metricsApi } from "./metrics.api";
|
||||
import type { Pod } from "./pods.api";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export class PersistentVolumeClaimsApi extends KubeApi<PersistentVolumeClaim> {
|
||||
getMetrics(pvcName: string, namespace: string): Promise<IPvcMetrics> {
|
||||
@ -42,12 +43,7 @@ export interface IPvcMetrics<T = IMetrics> {
|
||||
diskCapacity: T;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class PersistentVolumeClaim extends KubeObject {
|
||||
static kind = "PersistentVolumeClaim";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/persistentvolumeclaims";
|
||||
|
||||
export interface PersistentVolumeClaim {
|
||||
spec: {
|
||||
accessModes: string[];
|
||||
storageClassName: string;
|
||||
@ -70,6 +66,17 @@ export class PersistentVolumeClaim extends KubeObject {
|
||||
status: {
|
||||
phase: string; // Pending
|
||||
};
|
||||
}
|
||||
|
||||
export class PersistentVolumeClaim extends KubeObject {
|
||||
static kind = "PersistentVolumeClaim";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/persistentvolumeclaims";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getPods(allPods: Pod[]): Pod[] {
|
||||
const pods = allPods.filter(pod => pod.getNs() === this.getNs());
|
||||
|
||||
@ -21,15 +21,11 @@
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { unitsToBytes } from "../../utils/convertMemory";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
@autobind()
|
||||
export class PersistentVolume extends KubeObject {
|
||||
static kind = "PersistentVolume";
|
||||
static namespaced = false;
|
||||
static apiBase = "/api/v1/persistentvolumes";
|
||||
|
||||
export interface PersistentVolume {
|
||||
spec: {
|
||||
capacity: {
|
||||
storage: string; // 8Gi
|
||||
@ -65,6 +61,17 @@ export class PersistentVolume extends KubeObject {
|
||||
phase: string;
|
||||
reason?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class PersistentVolume extends KubeObject {
|
||||
static kind = "PersistentVolume";
|
||||
static namespaced = false;
|
||||
static apiBase = "/api/v1/persistentvolumes";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getCapacity(inBytes = false) {
|
||||
const capacity = this.spec.capacity;
|
||||
|
||||
@ -22,11 +22,7 @@
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
|
||||
export class PodMetrics extends KubeObject {
|
||||
static kind = "PodMetrics";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/metrics.k8s.io/v1beta1/pods";
|
||||
|
||||
export interface PodMetrics {
|
||||
timestamp: string;
|
||||
window: string;
|
||||
containers: {
|
||||
@ -38,6 +34,12 @@ export class PodMetrics extends KubeObject {
|
||||
}[];
|
||||
}
|
||||
|
||||
export class PodMetrics extends KubeObject {
|
||||
static kind = "PodMetrics";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/metrics.k8s.io/v1beta1/pods";
|
||||
}
|
||||
|
||||
export const podMetricsApi = new KubeApi({
|
||||
objectConstructor: PodMetrics,
|
||||
});
|
||||
|
||||
@ -19,16 +19,12 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
@autobind()
|
||||
export class PodDisruptionBudget extends KubeObject {
|
||||
static kind = "PodDisruptionBudget";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/policy/v1beta1/poddisruptionbudgets";
|
||||
|
||||
export interface PodDisruptionBudget {
|
||||
spec: {
|
||||
minAvailable: string;
|
||||
maxUnavailable: string;
|
||||
@ -40,6 +36,17 @@ export class PodDisruptionBudget extends KubeObject {
|
||||
disruptionsAllowed: number
|
||||
expectedPods: number
|
||||
};
|
||||
}
|
||||
|
||||
export class PodDisruptionBudget extends KubeObject {
|
||||
static kind = "PodDisruptionBudget";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/policy/v1beta1/poddisruptionbudgets";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getSelectors() {
|
||||
const selector = this.spec.selector;
|
||||
|
||||
@ -20,9 +20,10 @@
|
||||
*/
|
||||
|
||||
import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { IMetrics, metricsApi } from "./metrics.api";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export class PodsApi extends KubeApi<Pod> {
|
||||
async getLogs(params: { namespace: string; name: string }, query?: IPodLogsQuery): Promise<string> {
|
||||
@ -202,13 +203,17 @@ export interface IPodContainerStatus {
|
||||
started?: boolean;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class Pod extends WorkloadKubeObject {
|
||||
static kind = "Pod";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/pods";
|
||||
|
||||
spec: {
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
declare spec: {
|
||||
volumes?: {
|
||||
name: string;
|
||||
persistentVolumeClaim: {
|
||||
@ -265,7 +270,7 @@ export class Pod extends WorkloadKubeObject {
|
||||
};
|
||||
affinity?: IAffinity;
|
||||
};
|
||||
status?: {
|
||||
declare status?: {
|
||||
phase: string;
|
||||
conditions: {
|
||||
type: string;
|
||||
|
||||
@ -19,16 +19,12 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
@autobind()
|
||||
export class PodSecurityPolicy extends KubeObject {
|
||||
static kind = "PodSecurityPolicy";
|
||||
static namespaced = false;
|
||||
static apiBase = "/apis/policy/v1beta1/podsecuritypolicies";
|
||||
|
||||
export interface PodSecurityPolicy {
|
||||
spec: {
|
||||
allowPrivilegeEscalation?: boolean;
|
||||
allowedCSIDrivers?: {
|
||||
@ -88,6 +84,17 @@ export class PodSecurityPolicy extends KubeObject {
|
||||
};
|
||||
volumes?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export class PodSecurityPolicy extends KubeObject {
|
||||
static kind = "PodSecurityPolicy";
|
||||
static namespaced = false;
|
||||
static apiBase = "/apis/policy/v1beta1/podsecuritypolicies";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
isPrivileged() {
|
||||
return !!this.spec.privileged;
|
||||
|
||||
@ -20,10 +20,11 @@
|
||||
*/
|
||||
|
||||
import get from "lodash/get";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { WorkloadKubeObject } from "../workload-kube-object";
|
||||
import type { IPodContainer, Pod } from "./pods.api";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export class ReplicaSetApi extends KubeApi<ReplicaSet> {
|
||||
protected getScaleApiUrl(params: { namespace: string; name: string }) {
|
||||
@ -48,12 +49,17 @@ export class ReplicaSetApi extends KubeApi<ReplicaSet> {
|
||||
}
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class ReplicaSet extends WorkloadKubeObject {
|
||||
static kind = "ReplicaSet";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/apps/v1/replicasets";
|
||||
spec: {
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
declare spec: {
|
||||
replicas?: number;
|
||||
selector: { matchLabels: { [app: string]: string } };
|
||||
template?: {
|
||||
@ -66,7 +72,7 @@ export class ReplicaSet extends WorkloadKubeObject {
|
||||
};
|
||||
minReadySeconds?: number;
|
||||
};
|
||||
status: {
|
||||
declare status: {
|
||||
replicas: number;
|
||||
fullyLabeledReplicas?: number;
|
||||
readyReplicas?: number;
|
||||
|
||||
@ -21,7 +21,6 @@
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export interface IResourceQuotaValues {
|
||||
[quota: string]: string;
|
||||
@ -51,16 +50,7 @@ export interface IResourceQuotaValues {
|
||||
"count/deployments.extensions"?: string;
|
||||
}
|
||||
|
||||
export class ResourceQuota extends KubeObject {
|
||||
static kind = "ResourceQuota";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/resourcequotas";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
this.spec = this.spec || {} as any;
|
||||
}
|
||||
|
||||
export interface ResourceQuota {
|
||||
spec: {
|
||||
hard: IResourceQuotaValues;
|
||||
scopeSelector?: {
|
||||
@ -76,6 +66,12 @@ export class ResourceQuota extends KubeObject {
|
||||
hard: IResourceQuotaValues;
|
||||
used: IResourceQuotaValues;
|
||||
};
|
||||
}
|
||||
|
||||
export class ResourceQuota extends KubeObject {
|
||||
static kind = "ResourceQuota";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/resourcequotas";
|
||||
|
||||
getScopeSelector() {
|
||||
const { matchExpressions = [] } = this.spec.scopeSelector || {};
|
||||
|
||||
@ -19,9 +19,10 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export interface IRoleBindingSubject {
|
||||
kind: string;
|
||||
@ -30,18 +31,24 @@ export interface IRoleBindingSubject {
|
||||
apiGroup?: string;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class RoleBinding extends KubeObject {
|
||||
static kind = "RoleBinding";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/rbac.authorization.k8s.io/v1/rolebindings";
|
||||
|
||||
export interface RoleBinding {
|
||||
subjects?: IRoleBindingSubject[];
|
||||
roleRef: {
|
||||
kind: string;
|
||||
name: string;
|
||||
apiGroup?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class RoleBinding extends KubeObject {
|
||||
static kind = "RoleBinding";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/rbac.authorization.k8s.io/v1/rolebindings";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getSubjects() {
|
||||
return this.subjects || [];
|
||||
|
||||
@ -22,17 +22,19 @@
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
|
||||
export class Role extends KubeObject {
|
||||
static kind = "Role";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/rbac.authorization.k8s.io/v1/roles";
|
||||
|
||||
export interface Role {
|
||||
rules: {
|
||||
verbs: string[];
|
||||
apiGroups: string[];
|
||||
resources: string[];
|
||||
resourceNames?: string[];
|
||||
}[];
|
||||
}
|
||||
|
||||
export class Role extends KubeObject {
|
||||
static kind = "Role";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/rbac.authorization.k8s.io/v1/roles";
|
||||
|
||||
getRules() {
|
||||
return this.rules || [];
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
|
||||
export enum SecretType {
|
||||
@ -40,21 +40,24 @@ export interface ISecretRef {
|
||||
name: string;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class Secret extends KubeObject {
|
||||
static kind = "Secret";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/secrets";
|
||||
|
||||
export interface Secret {
|
||||
type: SecretType;
|
||||
data: {
|
||||
[prop: string]: string;
|
||||
token?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class Secret extends KubeObject {
|
||||
static kind = "Secret";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/secrets";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
this.data = this.data || {};
|
||||
autoBind(this);
|
||||
|
||||
this.data ??= {};
|
||||
}
|
||||
|
||||
getKeys(): string[] {
|
||||
|
||||
@ -28,8 +28,7 @@ export class SelfSubjectRulesReviewApi extends KubeApi<SelfSubjectRulesReview> {
|
||||
spec: {
|
||||
namespace
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,21 +40,21 @@ export interface ISelfSubjectReviewRule {
|
||||
nonResourceURLs?: string[];
|
||||
}
|
||||
|
||||
export class SelfSubjectRulesReview extends KubeObject {
|
||||
static kind = "SelfSubjectRulesReview";
|
||||
static namespaced = false;
|
||||
static apiBase = "/apis/authorization.k8s.io/v1/selfsubjectrulesreviews";
|
||||
|
||||
export interface SelfSubjectRulesReview {
|
||||
spec: {
|
||||
// todo: add more types from api docs
|
||||
namespace?: string;
|
||||
};
|
||||
|
||||
status: {
|
||||
resourceRules: ISelfSubjectReviewRule[];
|
||||
nonResourceRules: ISelfSubjectReviewRule[];
|
||||
incomplete: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export class SelfSubjectRulesReview extends KubeObject {
|
||||
static kind = "SelfSubjectRulesReview";
|
||||
static namespaced = false;
|
||||
static apiBase = "/apis/authorization.k8s.io/v1/selfsubjectrulesreviews";
|
||||
|
||||
getResourceRules() {
|
||||
const rules = this.status && this.status.resourceRules || [];
|
||||
|
||||
@ -19,22 +19,29 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
@autobind()
|
||||
export class ServiceAccount extends KubeObject {
|
||||
static kind = "ServiceAccount";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/serviceaccounts";
|
||||
|
||||
export interface ServiceAccount {
|
||||
secrets?: {
|
||||
name: string;
|
||||
}[];
|
||||
imagePullSecrets?: {
|
||||
name: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export class ServiceAccount extends KubeObject {
|
||||
static kind = "ServiceAccount";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/serviceaccounts";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getSecrets() {
|
||||
return this.secrets || [];
|
||||
|
||||
@ -19,25 +19,21 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export interface IServicePort {
|
||||
name?: string;
|
||||
protocol: string;
|
||||
port: number;
|
||||
targetPort: number;
|
||||
}
|
||||
|
||||
export class ServicePort implements IServicePort {
|
||||
export interface ServicePort {
|
||||
name?: string;
|
||||
protocol: string;
|
||||
port: number;
|
||||
targetPort: number;
|
||||
nodePort?: number;
|
||||
}
|
||||
|
||||
constructor(data: IServicePort) {
|
||||
export class ServicePort {
|
||||
constructor(data: ServicePort) {
|
||||
Object.assign(this, data);
|
||||
}
|
||||
|
||||
@ -50,12 +46,7 @@ export class ServicePort implements IServicePort {
|
||||
}
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class Service extends KubeObject {
|
||||
static kind = "Service";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/services";
|
||||
|
||||
export interface Service {
|
||||
spec: {
|
||||
type: string;
|
||||
clusterIP: string;
|
||||
@ -75,6 +66,17 @@ export class Service extends KubeObject {
|
||||
}[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export class Service extends KubeObject {
|
||||
static kind = "Service";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/services";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
getClusterIp() {
|
||||
return this.spec.clusterIP;
|
||||
|
||||
@ -22,8 +22,9 @@
|
||||
import get from "lodash/get";
|
||||
import type { IPodContainer } from "./pods.api";
|
||||
import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
export class StatefulSetApi extends KubeApi<StatefulSet> {
|
||||
protected getScaleApiUrl(params: { namespace: string; name: string }) {
|
||||
@ -48,13 +49,17 @@ export class StatefulSetApi extends KubeApi<StatefulSet> {
|
||||
}
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class StatefulSet extends WorkloadKubeObject {
|
||||
static kind = "StatefulSet";
|
||||
static namespaced = true;
|
||||
static apiBase = "/apis/apps/v1/statefulsets";
|
||||
|
||||
spec: {
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
declare spec: {
|
||||
serviceName: string;
|
||||
replicas: number;
|
||||
selector: {
|
||||
@ -107,7 +112,7 @@ export class StatefulSet extends WorkloadKubeObject {
|
||||
};
|
||||
}[];
|
||||
};
|
||||
status: {
|
||||
declare status: {
|
||||
observedGeneration: number;
|
||||
replicas: number;
|
||||
currentReplicas: number;
|
||||
|
||||
@ -19,16 +19,12 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { autobind } from "../../utils";
|
||||
import { autoBind } from "../../utils";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
|
||||
@autobind()
|
||||
export class StorageClass extends KubeObject {
|
||||
static kind = "StorageClass";
|
||||
static namespaced = false;
|
||||
static apiBase = "/apis/storage.k8s.io/v1/storageclasses";
|
||||
|
||||
export interface StorageClass {
|
||||
provisioner: string; // e.g. "storage.k8s.io/v1"
|
||||
mountOptions?: string[];
|
||||
volumeBindingMode: string;
|
||||
@ -36,6 +32,17 @@ export class StorageClass extends KubeObject {
|
||||
parameters: {
|
||||
[param: string]: string; // every provisioner has own set of these parameters
|
||||
};
|
||||
}
|
||||
|
||||
export class StorageClass extends KubeObject {
|
||||
static kind = "StorageClass";
|
||||
static namespaced = false;
|
||||
static apiBase = "/apis/storage.k8s.io/v1/storageclasses";
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
isDefault() {
|
||||
const annotations = this.metadata.annotations || {};
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
import moment from "moment";
|
||||
import type { KubeJsonApiData, KubeJsonApiDataList, KubeJsonApiListMetadata, KubeJsonApiMetadata } from "./kube-json-api";
|
||||
import { autobind, formatDuration } from "../utils";
|
||||
import { autoBind, formatDuration } from "../utils";
|
||||
import type { ItemObject } from "../item.store";
|
||||
import { apiKube } from "./index";
|
||||
import type { JsonApiParams } from "./json-api";
|
||||
@ -87,11 +87,16 @@ export class KubeStatus {
|
||||
|
||||
export type IKubeMetaField = keyof IKubeObjectMetadata;
|
||||
|
||||
@autobind()
|
||||
export class KubeObject implements ItemObject {
|
||||
export class KubeObject<Metadata extends IKubeObjectMetadata = IKubeObjectMetadata, Status = any, Spec = any> implements ItemObject {
|
||||
static readonly kind: string;
|
||||
static readonly namespaced: boolean;
|
||||
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
metadata: Metadata;
|
||||
status?: Status;
|
||||
spec?: Spec;
|
||||
|
||||
static create(data: any) {
|
||||
return new KubeObject(data);
|
||||
}
|
||||
@ -176,13 +181,9 @@ export class KubeObject implements ItemObject {
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
Object.assign(this, data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
metadata: IKubeObjectMetadata;
|
||||
status?: any; // todo: type-safety support
|
||||
|
||||
get selfLink() {
|
||||
return this.metadata.selfLink;
|
||||
}
|
||||
@ -265,7 +266,7 @@ export class KubeObject implements ItemObject {
|
||||
}
|
||||
|
||||
// use unified resource-applier api for updating all k8s objects
|
||||
async update<T extends KubeObject>(data: Partial<T>) {
|
||||
async update<T extends KubeObject>(data: Partial<T>): Promise<T> {
|
||||
return resourceApplierApi.update<T>({
|
||||
...this.toPlainObject(),
|
||||
...data,
|
||||
|
||||
@ -26,8 +26,8 @@ import type { KubeObjectStore } from "../kube-object.store";
|
||||
import type { ClusterContext } from "../components/context";
|
||||
|
||||
import plimit from "p-limit";
|
||||
import { comparer, IReactionDisposer, observable, reaction, when } from "mobx";
|
||||
import { autobind, noop } from "../utils";
|
||||
import { comparer, IReactionDisposer, observable, reaction, makeObservable } from "mobx";
|
||||
import { autoBind, noop } from "../utils";
|
||||
import type { KubeApi } from "./kube-api";
|
||||
import type { KubeJsonApiData } from "./kube-json-api";
|
||||
import { isDebugging, isProduction } from "../../common/vars";
|
||||
@ -50,11 +50,13 @@ export interface IKubeWatchLog {
|
||||
cssStyle?: string;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class KubeWatchApi {
|
||||
@observable context: ClusterContext = null;
|
||||
|
||||
contextReady = when(() => Boolean(this.context));
|
||||
constructor() {
|
||||
makeObservable(this);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
isAllowedApi(api: KubeApi): boolean {
|
||||
return Boolean(this.context?.cluster.isAllowedResource(api.kind));
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { stringify } from "querystring";
|
||||
import { autobind, base64, EventEmitter } from "../utils";
|
||||
import { boundMethod, base64, EventEmitter } from "../utils";
|
||||
import { WebSocketApi } from "./websocket-api";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { isDevelopment } from "../../common/vars";
|
||||
@ -106,7 +106,7 @@ export class TerminalApi extends WebSocketApi {
|
||||
this.onReady.removeAllListeners();
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
protected _onReady(data: string) {
|
||||
if (!data) return true;
|
||||
this.isReady = true;
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { observable } from "mobx";
|
||||
import { observable, makeObservable } from "mobx";
|
||||
import { EventEmitter } from "../../common/event-emitter";
|
||||
|
||||
interface IParams {
|
||||
@ -66,6 +66,7 @@ export class WebSocketApi {
|
||||
};
|
||||
|
||||
constructor(protected params: IParams) {
|
||||
makeObservable(this);
|
||||
this.params = Object.assign({}, WebSocketApi.defaultParams, params);
|
||||
const { autoConnect, pingIntervalSeconds } = this.params;
|
||||
|
||||
|
||||
@ -68,8 +68,6 @@ export interface IAffinity {
|
||||
}
|
||||
|
||||
export class WorkloadKubeObject extends KubeObject {
|
||||
spec: any; // todo: add proper types
|
||||
|
||||
getSelectors(): string[] {
|
||||
const selector = this.spec.selector;
|
||||
|
||||
|
||||
@ -26,13 +26,14 @@ import * as Mobx from "mobx";
|
||||
import * as MobxReact from "mobx-react";
|
||||
import * as ReactRouter from "react-router";
|
||||
import * as ReactRouterDom from "react-router-dom";
|
||||
import * as LensExtensionsCoreApi from "../extensions/core-api";
|
||||
import * as LensExtensionsRendererApi from "../extensions/renderer-api";
|
||||
import { render, unmountComponentAtNode } from "react-dom";
|
||||
import { delay } from "../common/utils";
|
||||
import { isMac, isDevelopment } from "../common/vars";
|
||||
import { HotbarStore } from "../common/hotbar-store";
|
||||
import { ClusterStore } from "../common/cluster-store";
|
||||
import { UserStore } from "../common/user-store";
|
||||
import * as LensExtensions from "../extensions/extension-api";
|
||||
import { ExtensionDiscovery } from "../extensions/extension-discovery";
|
||||
import { ExtensionLoader } from "../extensions/extension-loader";
|
||||
import { ExtensionsStore } from "../extensions/extensions-store";
|
||||
@ -43,6 +44,9 @@ import { ThemeStore } from "./theme.store";
|
||||
import { HelmRepoManager } from "../main/helm/helm-repo-manager";
|
||||
import { ExtensionInstallationStateStore } from "./components/+extensions/extension-install.store";
|
||||
import { DefaultProps } from "./mui-base-theme";
|
||||
import configurePackages from "../common/configure-packages";
|
||||
|
||||
configurePackages();
|
||||
|
||||
/**
|
||||
* If this is a development buid, wait a second to attach
|
||||
@ -59,15 +63,6 @@ type AppComponent = React.ComponentType & {
|
||||
init?(): Promise<void>;
|
||||
};
|
||||
|
||||
export {
|
||||
React,
|
||||
ReactRouter,
|
||||
ReactRouterDom,
|
||||
Mobx,
|
||||
MobxReact,
|
||||
LensExtensions
|
||||
};
|
||||
|
||||
export async function bootstrap(App: AppComponent) {
|
||||
const rootElem = document.getElementById("app");
|
||||
|
||||
@ -120,3 +115,23 @@ export async function bootstrap(App: AppComponent) {
|
||||
|
||||
// run
|
||||
bootstrap(process.isMainFrame ? LensApp : App);
|
||||
|
||||
|
||||
/**
|
||||
* Exports for virtual package "@k8slens/extensions" for renderer-process.
|
||||
* All exporting names available in global runtime scope:
|
||||
* e.g. Devtools -> Console -> window.LensExtensions (renderer)
|
||||
*/
|
||||
const LensExtensions = {
|
||||
...LensExtensionsCoreApi,
|
||||
...LensExtensionsRendererApi,
|
||||
};
|
||||
|
||||
export {
|
||||
React,
|
||||
ReactRouter,
|
||||
ReactRouterDom,
|
||||
Mobx,
|
||||
MobxReact,
|
||||
LensExtensions,
|
||||
};
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
import "./add-cluster.scss";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { action, observable, runInAction } from "mobx";
|
||||
import { action, observable, runInAction, makeObservable } from "mobx";
|
||||
import { KubeConfig } from "@kubernetes/client-node";
|
||||
import { AceEditor } from "../ace-editor";
|
||||
import { Button } from "../button";
|
||||
@ -50,6 +50,11 @@ export class AddCluster extends React.Component {
|
||||
|
||||
kubeContexts = observable.map<string, KubeConfig>();
|
||||
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
appEventBus.emit({ name: "cluster-add", action: "start" });
|
||||
}
|
||||
|
||||
@ -23,10 +23,10 @@ import "./helm-chart-details.scss";
|
||||
|
||||
import React, { Component } from "react";
|
||||
import { getChartDetails, HelmChart } from "../../api/endpoints/helm-charts.api";
|
||||
import { observable, autorun } from "mobx";
|
||||
import { observable, autorun, makeObservable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { Drawer, DrawerItem } from "../drawer";
|
||||
import { autobind, stopPropagation } from "../../utils";
|
||||
import { boundMethod, stopPropagation } from "../../utils";
|
||||
import { MarkdownViewer } from "../markdown-viewer";
|
||||
import { Spinner } from "../spinner";
|
||||
import { Button } from "../button";
|
||||
@ -48,6 +48,11 @@ export class HelmChartDetails extends Component<Props> {
|
||||
|
||||
private abortController?: AbortController;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.abortController?.abort();
|
||||
}
|
||||
@ -67,7 +72,7 @@ export class HelmChartDetails extends Component<Props> {
|
||||
});
|
||||
});
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
async onVersionChange({ value: version }: SelectOption<string>) {
|
||||
this.selectedChart = this.chartVersions.find(chart => chart.version === version);
|
||||
this.readme = null;
|
||||
@ -84,7 +89,7 @@ export class HelmChartDetails extends Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
install() {
|
||||
createInstallChartTab(this.selectedChart);
|
||||
this.props.hideDetails();
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
*/
|
||||
|
||||
import semver from "semver";
|
||||
import { observable } from "mobx";
|
||||
import { autobind } from "../../utils";
|
||||
import { observable, makeObservable } from "mobx";
|
||||
import { autoBind } from "../../utils";
|
||||
import { getChartDetails, HelmChart, listCharts } from "../../api/endpoints/helm-charts.api";
|
||||
import { ItemStore } from "../../item.store";
|
||||
import flatten from "lodash/flatten";
|
||||
@ -31,10 +31,16 @@ export interface IChartVersion {
|
||||
version: string;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class HelmChartStore extends ItemStore<HelmChart> {
|
||||
@observable versions = observable.map<string, IChartVersion[]>();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
makeObservable(this);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
async loadAll() {
|
||||
try {
|
||||
const res = await this.loadItems(() => listCharts());
|
||||
|
||||
@ -57,10 +57,10 @@ export class HelmCharts extends Component<Props> {
|
||||
|
||||
showDetails = (chart: HelmChart) => {
|
||||
if (!chart) {
|
||||
navigation.merge(helmChartsURL());
|
||||
navigation.push(helmChartsURL());
|
||||
}
|
||||
else {
|
||||
navigation.merge(helmChartsURL({
|
||||
navigation.push(helmChartsURL({
|
||||
params: {
|
||||
chartName: chart.getName(),
|
||||
repo: chart.getRepository(),
|
||||
|
||||
@ -24,7 +24,7 @@ import "./release-details.scss";
|
||||
import React, { Component } from "react";
|
||||
import groupBy from "lodash/groupBy";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { observable, reaction } from "mobx";
|
||||
import { observable, reaction, makeObservable } from "mobx";
|
||||
import { Link } from "react-router-dom";
|
||||
import kebabCase from "lodash/kebabCase";
|
||||
import { getRelease, getReleaseValues, HelmRelease, IReleaseDetails } from "../../api/endpoints/helm-releases.api";
|
||||
@ -72,7 +72,7 @@ export class ReleaseDetails extends Component<Props> {
|
||||
);
|
||||
|
||||
@disposeOnUnmount
|
||||
secretWatcher = reaction(() => secretsStore.items.toJS(), () => {
|
||||
secretWatcher = reaction(() => secretsStore.getItems(), () => {
|
||||
if (!this.props.release) return;
|
||||
const { getReleaseSecret } = releaseStore;
|
||||
const { release } = this.props;
|
||||
@ -85,6 +85,11 @@ export class ReleaseDetails extends Component<Props> {
|
||||
this.releaseSecret = secret;
|
||||
});
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
async loadDetails() {
|
||||
const { release } = this.props;
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
import React from "react";
|
||||
import type { HelmRelease } from "../../api/endpoints/helm-releases.api";
|
||||
import { autobind, cssNames } from "../../utils";
|
||||
import { boundMethod, cssNames } from "../../utils";
|
||||
import { releaseStore } from "./release.store";
|
||||
import { MenuActions, MenuActionsProps } from "../menu/menu-actions";
|
||||
import { MenuItem } from "../menu";
|
||||
@ -35,12 +35,12 @@ interface Props extends MenuActionsProps {
|
||||
}
|
||||
|
||||
export class HelmReleaseMenu extends React.Component<Props> {
|
||||
@autobind()
|
||||
@boundMethod
|
||||
remove() {
|
||||
return releaseStore.remove(this.props.release);
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
upgrade() {
|
||||
const { release, hideDetails } = this.props;
|
||||
|
||||
@ -48,7 +48,7 @@ export class HelmReleaseMenu extends React.Component<Props> {
|
||||
hideDetails && hideDetails();
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
rollback() {
|
||||
ReleaseRollbackDialog.open(this.props.release);
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
import "./release-rollback-dialog.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observable } from "mobx";
|
||||
import { observable, makeObservable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { Dialog, DialogProps } from "../dialog";
|
||||
import { Wizard, WizardStep } from "../wizard";
|
||||
@ -35,26 +35,33 @@ import orderBy from "lodash/orderBy";
|
||||
interface Props extends DialogProps {
|
||||
}
|
||||
|
||||
const dialogState = observable.object({
|
||||
isOpen: false,
|
||||
release: null as HelmRelease,
|
||||
});
|
||||
|
||||
@observer
|
||||
export class ReleaseRollbackDialog extends React.Component<Props> {
|
||||
@observable static isOpen = false;
|
||||
@observable.ref static release: HelmRelease = null;
|
||||
|
||||
@observable isLoading = false;
|
||||
@observable revision: IReleaseRevision;
|
||||
@observable revisions = observable.array<IReleaseRevision>();
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
static open(release: HelmRelease) {
|
||||
ReleaseRollbackDialog.isOpen = true;
|
||||
ReleaseRollbackDialog.release = release;
|
||||
dialogState.isOpen = true;
|
||||
dialogState.release = release;
|
||||
}
|
||||
|
||||
static close() {
|
||||
ReleaseRollbackDialog.isOpen = false;
|
||||
dialogState.isOpen = false;
|
||||
}
|
||||
|
||||
get release(): HelmRelease {
|
||||
return ReleaseRollbackDialog.release;
|
||||
return dialogState.release;
|
||||
}
|
||||
|
||||
onOpen = async () => {
|
||||
@ -115,7 +122,7 @@ export class ReleaseRollbackDialog extends React.Component<Props> {
|
||||
<Dialog
|
||||
{...dialogProps}
|
||||
className="ReleaseRollbackDialog"
|
||||
isOpen={ReleaseRollbackDialog.isOpen}
|
||||
isOpen={dialogState.isOpen}
|
||||
onOpen={this.onOpen}
|
||||
close={this.close}
|
||||
>
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
*/
|
||||
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { action, observable, reaction, when } from "mobx";
|
||||
import { autobind } from "../../utils";
|
||||
import { action, observable, reaction, when, makeObservable } from "mobx";
|
||||
import { autoBind } from "../../utils";
|
||||
import { createRelease, deleteRelease, HelmRelease, IReleaseCreatePayload, IReleaseUpdatePayload, listReleases, rollbackRelease, updateRelease } from "../../api/endpoints/helm-releases.api";
|
||||
import { ItemStore } from "../../item.store";
|
||||
import type { Secret } from "../../api/endpoints";
|
||||
@ -29,19 +29,21 @@ import { secretsStore } from "../+config-secrets/secrets.store";
|
||||
import { namespaceStore } from "../+namespaces/namespace.store";
|
||||
import { Notifications } from "../notifications";
|
||||
|
||||
@autobind()
|
||||
export class ReleaseStore extends ItemStore<HelmRelease> {
|
||||
releaseSecrets = observable.map<string, Secret>();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
makeObservable(this);
|
||||
autoBind(this);
|
||||
|
||||
when(() => secretsStore.isLoaded, () => {
|
||||
this.releaseSecrets.replace(this.getReleaseSecrets());
|
||||
});
|
||||
}
|
||||
|
||||
watchAssociatedSecrets(): (() => void) {
|
||||
return reaction(() => secretsStore.items.toJS(), () => {
|
||||
return reaction(() => secretsStore.getItems(), () => {
|
||||
if (this.isLoading) return;
|
||||
const newSecrets = this.getReleaseSecrets();
|
||||
const amountChanged = newSecrets.length !== this.releaseSecrets.size;
|
||||
|
||||
@ -67,7 +67,7 @@ export class HelmReleases extends Component<Props> {
|
||||
}
|
||||
|
||||
showDetails = (item: HelmRelease) => {
|
||||
navigation.merge(releaseURL({
|
||||
navigation.push(releaseURL({
|
||||
params: {
|
||||
name: item.getName(),
|
||||
namespace: item.getNs()
|
||||
@ -76,7 +76,7 @@ export class HelmReleases extends Component<Props> {
|
||||
};
|
||||
|
||||
hideDetails = () => {
|
||||
navigation.merge(releaseURL());
|
||||
navigation.push(releaseURL());
|
||||
};
|
||||
|
||||
renderRemoveDialogMessage(selectedItems: HelmRelease[]) {
|
||||
|
||||
@ -24,12 +24,10 @@ import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { HelmCharts, helmChartsRoute, helmChartsURL } from "../+apps-helm-charts";
|
||||
import { HelmReleases, releaseRoute, releaseURL } from "../+apps-releases";
|
||||
import { namespaceUrlParam } from "../+namespaces/namespace.store";
|
||||
|
||||
@observer
|
||||
export class Apps extends React.Component {
|
||||
static get tabRoutes(): TabLayoutRoute[] {
|
||||
const query = namespaceUrlParam.toObjectParam();
|
||||
|
||||
return [
|
||||
{
|
||||
@ -41,7 +39,7 @@ export class Apps extends React.Component {
|
||||
{
|
||||
title: "Releases",
|
||||
component: HelmReleases,
|
||||
url: releaseURL({ query }),
|
||||
url: releaseURL(),
|
||||
routePath: releaseRoute.path.toString(),
|
||||
},
|
||||
];
|
||||
|
||||
@ -24,8 +24,8 @@ import React from "react";
|
||||
import { SpeedDial, SpeedDialAction } from "@material-ui/lab";
|
||||
import { Icon } from "../icon";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { observable, reaction } from "mobx";
|
||||
import { autobind } from "../../../common/utils";
|
||||
import { observable, reaction, makeObservable } from "mobx";
|
||||
import { boundMethod } from "../../../common/utils";
|
||||
import type { CatalogCategory, CatalogEntityAddMenuContext, CatalogEntityAddMenu } from "../../api/catalog-entity";
|
||||
import { EventEmitter } from "events";
|
||||
import { navigate } from "../../navigation";
|
||||
@ -39,6 +39,11 @@ export class CatalogAddButton extends React.Component<CatalogAddButtonProps> {
|
||||
@observable protected isOpen = false;
|
||||
protected menuItems = observable.array<CatalogEntityAddMenu>([]);
|
||||
|
||||
constructor(props: CatalogAddButtonProps) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
disposeOnUnmount(this, [
|
||||
reaction(() => this.props.category, (category) => {
|
||||
@ -56,17 +61,17 @@ export class CatalogAddButton extends React.Component<CatalogAddButtonProps> {
|
||||
]);
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
onOpen() {
|
||||
this.isOpen = true;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
onClose() {
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
onButtonClick() {
|
||||
if (this.menuItems.length == 1) {
|
||||
this.menuItems[0].onClick();
|
||||
|
||||
@ -19,12 +19,12 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
|
||||
import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from "mobx";
|
||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||
import type { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
|
||||
import { ItemObject, ItemStore } from "../../item.store";
|
||||
import { autobind } from "../../utils";
|
||||
import { CatalogCategory } from "../../../common/catalog";
|
||||
import { autoBind } from "../../../common/utils";
|
||||
|
||||
export class CatalogEntityItem implements ItemObject {
|
||||
constructor(public entity: CatalogEntity) {}
|
||||
@ -84,8 +84,13 @@ export class CatalogEntityItem implements ItemObject {
|
||||
}
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class CatalogEntityStore extends ItemStore<CatalogEntityItem> {
|
||||
constructor() {
|
||||
super();
|
||||
makeObservable(this);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
@observable activeCategory?: CatalogCategory;
|
||||
|
||||
@computed get entities() {
|
||||
|
||||
@ -23,7 +23,7 @@ import "./catalog.scss";
|
||||
import React from "react";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { ItemListLayout } from "../item-object-list";
|
||||
import { action, observable, reaction, when } from "mobx";
|
||||
import { action, makeObservable, observable, reaction, when } from "mobx";
|
||||
import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store";
|
||||
import { navigate } from "../../navigation";
|
||||
import { kebabCase } from "lodash";
|
||||
@ -32,7 +32,6 @@ import { MenuItem, MenuActions } from "../menu";
|
||||
import { CatalogEntityContextMenu, CatalogEntityContextMenuContext, catalogEntityRunContext } from "../../api/catalog-entity";
|
||||
import { Badge } from "../badge";
|
||||
import { HotbarStore } from "../../../common/hotbar-store";
|
||||
import { autobind } from "../../utils";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
import { Tab, Tabs } from "../tabs";
|
||||
import { catalogCategoryRegistry } from "../../../common/catalog";
|
||||
@ -49,12 +48,18 @@ enum sortBy {
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps<ICatalogViewRouteParam> {}
|
||||
|
||||
@observer
|
||||
export class Catalog extends React.Component<Props> {
|
||||
@observable private catalogEntityStore?: CatalogEntityStore;
|
||||
@observable.deep private contextMenu: CatalogEntityContextMenuContext;
|
||||
@observable private contextMenu: CatalogEntityContextMenuContext;
|
||||
@observable activeTab?: string;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
get routeActiveTab(): string | undefined {
|
||||
const { group, kind } = this.props.match.params ?? {};
|
||||
|
||||
@ -155,8 +160,7 @@ export class Catalog extends React.Component<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
@autobind()
|
||||
renderItemMenu(item: CatalogEntityItem) {
|
||||
renderItemMenu = (item: CatalogEntityItem) => {
|
||||
const menuItems = this.contextMenu.menuItems.filter((menuItem) => !menuItem.onlyVisibleForSource || menuItem.onlyVisibleForSource === item.entity.metadata.source);
|
||||
|
||||
return (
|
||||
@ -173,7 +177,7 @@ export class Catalog extends React.Component<Props> {
|
||||
</MenuItem>
|
||||
</MenuActions>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderIcon(item: CatalogEntityItem) {
|
||||
const category = catalogCategoryRegistry.getCategoryForEntity(item.entity);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user