1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

cluster-feature extension guide

Signed-off-by: Jim Ehrismann <jehrismann@mirantis.com>
This commit is contained in:
Jim Ehrismann 2020-11-25 13:11:16 -05:00
parent b6137976f7
commit 0bbd958912
2 changed files with 46 additions and 17 deletions

View File

@ -269,19 +269,10 @@ export class HelpPage extends React.Component<{ extension: LensRendererExtension
`HelpIcon` introduces one of Lens' built-in components available to extension developers, the `Component.Icon`. Built in are the [Material Design](https://material.io) [icons](https://material.io/resources/icons/). One can be selected by name via the `material` field. `HelpIcon` introduces one of Lens' built-in components available to extension developers, the `Component.Icon`. Built in are the [Material Design](https://material.io) [icons](https://material.io/resources/icons/). One can be selected by name via the `material` field.
*********************************************************************
WIP below!
*********************************************************************
### `clusterFeatures` ### `clusterFeatures`
Cluster features are Kubernetes resources that can be applied to and managed within the active cluster. They can be installed/uninstalled from the [cluster settings page](). Cluster features are Kubernetes resources that can be applied to and managed within the active cluster. They can be installed/uninstalled by the Lens user from the [cluster settings page]().
The following example shows how to add a cluster feature: The following example shows how to add a cluster feature as part of a `LensRendererExtension`:
``` typescript ``` typescript
import { LensRendererExtension } from "@k8slens/extensions" import { LensRendererExtension } from "@k8slens/extensions"
@ -315,16 +306,25 @@ The `title` and `components.Description` fields provide content that appears on
abstract updateStatus(cluster: Cluster): Promise<ClusterFeatureStatus>; abstract updateStatus(cluster: Cluster): Promise<ClusterFeatureStatus>;
``` ```
The `install()` method is typically called by Lens when a user has indicated that this feature is to be installed (i.e. clicked **Install** for the feature on the cluster settings page). The implementation of this method should install kubernetes resources using the `applyResources()` method, or by directly accessing the kubernetes api ([`K8sApi`](tbd)).
The `upgrade()` method is typically called by Lens when a user has indicated that this feature is to be upgraded (i.e. clicked **Upgrade** for the feature on the cluster settings page). The implementation of this method should upgrade the kubernetes resources already installed, if relevant to the feature.
The `uninstall()` method is typically called by Lens when a user has indicated that this feature is to be uninstalled (i.e. clicked **Uninstall** for the feature on the cluster settings page). The implementation of this method should uninstall kubernetes resources using the kubernetes api (`K8sApi`)
The `updateStatus()` method is called periodically by Lens to determine details about the feature's current status. The implementation of this method should provide the current status information in the `status` field of the `ClusterFeature.Feature` parent class. The `status.currentVersion` and `status.latestVersion` fields may be displayed by Lens in describing the feature. The `status.installed` field should be set to true if the feature is currently installed, otherwise false. Also, Lens relies on the `status.canUpgrade` field to determine if the feature can be upgraded (i.e a new version could be available) so the implementation should set the `status.canUpgrade` field according to specific rules for the feature, if relevant.
The following shows a very simple implementation of a `ClusterFeature`: The following shows a very simple implementation of a `ClusterFeature`:
``` typescript ``` typescript
import { ClusterFeature, Store, K8sApi } from "@k8slens/extensions"; import { ClusterFeature, Store, K8sApi } from "@k8slens/extensions";
import * as path from "path";
export class ExampleFeature extends ClusterFeature.Feature { export class ExampleFeature extends ClusterFeature.Feature {
async install(cluster: Store.Cluster): Promise<void> { async install(cluster: Store.Cluster): Promise<void> {
super.applyResources(cluster, super.renderTemplates(path.join(__dirname, "../resources/"))); super.applyResources(cluster, path.join(__dirname, "../resources/"));
} }
async upgrade(cluster: Store.Cluster): Promise<void> { async upgrade(cluster: Store.Cluster): Promise<void> {
@ -333,18 +333,20 @@ export class ExampleFeature extends ClusterFeature.Feature {
async updateStatus(cluster: Store.Cluster): Promise<ClusterFeature.FeatureStatus> { async updateStatus(cluster: Store.Cluster): Promise<ClusterFeature.FeatureStatus> {
try { try {
const statefulSet = K8sApi.forCluster(cluster, K8sApi.Pod); const pod = K8sApi.forCluster(cluster, K8sApi.Pod);
const examplePod = await pod.get({name: "example-pod", namespace: "default"}); const examplePod = await pod.get({name: "example-pod", namespace: "default"});
if (examplePod?.kind) { if (examplePod?.kind) {
this.status.installed = true; this.status.installed = true;
this.status.currentVersion = examplePod.spec.template.spec.containers[0].image.split(":")[1]; this.status.currentVersion = examplePod.spec.containers[0].image.split(":")[1];
this.status.canUpgrade = true; // a real implementation would perform a check here that is relevant to the specific feature this.status.canUpgrade = true; // a real implementation would perform a check here that is relevant to the specific feature
} else { } else {
this.status.installed = false; this.status.installed = false;
this.status.canUpgrade = false;
} }
} catch(e) { } catch(e) {
if (e?.error?.code === 404) { if (e?.error?.code === 404) {
this.status.installed = false; this.status.installed = false;
this.status.canUpgrade = false;
} }
} }
@ -353,11 +355,38 @@ export class ExampleFeature extends ClusterFeature.Feature {
async uninstall(cluster: Store.Cluster): Promise<void> { async uninstall(cluster: Store.Cluster): Promise<void> {
const podApi = K8sApi.forCluster(cluster, K8sApi.Pod); const podApi = K8sApi.forCluster(cluster, K8sApi.Pod);
await podApi.delete({name: "example-pod", namespace: "default"}); await podApi.delete({name: "example-pod", namespace: "default"});
}
} }
``` ```
This example implements the `install()` method by simply invoking the helper `applyResources()` method. `applyResources()` tries to apply all resources read from all files found in the folder path provided. In this case this folder path is the `../resources` subfolder relative to 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 `upgrade()` method in the example above is implemented by simply invoking the `install()` method. Depending on the feature to be supported by an extension, upgrading may require additional and/or different steps.
The `uninstall()` method is implemented in the example above by utilizing the [`K8sApi`](tbd) provided by Lens to simply delete the `example-pod` pod applied by the `install()` method.
The `updateStatus()` method is implemented above by using the [`K8sApi`](tbd) as well, this time to get information from the `example-pod` pod, in particular to determine if it is installed, what version is associated with it, and if it can be upgraded. How the status is updated for a specific cluster feature is up to the implementation.
*********************************************************************
WIP below!
*********************************************************************
### `appPreferences` ### `appPreferences`
The Preferences page is essentially a global page. Extensions can add custom preferences to the Preferences page, thus providing a single location for users to configure global, for Lens and extensions alike. The Preferences page is essentially a global page. Extensions can add custom preferences to the Preferences page, thus providing a single location for users to configure global, for Lens and extensions alike.

View File

@ -47,7 +47,7 @@ export abstract class ClusterFeature {
abstract async install(cluster: Cluster): Promise<void>; abstract async install(cluster: Cluster): Promise<void>;
/** /**
* to be implemented in the derived class, this method is typically called by Lens when a user has indicated that this feature is to be ugraded. The implementation * to be implemented in the derived class, this method is typically called by Lens when a user has indicated that this feature is to be upgraded. The implementation
* of this method should upgrade the kubernetes resources already installed, if relevant to the feature * of this method should upgrade the kubernetes resources already installed, if relevant to the feature
* *
* @param cluster the cluster that the feature is to be upgraded on * @param cluster the cluster that the feature is to be upgraded on
@ -56,7 +56,7 @@ export abstract class ClusterFeature {
/** /**
* to be implemented in the derived class, this method is typically called by Lens when a user has indicated that this feature is to be uninstalled. The implementation * to be implemented in the derived class, this method is typically called by Lens when a user has indicated that this feature is to be uninstalled. The implementation
* of this method should install kubernetes resources using the kubernetes api (K8sApi) * of this method should uninstall kubernetes resources using the kubernetes api (K8sApi)
* *
* @param cluster the cluster that the feature is to be uninstalled from * @param cluster the cluster that the feature is to be uninstalled from
*/ */