= [];
+ get [registeredKubeObjectMenuItems]() {
+ return this.#privateGetters[registeredKubeObjectMenuItems].get();
+ }
+
async navigate(pageId?: string, params?: P) {
const { navigate } = await import("../renderer/navigation");
const pageUrl = getExtensionPageUrl({
diff --git a/src/extensions/registries/base-registry.ts b/src/extensions/registries/base-registry.ts
index 052a483cbd..bddc77de61 100644
--- a/src/extensions/registries/base-registry.ts
+++ b/src/extensions/registries/base-registry.ts
@@ -20,8 +20,9 @@ export class BaseRegistry {
return () => this.remove(...itemArray);
}
- // eslint-disable-next-line unused-imports/no-unused-vars-ts
protected getRegisteredItem(item: T, extension?: LensExtension): I {
+ void extension;
+
return item as any;
}
diff --git a/src/extensions/registries/index.ts b/src/extensions/registries/index.ts
index 9f0aba5f0d..ac03909bd0 100644
--- a/src/extensions/registries/index.ts
+++ b/src/extensions/registries/index.ts
@@ -1,5 +1,7 @@
// All registries managed by extensions api
+import { Cluster } from "../../main/cluster";
+
export * from "./page-registry";
export * from "./page-menu-registry";
export * from "./menu-registry";
@@ -9,3 +11,13 @@ export * from "./kube-object-detail-registry";
export * from "./kube-object-menu-registry";
export * from "./kube-object-status-registry";
export * from "./command-registry";
+
+export type Registrable = (T[]) | ((cluster?: Cluster | null) => T[]);
+
+export function recitfyRegisterable(src: Registrable, getCluster?: () => Cluster | null | undefined): T[] {
+ if (typeof src === "function") {
+ return src(getCluster());
+ }
+
+ return src;
+}
diff --git a/src/extensions/registries/kube-object-menu-registry.ts b/src/extensions/registries/kube-object-menu-registry.ts
deleted file mode 100644
index 25901f66ad..0000000000
--- a/src/extensions/registries/kube-object-menu-registry.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from "react";
-import { BaseRegistry } from "./base-registry";
-
-export interface KubeObjectMenuComponents {
- MenuItem: React.ComponentType;
-}
-
-export interface KubeObjectMenuRegistration {
- kind: string;
- apiVersions: string[];
- components: KubeObjectMenuComponents;
-}
-
-export class KubeObjectMenuRegistry extends BaseRegistry {
- getItemsForKind(kind: string, apiVersion: string) {
- return this.getItems().filter((item) => {
- return item.kind === kind && item.apiVersions.includes(apiVersion);
- });
- }
-}
-
-export const kubeObjectMenuRegistry = new KubeObjectMenuRegistry();
diff --git a/src/extensions/registries/kube-object-menu-registry.tsx b/src/extensions/registries/kube-object-menu-registry.tsx
new file mode 100644
index 0000000000..688a7a0df2
--- /dev/null
+++ b/src/extensions/registries/kube-object-menu-registry.tsx
@@ -0,0 +1,130 @@
+import { IObservableArray, observable } from "mobx";
+import { IKubeObjectConstructor } from "../../renderer/api/kube-object";
+import { finalizeEntry, RawRootMenuEntry, RootMenuEntry, safeWhen } from "../../renderer/descriptors/menu-entry";
+import { extensionLoader } from "../extension-loader";
+import { LensRendererExtension, registeredKubeObjectMenuItems } from "../lens-renderer-extension";
+import { KubeObject } from "../renderer-api/k8s-api";
+
+export type KubeObjectMenuRegistration = RawRootMenuEntry & {
+ kind: string;
+ apiVersions: string[];
+ when?: (object: KubeObject) => any,
+};
+
+export type LensKubeObjectMenuRegistration = RawRootMenuEntry & {
+ Object: IKubeObjectConstructor;
+ apiVersions?: string[];
+ when?: (object: KO) => any,
+};
+
+export interface RegisteredKubeObjectMenuItem extends KubeObjectMenuItemEntry {
+ kind: string;
+}
+
+export interface LensRegisteredKubeObjectMenuItem extends KubeObjectMenuItemEntry {
+ Object: IKubeObjectConstructor;
+}
+
+/**
+ * Used by a map, in a computed getter in `LensRendererExtension`
+ * @param registration An Extension's KubeObjectMenuItem registration descriptor
+ * @returns A split out descriptor
+ */
+export function getRegisteredKubeObjectMenuItems({ apiVersions, kind, when, ...menuItem }: KubeObjectMenuRegistration): RegisteredKubeObjectMenuItem {
+ return {
+ apiVersions: new Set(apiVersions),
+ kind,
+ when: safeWhen(when),
+ menuItem,
+ };
+}
+
+export function lensGetRegisteredKubeObjectMenuItems({ apiVersions, Object, when, ...menuItem }: LensKubeObjectMenuRegistration): LensRegisteredKubeObjectMenuItem {
+ return {
+ apiVersions: apiVersions ? new Set(apiVersions) : null,
+ Object,
+ menuItem,
+ when,
+ };
+}
+
+interface KubeObjectMenuItemEntry {
+ apiVersions: Set | null;
+ menuItem: RawRootMenuEntry,
+ when: (object: KO) => any,
+}
+const specificKubeObjectMenuItems = observable.map>>();
+
+/**
+ * Get all the registered `MenuItem` descriptors give the object's `kind`, `apiVersion`,
+ * and if it passes the filtering in the descriptor.
+ * @param object The KubeObject instance to get `MenuItem` descriptors for
+ * @returns The list of registered `MenuItem` descriptors. With all the
+ * extensions first and Lens entries last.
+ */
+export function getKubeObjectMenuItems(object?: KO): RootMenuEntry[] {
+ if (!object) {
+ return [];
+ }
+
+ const extensions = extensionLoader.allEnabledInstances as LensRendererExtension[];
+ const lensMenuItems = specificKubeObjectMenuItems
+ .get(object.kind)
+ ?.filter(({ apiVersions, when }) => (
+ (apiVersions === null
+ || apiVersions.has(object.apiVersion))
+ && when(object)
+ ))
+ .map(({ menuItem }) => finalizeEntry(object, menuItem));
+ const extensionsMenuItems = extensions
+ .flatMap(ext => ext[registeredKubeObjectMenuItems])
+ .filter(({ kind, apiVersions, when }) => (
+ kind === object.kind
+ && apiVersions.has(object.apiVersion)
+ && when(object)
+ ))
+ .map(({ menuItem }) => finalizeEntry(object, menuItem));
+
+ return [...extensionsMenuItems, ...lensMenuItems];
+}
+
+/**
+ * Add a KubeObjectMenuItem to the Lens specific collection
+ * @param src The arguments of the menu item registration
+ */
+export function addLensKubeObjectMenuItem(src: LensKubeObjectMenuRegistration): void {
+ const { Object, apiVersions, when, menuItem } = lensGetRegisteredKubeObjectMenuItems(src);
+ const items = specificKubeObjectMenuItems.get(Object.kind);
+ const pair: KubeObjectMenuItemEntry = {
+ apiVersions,
+ menuItem: menuItem as any,
+ when: safeWhen(when),
+ };
+
+ if (!items) {
+ specificKubeObjectMenuItems.set(Object.kind, observable.array([pair]));
+ } else {
+ items.push(pair);
+ }
+}
+
+/**
+ * Add a KubeObjectMenuItem to the Lens specific collection where a class extending KubeObject
+ * does not exist. For instance CRD instances.
+ * @param src The arguments of the menu item registration
+ */
+export function addLensKubeObjectMenuItemRaw(src: KubeObjectMenuRegistration): void {
+ const { kind, apiVersions, when, menuItem } = getRegisteredKubeObjectMenuItems(src);
+ const items = specificKubeObjectMenuItems.get(kind);
+ const pair = {
+ apiVersions,
+ menuItem,
+ when: safeWhen(when),
+ };
+
+ if (!items) {
+ specificKubeObjectMenuItems.set(kind, observable.array([pair]));
+ } else {
+ items.push(pair);
+ }
+}
diff --git a/src/extensions/renderer-api/components.ts b/src/extensions/renderer-api/components.ts
index 1a48fc54e3..6d6234b0c5 100644
--- a/src/extensions/renderer-api/components.ts
+++ b/src/extensions/renderer-api/components.ts
@@ -18,6 +18,13 @@ export * from "../../renderer/components/input/input";
// command-overlay
export { CommandOverlay } from "../../renderer/components/command-palette";
+// Material-UI
+import * as Mui from "@material-ui/core";
+import * as Icons from "@material-ui/icons";
+import * as LensIcons from "../../renderer/icons";
+
+export { Mui, Icons, LensIcons };
+
// other components
export * from "../../renderer/components/icon";
export * from "../../renderer/components/tooltip";
diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx
index b9392e2b95..af093ac152 100644
--- a/src/renderer/components/+add-cluster/add-cluster.tsx
+++ b/src/renderer/components/+add-cluster/add-cluster.tsx
@@ -9,7 +9,6 @@ import { Select, SelectOption } from "../select";
import { DropFileInput, Input } from "../input";
import { AceEditor } from "../ace-editor";
import { Button } from "../button";
-import { Icon } from "../icon";
import { kubeConfigDefaultPath, loadConfig, splitConfig, validateConfig, validateKubeConfig } from "../../../common/kube-helpers";
import { ClusterModel, ClusterStore, clusterStore } from "../../../common/cluster-store";
import { v4 as uuid } from "uuid";
@@ -23,6 +22,9 @@ import { appEventBus } from "../../../common/event-bus";
import { PageLayout } from "../layout/page-layout";
import { docsUrl } from "../../../common/vars";
import { catalogURL } from "../+catalog";
+import { IconButton, SvgIcon, Tooltip } from "@material-ui/core";
+import { LensIcons } from "../../../extensions/renderer-api/components";
+import { Check, FiberNew, Folder, SettingsBackupRestore } from "@material-ui/icons";
enum KubeConfigSourceTab {
FILE = "file",
@@ -232,17 +234,17 @@ export class AddCluster extends React.Component {
onBlur={this.onKubeConfigInputBlur}
/>
{this.kubeConfigPath !== kubeConfigDefaultPath && (
- this.setKubeConfig(kubeConfigDefaultPath)}
- tooltip="Reset"
- />
+
+ this.setKubeConfig(kubeConfigDefaultPath)}>
+
+
+
)}
-
+
+
+
+
+
Pro-Tip: you can also drag-n-drop kubeconfig file to this area
@@ -332,8 +334,8 @@ export class AddCluster extends React.Component {
return (
{context}
- {isNew && }
- {isSelected && }
+ {isNew && }
+ {isSelected && }
);
};
@@ -343,7 +345,7 @@ export class AddCluster extends React.Component {
return (
- Add Clusters
>} showOnTop={true}>
+ Add Clusters
>} showOnTop={true}>
Add Clusters from Kubeconfig
{this.renderInfo()}
{this.renderKubeConfigSource()}
diff --git a/src/renderer/components/+apps-releases/release-details.tsx b/src/renderer/components/+apps-releases/release-details.tsx
index e139569937..2a236878cb 100644
--- a/src/renderer/components/+apps-releases/release-details.tsx
+++ b/src/renderer/components/+apps-releases/release-details.tsx
@@ -7,7 +7,6 @@ import { observable, reaction } from "mobx";
import { Link } from "react-router-dom";
import kebabCase from "lodash/kebabCase";
import { HelmRelease, helmReleasesApi, IReleaseDetails } from "../../api/endpoints/helm-releases.api";
-import { HelmReleaseMenu } from "./release-menu";
import { Drawer, DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge";
import { cssNames, stopPropagation } from "../../utils";
@@ -25,6 +24,9 @@ import { SubTitle } from "../layout/sub-title";
import { secretsStore } from "../+config-secrets/secrets.store";
import { Secret } from "../../api/endpoints";
import { getDetailsUrl } from "../kube-object";
+import { MenuEntry } from "../../descriptors";
+import { ReleaseRollbackDialog } from "./release-rollback-dialog";
+import { History, Remove, Update } from "@material-ui/icons";
interface Props {
release: HelmRelease;
@@ -237,7 +239,6 @@ export class ReleaseDetails extends Component {
render() {
const { release, hideDetails } = this.props;
const title = release ? <>Release: {release.getName()}> : "";
- const toolbar = ;
return (
{
open={!!release}
title={title}
onClose={hideDetails}
- toolbar={toolbar}
+ toolbarMenuEntries={releaseMenuEntries(release)}
>
{this.renderContent()}
);
}
}
+
+export function releaseMenuEntries(release: HelmRelease): MenuEntry[] {
+ return [
+ {
+ Icon: History,
+ onClick: () => ReleaseRollbackDialog.open(release),
+ text: "Rollback",
+ },
+ {
+ Icon: Remove,
+ onClick: () => releaseStore.remove(release),
+ text: "Delete",
+ },
+ {
+ Icon: Update,
+ onClick: () => createUpgradeChartTab(release),
+ text: "Upgrade",
+ closeParent: true,
+ },
+ ];
+}
diff --git a/src/renderer/components/+apps-releases/release-menu.tsx b/src/renderer/components/+apps-releases/release-menu.tsx
deleted file mode 100644
index e482bb000a..0000000000
--- a/src/renderer/components/+apps-releases/release-menu.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import React from "react";
-import { HelmRelease } from "../../api/endpoints/helm-releases.api";
-import { autobind, cssNames } from "../../utils";
-import { releaseStore } from "./release.store";
-import { MenuActions, MenuActionsProps } from "../menu/menu-actions";
-import { MenuItem } from "../menu";
-import { Icon } from "../icon";
-import { ReleaseRollbackDialog } from "./release-rollback-dialog";
-import { createUpgradeChartTab } from "../dock/upgrade-chart.store";
-
-interface Props extends MenuActionsProps {
- release: HelmRelease;
- hideDetails?(): void;
-}
-
-export class HelmReleaseMenu extends React.Component {
- @autobind()
- remove() {
- return releaseStore.remove(this.props.release);
- }
-
- @autobind()
- upgrade() {
- const { release, hideDetails } = this.props;
-
- createUpgradeChartTab(release);
- hideDetails && hideDetails();
- }
-
- @autobind()
- rollback() {
- ReleaseRollbackDialog.open(this.props.release);
- }
-
- renderContent() {
- const { release, toolbar } = this.props;
-
- if (!release) return;
- const hasRollback = release && release.getRevision() > 1;
-
- return (
- <>
- {hasRollback && (
-
- )}
- >
- );
- }
-
- render() {
- const { className, release, ...menuProps } = this.props;
-
- return (
-
- {this.renderContent()}
-
- );
- }
-}
diff --git a/src/renderer/components/+apps-releases/releases.tsx b/src/renderer/components/+apps-releases/releases.tsx
index 71cf3d954f..b8bd2f91e2 100644
--- a/src/renderer/components/+apps-releases/releases.tsx
+++ b/src/renderer/components/+apps-releases/releases.tsx
@@ -7,11 +7,10 @@ import { RouteComponentProps } from "react-router";
import { releaseStore } from "./release.store";
import { IReleaseRouteParams, releaseURL } from "./release.route";
import { HelmRelease } from "../../api/endpoints/helm-releases.api";
-import { ReleaseDetails } from "./release-details";
+import { ReleaseDetails, releaseMenuEntries } from "./release-details";
import { ReleaseRollbackDialog } from "./release-rollback-dialog";
import { navigation } from "../../navigation";
import { ItemListLayout } from "../item-object-list/item-list-layout";
-import { HelmReleaseMenu } from "./release-menu";
import { secretsStore } from "../+config-secrets/secrets.store";
enum columnId {
@@ -130,14 +129,7 @@ export class HelmReleases extends Component {
release.getUpdated(),
];
}}
- renderItemMenu={(release: HelmRelease) => {
- return (
-
- );
- }}
+ getItemMenuEntries={releaseMenuEntries}
customizeRemoveDialog={(selectedItems: HelmRelease[]) => ({
message: this.renderRemoveDialogMessage(selectedItems)
})}
diff --git a/src/renderer/components/+cluster-settings/components/cluster-metrics-setting.tsx b/src/renderer/components/+cluster-settings/components/cluster-metrics-setting.tsx
index bc51ddf061..a37146f520 100644
--- a/src/renderer/components/+cluster-settings/components/cluster-metrics-setting.tsx
+++ b/src/renderer/components/+cluster-settings/components/cluster-metrics-setting.tsx
@@ -3,7 +3,7 @@ import "./cluster-metrics-setting.scss";
import React from "react";
import { disposeOnUnmount, observer } from "mobx-react";
import { Select, SelectOption } from "../../select/select";
-import { Icon } from "../../icon/icon";
+import { Icon } from "../../icon";
import { Button } from "../../button/button";
import { SubTitle } from "../../layout/sub-title";
import { Cluster } from "../../../../main/cluster";
diff --git a/src/renderer/components/+cluster-settings/components/show-metrics.tsx b/src/renderer/components/+cluster-settings/components/show-metrics.tsx
index a3e071182f..c7a6693121 100644
--- a/src/renderer/components/+cluster-settings/components/show-metrics.tsx
+++ b/src/renderer/components/+cluster-settings/components/show-metrics.tsx
@@ -5,7 +5,7 @@ import { disposeOnUnmount, observer } from "mobx-react";
import { Cluster } from "../../../../main/cluster";
import { observable, reaction } from "mobx";
import { Badge } from "../../badge/badge";
-import { Icon } from "../../icon/icon";
+import { Icon } from "../../icon";
interface Props {
cluster: Cluster;
diff --git a/src/renderer/components/+config-autoscalers/hpa.store.ts b/src/renderer/components/+config-autoscalers/hpa.store.ts
index 478c2ca563..dcefc09a15 100644
--- a/src/renderer/components/+config-autoscalers/hpa.store.ts
+++ b/src/renderer/components/+config-autoscalers/hpa.store.ts
@@ -2,6 +2,9 @@ import { autobind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { HorizontalPodAutoscaler, hpaApi } from "../../api/endpoints/hpa.api";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class HPAStore extends KubeObjectStore {
@@ -10,3 +13,17 @@ export class HPAStore extends KubeObjectStore {
export const hpaStore = new HPAStore();
apiManager.registerStore(hpaStore);
+
+addLensKubeObjectMenuItem({
+ Object: HorizontalPodAutoscaler,
+ Icon: Remove,
+ onClick: object => hpaStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: HorizontalPodAutoscaler,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+config-limit-ranges/limit-ranges.store.ts b/src/renderer/components/+config-limit-ranges/limit-ranges.store.ts
index bd760efadd..7b5ad023bd 100644
--- a/src/renderer/components/+config-limit-ranges/limit-ranges.store.ts
+++ b/src/renderer/components/+config-limit-ranges/limit-ranges.store.ts
@@ -2,6 +2,9 @@ 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";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class LimitRangesStore extends KubeObjectStore {
@@ -10,3 +13,17 @@ export class LimitRangesStore extends KubeObjectStore {
export const limitRangeStore = new LimitRangesStore();
apiManager.registerStore(limitRangeStore);
+
+addLensKubeObjectMenuItem({
+ Object: LimitRange,
+ Icon: Remove,
+ onClick: object => limitRangeStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: LimitRange,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+config-maps/config-maps.store.ts b/src/renderer/components/+config-maps/config-maps.store.ts
index 23a80a9f4a..d282d876bf 100644
--- a/src/renderer/components/+config-maps/config-maps.store.ts
+++ b/src/renderer/components/+config-maps/config-maps.store.ts
@@ -2,6 +2,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";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class ConfigMapsStore extends KubeObjectStore {
@@ -10,3 +13,17 @@ export class ConfigMapsStore extends KubeObjectStore {
export const configMapsStore = new ConfigMapsStore();
apiManager.registerStore(configMapsStore);
+
+addLensKubeObjectMenuItem({
+ Object: ConfigMap,
+ Icon: Remove,
+ onClick: object => configMapsStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: ConfigMap,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.store.ts b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.store.ts
index 0f0d79ab2d..a248a803db 100644
--- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.store.ts
+++ b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.store.ts
@@ -2,6 +2,9 @@ import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { PodDisruptionBudget, pdbApi } from "../../api/endpoints/poddisruptionbudget.api";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class PodDisruptionBudgetsStore extends KubeObjectStore {
@@ -10,3 +13,17 @@ export class PodDisruptionBudgetsStore extends KubeObjectStore podDisruptionBudgetsStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: PodDisruptionBudget,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+config-resource-quotas/resource-quotas.store.ts b/src/renderer/components/+config-resource-quotas/resource-quotas.store.ts
index 554fec891a..e083f0be8f 100644
--- a/src/renderer/components/+config-resource-quotas/resource-quotas.store.ts
+++ b/src/renderer/components/+config-resource-quotas/resource-quotas.store.ts
@@ -2,6 +2,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";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class ResourceQuotasStore extends KubeObjectStore {
@@ -10,3 +13,17 @@ export class ResourceQuotasStore extends KubeObjectStore {
export const resourceQuotaStore = new ResourceQuotasStore();
apiManager.registerStore(resourceQuotaStore);
+
+addLensKubeObjectMenuItem({
+ Object: ResourceQuota,
+ Icon: Remove,
+ onClick: object => resourceQuotaStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: ResourceQuota,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+config-secrets/secrets.store.ts b/src/renderer/components/+config-secrets/secrets.store.ts
index 9dfc8cb6af..b3df919cc3 100644
--- a/src/renderer/components/+config-secrets/secrets.store.ts
+++ b/src/renderer/components/+config-secrets/secrets.store.ts
@@ -2,6 +2,9 @@ import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { Secret, secretsApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class SecretsStore extends KubeObjectStore {
@@ -10,3 +13,17 @@ export class SecretsStore extends KubeObjectStore {
export const secretsStore = new SecretsStore();
apiManager.registerStore(secretsStore);
+
+addLensKubeObjectMenuItem({
+ Object: Secret,
+ Icon: Remove,
+ onClick: object => secretsStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: Secret,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+custom-resources/crd.store.ts b/src/renderer/components/+custom-resources/crd.store.ts
index 64aefc1fe1..c5cc0d6acb 100644
--- a/src/renderer/components/+custom-resources/crd.store.ts
+++ b/src/renderer/components/+custom-resources/crd.store.ts
@@ -6,15 +6,36 @@ import { apiManager } from "../../api/api-manager";
import { KubeApi } from "../../api/kube-api";
import { CRDResourceStore } from "./crd-resource.store";
import { KubeObject } from "../../api/kube-object";
+import { addLensKubeObjectMenuItem, addLensKubeObjectMenuItemRaw } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
function initStore(crd: CustomResourceDefinition) {
const apiBase = crd.getResourceApiBase();
const kind = crd.getResourceKind();
const isNamespaced = crd.isNamespaced();
const api = apiManager.getApi(apiBase) || new KubeApi({ apiBase, kind, isNamespaced });
-
+
if (!apiManager.getStore(api)) {
- apiManager.registerStore(new CRDResourceStore(api));
+ const store = new CRDResourceStore(api);
+
+ apiManager.registerStore(store);
+
+ addLensKubeObjectMenuItemRaw({
+ kind,
+ apiVersions: [api.apiVersion],
+ Icon: Remove,
+ onClick: object => store.remove(object),
+ text: "Delete",
+ });
+
+ addLensKubeObjectMenuItemRaw({
+ kind,
+ apiVersions: [api.apiVersion],
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+ });
}
}
@@ -60,7 +81,7 @@ export class CRDStore extends KubeObjectStore {
getByObject(obj: KubeObject) {
if (!obj) return null;
const { kind, apiVersion } = obj;
-
+
return this.items.find(crd => (
kind === crd.getResourceKind() && apiVersion === `${crd.getGroup()}/${crd.getVersion()}`
));
@@ -70,3 +91,17 @@ export class CRDStore extends KubeObjectStore {
export const crdStore = new CRDStore();
apiManager.registerStore(crdStore);
+
+addLensKubeObjectMenuItem({
+ Object: CustomResourceDefinition,
+ Icon: Remove,
+ onClick: object => crdStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: CustomResourceDefinition,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+events/event.store.ts b/src/renderer/components/+events/event.store.ts
index 03d272643f..c1b0ad90a3 100644
--- a/src/renderer/components/+events/event.store.ts
+++ b/src/renderer/components/+events/event.store.ts
@@ -7,6 +7,9 @@ import { 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";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class EventStore extends KubeObjectStore {
@@ -60,3 +63,17 @@ export class EventStore extends KubeObjectStore {
export const eventStore = new EventStore();
apiManager.registerStore(eventStore);
+
+addLensKubeObjectMenuItem({
+ Object: KubeEvent,
+ Icon: Remove,
+ onClick: object => eventStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: KubeEvent,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+namespaces/namespace.store.ts b/src/renderer/components/+namespaces/namespace.store.ts
index 4415f416b9..7f5cf4eed9 100644
--- a/src/renderer/components/+namespaces/namespace.store.ts
+++ b/src/renderer/components/+namespaces/namespace.store.ts
@@ -4,6 +4,9 @@ import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object
import { Namespace, namespacesApi } from "../../api/endpoints/namespaces.api";
import { createPageParam } from "../../navigation";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
const selectedNamespaces = createStorage("selected_namespaces", undefined);
@@ -180,3 +183,17 @@ export class NamespaceStore extends KubeObjectStore {
export const namespaceStore = new NamespaceStore();
apiManager.registerStore(namespaceStore);
+
+addLensKubeObjectMenuItem({
+ Object: Namespace,
+ Icon: Remove,
+ onClick: object => namespaceStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: Namespace,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+network-endpoints/endpoints.store.ts b/src/renderer/components/+network-endpoints/endpoints.store.ts
index d61a12126e..bdf5a01069 100644
--- a/src/renderer/components/+network-endpoints/endpoints.store.ts
+++ b/src/renderer/components/+network-endpoints/endpoints.store.ts
@@ -2,6 +2,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";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class EndpointStore extends KubeObjectStore {
@@ -10,3 +13,17 @@ export class EndpointStore extends KubeObjectStore {
export const endpointStore = new EndpointStore();
apiManager.registerStore(endpointStore);
+
+addLensKubeObjectMenuItem({
+ Object: Endpoint,
+ Icon: Remove,
+ onClick: object => endpointStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: Endpoint,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+network-ingresses/ingress.store.ts b/src/renderer/components/+network-ingresses/ingress.store.ts
index 37156c2741..bbd788ef36 100644
--- a/src/renderer/components/+network-ingresses/ingress.store.ts
+++ b/src/renderer/components/+network-ingresses/ingress.store.ts
@@ -3,6 +3,9 @@ import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { IIngressMetrics, Ingress, ingressApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class IngressStore extends KubeObjectStore {
@@ -20,3 +23,17 @@ export class IngressStore extends KubeObjectStore {
export const ingressStore = new IngressStore();
apiManager.registerStore(ingressStore);
+
+addLensKubeObjectMenuItem({
+ Object: Ingress,
+ Icon: Remove,
+ onClick: object => ingressStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: Ingress,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+network-policies/network-policy.store.ts b/src/renderer/components/+network-policies/network-policy.store.ts
index e33a17fa09..4511ff23f7 100644
--- a/src/renderer/components/+network-policies/network-policy.store.ts
+++ b/src/renderer/components/+network-policies/network-policy.store.ts
@@ -2,6 +2,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";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class NetworkPolicyStore extends KubeObjectStore {
@@ -10,3 +13,17 @@ export class NetworkPolicyStore extends KubeObjectStore {
export const networkPolicyStore = new NetworkPolicyStore();
apiManager.registerStore(networkPolicyStore);
+
+addLensKubeObjectMenuItem({
+ Object: NetworkPolicy,
+ Icon: Remove,
+ onClick: object => networkPolicyStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: NetworkPolicy,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+network-services/services.store.ts b/src/renderer/components/+network-services/services.store.ts
index 8c6d16d746..2749480782 100644
--- a/src/renderer/components/+network-services/services.store.ts
+++ b/src/renderer/components/+network-services/services.store.ts
@@ -2,6 +2,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";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class ServiceStore extends KubeObjectStore {
@@ -10,3 +13,17 @@ export class ServiceStore extends KubeObjectStore {
export const serviceStore = new ServiceStore();
apiManager.registerStore(serviceStore);
+
+addLensKubeObjectMenuItem({
+ Object: Service,
+ Icon: Remove,
+ onClick: object => serviceStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: Service,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+nodes/nodes.store.ts b/src/renderer/components/+nodes/nodes.store.ts
index b301015747..b8b94875f3 100644
--- a/src/renderer/components/+nodes/nodes.store.ts
+++ b/src/renderer/components/+nodes/nodes.store.ts
@@ -4,6 +4,9 @@ import { clusterApi, IClusterMetrics, INodeMetrics, Node, nodesApi } from "../..
import { autobind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class NodesStore extends KubeObjectStore {
@@ -78,3 +81,17 @@ export class NodesStore extends KubeObjectStore {
export const nodesStore = new NodesStore();
apiManager.registerStore(nodesStore);
+
+addLensKubeObjectMenuItem({
+ Object: Node,
+ Icon: Remove,
+ onClick: object => nodesStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: Node,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+pod-security-policies/pod-security-policies.store.ts b/src/renderer/components/+pod-security-policies/pod-security-policies.store.ts
index 53ecde3d9d..0f0c00c7dd 100644
--- a/src/renderer/components/+pod-security-policies/pod-security-policies.store.ts
+++ b/src/renderer/components/+pod-security-policies/pod-security-policies.store.ts
@@ -2,6 +2,9 @@ import { PodSecurityPolicy, pspApi } from "../../api/endpoints";
import { autobind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class PodSecurityPoliciesStore extends KubeObjectStore {
@@ -10,3 +13,17 @@ export class PodSecurityPoliciesStore extends KubeObjectStore
export const podSecurityPoliciesStore = new PodSecurityPoliciesStore();
apiManager.registerStore(podSecurityPoliciesStore);
+
+addLensKubeObjectMenuItem({
+ Object: PodSecurityPolicy,
+ Icon: Remove,
+ onClick: object => podSecurityPoliciesStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: PodSecurityPolicy,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+storage-classes/storage-class.store.ts b/src/renderer/components/+storage-classes/storage-class.store.ts
index 0050c432b5..323a8a9987 100644
--- a/src/renderer/components/+storage-classes/storage-class.store.ts
+++ b/src/renderer/components/+storage-classes/storage-class.store.ts
@@ -3,6 +3,9 @@ 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";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class StorageClassStore extends KubeObjectStore {
@@ -15,3 +18,17 @@ export class StorageClassStore extends KubeObjectStore {
export const storageClassStore = new StorageClassStore();
apiManager.registerStore(storageClassStore);
+
+addLensKubeObjectMenuItem({
+ Object: StorageClass,
+ Icon: Remove,
+ onClick: object => storageClassStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: StorageClass,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+storage-volume-claims/volume-claim.store.ts b/src/renderer/components/+storage-volume-claims/volume-claim.store.ts
index 8c4baaaf76..87cd69a190 100644
--- a/src/renderer/components/+storage-volume-claims/volume-claim.store.ts
+++ b/src/renderer/components/+storage-volume-claims/volume-claim.store.ts
@@ -3,6 +3,9 @@ import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { IPvcMetrics, PersistentVolumeClaim, pvcApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class VolumeClaimStore extends KubeObjectStore {
@@ -21,3 +24,17 @@ export class VolumeClaimStore extends KubeObjectStore {
export const volumeClaimStore = new VolumeClaimStore();
apiManager.registerStore(volumeClaimStore);
+
+addLensKubeObjectMenuItem({
+ Object: PersistentVolumeClaim,
+ Icon: Remove,
+ onClick: object => volumeClaimStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: PersistentVolumeClaim,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+storage-volumes/volumes.store.ts b/src/renderer/components/+storage-volumes/volumes.store.ts
index b2601f9b12..f1525cea73 100644
--- a/src/renderer/components/+storage-volumes/volumes.store.ts
+++ b/src/renderer/components/+storage-volumes/volumes.store.ts
@@ -3,6 +3,9 @@ import { autobind } from "../../utils";
import { PersistentVolume, persistentVolumeApi } from "../../api/endpoints/persistent-volume.api";
import { apiManager } from "../../api/api-manager";
import { StorageClass } from "../../api/endpoints/storage-class.api";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class PersistentVolumesStore extends KubeObjectStore {
@@ -17,3 +20,17 @@ export class PersistentVolumesStore extends KubeObjectStore {
export const volumesStore = new PersistentVolumesStore();
apiManager.registerStore(volumesStore);
+
+addLensKubeObjectMenuItem({
+ Object: PersistentVolume,
+ Icon: Remove,
+ onClick: object => volumesStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: PersistentVolume,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+user-management-roles-bindings/role-bindings.store.ts b/src/renderer/components/+user-management-roles-bindings/role-bindings.store.ts
index 620fbd86ac..8429f4156d 100644
--- a/src/renderer/components/+user-management-roles-bindings/role-bindings.store.ts
+++ b/src/renderer/components/+user-management-roles-bindings/role-bindings.store.ts
@@ -1,9 +1,12 @@
import difference from "lodash/difference";
import uniqBy from "lodash/uniqBy";
-import { clusterRoleBindingApi, IRoleBindingSubject, RoleBinding, roleBindingApi } from "../../api/endpoints";
+import { ClusterRoleBinding, clusterRoleBindingApi, IRoleBindingSubject, RoleBinding, roleBindingApi } from "../../api/endpoints";
import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store";
import { autobind } from "../../utils";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class RoleBindingsStore extends KubeObjectStore {
@@ -73,3 +76,32 @@ apiManager.registerStore(roleBindingsStore, [
roleBindingApi,
clusterRoleBindingApi,
]);
+
+addLensKubeObjectMenuItem({
+ Object: RoleBinding,
+ Icon: Remove,
+ onClick: object => roleBindingsStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: RoleBinding,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
+
+// TODO: move these out once RoleBindings are split out from ClusterRoleBindings
+addLensKubeObjectMenuItem({
+ Object: ClusterRoleBinding,
+ Icon: Remove,
+ onClick: object => roleBindingsStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: ClusterRoleBinding,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+user-management-roles/roles.store.ts b/src/renderer/components/+user-management-roles/roles.store.ts
index 82b0e66612..bce1844897 100644
--- a/src/renderer/components/+user-management-roles/roles.store.ts
+++ b/src/renderer/components/+user-management-roles/roles.store.ts
@@ -1,7 +1,10 @@
-import { clusterRoleApi, Role, roleApi } from "../../api/endpoints";
+import { ClusterRole, clusterRoleApi, Role, roleApi } from "../../api/endpoints";
import { autobind } from "../../utils";
import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class RolesStore extends KubeObjectStore {
@@ -48,3 +51,33 @@ apiManager.registerStore(rolesStore, [
roleApi,
clusterRoleApi,
]);
+
+addLensKubeObjectMenuItem({
+ Object: Role,
+ Icon: Remove,
+ onClick: object => rolesStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: Role,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
+
+// TODO: move these out once ClusterRoles are split out from Roles
+
+addLensKubeObjectMenuItem({
+ Object: ClusterRole,
+ Icon: Remove,
+ onClick: object => rolesStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: ClusterRole,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+user-management-service-accounts/service-accounts.store.ts b/src/renderer/components/+user-management-service-accounts/service-accounts.store.ts
index 808fcab046..fd04e7ac14 100644
--- a/src/renderer/components/+user-management-service-accounts/service-accounts.store.ts
+++ b/src/renderer/components/+user-management-service-accounts/service-accounts.store.ts
@@ -2,6 +2,11 @@ import { autobind } from "../../utils";
import { ServiceAccount, serviceAccountsApi } from "../../api/endpoints";
import { KubeObjectStore } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { InsertDriveFile, Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
+import { KubeConfigDialog } from "../kubeconfig-dialog";
+import { apiBase } from "../../api";
@autobind()
export class ServiceAccountsStore extends KubeObjectStore {
@@ -10,9 +15,42 @@ export class ServiceAccountsStore extends KubeObjectStore {
protected async createItem(params: { name: string; namespace?: string }) {
await super.createItem(params);
- return this.api.get(params); // hackfix: load freshly created account, cause it doesn't have "secrets" field yet
+ return this.api.get(params); // hack-fix: load freshly created account, cause it doesn't have "secrets" field yet
}
}
export const serviceAccountsStore = new ServiceAccountsStore();
apiManager.registerStore(serviceAccountsStore);
+
+function openServiceAccountKubeConfig(account: ServiceAccount) {
+ const accountName = account.getName();
+ const namespace = account.getNs();
+
+ // TODO: fix this
+ KubeConfigDialog.open({
+ title: "{accountName} kubeconfig",
+ loader: () => apiBase.get(`/kubeconfig/service-account/${namespace}/${accountName}`)
+ });
+}
+
+addLensKubeObjectMenuItem({
+ Object: ServiceAccount,
+ Icon: Remove,
+ onClick: sa => serviceAccountsStore.remove(sa),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: ServiceAccount,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
+
+addLensKubeObjectMenuItem({
+ Object: ServiceAccount,
+ apiVersions: ["v1"],
+ Icon: InsertDriveFile,
+ onClick: object => openServiceAccountKubeConfig(object),
+ text: "Kubeconfig File",
+});
diff --git a/src/renderer/components/+user-management-service-accounts/service-accounts.tsx b/src/renderer/components/+user-management-service-accounts/service-accounts.tsx
index 34b3fc8d0a..d3836cfd3a 100644
--- a/src/renderer/components/+user-management-service-accounts/service-accounts.tsx
+++ b/src/renderer/components/+user-management-service-accounts/service-accounts.tsx
@@ -4,15 +4,10 @@ import React from "react";
import { observer } from "mobx-react";
import { ServiceAccount } from "../../api/endpoints/service-accounts.api";
import { RouteComponentProps } from "react-router";
-import { KubeObjectMenuProps } from "../kube-object/kube-object-menu";
-import { MenuItem } from "../menu";
-import { openServiceAccountKubeConfig } from "../kubeconfig-dialog";
-import { Icon } from "../icon";
import { KubeObjectListLayout } from "../kube-object";
import { IServiceAccountsRouteParams } from "../+user-management";
import { serviceAccountsStore } from "./service-accounts.store";
import { CreateServiceAccountDialog } from "./create-service-account-dialog";
-import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum columnId {
@@ -54,9 +49,6 @@ export class ServiceAccounts extends React.Component {
account.getNs(),
account.getAge(),
]}
- renderItemMenu={(item: ServiceAccount) => {
- return ;
- }}
addRemoveButtons={{
onAdd: () => CreateServiceAccountDialog.open(),
addTooltip: "Create new Service Account",
@@ -67,22 +59,3 @@ export class ServiceAccounts extends React.Component {
);
}
}
-
-function ServiceAccountMenu(props: KubeObjectMenuProps) {
- const { object, toolbar } = props;
-
- return (
-
- );
-}
-
-kubeObjectMenuRegistry.add({
- kind: "ServiceAccount",
- apiVersions: ["v1"],
- components: {
- MenuItem: ServiceAccountMenu
- }
-});
diff --git a/src/renderer/components/+whats-new/whats-new.scss b/src/renderer/components/+whats-new/whats-new.scss
index d7b13ac3e7..b56b56262b 100644
--- a/src/renderer/components/+whats-new/whats-new.scss
+++ b/src/renderer/components/+whats-new/whats-new.scss
@@ -3,7 +3,7 @@
&::after {
content: "";
- background: url(../../components/icon/crane.svg) no-repeat;
+ background: url(../../icons/crane.svg) no-repeat;
background-position: 0 35%;
background-size: 85%;
background-clip: content-box;
@@ -47,4 +47,4 @@
padding: $spacing;
background: $contentColor;
}
-}
\ No newline at end of file
+}
diff --git a/src/renderer/components/+whats-new/whats-new.tsx b/src/renderer/components/+whats-new/whats-new.tsx
index aa132cc178..2420860b6c 100644
--- a/src/renderer/components/+whats-new/whats-new.tsx
+++ b/src/renderer/components/+whats-new/whats-new.tsx
@@ -18,7 +18,7 @@ export class WhatsNew extends React.Component {
};
render() {
- const logo = require("../../components/icon/lens-logo.svg");
+ const logo = require("../../icons/logo-lens.svg");
const releaseNotes = marked(this.releaseNotes);
return (
diff --git a/src/renderer/components/+workloads-cronjobs/cronjob.store.ts b/src/renderer/components/+workloads-cronjobs/cronjob.store.ts
deleted file mode 100644
index 84fd0fc4f2..0000000000
--- a/src/renderer/components/+workloads-cronjobs/cronjob.store.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { KubeObjectStore } from "../../kube-object.store";
-import { autobind } from "../../utils";
-import { CronJob, cronJobApi } from "../../api/endpoints/cron-job.api";
-import { jobStore } from "../+workloads-jobs/job.store";
-import { apiManager } from "../../api/api-manager";
-
-@autobind()
-export class CronJobStore extends KubeObjectStore {
- api = cronJobApi;
-
- getStatuses(cronJobs?: CronJob[]) {
- const status = { suspended: 0, scheduled: 0 };
-
- cronJobs.forEach(cronJob => {
- if (cronJob.spec.suspend) {
- status.suspended++;
- }
- else {
- status.scheduled++;
- }
- });
-
- return status;
- }
-
- getActiveJobsNum(cronJob: CronJob) {
- // Active jobs are jobs without any condition 'Complete' nor 'Failed'
- const jobs = jobStore.getJobsByOwner(cronJob);
-
- if (!jobs.length) return 0;
-
- return jobs.filter(job => !job.getCondition()).length;
- }
-}
-
-export const cronJobStore = new CronJobStore();
-apiManager.registerStore(cronJobStore);
diff --git a/src/renderer/components/+workloads-cronjobs/cronjob.store.tsx b/src/renderer/components/+workloads-cronjobs/cronjob.store.tsx
new file mode 100644
index 0000000000..fb00f91a37
--- /dev/null
+++ b/src/renderer/components/+workloads-cronjobs/cronjob.store.tsx
@@ -0,0 +1,103 @@
+import React from "react";
+import { KubeObjectStore } from "../../kube-object.store";
+import { autobind } from "../../utils";
+import { CronJob, cronJobApi } from "../../api/endpoints/cron-job.api";
+import { jobStore } from "../+workloads-jobs/job.store";
+import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { PauseCircleFilled, PlayCircleFilled, PlayCircleOutline, Remove, Update } from "@material-ui/icons";
+import { CronJobTriggerDialog } from "./cronjob-trigger-dialog";
+import { Notifications } from "../notifications";
+import { editResourceTab } from "../dock/edit-resource.store";
+
+@autobind()
+export class CronJobStore extends KubeObjectStore {
+ api = cronJobApi;
+
+ getStatuses(cronJobs?: CronJob[]) {
+ const status = { suspended: 0, scheduled: 0 };
+
+ cronJobs.forEach(cronJob => {
+ if (cronJob.spec.suspend) {
+ status.suspended++;
+ }
+ else {
+ status.scheduled++;
+ }
+ });
+
+ return status;
+ }
+
+ getActiveJobsNum(cronJob: CronJob) {
+ // Active jobs are jobs without any condition 'Complete' nor 'Failed'
+ const jobs = jobStore.getJobsByOwner(cronJob);
+
+ if (!jobs.length) return 0;
+
+ return jobs.filter(job => !job.getCondition()).length;
+ }
+}
+
+export const cronJobStore = new CronJobStore();
+apiManager.registerStore(cronJobStore);
+
+addLensKubeObjectMenuItem({
+ Object: CronJob,
+ Icon: Remove,
+ onClick: sa => cronJobStore.remove(sa),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: CronJob,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
+
+addLensKubeObjectMenuItem({
+ Object: CronJob,
+ apiVersions: ["batch/v1beta1"],
+ Icon: PlayCircleFilled,
+ text: "Trigger",
+ onClick: CronJobTriggerDialog.open,
+});
+
+addLensKubeObjectMenuItem({
+ Object: CronJob,
+ apiVersions: ["batch/v1beta1"],
+ Icon: PlayCircleOutline,
+ text: "Resume",
+ when: object => object.isSuspend(),
+ onClick: object => (
+ cronJobApi.resume({ namespace: object.getNs(), name: object.getName() })
+ .catch(Notifications.error)
+ ),
+ confirmation: {
+ Message: ({ object }) => (
+
+ Resume CronJob{ object.getName() }?
+
+ ),
+ },
+});
+
+addLensKubeObjectMenuItem({
+ Object: CronJob,
+ apiVersions: ["batch/v1beta1"],
+ Icon: PauseCircleFilled,
+ text: "Suspend",
+ when: object => !object.isSuspend(),
+ onClick: object => (
+ cronJobApi.suspend({ namespace: object.getNs(), name: object.getName() })
+ .catch(Notifications.error)
+ ),
+ confirmation: {
+ Message: ({ object }) => (
+
+ Suspend CronJob{ object.getName() }?
+
+ ),
+ },
+});
diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx
index 44c87cfba3..656bfa34ac 100644
--- a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx
+++ b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx
@@ -3,20 +3,13 @@ import "./cronjobs.scss";
import React from "react";
import { observer } from "mobx-react";
import { RouteComponentProps } from "react-router";
-import { CronJob, cronJobApi } from "../../api/endpoints/cron-job.api";
-import { MenuItem } from "../menu";
-import { Icon } from "../icon";
+import { CronJob } from "../../api/endpoints/cron-job.api";
import { cronJobStore } from "./cronjob.store";
import { jobStore } from "../+workloads-jobs/job.store";
import { eventStore } from "../+events/event.store";
-import { KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { ICronJobsRouteParams } from "../+workloads";
import { KubeObjectListLayout } from "../kube-object";
-import { CronJobTriggerDialog } from "./cronjob-trigger-dialog";
-import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
-import { ConfirmDialog } from "../confirm-dialog/confirm-dialog";
-import { Notifications } from "../notifications/notifications";
enum columnId {
name = "name",
@@ -73,69 +66,7 @@ export class CronJobs extends React.Component {
cronJob.getLastScheduleTime(),
cronJob.getAge(),
]}
- renderItemMenu={(item: CronJob) => {
- return ;
- }}
/>
);
}
}
-
-export function CronJobMenu(props: KubeObjectMenuProps) {
- const { object, toolbar } = props;
-
- return (
- <>
-
-
- {object.isSuspend() ?
-
-
- :
- }
- >
- );
-}
-
-kubeObjectMenuRegistry.add({
- kind: "CronJob",
- apiVersions: ["batch/v1beta1"],
- components: {
- MenuItem: CronJobMenu
- }
-});
diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts b/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts
index b8c8dee573..32e7ec19cf 100644
--- a/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts
+++ b/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts
@@ -4,6 +4,9 @@ import { autobind } from "../../utils";
import { DaemonSet, daemonSetApi, IPodMetrics, Pod, podsApi, PodStatus } from "../../api/endpoints";
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class DaemonSetStore extends KubeObjectStore {
@@ -48,3 +51,17 @@ export class DaemonSetStore extends KubeObjectStore {
export const daemonSetStore = new DaemonSetStore();
apiManager.registerStore(daemonSetStore);
+
+addLensKubeObjectMenuItem({
+ Object: DaemonSet,
+ Icon: Remove,
+ onClick: object => daemonSetStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: DaemonSet,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+workloads-deployments/deployments.store.ts b/src/renderer/components/+workloads-deployments/deployments.store.tsx
similarity index 57%
rename from src/renderer/components/+workloads-deployments/deployments.store.ts
rename to src/renderer/components/+workloads-deployments/deployments.store.tsx
index b9dc85eae5..85f430c0d6 100644
--- a/src/renderer/components/+workloads-deployments/deployments.store.ts
+++ b/src/renderer/components/+workloads-deployments/deployments.store.tsx
@@ -1,9 +1,15 @@
+import React from "react";
import { observable } from "mobx";
import { Deployment, deploymentApi, IPodMetrics, podsApi, PodStatus } from "../../api/endpoints";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Autorenew, OpenWith, Remove, Update } from "@material-ui/icons";
+import { DeploymentScaleDialog } from "./deployment-scale-dialog";
+import { Notifications } from "../notifications";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class DeploymentStore extends KubeObjectStore {
@@ -55,3 +61,44 @@ export class DeploymentStore extends KubeObjectStore {
export const deploymentStore = new DeploymentStore();
apiManager.registerStore(deploymentStore);
+
+addLensKubeObjectMenuItem({
+ Object: Deployment,
+ Icon: Remove,
+ onClick: sa => deploymentStore.remove(sa),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: Deployment,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
+
+addLensKubeObjectMenuItem({
+ Object: Deployment,
+ apiVersions: ["apps/v1"],
+ Icon: OpenWith,
+ text: "Scale",
+ onClick: DeploymentScaleDialog.open,
+});
+
+
+addLensKubeObjectMenuItem({
+ Object: Deployment,
+ apiVersions: ["apps/v1"],
+ Icon: Autorenew,
+ text: "Restart",
+ onClick: object => (
+ deploymentApi.restart({ namespace: object.getNs(), name: object.getName() })
+ .catch(Notifications.error)
+ ),
+ confirmation: {
+ Message: ({ object }) => (
+
+ Are you sure you want to restart deployment {object.getName()}?
+
+ )
+ }
+});
diff --git a/src/renderer/components/+workloads-deployments/deployments.tsx b/src/renderer/components/+workloads-deployments/deployments.tsx
index a2510327ee..40cb593097 100644
--- a/src/renderer/components/+workloads-deployments/deployments.tsx
+++ b/src/renderer/components/+workloads-deployments/deployments.tsx
@@ -3,12 +3,7 @@ import "./deployments.scss";
import React from "react";
import { observer } from "mobx-react";
import { RouteComponentProps } from "react-router";
-import { Deployment, deploymentApi } from "../../api/endpoints";
-import { KubeObjectMenuProps } from "../kube-object/kube-object-menu";
-import { MenuItem } from "../menu";
-import { Icon } from "../icon";
-import { DeploymentScaleDialog } from "./deployment-scale-dialog";
-import { ConfirmDialog } from "../confirm-dialog";
+import { Deployment } from "../../api/endpoints";
import { deploymentStore } from "./deployments.store";
import { replicaSetStore } from "../+workloads-replicasets/replicasets.store";
import { podsStore } from "../+workloads-pods/pods.store";
@@ -19,9 +14,7 @@ import { IDeploymentsRouteParams } from "../+workloads";
import { cssNames } from "../../utils";
import kebabCase from "lodash/kebabCase";
import orderBy from "lodash/orderBy";
-import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
-import { Notifications } from "../notifications";
enum columnId {
name = "name",
@@ -90,53 +83,7 @@ export class Deployments extends React.Component {
deployment.getAge(),
this.renderConditions(deployment),
]}
- renderItemMenu={(item: Deployment) => {
- return ;
- }}
/>
);
}
}
-
-export function DeploymentMenu(props: KubeObjectMenuProps) {
- const { object, toolbar } = props;
-
- return (
- <>
-
-
- >
- );
-}
-
-kubeObjectMenuRegistry.add({
- kind: "Deployment",
- apiVersions: ["apps/v1"],
- components: {
- MenuItem: DeploymentMenu
- }
-});
diff --git a/src/renderer/components/+workloads-jobs/job.store.ts b/src/renderer/components/+workloads-jobs/job.store.ts
index 41d514df8d..2f4379cc25 100644
--- a/src/renderer/components/+workloads-jobs/job.store.ts
+++ b/src/renderer/components/+workloads-jobs/job.store.ts
@@ -4,6 +4,9 @@ import { Job, jobApi } from "../../api/endpoints/job.api";
import { CronJob, Pod, PodStatus } from "../../api/endpoints";
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class JobStore extends KubeObjectStore {
@@ -46,3 +49,17 @@ export class JobStore extends KubeObjectStore {
export const jobStore = new JobStore();
apiManager.registerStore(jobStore);
+
+addLensKubeObjectMenuItem({
+ Object: Job,
+ Icon: Remove,
+ onClick: object => jobStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: Job,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+workloads-pods/pods.store.ts b/src/renderer/components/+workloads-pods/pods.store.ts
index 5a535cec66..5bff28f204 100644
--- a/src/renderer/components/+workloads-pods/pods.store.ts
+++ b/src/renderer/components/+workloads-pods/pods.store.ts
@@ -5,6 +5,9 @@ import { autobind, cpuUnitsToNumber, unitsToBytes } from "../../utils";
import { IPodMetrics, Pod, PodMetrics, podMetricsApi, podsApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
import { WorkloadKubeObject } from "../../api/workload-kube-object";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
@autobind()
export class PodsStore extends KubeObjectStore {
@@ -63,12 +66,10 @@ export class PodsStore extends KubeObjectStore {
getPodKubeMetrics(pod: Pod) {
const containers = pod.getContainers();
const empty = { cpu: 0, memory: 0 };
- const metrics = this.kubeMetrics.find(metric => {
- return [
- metric.getName() === pod.getName(),
- metric.getNs() === pod.getNs()
- ].every(v => v);
- });
+ const metrics = this.kubeMetrics.find(metric => (
+ metric.getName() === pod.getName()
+ && metric.getNs() === pod.getNs()
+ ));
if (!metrics) return empty;
@@ -96,3 +97,17 @@ export class PodsStore extends KubeObjectStore {
export const podsStore = new PodsStore();
apiManager.registerStore(podsStore);
+
+addLensKubeObjectMenuItem({
+ Object: Pod,
+ Icon: Remove,
+ onClick: object => podsStore.remove(object),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: Pod,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
diff --git a/src/renderer/components/+workloads-replicasets/replicasets.store.ts b/src/renderer/components/+workloads-replicasets/replicasets.store.tsx
similarity index 71%
rename from src/renderer/components/+workloads-replicasets/replicasets.store.ts
rename to src/renderer/components/+workloads-replicasets/replicasets.store.tsx
index ca58006930..73140643bc 100644
--- a/src/renderer/components/+workloads-replicasets/replicasets.store.ts
+++ b/src/renderer/components/+workloads-replicasets/replicasets.store.tsx
@@ -5,6 +5,10 @@ import { Deployment, IPodMetrics, podsApi, ReplicaSet, replicaSetApi } from "../
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
import { PodStatus } from "../../api/endpoints/pods.api";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
+import { ReplicaSetScaleDialog } from "./replicaset-scale-dialog";
@autobind()
export class ReplicaSetStore extends KubeObjectStore {
@@ -54,3 +58,25 @@ export class ReplicaSetStore extends KubeObjectStore {
export const replicaSetStore = new ReplicaSetStore();
apiManager.registerStore(replicaSetStore);
+
+addLensKubeObjectMenuItem({
+ Object: ReplicaSet,
+ Icon: Remove,
+ onClick: sa => replicaSetStore.remove(sa),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: ReplicaSet,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
+
+addLensKubeObjectMenuItem({
+ Object: ReplicaSet,
+ apiVersions: ["apps/v1"],
+ Icon: Update,
+ text: "Scale",
+ onClick: ReplicaSetScaleDialog.open,
+});
diff --git a/src/renderer/components/+workloads-replicasets/replicasets.tsx b/src/renderer/components/+workloads-replicasets/replicasets.tsx
index 1caa394df6..5276ba0ea6 100644
--- a/src/renderer/components/+workloads-replicasets/replicasets.tsx
+++ b/src/renderer/components/+workloads-replicasets/replicasets.tsx
@@ -3,16 +3,11 @@ import "./replicasets.scss";
import React from "react";
import { observer } from "mobx-react";
import { ReplicaSet } from "../../api/endpoints";
-import { KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { replicaSetStore } from "./replicasets.store";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { RouteComponentProps } from "react-router";
import { IReplicaSetsRouteParams } from "../+workloads/workloads.route";
import { KubeObjectListLayout } from "../kube-object/kube-object-list-layout";
-import { MenuItem } from "../menu/menu";
-import { Icon } from "../icon/icon";
-import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry";
-import { ReplicaSetScaleDialog } from "./replicaset-scale-dialog";
enum columnId {
name = "name",
@@ -64,31 +59,7 @@ export class ReplicaSets extends React.Component {
replicaSet.getReady(),
replicaSet.getAge(),
]}
- renderItemMenu={(item: ReplicaSet) => {
- return ;
- }}
/>
);
}
}
-
-export function ReplicaSetMenu(props: KubeObjectMenuProps) {
- const { object, toolbar } = props;
-
- return (
- <>
-
- >
- );
-}
-
-kubeObjectMenuRegistry.add({
- kind: "ReplicaSet",
- apiVersions: ["apps/v1"],
- components: {
- MenuItem: ReplicaSetMenu
- }
-});
diff --git a/src/renderer/components/+workloads-statefulsets/statefulset.store.ts b/src/renderer/components/+workloads-statefulsets/statefulset.store.ts
index 6ee4bb5c28..bfe8a0cd6f 100644
--- a/src/renderer/components/+workloads-statefulsets/statefulset.store.ts
+++ b/src/renderer/components/+workloads-statefulsets/statefulset.store.ts
@@ -4,6 +4,10 @@ import { KubeObjectStore } from "../../kube-object.store";
import { IPodMetrics, podsApi, PodStatus, StatefulSet, statefulSetApi } from "../../api/endpoints";
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
+import { addLensKubeObjectMenuItem } from "../../../extensions/registries";
+import { OpenWith, Remove, Update } from "@material-ui/icons";
+import { editResourceTab } from "../dock/edit-resource.store";
+import { StatefulSetScaleDialog } from "./statefulset-scale-dialog";
@autobind()
export class StatefulSetStore extends KubeObjectStore {
@@ -47,3 +51,25 @@ export class StatefulSetStore extends KubeObjectStore {
export const statefulSetStore = new StatefulSetStore();
apiManager.registerStore(statefulSetStore);
+
+addLensKubeObjectMenuItem({
+ Object: StatefulSet,
+ Icon: Remove,
+ onClick: sa => statefulSetStore.remove(sa),
+ text: "Delete",
+});
+
+addLensKubeObjectMenuItem({
+ Object: StatefulSet,
+ Icon: Update,
+ onClick: editResourceTab,
+ text: "Update",
+});
+
+addLensKubeObjectMenuItem({
+ Object: StatefulSet,
+ apiVersions: ["apps/v1"],
+ text: "Scale",
+ Icon: OpenWith,
+ onClick: StatefulSetScaleDialog.open,
+});
diff --git a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx
index 868a6afc45..36a2288d53 100644
--- a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx
+++ b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx
@@ -8,14 +8,9 @@ import { podsStore } from "../+workloads-pods/pods.store";
import { statefulSetStore } from "./statefulset.store";
import { nodesStore } from "../+nodes/nodes.store";
import { eventStore } from "../+events/event.store";
-import { KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { KubeObjectListLayout } from "../kube-object";
import { IStatefulSetsRouteParams } from "../+workloads";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
-import { StatefulSetScaleDialog } from "./statefulset-scale-dialog";
-import { MenuItem } from "../menu/menu";
-import { Icon } from "../icon/icon";
-import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry";
enum columnId {
name = "name",
@@ -69,31 +64,7 @@ export class StatefulSets extends React.Component {
,
statefulSet.getAge(),
]}
- renderItemMenu={(item: StatefulSet) => {
- return ;
- }}
/>
);
}
}
-
-export function StatefulSetMenu(props: KubeObjectMenuProps) {
- const { object, toolbar } = props;
-
- return (
- <>
-
- >
- );
-}
-
-kubeObjectMenuRegistry.add({
- kind: "StatefulSet",
- apiVersions: ["apps/v1"],
- components: {
- MenuItem: StatefulSetMenu
- }
-});
diff --git a/src/renderer/components/dock/dock-tabs.tsx b/src/renderer/components/dock/dock-tabs.tsx
index d0a3c3d125..0a7ecaa219 100644
--- a/src/renderer/components/dock/dock-tabs.tsx
+++ b/src/renderer/components/dock/dock-tabs.tsx
@@ -1,6 +1,7 @@
+import { SvgIcon } from "@material-ui/core";
import React, { Fragment } from "react";
+import { Install } from "../../icons";
-import { Icon } from "../icon";
import { Tabs } from "../tabs/tabs";
import { isCreateResourceTab } from "./create-resource.store";
import { DockTab } from "./dock-tab";
@@ -30,7 +31,7 @@ export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab }: Props) =
}
if (isInstallChartTab(tab) || isUpgradeChartTab(tab)) {
- return } />;
+ return } />;
}
if (isLogsTab(tab)) {
diff --git a/src/renderer/components/dock/dock.tsx b/src/renderer/components/dock/dock.tsx
index 13f9d9f914..8cacaaaa5e 100644
--- a/src/renderer/components/dock/dock.tsx
+++ b/src/renderer/components/dock/dock.tsx
@@ -4,7 +4,6 @@ import React from "react";
import { observer } from "mobx-react";
import { cssNames, prevDefault } from "../../utils";
-import { Icon } from "../icon";
import { MenuItem } from "../menu";
import { MenuActions } from "../menu/menu-actions";
import { ResizeDirection, ResizingAnchor } from "../resizing-anchor";
@@ -23,6 +22,9 @@ import { createTerminalTab, isTerminalTab } from "./terminal.store";
import { UpgradeChart } from "./upgrade-chart";
import { isUpgradeChartTab } from "./upgrade-chart.store";
import { commandRegistry } from "../../../extensions/registries/command-registry";
+import { IconButton, SvgIcon, Tooltip } from "@material-ui/core";
+import { Terminal } from "../../icons";
+import { Create, ExpandLess, ExpandMore, Fullscreen, FullscreenExit } from "@material-ui/icons";
interface Props {
className?: string;
@@ -70,9 +72,53 @@ export class Dock extends React.Component {
);
}
+ renderFullscreenButton() {
+ const { toggleFillSize, fullSize } = dockStore;
+
+ if (fullSize) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ );
+ }
+
+ renderMinimizeButton() {
+ const { isOpen, toggle } = dockStore;
+
+ if (isOpen) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ );
+ }
+
render() {
const { className } = this.props;
- const { isOpen, toggle, tabs, toggleFillSize, selectedTab, hasTabs, fullSize } = dockStore;
+ const { isOpen, toggle, tabs, selectedTab, hasTabs, fullSize } = dockStore;
return (
{
- {hasTabs() && (
- <>
-
-
- >
- )}
+ {
+ hasTabs() && (
+ <>
+ {this.renderFullscreenButton()}
+ {this.renderMinimizeButton()}
+ >
+ )
+ }
{this.renderTabContent()}
diff --git a/src/renderer/components/dock/terminal-tab.tsx b/src/renderer/components/dock/terminal-tab.tsx
index 1e84a9501e..ea17be4423 100644
--- a/src/renderer/components/dock/terminal-tab.tsx
+++ b/src/renderer/components/dock/terminal-tab.tsx
@@ -8,6 +8,8 @@ import { Icon } from "../icon";
import { terminalStore } from "./terminal.store";
import { dockStore } from "./dock.store";
import { reaction } from "mobx";
+import { SvgIcon } from "@material-ui/core";
+import { Terminal } from "../../icons";
interface Props extends DockTabProps {
}
@@ -34,7 +36,7 @@ export class TerminalTab extends React.Component {
}
render() {
- const tabIcon = ;
+ const tabIcon = ;
const className = cssNames("TerminalTab", this.props.className, {
disconnected: this.isDisconnected,
});
diff --git a/src/renderer/components/drawer/drawer.tsx b/src/renderer/components/drawer/drawer.tsx
index 64098a8ed1..d8c7810f69 100644
--- a/src/renderer/components/drawer/drawer.tsx
+++ b/src/renderer/components/drawer/drawer.tsx
@@ -3,9 +3,12 @@ import "./drawer.scss";
import React from "react";
import { createPortal } from "react-dom";
import { cssNames, noop } from "../../utils";
-import { Icon } from "../icon";
import { Animate, AnimateName } from "../animate";
import { history } from "../../navigation";
+import { MenuEntry, RootMenuEntry } from "../../descriptors";
+import { computed } from "mobx";
+import { Close } from "@material-ui/icons";
+import { IconButton, Tooltip } from "@material-ui/core";
export interface DrawerProps {
open: boolean;
@@ -17,7 +20,7 @@ export interface DrawerProps {
position?: "top" | "left" | "right" | "bottom";
animation?: AnimateName;
onClose?: () => void;
- toolbar?: React.ReactNode;
+ toolbarMenuEntries?: RootMenuEntry[];
}
const defaultProps: Partial = {
@@ -53,6 +56,14 @@ export class Drawer extends React.Component {
window.removeEventListener("keydown", this.onEscapeKey);
}
+ @computed get toolbarMenuEntries(): RootMenuEntry[] {
+ return [...(this.props.toolbarMenuEntries ?? []), {
+ Icon: Close,
+ text: "Close",
+ onClick: () => this.close(),
+ }];
+ }
+
saveScrollPos = () => {
if (!this.scrollElem) return;
const key = history.location.key;
@@ -104,8 +115,24 @@ export class Drawer extends React.Component {
if (open) onClose();
};
+ renderToolbarMenu() {
+ return (this.toolbarMenuEntries as MenuEntry[]).map(({ Icon, onClick, text, closeParent }) => (
+
+ {
+ onClick();
+
+ if (closeParent) {
+ this.close();
+ }
+ }}>
+
+
+
+ ));
+ }
+
render() {
- const { open, position, title, animation, children, toolbar, size, usePortal } = this.props;
+ const { open, position, title, animation, children, size, usePortal } = this.props;
let { className, contentClass } = this.props;
className = cssNames("Drawer", className, position);
@@ -117,8 +144,7 @@ export class Drawer extends React.Component {
{title}
- {toolbar}
-
+ {this.renderToolbarMenu()}
this.scrollElem = e}>
{children}
diff --git a/src/renderer/components/icon/icon.scss b/src/renderer/components/icon/icon.scss
index e581ccbbdc..5f8dde1789 100644
--- a/src/renderer/components/icon/icon.scss
+++ b/src/renderer/components/icon/icon.scss
@@ -67,30 +67,6 @@
}
}
- // inline svg icon
- &.svg {
- box-sizing: content-box;
-
- > .icon {
- width: 100%;
- height: 100%;
- }
-
- svg {
- pointer-events: none;
- width: 100%;
- height: 100%;
-
- * {
- fill: currentColor;
- }
-
- line {
- stroke: currentColor;
- }
- }
- }
-
&.disabled {
opacity: .5;
color: inherit !important;
@@ -132,4 +108,4 @@
@extend .active;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/renderer/components/icon/icon.tsx b/src/renderer/components/icon/icon.tsx
index 806784dfcf..2d93d7abcb 100644
--- a/src/renderer/components/icon/icon.tsx
+++ b/src/renderer/components/icon/icon.tsx
@@ -10,7 +10,6 @@ import isNumber from "lodash/isNumber";
export interface IconProps extends React.HTMLAttributes
, TooltipDecoratorProps {
material?: string; // material-icon, see available names at https://material.io/icons/
- svg?: string; // svg-filename without extension in current folder
link?: LocationDescriptor; // render icon as NavLink from react-router-dom
href?: string; // render icon as hyperlink
size?: string | number; // icon-size
@@ -71,7 +70,7 @@ export class Icon extends React.PureComponent {
const { isInteractive } = this;
const {
// skip passing props to icon's html element
- className, href, link, material, svg, size, smallest, small, big,
+ className, href, link, material, size, smallest, small, big,
disabled, sticker, active, focusable, children,
interactive: _interactive,
onClick: _onClick,
@@ -82,7 +81,7 @@ export class Icon extends React.PureComponent {
let iconContent: ReactNode;
const iconProps: Partial = {
className: cssNames("Icon", className,
- { svg, material, interactive: isInteractive, disabled, sticker, active, focusable },
+ { material, interactive: isInteractive, disabled, sticker, active, focusable },
!size ? { smallest, small, big } : {}
),
onClick: isInteractive ? this.onClick : undefined,
@@ -92,13 +91,6 @@ export class Icon extends React.PureComponent {
...elemProps
};
- // render as inline svg-icon
- if (svg) {
- const svgIconText = require(`!!raw-loader!./${svg}.svg`).default;
-
- iconContent = ;
- }
-
// render as material-icon
if (material) {
iconContent = {material};
diff --git a/src/renderer/components/item-object-list/item-list-layout.tsx b/src/renderer/components/item-object-list/item-list-layout.tsx
index 5c7db4cd41..2808e8148f 100644
--- a/src/renderer/components/item-object-list/item-list-layout.tsx
+++ b/src/renderer/components/item-object-list/item-list-layout.tsx
@@ -22,6 +22,7 @@ import { MenuItem } from "../menu";
import { Checkbox } from "../checkbox";
import { userStore } from "../../../common/user-store";
import { namespaceStore } from "../+namespaces/namespace.store";
+import { RootMenuEntry } from "../../descriptors";
// todo: refactor, split to small re-usable components
@@ -65,6 +66,7 @@ export interface ItemListLayoutProps {
renderTableHeader: TableCellProps[] | null;
renderTableContents: (item: T) => (ReactNode | TableCellProps)[];
renderItemMenu?: (item: T, store: ItemStore) => ReactNode;
+ getItemMenuEntries?: (item: T) => RootMenuEntry[],
customizeTableRowProps?: (item: T) => Partial;
addRemoveButtons?: Partial;
virtual?: boolean;
diff --git a/src/renderer/components/kube-object/index.ts b/src/renderer/components/kube-object/index.ts
index a60297d225..5a9d238272 100644
--- a/src/renderer/components/kube-object/index.ts
+++ b/src/renderer/components/kube-object/index.ts
@@ -1,4 +1,3 @@
export * from "./kube-object-details";
export * from "./kube-object-list-layout";
-export * from "./kube-object-menu";
export * from "./kube-object-meta";
diff --git a/src/renderer/components/kube-object/kube-object-details.tsx b/src/renderer/components/kube-object/kube-object-details.tsx
index b07b0753fb..80111bce9c 100644
--- a/src/renderer/components/kube-object/kube-object-details.tsx
+++ b/src/renderer/components/kube-object/kube-object-details.tsx
@@ -10,8 +10,8 @@ import { Spinner } from "../spinner";
import { apiManager } from "../../api/api-manager";
import { crdStore } from "../+custom-resources/crd.store";
import { CrdResourceDetails } from "../+custom-resources";
-import { KubeObjectMenu } from "./kube-object-menu";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
+import { getKubeObjectMenuItems } from "../../../extensions/registries";
/**
* Used to store `object.selfLink` to show more info about resource in the details panel.
@@ -136,7 +136,7 @@ export class KubeObjectDetails extends React.Component {
className="KubeObjectDetails flex column"
open={isOpen}
title={title}
- toolbar={}
+ toolbarMenuEntries={getKubeObjectMenuItems(object)}
onClose={hideDetails}
>
{isLoading && }
diff --git a/src/renderer/components/kube-object/kube-object-list-layout.tsx b/src/renderer/components/kube-object/kube-object-list-layout.tsx
index 0ecb602c16..721f7bb175 100644
--- a/src/renderer/components/kube-object/kube-object-list-layout.tsx
+++ b/src/renderer/components/kube-object/kube-object-list-layout.tsx
@@ -5,14 +5,15 @@ import { cssNames } from "../../utils";
import { KubeObject } from "../../api/kube-object";
import { ItemListLayout, ItemListLayoutProps } from "../item-object-list/item-list-layout";
import { KubeObjectStore } from "../../kube-object.store";
-import { KubeObjectMenu } from "./kube-object-menu";
import { kubeSelectedUrlParam, showDetails } from "./kube-object-details";
import { kubeWatchApi } from "../../api/kube-watch-api";
import { clusterContext } from "../context";
+import { getKubeObjectMenuItems } from "../../../extensions/registries";
export interface KubeObjectListLayoutProps extends ItemListLayoutProps {
store: KubeObjectStore;
dependentStores?: KubeObjectStore[];
+ renderItemMenu?: undefined;
}
@observer
@@ -53,9 +54,7 @@ export class KubeObjectListLayout extends React.Component {
- return ;
- }}
+ getItemMenuEntries={item => getKubeObjectMenuItems(item as KubeObject)}
/>
);
}
diff --git a/src/renderer/components/kube-object/kube-object-menu.tsx b/src/renderer/components/kube-object/kube-object-menu.tsx
deleted file mode 100644
index 036f0679db..0000000000
--- a/src/renderer/components/kube-object/kube-object-menu.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import React from "react";
-import { autobind, cssNames } from "../../utils";
-import { KubeObject } from "../../api/kube-object";
-import { editResourceTab } from "../dock/edit-resource.store";
-import { MenuActions, MenuActionsProps } from "../menu/menu-actions";
-import { hideDetails } from "./kube-object-details";
-import { apiManager } from "../../api/api-manager";
-import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry";
-
-export interface KubeObjectMenuProps extends MenuActionsProps {
- object: T;
- editable?: boolean;
- removable?: boolean;
-}
-
-export class KubeObjectMenu extends React.Component {
- get store() {
- const { object } = this.props;
-
- if (!object) return;
-
- return apiManager.getStore(object.selfLink);
- }
-
- get isEditable() {
- const { editable } = this.props;
-
- return editable !== undefined ? editable : !!(this.store && this.store.update);
- }
-
- get isRemovable() {
- const { removable } = this.props;
-
- return removable !== undefined ? removable : !!(this.store && this.store.remove);
- }
-
- @autobind()
- async update() {
- hideDetails();
- editResourceTab(this.props.object);
- }
-
- @autobind()
- async remove() {
- hideDetails();
- const { object, removeAction } = this.props;
-
- if (removeAction) await removeAction();
- else await this.store.remove(object);
- }
-
- @autobind()
- renderRemoveMessage() {
- const { object } = this.props;
- const resourceKind = object.kind;
- const resourceName = object.getName();
-
- return (
- Remove {resourceKind} {resourceName}?
- );
- }
-
- render() {
- const { remove, update, renderRemoveMessage, isEditable, isRemovable } = this;
- const { className, object, editable, removable, toolbar, ...menuProps } = this.props;
-
- if (!object) return null;
-
- const menuItems = kubeObjectMenuRegistry.getItemsForKind(object.kind, object.apiVersion).map((item, index) => {
- return ;
- });
-
- return (
-
- {menuItems}
-
- );
- }
-}
diff --git a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx
index 21684549e1..33c29c8d9c 100644
--- a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx
+++ b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx
@@ -5,14 +5,12 @@ import { observable } from "mobx";
import { observer } from "mobx-react";
import jsYaml from "js-yaml";
import { AceEditor } from "../ace-editor";
-import { ServiceAccount } from "../../api/endpoints";
import { copyToClipboard, cssNames, saveFileDialog } from "../../utils";
import { Button } from "../button";
import { Dialog, DialogProps } from "../dialog";
import { Icon } from "../icon";
import { Notifications } from "../notifications";
import { Wizard, WizardStep } from "../wizard";
-import { apiBase } from "../../api";
interface IKubeconfigDialogData {
title?: React.ReactNode;
@@ -111,13 +109,3 @@ export class KubeConfigDialog extends React.Component {
);
}
}
-
-export function openServiceAccountKubeConfig(account: ServiceAccount) {
- const accountName = account.getName();
- const namespace = account.getNs();
-
- KubeConfigDialog.open({
- title: "{accountName} kubeconfig",
- loader: () => apiBase.get(`/kubeconfig/service-account/${namespace}/${accountName}`)
- });
-}
diff --git a/src/renderer/components/layout/login-layout.scss b/src/renderer/components/layout/login-layout.scss
deleted file mode 100755
index 228d192dad..0000000000
--- a/src/renderer/components/layout/login-layout.scss
+++ /dev/null
@@ -1,50 +0,0 @@
-
-.LoginLayout {
- $logo-size: 6 * $unit;
- height: 100vh;
- padding: $padding * 2;
-
- .logo {
- width: $logo-size;
- height: $logo-size;
- margin: auto;
-
- svg * {
- fill: $lensBlue;
- }
- }
-
- .header {
- font-size: $font-size-small;
- }
-
- .main {
- $spacing: $padding * 3;
- width: 100%;
- min-width: 34 * $unit;
- max-width: 42 * $unit;
- margin: $padding * 2 0;
- overflow: hidden;
-
- > * {
- padding: $spacing;
- }
-
- .title {
- background: $layoutBackground;
- text-align: center;
-
- h5 {
- color: $textColorAccent;
- }
- }
-
- .content {
- background: $contentColor;
- }
- }
-
- .footer {
- font-size: $font-size-small;
- }
-}
\ No newline at end of file
diff --git a/src/renderer/components/layout/login-layout.tsx b/src/renderer/components/layout/login-layout.tsx
deleted file mode 100755
index 669f783769..0000000000
--- a/src/renderer/components/layout/login-layout.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import "./login-layout.scss";
-
-import React from "react";
-import { Link } from "react-router-dom";
-import { cssNames } from "../../utils";
-import { Icon } from "../icon";
-
-interface Props {
- className?: any;
- header?: any;
- title?: any;
- footer?: any;
-}
-
-export class LoginLayout extends React.Component {
- render() {
- const { className, header, title, footer, children } = this.props;
-
- return (
-
- {header}
-
-
-
-
-
- {title}
-
-
- {children}
-
-
- {footer}
-
- );
- }
-}
diff --git a/src/renderer/components/layout/sidebar.tsx b/src/renderer/components/layout/sidebar.tsx
index 531c9db1a0..4aebd483e2 100644
--- a/src/renderer/components/layout/sidebar.tsx
+++ b/src/renderer/components/layout/sidebar.tsx
@@ -30,6 +30,8 @@ import { isAllowedResource } from "../../../common/rbac";
import { Spinner } from "../spinner";
import { ClusterPageMenuRegistration, clusterPageMenuRegistry, clusterPageRegistry, getExtensionPageUrl } from "../../../extensions/registries";
import { SidebarItem } from "./sidebar-item";
+import { SvgIcon } from "@material-ui/core";
+import { Kube, LensLogo, Nodes, Workloads as WorkloadsIcon, Storage as StorageIcon } from "../../icons";
interface Props {
className?: string;
@@ -157,7 +159,7 @@ export class Sidebar extends React.Component {
-
+
Lens
{
isActive={isActiveRoute(clusterRoute)}
isHidden={!isAllowedResource("nodes")}
url={clusterURL()}
- icon={}
+ icon={}
/>
{
isActive={isActiveRoute(nodesRoute)}
isHidden={!isAllowedResource("nodes")}
url={nodesURL()}
- icon={}
+ icon={}
/>
{
isActive={isActiveRoute(workloadsRoute)}
isHidden={Workloads.tabRoutes.length == 0}
url={workloadsURL({ query })}
- icon={}
+ icon={}
>
{this.renderTreeFromTabRoutes(Workloads.tabRoutes)}
@@ -221,7 +223,7 @@ export class Sidebar extends React.Component {
isActive={isActiveRoute(storageRoute)}
isHidden={Storage.tabRoutes.length == 0}
url={storageURL({ query })}
- icon={}
+ icon={}
>
{this.renderTreeFromTabRoutes(Storage.tabRoutes)}
diff --git a/src/renderer/descriptors/index.ts b/src/renderer/descriptors/index.ts
new file mode 100644
index 0000000000..d55a21d498
--- /dev/null
+++ b/src/renderer/descriptors/index.ts
@@ -0,0 +1 @@
+export * from "./menu-entry";
diff --git a/src/renderer/descriptors/menu-entry.tsx b/src/renderer/descriptors/menu-entry.tsx
new file mode 100644
index 0000000000..9261d2718b
--- /dev/null
+++ b/src/renderer/descriptors/menu-entry.tsx
@@ -0,0 +1,108 @@
+import React from "react";
+import { KubeObject } from "../api/kube-object";
+import { ConfirmDialog } from "../components/confirm-dialog";
+
+/**
+ * The MenuItem descriptor that all locations should derive the display from
+ */
+export interface MenuEntry {
+ Icon: React.ComponentType;
+ text: string,
+ onClick: () => void;
+ closeParent?: boolean;
+}
+
+export interface SubMenuEntry {
+ Icon: React.ComponentType;
+ text: string,
+ children: RootMenuEntry[];
+}
+
+export type RootMenuEntry = (MenuEntry | SubMenuEntry);
+
+export interface MessageProps