mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
address review comments
Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>
This commit is contained in:
parent
da8017a0ee
commit
ee76d81d15
@ -37,7 +37,7 @@ Keep up with Lens and the Lens Extension API by reviewing the [release notes](ht
|
||||
|
||||
## Important changes since Lens v4
|
||||
|
||||
Lens has undergone major design changes in v5 which have resulted in a new extension API.
|
||||
Lens has undergone major design improvements in v5, which have resulted in several large changes to the 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.
|
||||
|
||||
@ -7,15 +7,17 @@ The `package.json` for your extension must have an `"engines"` field specifying
|
||||
"lens": "^5.0.0-beta.7"
|
||||
},
|
||||
```
|
||||
Note that Lens v5 supports all the range semantics that [semver](https://www.npmjs.com/package/semver) provides.
|
||||
* Types and components have been reorganized, many have been grouped by process (`Main` and `Renderer`) plus those not specific to a process (`Common`).
|
||||
For example the `LensMainExtension` class is now referred to by `Main.LensExtension`.
|
||||
See the [API Refernce](extensions/api/README.md) for the new organization.
|
||||
See the [API Reference](api/README.md) for the new organization.
|
||||
* 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.
|
||||
Cluster features can still be implemented but Lens no longer dictates how a 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).
|
||||
It is up to the extension developer to manage the lifecycle.
|
||||
It could be applied automatically to a cluster by the extension or the end-user could be expected to install it, etc. from the cluster **Settings** page.
|
||||
* Lens v5 now relies on mobx 6 for state management. Extensions that use mobx will need to be modified to work with mobx 6.
|
||||
See [Migrating from Mobx 4/5](https://mobx.js.org/migrating-from-4-or-5.html) for specific details.
|
||||
|
||||
|
||||
5
docs/extensions/guides/catalog.md
Normal file
5
docs/extensions/guides/catalog.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Catalog (WIP)
|
||||
|
||||
## CatalogCategoryRegistry
|
||||
|
||||
## CatalogEntityRegistry
|
||||
@ -96,5 +96,7 @@ export default class SamplePageMainExtension extends Main.LensExtension {
|
||||
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)).
|
||||
|
||||
### `addCatalogSource()` and `removeCatalogSource()` Methods
|
||||
|
||||
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.
|
||||
See the [`Catalog`](catalog.md) documentation for full details about the catalog.
|
||||
@ -1,4 +1,4 @@
|
||||
# Renderer Extension
|
||||
# Renderer Extension (WIP)
|
||||
|
||||
The Renderer Extension API is the interface to Lens's renderer process.
|
||||
Lens runs in both the main and renderer processes.
|
||||
@ -25,7 +25,7 @@ as well as catalog-related UI elements:
|
||||
|
||||
All UI elements are based on React components.
|
||||
|
||||
As well you can add commands and protocol handlers:
|
||||
Finally, you can also add commands and protocol handlers:
|
||||
|
||||
* [Command palette commands](#commandpalettecommands)
|
||||
* [protocol handlers](protocol-handlers.md)
|
||||
@ -332,89 +332,7 @@ Global pages can be made available in the following ways:
|
||||
* To add global pages as an interactive element in the blue status bar along the bottom of the Lens UI, see [`statusBarItems`](#statusbaritems).
|
||||
* To add global pages to the left side menu, see [`globalPageMenus`](#globalpagemenus).
|
||||
|
||||
### `globalPageMenus`
|
||||
|
||||
`globalPageMenus` allows you to add global page menu items to the left nav.
|
||||
|
||||
By expanding on the above example, you can add a global page menu item to the `HelpExtension` definition:
|
||||
|
||||
```typescript
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { HelpIcon, HelpPage } from "./page"
|
||||
import React from "react"
|
||||
|
||||
export default class HelpExtension extends Renderer.LensExtension {
|
||||
globalPages = [
|
||||
{
|
||||
id: "help",
|
||||
components: {
|
||||
Page: () => <HelpPage extension={this}/>,
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
globalPageMenus = [
|
||||
{
|
||||
target: { pageId: "help" },
|
||||
title: "Help",
|
||||
components: {
|
||||
Icon: HelpIcon,
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
`globalPageMenus` is an array of objects that satisfy the `PageMenuRegistration` interface.
|
||||
This element defines how the global page menu item will appear and what it will do when you click it.
|
||||
The properties of the `globalPageMenus` array objects are defined as follows:
|
||||
|
||||
* `target` links to the relevant global page using `pageId`.
|
||||
* `pageId` takes the value of the relevant global page's `id` property.
|
||||
* `title` sets the name of the global page menu item that will display as a tooltip in the left nav.
|
||||
* `components` is used to set an icon that appears in the left nav.
|
||||
|
||||
The above example creates a "Help" icon menu item.
|
||||
When users click the icon, the Lens UI will display the contents of `ExamplePage`.
|
||||
|
||||
This example requires the definition of another React-based component, `HelpIcon`.
|
||||
Update `page.tsx` from the example above with the `HelpIcon` definition, as follows:
|
||||
|
||||
```typescript
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import React from "react"
|
||||
|
||||
type IconProps = Renderer.Component.IconProps;
|
||||
|
||||
const {
|
||||
Component: { Icon },
|
||||
} = Renderer;
|
||||
|
||||
export function HelpIcon(props: IconProps) {
|
||||
return <Icon {...props} material="help"/>
|
||||
}
|
||||
|
||||
export class HelpPage extends React.Component<{ extension: Renderer.LensExtension }> {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<p>Help</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Lens includes various built-in components available for extension developers to use.
|
||||
One of these is the `Renderer.Component.Icon`, introduced in `HelpIcon`, which you can use to access any of the [icons](https://material.io/resources/icons/) available at [Material Design](https://material.io).
|
||||
The property that `Renderer.Component.Icon` uses is defined as follows:
|
||||
|
||||
* `material` takes the name of the icon you want to use.
|
||||
|
||||
This is what the example will look like, including how the menu item will appear in the left nav:
|
||||
|
||||

|
||||
|
||||
### `welcomeMenus`
|
||||
### `appPreferences`
|
||||
|
||||
The Lens **Preferences** page is a built-in global page.
|
||||
@ -525,6 +443,8 @@ Note that you can manage an extension's state data using an `ExtensionStore` obj
|
||||
To simplify this guide, the example above defines a `preference` field in the `ExampleRendererExtension` class definition to hold the extension's state.
|
||||
However, we recommend that you manage your extension's state data using [`ExtensionStore`](../stores#extensionstore).
|
||||
|
||||
### `topBarItems`
|
||||
|
||||
### `statusBarItems`
|
||||
|
||||
The status bar is the blue strip along the bottom of the Lens UI.
|
||||
@ -846,3 +766,17 @@ Construct the table using the `Renderer.Component.Table` and related elements.
|
||||
For each pod the name, age, and status are obtained using the `Renderer.K8sApi.Pod` methods.
|
||||
The table is constructed using the `Renderer.Component.Table` and related elements.
|
||||
See [Component documentation](https://docs.k8slens.dev/latest/extensions/api/modules/_renderer_api_components_/) for further details.
|
||||
|
||||
### `kubeObjectStatusTexts`
|
||||
|
||||
### `kubeWorkloadsOverviewItems`
|
||||
|
||||
### `entitySettings`
|
||||
|
||||
### `catalogEntityDetailItems`
|
||||
|
||||
### `commandPaletteCommands`
|
||||
|
||||
### `protocolHandlers`
|
||||
|
||||
See the [Protocol Handlers Guide](protocol-handlers.md)
|
||||
|
||||
@ -1,4 +1,14 @@
|
||||
# Resource Stack (Cluster Feature)
|
||||
# Resource Stack (Cluster Feature) (WIP)
|
||||
|
||||
Cluster features are Kubernetes resources that can be applied to and managed within the active cluster.
|
||||
The `Renderer.K8sApi.ResourceStack` class provides the functionality to input and apply kubernetes resources to a cluster.
|
||||
It is up to the extension developer to manage the lifecycle of the resource stack.
|
||||
It could be applied automatically to a cluster by the extension or the end-user could be required to install it, etc. 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 using a resource stack as part of a `Renderer.LensExtension`:
|
||||
|
||||
```typescript
|
||||
import { Renderer, Common } from "@k8slens/extensions";
|
||||
@ -56,168 +66,90 @@ export class ExampleResourceStack {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
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`:
|
||||
The `ExampleResourceStack` cluster feature can be managed by the end-user on the cluster **Settings** page via a `Renderer.LensExtension.entitySettings` entry.
|
||||
|
||||
```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 (
|
||||
<span>
|
||||
Enable an example feature.
|
||||
</span>
|
||||
)
|
||||
}
|
||||
},
|
||||
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<void>;
|
||||
abstract upgrade(cluster: Cluster): Promise<void>;
|
||||
abstract uninstall(cluster: Cluster): Promise<void>;
|
||||
abstract updateStatus(cluster: Cluster): Promise<ClusterFeatureStatus>;
|
||||
```
|
||||
|
||||
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);
|
||||
import React from "react";
|
||||
import { Common, Renderer } from "@k8slens/extensions";
|
||||
import { observer } from "mobx-react";
|
||||
import { computed, observable, makeObservable } from "mobx";
|
||||
import { ExampleResourceStack } from "./example-resource-stack";
|
||||
|
||||
const {
|
||||
K8sApi: {
|
||||
forCluster, StatefulSet, DaemonSet, Deployment,
|
||||
},
|
||||
Component: {
|
||||
SubTitle, Button,
|
||||
}
|
||||
} = Renderer;
|
||||
|
||||
interface Props {
|
||||
cluster: Common.Catalog.KubernetesCluster;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class ExampleResourceStackSettings extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
install(): Promise<string> {
|
||||
return this.stack.kubectlApplyFolder(path.join(__dirname, "../resources/"));
|
||||
}
|
||||
@observable installed = false;
|
||||
@observable inProgress = false;
|
||||
|
||||
upgrade(): Promise<string> {
|
||||
return this.install(config);
|
||||
}
|
||||
feature: ExampleResourceStack;
|
||||
|
||||
async getStatus(): Promise<MetricsStatus> {
|
||||
const status: MetricsStatus = { installed: false, canUpgrade: false};
|
||||
|
||||
async componentDidMount() {
|
||||
this.feature = new ExampleResourceStack(this.props.cluster);
|
||||
|
||||
await this.updateFeatureState();
|
||||
}
|
||||
|
||||
async updateFeatureState() {
|
||||
this.installed = await this.feature.isInstalled();
|
||||
}
|
||||
|
||||
async save() {
|
||||
this.inProgress = true;
|
||||
|
||||
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
|
||||
if (this.installed) {
|
||||
await this.feature.uninstall();
|
||||
} else {
|
||||
status.installed = false;
|
||||
status.canUpgrade = false;
|
||||
}
|
||||
} catch(e) {
|
||||
if (e?.error?.code === 404) {
|
||||
status.installed = false;
|
||||
status.canUpgrade = false;
|
||||
await this.feature.install();
|
||||
}
|
||||
} finally {
|
||||
this.inProgress = false;
|
||||
|
||||
await this.updateFeatureState();
|
||||
}
|
||||
}
|
||||
|
||||
@computed get buttonLabel() {
|
||||
if (this.inProgress && this.installed) return "Uninstalling ...";
|
||||
if (this.inProgress) return "Applying ...";
|
||||
|
||||
if (this.installed) {
|
||||
return "Uninstall";
|
||||
}
|
||||
|
||||
return status;
|
||||
return "Apply";
|
||||
}
|
||||
|
||||
async uninstall(): Promise<string> {
|
||||
return this.stack.kubectlDeleteFolder(this.resourceFolder);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<SubTitle title="Example Resource Stack" />
|
||||
<Button
|
||||
label={this.buttonLabel}
|
||||
waiting={this.inProgress}
|
||||
onClick={() => this.save()}
|
||||
primary />
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kube-object-event-status",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lens-metrics-cluster-feature",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
2
extensions/node-menu/package-lock.json
generated
2
extensions/node-menu/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lens-node-menu",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
2
extensions/pod-menu/package-lock.json
generated
2
extensions/pod-menu/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lens-pod-menu",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@ -22,6 +22,7 @@ nav:
|
||||
- Generator: extensions/guides/generator.md
|
||||
- Main Extension: extensions/guides/main-extension.md
|
||||
- Renderer Extension: extensions/guides/renderer-extension.md
|
||||
- Catalog: extensions/guides/catalog.md
|
||||
- Resource Stack: extensions/guides/resource-stack.md
|
||||
- Stores: extensions/guides/stores.md
|
||||
- Working with MobX: extensions/guides/working-with-mobx.md
|
||||
|
||||
Loading…
Reference in New Issue
Block a user