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:
RomanandJari Kolehmainen authored and GitHub committed 2021-05-25 10:24:31 +03:00
1 parent 0c45dd807e
commit 2c3b510997
256 files changed
+2359 -1473

No files matched your search

+7 -3
View File
@@ -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);
+4 -2
View File
@@ -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;
+6 -4
View File
@@ -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");
}
+10 -7
View File
@@ -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);
+7 -5
View File
@@ -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({
+14 -23
View File
@@ -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();
+9 -4
View File
@@ -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;
+9 -4
View File
@@ -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;
+10 -3
View File
@@ -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 || [];
+7 -7
View File
@@ -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";
+13 -11
View File
@@ -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}`;
+13 -11
View File
@@ -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;
+7 -5
View File
@@ -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;
+15 -8
View File
@@ -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;
@@ -188,7 +195,7 @@ export class Ingress extends KubeObject {
getLoadBalancers() {
const { status: { loadBalancer = { ingress: [] } } } = this;
return (loadBalancer.ingress ?? []).map(address => (
address.hostname || address.ip
));
+9 -4
View File
@@ -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;
+12 -5
View File
@@ -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);
+13 -6
View File
@@ -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 [];
+15 -8
View File
@@ -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;
+9 -4
View File
@@ -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;
+10 -4
View File
@@ -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 || {};
+14 -7
View File
@@ -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 || [];
+7 -5
View File
@@ -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 || [];
+11 -8
View File
@@ -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 || [];
+18 -16
View File
@@ -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 || {};
+10 -9
View File
@@ -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,
+6 -4
View File
@@ -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));
+2 -2
View File
@@ -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;
+2 -1
View File
@@ -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;
-2
View File
@@ -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;
+25 -10
View File
@@ -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[]) {
+1 -3
View File
@@ -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() {
+10 -6
View File
@@ -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);
@@ -23,13 +23,13 @@ import "./cluster-issues.scss";
import React from "react";
import { observer } from "mobx-react";
import { computed } from "mobx";
import { computed, makeObservable } from "mobx";
import { Icon } from "../icon";
import { SubHeader } from "../layout/sub-header";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { nodesStore } from "../+nodes/nodes.store";
import { eventStore } from "../+events/event.store";
import { autobind, cssNames, prevDefault } from "../../utils";
import { boundMethod, cssNames, prevDefault } from "../../utils";
import type { ItemObject } from "../../item.store";
import { Spinner } from "../spinner";
import { ThemeStore } from "../../theme.store";
@@ -62,6 +62,11 @@ export class ClusterIssues extends React.Component<Props> {
[sortBy.age]: (warning: IWarning) => warning.timeDiffFromNow,
};
constructor(props: Props) {
super(props);
makeObservable(this);
}
@computed get warnings() {
const warnings: IWarning[] = [];
@@ -103,7 +108,7 @@ export class ClusterIssues extends React.Component<Props> {
return warnings;
}
@autobind()
@boundMethod
getTableRow(uid: string) {
const { warnings } = this;
const warning = warnings.find(warn => warn.getId() == uid);
@@ -19,10 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { action, observable, reaction, when } from "mobx";
import { action, observable, reaction, when, makeObservable } from "mobx";
import { KubeObjectStore } from "../../kube-object.store";
import { Cluster, clusterApi, IClusterMetrics } from "../../api/endpoints";
import { autobind, createStorage } from "../../utils";
import { autoBind, createStorage } from "../../utils";
import { IMetricsReqParams, normalizeMetrics } from "../../api/endpoints/metrics.api";
import { nodesStore } from "../+nodes/nodes.store";
import { apiManager } from "../../api/api-manager";
@@ -42,7 +42,6 @@ export interface ClusterOverviewStorageState {
metricNodeRole: MetricNodeRole,
}
@autobind()
export class ClusterOverviewStore extends KubeObjectStore<Cluster> implements ClusterOverviewStorageState {
api = clusterApi;
@@ -72,6 +71,9 @@ export class ClusterOverviewStore extends KubeObjectStore<Cluster> implements Cl
constructor() {
super();
makeObservable(this);
autoBind(this);
this.init();
}
@@ -19,12 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { HorizontalPodAutoscaler, hpaApi } from "../../api/endpoints/hpa.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class HPAStore extends KubeObjectStore<HorizontalPodAutoscaler> {
api = hpaApi;
}
@@ -19,12 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../../common/utils/autobind";
import { KubeObjectStore } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
import { LimitRange, limitRangeApi } from "../../api/endpoints/limit-range.api";
@autobind()
export class LimitRangesStore extends KubeObjectStore<LimitRange> {
api = limitRangeApi;
}
@@ -22,7 +22,7 @@
import "./config-map-details.scss";
import React from "react";
import { autorun, observable } from "mobx";
import { autorun, makeObservable, observable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { DrawerTitle } from "../drawer";
import { Notifications } from "../notifications";
@@ -41,7 +41,12 @@ interface Props extends KubeObjectDetailsProps<ConfigMap> {
@observer
export class ConfigMapDetails extends React.Component<Props> {
@observable isSaving = false;
@observable data = observable.map();
@observable data = observable.map<string, string>();
constructor(props: Props) {
super(props);
makeObservable(this);
}
async componentDidMount() {
disposeOnUnmount(this, [
@@ -60,7 +65,10 @@ export class ConfigMapDetails extends React.Component<Props> {
try {
this.isSaving = true;
await configMapsStore.update(configMap, { ...configMap, data: this.data.toJSON() });
await configMapsStore.update(configMap, {
...configMap,
data: Object.fromEntries(this.data),
});
Notifications.ok(
<p>
<>ConfigMap <b>{configMap.getName()}</b> successfully updated.</>
@@ -75,7 +83,7 @@ export class ConfigMapDetails extends React.Component<Props> {
const { object: configMap } = this.props;
if (!configMap) return null;
const data = Object.entries(this.data.toJSON());
const data = Array.from(this.data.entries());
return (
<div className="ConfigMapDetails">
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { ConfigMap, configMapApi } from "../../api/endpoints/configmap.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class ConfigMapsStore extends KubeObjectStore<ConfigMap> {
api = configMapApi;
}
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { PodDisruptionBudget, pdbApi } from "../../api/endpoints/poddisruptionbudget.api";
import { pdbApi, PodDisruptionBudget } from "../../api/endpoints/poddisruptionbudget.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class PodDisruptionBudgetsStore extends KubeObjectStore<PodDisruptionBudget> {
api = pdbApi;
}
@@ -22,7 +22,7 @@
import "./add-quota-dialog.scss";
import React from "react";
import { computed, observable } from "mobx";
import { computed, observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -39,10 +39,12 @@ import { SubTitle } from "../layout/sub-title";
interface Props extends DialogProps {
}
const dialogState = observable.object({
isOpen: false,
});
@observer
export class AddQuotaDialog extends React.Component<Props> {
@observable static isOpen = false;
static defaultQuotas: IResourceQuotaValues = {
"limits.cpu": "",
"limits.memory": "",
@@ -72,12 +74,17 @@ export class AddQuotaDialog extends React.Component<Props> {
@observable namespace = this.defaultNamespace;
@observable quotas = AddQuotaDialog.defaultQuotas;
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open() {
AddQuotaDialog.isOpen = true;
dialogState.isOpen = true;
}
static close() {
AddQuotaDialog.isOpen = false;
dialogState.isOpen = false;
}
@computed get quotaEntries() {
@@ -154,7 +161,7 @@ export class AddQuotaDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="AddQuotaDialog"
isOpen={AddQuotaDialog.isOpen}
isOpen={dialogState.isOpen}
close={this.close}
>
<Wizard header={header} done={this.close}>
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { ResourceQuota, resourceQuotaApi } from "../../api/endpoints/resource-quota.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class ResourceQuotasStore extends KubeObjectStore<ResourceQuota> {
api = resourceQuotaApi;
}
@@ -22,7 +22,7 @@
import "./add-secret-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";
@@ -57,16 +57,23 @@ interface ISecretTemplate {
type ISecretField = keyof ISecretTemplate;
const dialogState = observable.object({
isOpen: false,
});
@observer
export class AddSecretDialog extends React.Component<Props> {
@observable static isOpen = false;
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open() {
AddSecretDialog.isOpen = true;
dialogState.isOpen = true;
}
static close() {
AddSecretDialog.isOpen = false;
dialogState.isOpen = false;
}
private secretTemplate: { [p: string]: ISecretTemplate } = {
@@ -205,7 +212,7 @@ export class AddSecretDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="AddSecretDialog"
isOpen={AddSecretDialog.isOpen}
isOpen={dialogState.isOpen}
onOpen={this.reset}
close={this.close}
>
@@ -23,7 +23,7 @@ import "./secret-details.scss";
import React from "react";
import isEmpty from "lodash/isEmpty";
import { autorun, observable } from "mobx";
import { autorun, observable, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { DrawerItem, DrawerTitle } from "../drawer";
import { Input } from "../input";
@@ -46,6 +46,11 @@ export class SecretDetails extends React.Component<Props> {
@observable data: { [name: string]: string } = {};
@observable revealSecret: { [name: string]: boolean } = {};
constructor(props: Props) {
super(props);
makeObservable(this);
}
async componentDidMount() {
disposeOnUnmount(this, [
autorun(() => {
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { Secret, secretsApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
@autobind()
export class SecretsStore extends KubeObjectStore<Secret> {
api = secretsApi;
}
+6 -8
View File
@@ -24,7 +24,6 @@ import { observer } from "mobx-react";
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
import { ConfigMaps, configMapsRoute, configMapsURL } from "../+config-maps";
import { Secrets, secretsRoute, secretsURL } from "../+config-secrets";
import { namespaceUrlParam } from "../+namespaces/namespace.store";
import { resourceQuotaRoute, ResourceQuotas, resourceQuotaURL } from "../+config-resource-quotas";
import { pdbRoute, pdbURL, PodDisruptionBudgets } from "../+config-pod-disruption-budgets";
import { HorizontalPodAutoscalers, hpaRoute, hpaURL } from "../+config-autoscalers";
@@ -34,14 +33,13 @@ import { LimitRanges, limitRangesRoute, limitRangeURL } from "../+config-limit-r
@observer
export class Config extends React.Component {
static get tabRoutes(): TabLayoutRoute[] {
const query = namespaceUrlParam.toObjectParam();
const routes: TabLayoutRoute[] = [];
if (isAllowedResource("configmaps")) {
routes.push({
title: "ConfigMaps",
component: ConfigMaps,
url: configMapsURL({ query }),
url: configMapsURL(),
routePath: configMapsRoute.path.toString(),
});
}
@@ -50,7 +48,7 @@ export class Config extends React.Component {
routes.push({
title: "Secrets",
component: Secrets,
url: secretsURL({ query }),
url: secretsURL(),
routePath: secretsRoute.path.toString(),
});
}
@@ -59,7 +57,7 @@ export class Config extends React.Component {
routes.push({
title: "Resource Quotas",
component: ResourceQuotas,
url: resourceQuotaURL({ query }),
url: resourceQuotaURL(),
routePath: resourceQuotaRoute.path.toString(),
});
}
@@ -68,7 +66,7 @@ export class Config extends React.Component {
routes.push({
title: "Limit Ranges",
component: LimitRanges,
url: limitRangeURL({ query }),
url: limitRangeURL(),
routePath: limitRangesRoute.path.toString(),
});
}
@@ -77,7 +75,7 @@ export class Config extends React.Component {
routes.push({
title: "HPA",
component: HorizontalPodAutoscalers,
url: hpaURL({ query }),
url: hpaURL(),
routePath: hpaRoute.path.toString(),
});
}
@@ -86,7 +84,7 @@ export class Config extends React.Component {
routes.push({
title: "Pod Disruption Budgets",
component: PodDisruptionBudgets,
url: pdbURL({ query }),
url: pdbURL(),
routePath: pdbRoute.path.toString(),
});
}
@@ -22,7 +22,7 @@
import "./crd-list.scss";
import React from "react";
import { computed } from "mobx";
import { computed, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Link } from "react-router-dom";
import { stopPropagation } from "../../utils";
@@ -35,8 +35,6 @@ import { Icon } from "../icon";
export const crdGroupsUrlParam = createPageParam<string[]>({
name: "groups",
multiValues: true,
isSystem: true,
defaultValue: [],
});
@@ -50,6 +48,11 @@ enum columnId {
@observer
export class CrdList extends React.Component {
constructor(props: {}) {
super(props);
makeObservable(this);
}
get selectedGroups(): string[] {
return crdGroupsUrlParam.get();
}
@@ -24,7 +24,7 @@ import "./crd-resource-details.scss";
import React from "react";
import jsonPath from "jsonpath";
import { observer } from "mobx-react";
import { computed } from "mobx";
import { computed, makeObservable } from "mobx";
import { cssNames } from "../../utils";
import { Badge } from "../badge";
import { DrawerItem } from "../drawer";
@@ -60,6 +60,11 @@ function convertSpecValue(value: any): any {
@observer
export class CrdResourceDetails extends React.Component<Props> {
constructor(props: Props) {
super(props);
makeObservable(this);
}
@computed get crd() {
return crdStore.getByObject(this.props.object);
}
@@ -19,12 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { KubeApi } from "../../api/kube-api";
import type { KubeApi } from "../../api/kube-api";
import { KubeObjectStore } from "../../kube-object.store";
import type { KubeObject } from "../../api/kube-object";
@autobind()
export class CRDResourceStore<T extends KubeObject = any> extends KubeObjectStore<T> {
api: KubeApi;
@@ -28,7 +28,7 @@ import type { RouteComponentProps } from "react-router";
import { KubeObjectListLayout } from "../kube-object";
import type { KubeObject } from "../../api/kube-object";
import type { ICRDRouteParams } from "./crd.route";
import { autorun, computed } from "mobx";
import { autorun, computed, makeObservable } from "mobx";
import { crdStore } from "./crd.store";
import type { TableSortCallback } from "../table";
import { apiManager } from "../../api/api-manager";
@@ -45,6 +45,11 @@ enum columnId {
@observer
export class CrdResources extends React.Component<Props> {
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
disposeOnUnmount(this, [
autorun(() => {
@@ -19,9 +19,9 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { computed, reaction } from "mobx";
import { computed, reaction, makeObservable } from "mobx";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { crdApi, CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { apiManager } from "../../api/api-manager";
import { KubeApi } from "../../api/kube-api";
@@ -39,15 +39,17 @@ function initStore(crd: CustomResourceDefinition) {
}
}
@autobind()
export class CRDStore extends KubeObjectStore<CustomResourceDefinition> {
api = crdApi;
constructor() {
super();
makeObservable(this);
autoBind(this);
// auto-init stores for crd-s
reaction(() => this.items.toJS(), items => items.forEach(initStore));
reaction(() => this.getItems(), items => items.forEach(initStore));
}
protected sortItems(items: CustomResourceDefinition[]) {
@@ -22,7 +22,7 @@
import "./entity-settings.scss";
import React from "react";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import type { RouteComponentProps } from "react-router";
import { observer } from "mobx-react";
import { PageLayout } from "../layout/page-layout";
@@ -41,6 +41,11 @@ interface Props extends RouteComponentProps<EntitySettingsRouteParams> {
export class EntitySettings extends React.Component<Props> {
@observable activeTab: string;
constructor(props: Props) {
super(props);
makeObservable(this);
}
get entityId() {
return this.props.match.params.entityId;
}
@@ -22,19 +22,23 @@
import groupBy from "lodash/groupBy";
import compact from "lodash/compact";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { eventApi, KubeEvent } from "../../api/endpoints/events.api";
import type { KubeObject } from "../../api/kube-object";
import { Pod } from "../../api/endpoints/pods.api";
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class EventStore extends KubeObjectStore<KubeEvent> {
api = eventApi;
limit = 1000;
saveLimit = 50000;
constructor() {
super();
autoBind(this);
}
protected bindWatchEventsUpdater() {
return super.bindWatchEventsUpdater(5000);
}
+6 -1
View File
@@ -22,7 +22,7 @@
import "./events.scss";
import React, { Fragment } from "react";
import { computed, observable } from "mobx";
import { computed, observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { orderBy } from "lodash";
import { TabLayout } from "../layout/tab-layout";
@@ -81,6 +81,11 @@ export class Events extends React.Component<Props> {
onSort: params => this.sorting = params,
};
constructor(props: Props) {
super(props);
makeObservable(this);
}
get store(): EventStore {
return eventStore;
}
@@ -24,33 +24,17 @@ import "./extensions.scss";
import { remote, shell } from "electron";
import fse from "fs-extra";
import _ from "lodash";
import { observable, reaction, when } from "mobx";
import { makeObservable, observable, reaction, when } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import os from "os";
import path from "path";
import React from "react";
import { SemVer } from "semver";
import URLParse from "url-parse";
import {
Disposer,
disposer,
downloadFile,
downloadJson,
ExtendableDisposer,
extractTar,
listTarEntries,
noop,
readFileFromTar,
} from "../../../common/utils";
import { Disposer, disposer, downloadFile, downloadJson, ExtendableDisposer, extractTar, listTarEntries, noop, readFileFromTar, } from "../../../common/utils";
import { ExtensionDiscovery, InstalledExtension, manifestFilename } from "../../../extensions/extension-discovery";
import { ExtensionLoader } from "../../../extensions/extension-loader";
import {
extensionDisplayName,
LensExtensionId,
LensExtensionManifest,
sanitizeExtensionName,
} from "../../../extensions/lens-extension";
import { extensionDisplayName, LensExtensionId, LensExtensionManifest, sanitizeExtensionName, } from "../../../extensions/lens-extension";
import logger from "../../../main/logger";
import { Button } from "../button";
import { ConfirmDialog } from "../confirm-dialog";
@@ -193,7 +177,7 @@ async function validatePackage(filePath: string): Promise<LensExtensionManifest>
// tarball from npm contains single root folder "package/*"
const firstFile = tarFiles[0];
if(!firstFile) {
if (!firstFile) {
throw new Error(`invalid extension bundle, ${manifestFilename} not found`);
}
@@ -201,7 +185,7 @@ async function validatePackage(filePath: string): Promise<LensExtensionManifest>
const packedInRootFolder = tarFiles.every(entry => entry.startsWith(rootFolder));
const manifestLocation = packedInRootFolder ? path.join(rootFolder, manifestFilename) : manifestFilename;
if(!tarFiles.includes(manifestLocation)) {
if (!tarFiles.includes(manifestLocation)) {
throw new Error(`invalid extension bundle, ${manifestFilename} not found`);
}
@@ -415,7 +399,7 @@ async function attemptInstall(request: InstallRequest, d?: ExtendableDisposer):
} else {
dispose();
}
}} />
}}/>
</div>,
{
onClose: dispose,
@@ -460,7 +444,7 @@ async function installFromInput(input: string) {
await attemptInstall({ fileName, dataP: readFileNotify(input) });
} else if (InputValidators.isExtensionNameInstall.validate(input)) {
const [{ groups: { name, version }}] = [...input.matchAll(InputValidators.isExtensionNameInstallRegex)];
const [{ groups: { name, version } }] = [...input.matchAll(InputValidators.isExtensionNameInstallRegex)];
await attemptInstallByInfo({ name, version });
}
@@ -493,10 +477,18 @@ async function installFromSelectFileDialog() {
}
}
interface Props {
}
@observer
export class Extensions extends React.Component {
export class Extensions extends React.Component<Props> {
@observable installPath = "";
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
// TODO: change this after upgrading to mobx6 as that versions' reactions have this functionality
let prevSize = ExtensionLoader.getInstance().userExtensions.size;
@@ -22,7 +22,7 @@
import "./add-namespace-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";
@@ -37,17 +37,25 @@ interface Props extends DialogProps {
onError?(error: any): void;
}
const dialogState = observable.object({
isOpen: false,
});
@observer
export class AddNamespaceDialog extends React.Component<Props> {
@observable static isOpen = false;
@observable namespace = "";
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open() {
AddNamespaceDialog.isOpen = true;
dialogState.isOpen = true;
}
static close() {
AddNamespaceDialog.isOpen = false;
dialogState.isOpen = false;
}
reset = () => {
@@ -78,7 +86,7 @@ export class AddNamespaceDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="AddNamespaceDialog"
isOpen={AddNamespaceDialog.isOpen}
isOpen={dialogState.isOpen}
onOpen={this.reset}
close={AddNamespaceDialog.close}
>
@@ -22,7 +22,7 @@
import "./namespace-details.scss";
import React from "react";
import { computed } from "mobx";
import { computed, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { DrawerItem } from "../drawer";
import { cssNames } from "../../utils";
@@ -40,6 +40,11 @@ interface Props extends KubeObjectDetailsProps<Namespace> {
@observer
export class NamespaceDetails extends React.Component<Props> {
constructor(props: Props) {
super(props);
makeObservable(this);
}
@computed get quotas() {
const namespace = this.props.object.getName();
@@ -22,7 +22,7 @@
import "./namespace-select.scss";
import React from "react";
import { computed } from "mobx";
import { computed, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { Select, SelectOption, SelectProps } from "../select";
import { cssNames } from "../../utils";
@@ -46,6 +46,11 @@ const defaultProps: Partial<Props> = {
export class NamespaceSelect extends React.Component<Props> {
static defaultProps = defaultProps as object;
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
disposeOnUnmount(this, [
kubeWatchApi.subscribeStores([namespaceStore], {
@@ -19,71 +19,39 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { action, comparer, computed, IReactionDisposer, IReactionOptions, observable, reaction } from "mobx";
import { autobind, createStorage } from "../../utils";
import { action, comparer, computed, IReactionDisposer, IReactionOptions, makeObservable, reaction, } from "mobx";
import { autoBind, createStorage } from "../../utils";
import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store";
import { Namespace, namespacesApi } from "../../api/endpoints/namespaces.api";
import { createPageParam } from "../../navigation";
import { apiManager } from "../../api/api-manager";
const selectedNamespaces = createStorage<string[] | undefined>("selected_namespaces", undefined);
export const namespaceUrlParam = createPageParam<string[]>({
name: "namespaces",
isSystem: true,
multiValues: true,
defaultValue: [],
});
export function getDummyNamespace(name: string) {
return new Namespace({
kind: Namespace.kind,
apiVersion: "v1",
metadata: {
name,
uid: "",
resourceVersion: "",
selfLink: `/api/v1/namespaces/${name}`
}
});
}
@autobind()
export class NamespaceStore extends KubeObjectStore<Namespace> {
api = namespacesApi;
@observable private contextNs = observable.set<string>();
private storage = createStorage<string[] | undefined>("selected_namespaces", undefined);
constructor() {
super();
makeObservable(this);
autoBind(this);
this.init();
}
private async init() {
await this.contextReady;
await selectedNamespaces.whenReady;
await this.storage.whenReady;
this.setContext(this.initialNamespaces);
this.selectNamespaces(this.initialNamespaces);
this.autoLoadAllowedNamespaces();
this.autoUpdateUrlAndLocalStorage();
}
public onContextChange(callback: (contextNamespaces: string[]) => void, opts: IReactionOptions = {}): IReactionDisposer {
return reaction(() => Array.from(this.contextNs), callback, {
public onContextChange(callback: (namespaces: string[]) => void, opts: IReactionOptions = {}): IReactionDisposer {
return reaction(() => Array.from(this.contextNamespaces), callback, {
equals: comparer.shallow,
...opts,
});
}
private autoUpdateUrlAndLocalStorage(): IReactionDisposer {
return this.onContextChange(namespaces => {
selectedNamespaces.set(namespaces); // save to local-storage
namespaceUrlParam.set(namespaces, { replaceHistory: true }); // update url
}, {
fireImmediately: true,
});
}
private autoLoadAllowedNamespaces(): IReactionDisposer {
return reaction(() => this.allowedNamespaces, namespaces => this.loadAll({ namespaces }), {
fireImmediately: true,
@@ -92,24 +60,28 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
}
private get initialNamespaces(): string[] {
const namespaces = new Set(this.allowedNamespaces);
const prevSelectedNamespaces = selectedNamespaces.get();
const { allowedNamespaces } = this;
const selectedNamespaces = this.storage.get(); // raw namespaces, undefined on first load
// return previously saved namespaces from local-storage (if any)
if (prevSelectedNamespaces) {
return prevSelectedNamespaces.filter(namespace => namespaces.has(namespace));
if (Array.isArray(selectedNamespaces)) {
return selectedNamespaces.filter(namespace => allowedNamespaces.includes(namespace));
}
// otherwise select "default" or first allowed namespace
if (namespaces.has("default")) {
if (allowedNamespaces.includes("default")) {
return ["default"];
} else if (namespaces.size) {
return [Array.from(namespaces)[0]];
} else if (allowedNamespaces.length) {
return [allowedNamespaces[0]];
}
return [];
}
@computed get selectedNamespaces(): string[] {
return this.storage.get() ?? [];
}
@computed get allowedNamespaces(): string[] {
return Array.from(new Set([
...(this.context?.allNamespaces ?? []), // allowed namespaces from cluster (main), updating every 30s
@@ -118,13 +90,11 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
}
@computed get contextNamespaces(): string[] {
const namespaces = Array.from(this.contextNs);
if (!namespaces.length) {
if (!this.selectedNamespaces.length) {
return this.allowedNamespaces; // show all namespaces when nothing selected
}
return namespaces;
return this.selectedNamespaces;
}
getSubscribeApis() {
@@ -151,31 +121,40 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
}
@action
setContext(namespace: string | string[]) {
const namespaces = [namespace].flat();
selectNamespaces(namespace: string | string[]) {
const namespaces = Array.from(new Set([namespace].flat()));
this.contextNs.replace(namespaces);
this.storage.set(namespaces);
}
@action
resetContext() {
this.contextNs.clear();
clearSelected(namespaces?: string | string[]) {
if (namespaces) {
const resettingNamespaces = [namespaces].flat();
const newNamespaces = this.storage.get().filter(ns => !resettingNamespaces.includes(ns));
this.storage.set(newNamespaces);
} else {
this.storage.reset();
}
}
hasContext(namespaces: string | string[]) {
return [namespaces].flat().every(namespace => this.contextNs.has(namespace));
hasContext(namespaces: string | string[]): boolean {
return [namespaces]
.flat()
.every(namespace => this.selectedNamespaces.includes(namespace));
}
@computed get hasAllContexts(): boolean {
return this.contextNs.size === this.allowedNamespaces.length;
return this.selectedNamespaces.length === this.allowedNamespaces.length;
}
@action
toggleContext(namespace: string) {
if (this.hasContext(namespace)) {
this.contextNs.delete(namespace);
toggleContext(namespaces: string | string[]) {
if (this.hasContext(namespaces)) {
this.clearSelected(namespaces);
} else {
this.contextNs.add(namespace);
this.selectNamespaces([this.selectedNamespaces, namespaces].flat());
}
}
@@ -183,9 +162,9 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
toggleAll(showAll?: boolean) {
if (typeof showAll === "boolean") {
if (showAll) {
this.setContext(this.allowedNamespaces);
this.selectNamespaces(this.allowedNamespaces);
} else {
this.resetContext(); // empty context considered as "All namespaces"
this.selectNamespaces([]); // empty context considered as "All namespaces"
}
} else {
this.toggleAll(!this.hasAllContexts);
@@ -195,9 +174,22 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
@action
async remove(item: Namespace) {
await super.remove(item);
this.contextNs.delete(item.getName());
this.clearSelected(item.getName());
}
}
export const namespaceStore = new NamespaceStore();
apiManager.registerStore(namespaceStore);
export function getDummyNamespace(name: string) {
return new Namespace({
kind: Namespace.kind,
apiVersion: "v1",
metadata: {
name,
uid: "",
resourceVersion: "",
selfLink: `/api/v1/namespaces/${name}`
}
});
}
@@ -25,7 +25,7 @@ import React from "react";
import { observer } from "mobx-react";
import { EndpointSubset, Endpoint, EndpointAddress} from "../../api/endpoints";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { autobind } from "../../utils";
import { boundMethod } from "../../utils";
import { lookupApiLink } from "../../api/kube-api";
import { Link } from "react-router-dom";
import { getDetailsUrl } from "../kube-object";
@@ -45,7 +45,7 @@ export class EndpointSubsetList extends React.Component<Props> {
return this.renderAddressTableRow(address);
}
@autobind()
@boundMethod
getNotReadyAddressTableRow(ip: string) {
const { subset} = this.props;
const address = subset.getNotReadyAddresses().find(address => address.getId() == ip);
@@ -53,7 +53,7 @@ export class EndpointSubsetList extends React.Component<Props> {
return this.renderAddressTableRow(address);
}
@autobind()
@boundMethod
renderAddressTable(addresses: EndpointAddress[], virtual: boolean) {
return (
<div>
@@ -79,7 +79,7 @@ export class EndpointSubsetList extends React.Component<Props> {
);
}
@autobind()
@boundMethod
renderAddressTableRow(address: EndpointAddress) {
const { endpoint } = this.props;
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { Endpoint, endpointApi } from "../../api/endpoints/endpoint.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class EndpointStore extends KubeObjectStore<Endpoint> {
api = endpointApi;
}
@@ -19,17 +19,23 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { IIngressMetrics, Ingress, ingressApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
@autobind()
export class IngressStore extends KubeObjectStore<Ingress> {
api = ingressApi;
@observable metrics: IIngressMetrics = null;
constructor() {
super();
makeObservable(this);
autoBind(this);
}
async loadMetrics(ingress: Ingress) {
this.metrics = await this.api.getMetrics(ingress.getName(), ingress.getNs());
}
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { NetworkPolicy, networkPolicyApi } from "../../api/endpoints/network-policy.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class NetworkPolicyStore extends KubeObjectStore<NetworkPolicy> {
api = networkPolicyApi;
}
@@ -25,7 +25,7 @@ import React from "react";
import { observer } from "mobx-react";
import type { Service, ServicePort } from "../../api/endpoints";
import { apiBase } from "../../api";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { cssNames } from "../../utils";
import { Notifications } from "../notifications";
import { Spinner } from "../spinner";
@@ -39,6 +39,11 @@ interface Props {
export class ServicePortComponent extends React.Component<Props> {
@observable waiting = false;
constructor(props: Props) {
super(props);
makeObservable(this);
}
async portForward() {
const { service, port } = this.props;
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { Service, serviceApi } from "../../api/endpoints/service.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class ServiceStore extends KubeObjectStore<Service> {
api = serviceApi;
}
+4 -6
View File
@@ -28,20 +28,18 @@ import { Services, servicesRoute, servicesURL } from "../+network-services";
import { endpointRoute, Endpoints, endpointURL } from "../+network-endpoints";
import { Ingresses, ingressRoute, ingressURL } from "../+network-ingresses";
import { NetworkPolicies, networkPoliciesRoute, networkPoliciesURL } from "../+network-policies";
import { namespaceUrlParam } from "../+namespaces/namespace.store";
import { isAllowedResource } from "../../../common/rbac";
@observer
export class Network extends React.Component {
static get tabRoutes(): TabLayoutRoute[] {
const query = namespaceUrlParam.toObjectParam();
const routes: TabLayoutRoute[] = [];
if (isAllowedResource("services")) {
routes.push({
title: "Services",
component: Services,
url: servicesURL({ query }),
url: servicesURL(),
routePath: servicesRoute.path.toString(),
});
}
@@ -50,7 +48,7 @@ export class Network extends React.Component {
routes.push({
title: "Endpoints",
component: Endpoints,
url: endpointURL({ query }),
url: endpointURL(),
routePath: endpointRoute.path.toString(),
});
}
@@ -59,7 +57,7 @@ export class Network extends React.Component {
routes.push({
title: "Ingresses",
component: Ingresses,
url: ingressURL({ query }),
url: ingressURL(),
routePath: ingressRoute.path.toString(),
});
}
@@ -68,7 +66,7 @@ export class Network extends React.Component {
routes.push({
title: "Network Policies",
component: NetworkPolicies,
url: networkPoliciesURL({ query }),
url: networkPoliciesURL(),
routePath: networkPoliciesRoute.path.toString(),
});
}
@@ -20,13 +20,12 @@
*/
import { sum } from "lodash";
import { action, computed, observable } from "mobx";
import { action, computed, observable, makeObservable } from "mobx";
import { clusterApi, IClusterMetrics, INodeMetrics, Node, nodesApi } from "../../api/endpoints";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class NodesStore extends KubeObjectStore<Node> {
api = nodesApi;
@@ -35,6 +34,13 @@ export class NodesStore extends KubeObjectStore<Node> {
@observable metricsLoading = false;
@observable metricsLoaded = false;
constructor() {
super();
makeObservable(this);
autoBind(this);
}
@action
async loadUsageMetrics() {
this.metricsLoading = true;
@@ -20,11 +20,9 @@
*/
import { PodSecurityPolicy, pspApi } from "../../api/endpoints";
import { autobind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class PodSecurityPoliciesStore extends KubeObjectStore<PodSecurityPolicy> {
api = pspApi;
}
@@ -23,7 +23,7 @@ import "./add-helm-repo-dialog.scss";
import React from "react";
import { remote, FileFilter } from "electron";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -46,6 +46,10 @@ enum FileType {
CertFile = "certFile",
}
const dialogState = observable.object({
isOpen: false,
});
@observer
export class AddHelmRepoDialog extends React.Component<Props> {
private emptyRepo = {name: "", url: "", username: "", password: "", insecureSkipTlsVerify: false, caFile:"", keyFile: "", certFile: ""};
@@ -53,14 +57,17 @@ export class AddHelmRepoDialog extends React.Component<Props> {
private static keyExtensions = ["key", "keystore", "jks", "p12", "pfx", "pem"];
private static certExtensions = ["crt", "cer", "ca-bundle", "p7b", "p7c" , "p7s", "p12", "pfx", "pem"];
@observable static isOpen = false;
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open() {
AddHelmRepoDialog.isOpen = true;
dialogState.isOpen = true;
}
static close() {
AddHelmRepoDialog.isOpen = false;
dialogState.isOpen = false;
}
@observable helmRepo : HelmRepo = this.emptyRepo;
@@ -161,7 +168,7 @@ export class AddHelmRepoDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="AddHelmRepoDialog"
isOpen={AddHelmRepoDialog.isOpen}
isOpen={dialogState.isOpen}
close={this.close}
>
<Wizard header={header} done={this.close}>
@@ -22,7 +22,7 @@
import "./helm-charts.scss";
import React from "react";
import { action, computed, observable } from "mobx";
import { action, computed, observable, makeObservable } from "mobx";
import { HelmRepo, HelmRepoManager } from "../../../main/helm/helm-repo-manager";
import { Button } from "../button";
@@ -38,6 +38,11 @@ export class HelmCharts extends React.Component {
@observable repos: HelmRepo[] = [];
@observable addedRepos = observable.map<string, HelmRepo>();
constructor(props: {}) {
super(props);
makeObservable(this);
}
@computed get options(): SelectOption<HelmRepo>[] {
return this.repos.map(repo => ({
label: repo.name,
@@ -23,7 +23,7 @@ import React from "react";
import { remote } from "electron";
import { Avatar, IconButton, List, ListItem, ListItemAvatar, ListItemSecondaryAction, ListItemText, Paper } from "@material-ui/core";
import { Description, Folder, Delete, HelpOutline } from "@material-ui/icons";
import { action, computed, observable, reaction } from "mobx";
import { action, computed, observable, reaction, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import fse from "fs-extra";
import { KubeconfigSyncEntry, KubeconfigSyncValue, UserStore } from "../../../common/user-store";
@@ -74,6 +74,11 @@ export class KubeconfigSyncs extends React.Component {
syncs = observable.map<string, Value>();
@observable loaded = false;
constructor(props: {}) {
super(props);
makeObservable(this);
}
async componentDidMount() {
const mapEntries = await Promise.all(
iter.map(
@@ -23,7 +23,7 @@ import "./preferences.scss";
import React from "react";
import moment from "moment-timezone";
import { computed, observable, reaction } from "mobx";
import { computed, observable, reaction, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { isWindows } from "../../../common/vars";
@@ -56,6 +56,11 @@ export class Preferences extends React.Component {
@observable shell = UserStore.getInstance().shell || "";
@observable activeTab = Pages.Application;
constructor(props: {}) {
super(props);
makeObservable(this);
}
@computed get themeOptions(): SelectOption<string>[] {
return ThemeStore.getInstance().themes.map(theme => ({
label: theme.name,
@@ -20,15 +20,19 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { StorageClass, storageClassApi } from "../../api/endpoints/storage-class.api";
import { apiManager } from "../../api/api-manager";
import { volumesStore } from "../+storage-volumes/volumes.store";
@autobind()
export class StorageClassStore extends KubeObjectStore<StorageClass> {
api = storageClassApi;
constructor() {
super();
autoBind(this);
}
getPersistentVolumes(storageClass: StorageClass) {
return volumesStore.getByStorageClass(storageClass);
}
@@ -19,17 +19,23 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { action, observable } from "mobx";
import { action, observable, makeObservable } from "mobx";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { IPvcMetrics, PersistentVolumeClaim, pvcApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
@autobind()
export class VolumeClaimStore extends KubeObjectStore<PersistentVolumeClaim> {
api = pvcApi;
@observable metrics: IPvcMetrics = null;
constructor() {
super();
makeObservable(this);
autoBind(this);
}
@action
async loadMetrics(pvc: PersistentVolumeClaim) {
this.metrics = await pvcApi.getMetrics(pvc.getName(), pvc.getNs());
@@ -24,7 +24,7 @@ import "./volume-details-list.scss";
import React from "react";
import { observer } from "mobx-react";
import type { PersistentVolume } from "../../api/endpoints/persistent-volume.api";
import { autobind } from "../../../common/utils/autobind";
import { boundMethod } from "../../../common/utils/autobind";
import { TableRow } from "../table/table-row";
import { cssNames, prevDefault } from "../../utils";
import { showDetails } from "../kube-object/kube-object-details";
@@ -54,7 +54,7 @@ export class VolumeDetailsList extends React.Component<Props> {
[sortBy.status]: (volume: PersistentVolume) => volume.getStatus(),
};
@autobind()
@boundMethod
getTableRow(uid: string) {
const { persistentVolumes } = this.props;
const volume = persistentVolumes.find(volume => volume.getId() === uid);
@@ -20,15 +20,19 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { PersistentVolume, persistentVolumeApi } from "../../api/endpoints/persistent-volume.api";
import { apiManager } from "../../api/api-manager";
import type { StorageClass } from "../../api/endpoints/storage-class.api";
@autobind()
export class PersistentVolumesStore extends KubeObjectStore<PersistentVolume> {
api = persistentVolumeApi;
constructor() {
super();
autoBind(this);
}
getByStorageClass(storageClass: StorageClass): PersistentVolume[] {
return this.items.filter(volume =>
volume.getStorageClassName() === storageClass.getName()
+1 -3
View File
@@ -27,20 +27,18 @@ import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
import { PersistentVolumes, volumesRoute, volumesURL } from "../+storage-volumes";
import { StorageClasses, storageClassesRoute, storageClassesURL } from "../+storage-classes";
import { PersistentVolumeClaims, volumeClaimsRoute, volumeClaimsURL } from "../+storage-volume-claims";
import { namespaceUrlParam } from "../+namespaces/namespace.store";
import { isAllowedResource } from "../../../common/rbac";
@observer
export class Storage extends React.Component {
static get tabRoutes() {
const tabRoutes: TabLayoutRoute[] = [];
const query = namespaceUrlParam.toObjectParam();
if (isAllowedResource("persistentvolumeclaims")) {
tabRoutes.push({
title: "Persistent Volume Claims",
component: PersistentVolumeClaims,
url: volumeClaimsURL({ query }),
url: volumeClaimsURL(),
routePath: volumeClaimsRoute.path.toString(),
});
}
Loaded 100 of 256 files, more files were not shown because too many files have changed in this diff. Show more