diff --git a/docs/README.md b/docs/README.md
index f98403eb75..129c05f3b8 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -35,6 +35,14 @@ Just like Lens itself, the extension API updates on a monthly cadence, rolling o
Keep up with Lens and the Lens Extension API by reviewing the [release notes](https://github.com/lensapp/lens/releases).
+## Important changes since Lens v4
+
+Lens has undergone major design changes in v5 which have resulted in a new extension API.
+Workspaces are gone, and the catalog is introduced for containing clusters, as well as other items, including custom entities.
+Lens has migrated from using mobx 5 to mobx 6 for internal state management, and this may have ramifications for extension implementations.
+Although the API retains many components from v4, given these changes, extensions written for Lens v4 are not compatible with the Lens v5 extension API.
+See the [Lens v4 to v5 extension migration notes](extensions/extension-migration.md) on getting old extensions working in Lens v5.
+
## Looking for Help
If you have questions for extension development, try asking on the [Lens Dev Slack](http://k8slens.slack.com/). It's a public chatroom for Lens developers, where Lens team members chime in from time to time.
diff --git a/docs/extensions/extension-migration.md b/docs/extensions/extension-migration.md
new file mode 100644
index 0000000000..073a49e5f9
--- /dev/null
+++ b/docs/extensions/extension-migration.md
@@ -0,0 +1,10 @@
+# Lens v4 to v5 Extension Migration Notes
+
+* Types and components have been reorganized, many have been grouped by process (`Main` and `Renderer`).
+For example the `LensMainExtension` class is now referred to by `Main.LensExtension`.
+* The `globalPageMenus` field of the Renderer extension class (now `Renderer.LensExtension`) is removed.
+Global pages can still be made accessible via the application menus and the status bar, as well as from the newly added Welcome menu.
+* The `clusterFeatures` field of the Renderer extension class (now `Renderer.LensExtension`) is removed.
+Cluster features can still be implemented but Lens no longer dictates how the feature's lifecycle (install/upgrade/uninstall) is managed.
+`Renderer.K8sApi.ResourceStack` provides the functionality to input and apply kubernetes resources to a cluster.
+It is up to the extension developer to manage the lifecycle (i.e. done automatically by the extension or allow the end-user to control via a `Renderer.LensExtension.entitySettings` entry).
\ No newline at end of file
diff --git a/docs/extensions/guides/README.md b/docs/extensions/guides/README.md
index c1c2521f5a..434aec7444 100644
--- a/docs/extensions/guides/README.md
+++ b/docs/extensions/guides/README.md
@@ -17,8 +17,9 @@ Each guide or code sample includes the following:
| Guide | APIs |
| ----- | ----- |
| [Generate new extension project](generator.md) ||
-| [Main process extension](main-extension.md) | LensMainExtension |
-| [Renderer process extension](renderer-extension.md) | LensRendererExtension |
+| [Main process extension](main-extension.md) | Main.LensExtension |
+| [Renderer process extension](renderer-extension.md) | Renderer.LensExtension |
+| [Resource stack (cluster feature)](resource-stack.md) | |
| [Stores](stores.md) | |
| [Components](components.md) | |
| [KubeObjectListLayout](kube-object-list-layout.md) | |
diff --git a/docs/extensions/guides/main-extension.md b/docs/extensions/guides/main-extension.md
index 32d24acdef..654a9707df 100644
--- a/docs/extensions/guides/main-extension.md
+++ b/docs/extensions/guides/main-extension.md
@@ -2,13 +2,14 @@
The Main Extension API is the interface to Lens's main process.
Lens runs in both main and renderer processes.
-The Main Extension API allows you to access, configure, and customize Lens data, add custom application menu items, and run custom code in Lens's main process.
+The Main Extension API allows you to access, configure, and customize Lens data, add custom application menu items and [protocol handlers](protocol-handlers.md), and run custom code in Lens's main process.
+It also provides convenient methods for navigating to built-in Lens pages and extension pages, as well as adding and removing sources of catalog entities.
-## `LensMainExtension` Class
+## `Main.LensExtension` Class
### `onActivate()` and `onDeactivate()` Methods
-To create a main extension simply extend the `LensMainExtension` class:
+To create a main extension simply extend the `Main.LensExtension` class:
```typescript
import { Main } from "@k8slens/extensions";
@@ -75,3 +76,25 @@ Valid values include: `"file"`, `"edit"`, `"view"`, and `"help"`.
In this example, we simply log a message.
However, you would typically have this navigate to a specific page or perform another operation.
Note that pages are associated with the [`Renderer.LensExtension`](renderer-extension.md) class and can be defined in the process of extending it.
+
+The following example demonstrates how an application menu can be used to navigate to such a page:
+
+``` typescript
+import { Main } from "@k8slens/extensions";
+
+export default class SamplePageMainExtension extends Main.LensExtension {
+ appMenus = [
+ {
+ parentId: "help",
+ label: "Sample",
+ click: () => this.navigate("myGlobalPage")
+ }
+ ]
+}
+```
+
+When the menu item is clicked the `navigate()` method looks for and displays a global page with id `"myGlobalPage"`.
+This page would be defined in your extension's `Renderer.LensExtension` implmentation (See [`Renderer.LensExtension`](renderer-extension.md)).
+
+The `Main.LensExtension` class also provides the `addCatalogSource()` and `removeCatalogSource()` methods, for managing custom catalog items (or entities).
+See the [`Catalog`](tbd) documentation for full details about the catalog.
\ No newline at end of file
diff --git a/docs/extensions/guides/renderer-extension.md b/docs/extensions/guides/renderer-extension.md
index c96391fde0..1fd1ff8314 100644
--- a/docs/extensions/guides/renderer-extension.md
+++ b/docs/extensions/guides/renderer-extension.md
@@ -2,26 +2,39 @@
The Renderer Extension API is the interface to Lens's renderer process.
Lens runs in both the main and renderer processes.
-The Renderer Extension API allows you to access, configure, and customize Lens data, add custom Lens UI elements, and run custom code in Lens's renderer process.
+The Renderer Extension API allows you to access, configure, and customize Lens data, add custom Lens UI elements, protocol handlers, and command palette commands, as well as run custom code in Lens's renderer process.
The custom Lens UI elements that you can add include:
* [Cluster pages](#clusterpages)
* [Cluster page menus](#clusterpagemenus)
* [Global pages](#globalpages)
-* [Global page menus](#globalpagemenus)
+* [Welcome menus](#welcomemenus)
* [App preferences](#apppreferences)
+* [Top bar items](#topbaritems)
* [Status bar items](#statusbaritems)
* [KubeObject menu items](#kubeobjectmenuitems)
* [KubeObject detail items](#kubeobjectdetailitems)
+* [KubeObject status texts](#kubeobjectstatustexts)
+* [Kube workloads overview items](#kubeworkloadsoverviewitems)
+
+as well as catalog-related UI elements:
+
+* [Entity settings](#entitysettings)
+* [Catalog entity detail items](#catalogentitydetailitems)
All UI elements are based on React components.
-## `LensRendererExtension` Class
+As well you can add commands and protocol handlers:
+
+* [Command palette commands](#commandpalettecommands)
+* [protocol handlers](protocol-handlers.md)
+
+## `Renderer.LensExtension` Class
### `onActivate()` and `onDeactivate()` Methods
-To create a renderer extension, extend the `LensRendererExtension` class:
+To create a renderer extension, extend the `Renderer.LensExtension` class:
```typescript
import { Renderer } from "@k8slens/extensions";
@@ -402,171 +415,6 @@ This is what the example will look like, including how the menu item will appear

-### `clusterFeatures`
-
-Cluster features are Kubernetes resources that can be applied to and managed within the active cluster.
-They can be installed and uninstalled by the Lens user from the cluster **Settings** page.
-
-!!! info
- To access the cluster **Settings** page, right-click the relevant cluster in the left side menu and click **Settings**.
-
-The following example shows how to add a cluster feature as part of a `LensRendererExtension`:
-
-```typescript
-import { Renderer } from "@k8slens/extensions"
-import { ExampleFeature } from "./src/example-feature"
-import React from "react"
-
-export default class ExampleFeatureExtension extends Renderer.LensExtension {
- clusterFeatures = [
- {
- title: "Example Feature",
- components: {
- Description: () => {
- return (
-
- Enable an example feature.
-
- )
- }
- },
- feature: new ExampleFeature()
- }
- ];
-}
-```
-
-The properties of the `clusterFeatures` array objects are defined as follows:
-
-* `title` and `components.Description` provide content that appears on the cluster settings page, in the **Features** section.
-* `feature` specifies an instance which extends the abstract class `ClusterFeature.Feature`, and specifically implements the following methods:
-
-```typescript
- abstract install(cluster: Cluster): Promise;
- abstract upgrade(cluster: Cluster): Promise;
- abstract uninstall(cluster: Cluster): Promise;
- abstract updateStatus(cluster: Cluster): Promise;
-```
-
-The four methods listed above are defined as follows:
-
-* The `install()` method installs Kubernetes resources using the `applyResources()` method, or by directly accessing the [Kubernetes API](../api/README.md).
-This method is typically called when a user indicates that they want to install the feature (i.e., by clicking **Install** for the feature in the cluster settings page).
-
-* The `upgrade()` method upgrades the Kubernetes resources already installed, if they are relevant to the feature.
-This method is typically called when a user indicates that they want to upgrade the feature (i.e., by clicking **Upgrade** for the feature in the cluster settings page).
-
-* The `uninstall()` method uninstalls Kubernetes resources using the [Kubernetes API](../api/README.md).
-This method is typically called when a user indicates that they want to uninstall the feature (i.e., by clicking **Uninstall** for the feature in the cluster settings page).
-
-* The `updateStatus()` method provides the current status information in the `status` field of the `ClusterFeature.Feature` parent class.
-Lens periodically calls this method to determine details about the feature's current status.
-The implementation of this method should uninstall Kubernetes resources using the Kubernetes api (`K8sApi`)
-Consider using the following properties with `updateStatus()`:
-
- * `status.currentVersion` and `status.latestVersion` may be displayed by Lens in the feature's description.
-
- * `status.installed` should be set to `true` if the feature is installed, and `false` otherwise.
-
- * `status.canUpgrade` is set according to a rule meant to determine whether the feature can be upgraded.
- This rule can involve `status.currentVersion` and `status.latestVersion`, if desired.
-
-The following shows a very simple implementation of a `ClusterFeature`:
-
-```typescript
-import { Renderer, Common } from "@k8slens/extensions";
-import * as path from "path";
-
-const {
- K8sApi: {
- ResourceStack,
- forCluster,
- StorageClass,
- Namespace,
- }
-} = Renderer;
-
-type ResourceStack = Renderer.K8sApi.ResourceStack;
-type Pod = Renderer.K8sApi.Pod;
-type KubernetesCluster = Common.Catalog.KubernetesCluster;
-
-export interface MetricsStatus {
- installed: boolean;
- canUpgrade: boolean;
-}
-
-export class ExampleFeature {
- protected stack: ResourceStack;
-
- constructor(protected cluster: KubernetesCluster) {
- this.stack = new ResourceStack(cluster, this.name);
- }
-
- install(): Promise {
- return this.stack.kubectlApplyFolder(path.join(__dirname, "../resources/"));
- }
-
- upgrade(): Promise {
- return this.install(config);
- }
-
- async getStatus(): Promise {
- const status: MetricsStatus = { installed: false, canUpgrade: false};
-
- try {
- const pod = forCluster(cluster, Pod);
- const examplePod = await pod.get({name: "example-pod", namespace: "default"});
-
- if (examplePod?.kind) {
- status.installed = true;
- status.currentVersion = examplePod.spec.containers[0].image.split(":")[1];
- status.canUpgrade = true; // a real implementation would perform a check here that is relevant to the specific feature
- } else {
- status.installed = false;
- status.canUpgrade = false;
- }
- } catch(e) {
- if (e?.error?.code === 404) {
- status.installed = false;
- status.canUpgrade = false;
- }
- }
-
- return status;
- }
-
- async uninstall(): Promise {
- return this.stack.kubectlDeleteFolder(this.resourceFolder);
- }
-}
-```
-
-This example implements the `install()` method by invoking the helper `applyResources()` method.
-`applyResources()` tries to apply all resources read from all files found in the folder path provided.
-In this case the folder path is the `../resources` subfolder relative to the current source code's folder.
-The file `../resources/example-pod.yml` could contain:
-
-``` yaml
-apiVersion: v1
-kind: Pod
-metadata:
- name: example-pod
-spec:
- containers:
- - name: example-pod
- image: nginx
-```
-
-The example above implements the four methods as follows:
-
-* It implements `upgrade()` by invoking the `install()` method.
-Depending on the feature to be supported by an extension, upgrading may require additional and/or different steps.
-
-* It implements `uninstall()` by utilizing the [Kubernetes API](../api/README.md) which Lens provides to delete the `example-pod` applied by the `install()` method.
-
-* It implements `updateStatus()` by using the [Kubernetes API](../api/README.md) which Lens provides to determine whether the `example-pod` is installed, what version is associated with it, and whether it can be upgraded.
-The implementation determines what the status is for a specific cluster feature.
-
### `appPreferences`
The Lens **Preferences** page is a built-in global page.
diff --git a/docs/extensions/guides/resource-stack.md b/docs/extensions/guides/resource-stack.md
new file mode 100644
index 0000000000..187404df8e
--- /dev/null
+++ b/docs/extensions/guides/resource-stack.md
@@ -0,0 +1,223 @@
+# Resource Stack (Cluster Feature)
+
+```typescript
+import { Renderer, Common } from "@k8slens/extensions";
+import * as path from "path";
+
+const {
+ K8sApi: {
+ ResourceStack,
+ forCluster,
+ Pod,
+ }
+} = Renderer;
+
+type ResourceStack = Renderer.K8sApi.ResourceStack;
+type Pod = Renderer.K8sApi.Pod;
+type KubernetesCluster = Common.Catalog.KubernetesCluster;
+
+export class ExampleResourceStack {
+ protected stack: ResourceStack;
+
+ constructor(protected cluster: KubernetesCluster) {
+ this.stack = new ResourceStack(cluster, "example-resource-stack");
+ }
+
+ get resourceFolder() {
+ return path.join(__dirname, "../resources/");
+ }
+
+ install(): Promise {
+ console.log("installing example-pod");
+ return this.stack.kubectlApplyFolder(this.resourceFolder);
+ }
+
+ async isInstalled(): Promise {
+ try {
+ const podApi = forCluster(this.cluster, Pod);
+ const examplePod = await podApi.get({name: "example-pod", namespace: "default"});
+
+ if (examplePod?.kind) {
+ console.log("found example-pod");
+ return true;
+ }
+ } catch(e) {
+ console.log("Error getting example-pod:", e);
+ }
+ console.log("didn't find example-pod");
+
+ return false;
+ }
+
+ async uninstall(): Promise {
+ console.log("uninstalling example-pod");
+ return this.stack.kubectlDeleteFolder(this.resourceFolder);
+ }
+}
+```
+
+
+
+Cluster features are Kubernetes resources that can be applied to and managed within the active cluster.
+They can be installed and uninstalled by the Lens user from the cluster **Settings** page.
+
+!!! info
+ To access the cluster **Settings** page, right-click the relevant cluster in the left side menu and click **Settings**.
+
+The following example shows how to add a cluster feature as part of a `LensRendererExtension`:
+
+```typescript
+import { Renderer } from "@k8slens/extensions"
+import { ExampleFeature } from "./src/example-feature"
+import React from "react"
+
+export default class ExampleFeatureExtension extends Renderer.LensExtension {
+ clusterFeatures = [
+ {
+ title: "Example Feature",
+ components: {
+ Description: () => {
+ return (
+
+ Enable an example feature.
+
+ )
+ }
+ },
+ feature: new ExampleFeature()
+ }
+ ];
+}
+```
+
+The properties of the `clusterFeatures` array objects are defined as follows:
+
+* `title` and `components.Description` provide content that appears on the cluster settings page, in the **Features** section.
+* `feature` specifies an instance which extends the abstract class `ClusterFeature.Feature`, and specifically implements the following methods:
+
+```typescript
+ abstract install(cluster: Cluster): Promise;
+ abstract upgrade(cluster: Cluster): Promise;
+ abstract uninstall(cluster: Cluster): Promise;
+ abstract updateStatus(cluster: Cluster): Promise;
+```
+
+The four methods listed above are defined as follows:
+
+* The `install()` method installs Kubernetes resources using the `applyResources()` method, or by directly accessing the [Kubernetes API](../api/README.md).
+This method is typically called when a user indicates that they want to install the feature (i.e., by clicking **Install** for the feature in the cluster settings page).
+
+* The `upgrade()` method upgrades the Kubernetes resources already installed, if they are relevant to the feature.
+This method is typically called when a user indicates that they want to upgrade the feature (i.e., by clicking **Upgrade** for the feature in the cluster settings page).
+
+* The `uninstall()` method uninstalls Kubernetes resources using the [Kubernetes API](../api/README.md).
+This method is typically called when a user indicates that they want to uninstall the feature (i.e., by clicking **Uninstall** for the feature in the cluster settings page).
+
+* The `updateStatus()` method provides the current status information in the `status` field of the `ClusterFeature.Feature` parent class.
+Lens periodically calls this method to determine details about the feature's current status.
+The implementation of this method should uninstall Kubernetes resources using the Kubernetes api (`K8sApi`)
+Consider using the following properties with `updateStatus()`:
+
+ * `status.currentVersion` and `status.latestVersion` may be displayed by Lens in the feature's description.
+
+ * `status.installed` should be set to `true` if the feature is installed, and `false` otherwise.
+
+ * `status.canUpgrade` is set according to a rule meant to determine whether the feature can be upgraded.
+ This rule can involve `status.currentVersion` and `status.latestVersion`, if desired.
+
+The following shows a very simple implementation of a `ClusterFeature`:
+
+```typescript
+import { Renderer, Common } from "@k8slens/extensions";
+import * as path from "path";
+
+const {
+ K8sApi: {
+ ResourceStack,
+ forCluster,
+ StorageClass,
+ Namespace,
+ }
+} = Renderer;
+
+type ResourceStack = Renderer.K8sApi.ResourceStack;
+type Pod = Renderer.K8sApi.Pod;
+type KubernetesCluster = Common.Catalog.KubernetesCluster;
+
+export interface MetricsStatus {
+ installed: boolean;
+ canUpgrade: boolean;
+}
+
+export class ExampleFeature {
+ protected stack: ResourceStack;
+
+ constructor(protected cluster: KubernetesCluster) {
+ this.stack = new ResourceStack(cluster, this.name);
+ }
+
+ install(): Promise {
+ return this.stack.kubectlApplyFolder(path.join(__dirname, "../resources/"));
+ }
+
+ upgrade(): Promise {
+ return this.install(config);
+ }
+
+ async getStatus(): Promise {
+ const status: MetricsStatus = { installed: false, canUpgrade: false};
+
+ try {
+ const pod = forCluster(cluster, Pod);
+ const examplePod = await pod.get({name: "example-pod", namespace: "default"});
+
+ if (examplePod?.kind) {
+ status.installed = true;
+ status.currentVersion = examplePod.spec.containers[0].image.split(":")[1];
+ status.canUpgrade = true; // a real implementation would perform a check here that is relevant to the specific feature
+ } else {
+ status.installed = false;
+ status.canUpgrade = false;
+ }
+ } catch(e) {
+ if (e?.error?.code === 404) {
+ status.installed = false;
+ status.canUpgrade = false;
+ }
+ }
+
+ return status;
+ }
+
+ async uninstall(): Promise {
+ return this.stack.kubectlDeleteFolder(this.resourceFolder);
+ }
+}
+```
+
+This example implements the `install()` method by invoking the helper `applyResources()` method.
+`applyResources()` tries to apply all resources read from all files found in the folder path provided.
+In this case the folder path is the `../resources` subfolder relative to the current source code's folder.
+The file `../resources/example-pod.yml` could contain:
+
+``` yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: example-pod
+spec:
+ containers:
+ - name: example-pod
+ image: nginx
+```
+
+The example above implements the four methods as follows:
+
+* It implements `upgrade()` by invoking the `install()` method.
+Depending on the feature to be supported by an extension, upgrading may require additional and/or different steps.
+
+* It implements `uninstall()` by utilizing the [Kubernetes API](../api/README.md) which Lens provides to delete the `example-pod` applied by the `install()` method.
+
+* It implements `updateStatus()` by using the [Kubernetes API](../api/README.md) which Lens provides to determine whether the `example-pod` is installed, what version is associated with it, and whether it can be upgraded.
+The implementation determines what the status is for a specific cluster feature.
+
diff --git a/mkdocs.yml b/mkdocs.yml
index 123506a585..43f089320f 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -22,6 +22,7 @@ nav:
- Generator: extensions/guides/generator.md
- Main Extension: extensions/guides/main-extension.md
- Renderer Extension: extensions/guides/renderer-extension.md
+ - Resource Stack: extensions/guides/resource-stack.md
- Stores: extensions/guides/stores.md
- Working with MobX: extensions/guides/working-with-mobx.md
- Protocol Handlers: extensions/guides/protocol-handlers.md