mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Update docs to be correct with the new api changes (#2916)
* Update docs to be correct with the new api changes Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix renderer-extension.md Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
a9761c4374
commit
0d0c67f13f
@ -14,9 +14,9 @@ In order to see logs from this extension, you need to start Lens from the comman
|
||||
This extension can register a custom callback that is executed when an extension is activated (started).
|
||||
|
||||
``` javascript
|
||||
import { LensMainExtension } from "@k8slens/extensions"
|
||||
import { Main } from "@k8slens/extensions"
|
||||
|
||||
export default class ExampleMainExtension extends LensMainExtension {
|
||||
export default class ExampleMainExtension extends Main.LensExtension {
|
||||
async onActivate() {
|
||||
console.log("hello world")
|
||||
}
|
||||
@ -28,9 +28,9 @@ export default class ExampleMainExtension extends LensMainExtension {
|
||||
This extension can register a custom callback that is executed when an extension is deactivated (stopped).
|
||||
|
||||
``` javascript
|
||||
import { LensMainExtension } from "@k8slens/extensions"
|
||||
import { Main } from "@k8slens/extensions"
|
||||
|
||||
export default class ExampleMainExtension extends LensMainExtension {
|
||||
export default class ExampleMainExtension extends Main.LensExtension {
|
||||
async onDeactivate() {
|
||||
console.log("bye bye")
|
||||
}
|
||||
@ -44,15 +44,15 @@ This extension can register custom app menus that will be displayed on OS native
|
||||
Example:
|
||||
|
||||
```typescript
|
||||
import { LensMainExtension, windowManager } from "@k8slens/extensions"
|
||||
import { Main } from "@k8slens/extensions"
|
||||
|
||||
export default class ExampleMainExtension extends LensMainExtension {
|
||||
export default class ExampleMainExtension extends Main.LensExtension {
|
||||
appMenus = [
|
||||
{
|
||||
parentId: "help",
|
||||
label: "Example item",
|
||||
click() {
|
||||
windowManager.navigate("https://k8slens.dev");
|
||||
Main.Navigation.navigate("https://k8slens.dev");
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -69,9 +69,9 @@ In order to see logs from this extension you need to check them via **View** > *
|
||||
This extension can register a custom callback that is executed when an extension is activated (started).
|
||||
|
||||
``` javascript
|
||||
import { LensRendererExtension } from "@k8slens/extensions"
|
||||
import { Renderer } from "@k8slens/extensions"
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
async onActivate() {
|
||||
console.log("hello world")
|
||||
}
|
||||
@ -83,9 +83,9 @@ export default class ExampleExtension extends LensRendererExtension {
|
||||
This extension can register a custom callback that is executed when an extension is deactivated (stopped).
|
||||
|
||||
``` javascript
|
||||
import { LensRendererExtension } from "@k8slens/extensions"
|
||||
import { Renderer } from "@k8slens/extensions"
|
||||
|
||||
export default class ExampleMainExtension extends LensRendererExtension {
|
||||
export default class ExampleMainExtension extends Renderer.LensExtension {
|
||||
async onDeactivate() {
|
||||
console.log("bye bye")
|
||||
}
|
||||
@ -99,10 +99,16 @@ The global page is a full-screen page that hides all other content from a window
|
||||
|
||||
```typescript
|
||||
import React from "react"
|
||||
import { Component, LensRendererExtension } from "@k8slens/extensions"
|
||||
import { Renderer } from "@k8slens/extensions"
|
||||
import { ExamplePage } from "./src/example-page"
|
||||
|
||||
export default class ExampleRendererExtension extends LensRendererExtension {
|
||||
const {
|
||||
Component: {
|
||||
Icon,
|
||||
}
|
||||
} = Renderer;
|
||||
|
||||
export default class ExampleRendererExtension extends Renderer.LensExtension {
|
||||
globalPages = [
|
||||
{
|
||||
id: "example",
|
||||
@ -117,7 +123,7 @@ export default class ExampleRendererExtension extends LensRendererExtension {
|
||||
title: "Example page", // used in icon's tooltip
|
||||
target: { pageId: "example" }
|
||||
components: {
|
||||
Icon: () => <Component.Icon material="arrow"/>,
|
||||
Icon: () => <Icon material="arrow"/>,
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -131,12 +137,12 @@ It is responsible for storing a state for custom preferences.
|
||||
|
||||
```typescript
|
||||
import React from "react"
|
||||
import { LensRendererExtension } from "@k8slens/extensions"
|
||||
import { Renderer } from "@k8slens/extensions"
|
||||
import { myCustomPreferencesStore } from "./src/my-custom-preferences-store"
|
||||
import { MyCustomPreferenceHint, MyCustomPreferenceInput } from "./src/my-custom-preference"
|
||||
|
||||
|
||||
export default class ExampleRendererExtension extends LensRendererExtension {
|
||||
export default class ExampleRendererExtension extends Renderer.LensExtension {
|
||||
appPreferences = [
|
||||
{
|
||||
title: "My Custom Preference",
|
||||
@ -156,10 +162,10 @@ These pages are visible in a cluster menu when a cluster is opened.
|
||||
|
||||
```typescript
|
||||
import React from "react"
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { ExampleIcon, ExamplePage } from "./src/page"
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
clusterPages = [
|
||||
{
|
||||
id: "extension-example", // optional
|
||||
@ -190,10 +196,10 @@ These features are visible in the "Cluster Settings" page.
|
||||
|
||||
```typescript
|
||||
import React from "react"
|
||||
import { LensRendererExtension } from "@k8slens/extensions"
|
||||
import { Renderer } from "@k8slens/extensions"
|
||||
import { MyCustomFeature } from "./src/my-custom-feature"
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
clusterFeatures = [
|
||||
{
|
||||
title: "My Custom Feature",
|
||||
@ -219,15 +225,21 @@ This extension can register custom icons and text to a status bar area.
|
||||
|
||||
```typescript
|
||||
import React from "react";
|
||||
import { Component, LensRendererExtension, Navigation } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
const {
|
||||
Component: {
|
||||
Icon,
|
||||
}
|
||||
} = Renderer;
|
||||
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
statusBarItems = [
|
||||
{
|
||||
components: {
|
||||
Item: (
|
||||
<div className="flex align-center gaps hover-highlight" onClick={() => this.navigate("/example-page")} >
|
||||
<Component.Icon material="favorite" />
|
||||
<Icon material="favorite" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -243,10 +255,10 @@ This extension can register custom menu items (actions) for specified Kubernetes
|
||||
|
||||
```typescript
|
||||
import React from "react"
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { CustomMenuItem, CustomMenuItemProps } from "./src/custom-menu-item"
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
kubeObjectMenuItems = [
|
||||
{
|
||||
kind: "Node",
|
||||
@ -266,10 +278,10 @@ This extension can register custom details (content) for specified Kubernetes ki
|
||||
|
||||
```typescript
|
||||
import React from "react"
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { CustomKindDetails, CustomKindDetailsProps } from "./src/custom-kind-details"
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
kubeObjectDetailItems = [
|
||||
{
|
||||
kind: "CustomKind",
|
||||
|
||||
@ -114,14 +114,14 @@ There is a way of detect active theme and its changes in JS. [MobX observer func
|
||||
```js
|
||||
import React from "react"
|
||||
import { observer } from "mobx-react"
|
||||
import { App, Component, Theme } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
|
||||
@observer
|
||||
export class SupportPage extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="SupportPage">
|
||||
<h1>Active theme is {Theme.getActiveTheme().name}</h1>
|
||||
<h1>Active theme is {Renderer.Theme.getActiveTheme().name}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -96,11 +96,11 @@ It also registers the `MenuItem` component that displays the `ExampleIcon` React
|
||||
These React components are defined in the additional `./src/page.tsx` file.
|
||||
|
||||
``` typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { ExampleIcon, ExamplePage } from "./page"
|
||||
import React from "react"
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
clusterPages = [
|
||||
{
|
||||
id: "extension-example",
|
||||
|
||||
@ -30,9 +30,8 @@ Each guide or code sample includes the following:
|
||||
|
||||
| Sample | APIs |
|
||||
| ----- | ----- |
|
||||
[hello-world](https://github.com/lensapp/lens-extension-samples/tree/master/helloworld-sample) | LensMainExtension <br> LensRendererExtension <br> Component.Icon <br> Component.IconProps |
|
||||
[minikube](https://github.com/lensapp/lens-extension-samples/tree/master/minikube-sample) | LensMainExtension <br> Store.ClusterStore <br> Store.workspaceStore |
|
||||
[styling-css-modules-sample](https://github.com/lensapp/lens-extension-samples/tree/master/styling-css-modules-sample) | LensMainExtension <br> LensRendererExtension <br> Component.Icon <br> Component.IconProps |
|
||||
[styling-emotion-sample](https://github.com/lensapp/lens-extension-samples/tree/master/styling-emotion-sample) | LensMainExtension <br> LensRendererExtension <br> Component.Icon <br> Component.IconProps |
|
||||
[styling-sass-sample](https://github.com/lensapp/lens-extension-samples/tree/master/styling-sass-sample) | LensMainExtension <br> LensRendererExtension <br> Component.Icon <br> Component.IconProps |
|
||||
[custom-resource-page](https://github.com/lensapp/lens-extension-samples/tree/master/custom-resource-page) | LensRendererExtension <br> K8sApi.KubeApi <br> K8sApi.KubeObjectStore <br> Component.KubeObjectListLayout <br> Component.KubeObjectDetailsProps <br> Component.IconProps |
|
||||
[hello-world](https://github.com/lensapp/lens-extension-samples/tree/master/helloworld-sample) | LensMainExtension <br> LensRendererExtension <br> Renderer.Component.Icon <br> Renderer.Component.IconProps |
|
||||
[styling-css-modules-sample](https://github.com/lensapp/lens-extension-samples/tree/master/styling-css-modules-sample) | LensMainExtension <br> LensRendererExtension <br> Renderer.Component.Icon <br> Renderer.Component.IconProps |
|
||||
[styling-emotion-sample](https://github.com/lensapp/lens-extension-samples/tree/master/styling-emotion-sample) | LensMainExtension <br> LensRendererExtension <br> Renderer.Component.Icon <br> Renderer.Component.IconProps |
|
||||
[styling-sass-sample](https://github.com/lensapp/lens-extension-samples/tree/master/styling-sass-sample) | LensMainExtension <br> LensRendererExtension <br> Renderer.Component.Icon <br> Renderer.Component.IconProps |
|
||||
[custom-resource-page](https://github.com/lensapp/lens-extension-samples/tree/master/custom-resource-page) | LensRendererExtension <br> Renderer.K8sApi.KubeApi <br> Renderer.K8sApi.KubeObjectStore <br> Renderer.Component.KubeObjectListLayout <br> Renderer.Component.KubeObjectDetailsProps <br> Renderer.Component.IconProps |
|
||||
|
||||
@ -43,10 +43,10 @@ To register either a handler or a listener, you should do something like the fol
|
||||
|
||||
`main.ts`:
|
||||
```typescript
|
||||
import { LensMainExtension } from "@k8slens/extensions";
|
||||
import { Main } from "@k8slens/extensions";
|
||||
import { IpcMain } from "./helpers/main";
|
||||
|
||||
export class ExampleExtensionMain extends LensMainExtension {
|
||||
export class ExampleExtensionMain extends Main.LensExtension {
|
||||
onActivate() {
|
||||
IpcMain.createInstance(this);
|
||||
}
|
||||
@ -60,10 +60,10 @@ Lens will automatically clean up that store and all the handlers on deactivation
|
||||
|
||||
`helpers/main.ts`:
|
||||
```typescript
|
||||
import { Ipc, Types } from "@k8slens/extensions";
|
||||
import { Main } from "@k8slens/extensions";
|
||||
|
||||
export class IpcMain extends Ipc.Main {
|
||||
constructor(extension: LensMainExtension) {
|
||||
export class IpcMain extends Main.Ipc {
|
||||
constructor(extension: Main.LensExtension) {
|
||||
super(extension);
|
||||
|
||||
this.listen("initialize", onInitialize);
|
||||
@ -82,10 +82,10 @@ You should be able to just call `IpcMain.getInstance()` anywhere it is needed in
|
||||
|
||||
`renderer.ts`:
|
||||
```typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { IpcRenderer } from "./helpers/renderer";
|
||||
|
||||
export class ExampleExtensionRenderer extends LensRendererExtension {
|
||||
export class ExampleExtensionRenderer extends Renderer.LensExtension {
|
||||
onActivate() {
|
||||
const ipc = IpcRenderer.createInstance(this);
|
||||
|
||||
@ -100,9 +100,9 @@ It is also needed to create an instance to broadcast messages too.
|
||||
|
||||
`helpers/renderer.ts`:
|
||||
```typescript
|
||||
import { Ipc } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
|
||||
export class IpcRenderer extends Ipc.Renderer {}
|
||||
export class IpcRenderer extends Renderer.Ipc {}
|
||||
```
|
||||
|
||||
It is necessary to create child classes of these `abstract class`'s in your extension before you can use them.
|
||||
@ -116,7 +116,7 @@ There is no behind the scenes transfer of these functions.
|
||||
To register a "handler" call `IpcMain.getInstance().handle(...)`.
|
||||
The cleanup of these handlers is handled by Lens itself.
|
||||
|
||||
The `listen()` methods on `Ipc.Main` and `Ipc.Renderer` return a `Disposer`, or more specifically, a `() => void`.
|
||||
The `listen()` methods on `Main.Ipc` and `Renderer.Ipc` return a `Disposer`, or more specifically, a `() => void`.
|
||||
This can be optionally called to remove the listener early.
|
||||
|
||||
Calling either `IpcRenderer.getInstance().broadcast(...)` or `IpcMain.getInstance().broadcast(...)` sends an event to all `renderer` frames and to `main`.
|
||||
|
||||
@ -18,7 +18,7 @@ First thing we need to do with our extension is to register new menu item in the
|
||||
We will do this in our extension class `CrdSampleExtension` that is derived `LensRendererExtension` class:
|
||||
|
||||
```typescript
|
||||
export default class CrdSampleExtension extends LensRendererExtension {
|
||||
export default class CrdSampleExtension extends Renderer.LensExtension {
|
||||
}
|
||||
```
|
||||
|
||||
@ -27,11 +27,21 @@ This object will register a menu item with "Certificates" text.
|
||||
It will also use `CertificateIcon` component to render an icon and navigate to cluster page that is having `certificates` page id.
|
||||
|
||||
```typescript
|
||||
export function CertificateIcon(props: Component.IconProps) {
|
||||
return <Component.Icon {...props} material="security" tooltip="Certificates"/>
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
|
||||
type IconProps = Renderer.Component.IconProps;
|
||||
|
||||
const {
|
||||
Component: {
|
||||
Icon,
|
||||
},
|
||||
} = Renderer;
|
||||
|
||||
export function CertificateIcon(props: IconProps) {
|
||||
return <Icon {...props} material="security" tooltip="Certificates"/>
|
||||
}
|
||||
|
||||
export default class CrdSampleExtension extends LensRendererExtension {
|
||||
export default class CrdSampleExtension extends Renderer.LensExtension {
|
||||
|
||||
clusterPageMenus = [
|
||||
{
|
||||
@ -48,7 +58,7 @@ export default class CrdSampleExtension extends LensRendererExtension {
|
||||
Then we need to register `PageRegistration` object with `certificates` id and define `CertificatePage` component to render certificates.
|
||||
|
||||
```typescript
|
||||
export default class CrdSampleExtension extends LensRendererExtension {
|
||||
export default class CrdSampleExtension extends Renderer.LensExtension {
|
||||
...
|
||||
|
||||
clusterPages = [{
|
||||
@ -65,18 +75,29 @@ export default class CrdSampleExtension extends LensRendererExtension {
|
||||
|
||||
In the previous step we defined `CertificatePage` component to render certificates.
|
||||
In this step we will actually implement that.
|
||||
`CertificatePage` is a React component that will render `Component.KubeObjectListLayout` component to list `Certificate` CRD objects.
|
||||
`CertificatePage` is a React component that will render `Renderer.Component.KubeObjectListLayout` component to list `Certificate` CRD objects.
|
||||
|
||||
### Get CRD objects
|
||||
|
||||
In order to list CRD objects, we need first fetch those from Kubernetes API.
|
||||
Lens Extensions API provides easy mechanism to do this.
|
||||
We just need to define `Certificate` class derived from `K8sApi.KubeObject`, `CertificatesApi`derived from `K8sApi.KubeApi` and `CertificatesStore` derived from `K8sApi.KubeObjectStore`.
|
||||
We just need to define `Certificate` class derived from `Renderer.K8sApi.KubeObject`, `CertificatesApi`derived from `Renderer.K8sApi.KubeApi` and `CertificatesStore` derived from `Renderer.K8sApi.KubeObjectStore`.
|
||||
|
||||
`Certificate` class defines properties found in the CRD object:
|
||||
|
||||
```typescript
|
||||
export class Certificate extends K8sApi.KubeObject {
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
|
||||
const {
|
||||
K8sApi: {
|
||||
KubeObject,
|
||||
KubeObjectStore,
|
||||
KubeApi,
|
||||
apiManager,
|
||||
},
|
||||
} = Renderer;
|
||||
|
||||
export class Certificate extends KubeObject {
|
||||
static kind = "Certificate"
|
||||
static namespaced = true
|
||||
static apiBase = "/apis/cert-manager.io/v1alpha2/certificates"
|
||||
@ -121,8 +142,8 @@ export class Certificate extends K8sApi.KubeObject {
|
||||
With `CertificatesApi` class we are able to manage `Certificate` objects in Kubernetes API:
|
||||
|
||||
```typescript
|
||||
export class CertificatesApi extends K8sApi.KubeApi<Certificate> {
|
||||
}
|
||||
export class CertificatesApi extends KubeApi<Certificate> {}
|
||||
|
||||
export const certificatesApi = new CertificatesApi({
|
||||
objectConstructor: Certificate
|
||||
});
|
||||
@ -131,7 +152,7 @@ export const certificatesApi = new CertificatesApi({
|
||||
`CertificateStore` defines storage for `Certificate` objects
|
||||
|
||||
```typescript
|
||||
export class CertificatesStore extends K8sApi.KubeObjectStore<Certificate> {
|
||||
export class CertificatesStore extends KubeObjectStore<Certificate> {
|
||||
api = certificatesApi
|
||||
}
|
||||
|
||||
@ -141,7 +162,7 @@ export const certificatesStore = new CertificatesStore();
|
||||
And, finally, we register this store to Lens's API manager.
|
||||
|
||||
```typescript
|
||||
K8sApi.apiManager.registerStore(certificatesStore);
|
||||
apiManager.registerStore(certificatesStore);
|
||||
```
|
||||
|
||||
|
||||
@ -153,23 +174,32 @@ Then we need to fetch those and render them in the UI.
|
||||
First we define `CertificatePage` class that extends `React.Component`.
|
||||
|
||||
```typescript
|
||||
import { Component, LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import React from "react";
|
||||
import { certificatesStore } from "../certificate-store";
|
||||
import { Certificate } from "../certificate"
|
||||
|
||||
export class CertificatePage extends React.Component<{ extension: LensRendererExtension }> {
|
||||
export class CertificatePage extends React.Component<{ extension: Renderer.LensExtension }> {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Next we will implement `render` method that will display certificates in a list.
|
||||
To do that, we just need to add `Component.KubeObjectListLayout` component inside `Component.TabLayout` component in render method.
|
||||
To define which objects the list is showing, we need to pass `certificateStore` object to `Component.KubeObjectListLayout` in `store` property.
|
||||
`Component.KubeObjectListLayout` will fetch automatically items from the given store when component is mounted.
|
||||
To do that, we just need to add `Renderer.Component.KubeObjectListLayout` component inside `Renderer.Component.TabLayout` component in render method.
|
||||
To define which objects the list is showing, we need to pass `certificateStore` object to `Renderer.Component.KubeObjectListLayout` in `store` property.
|
||||
`Renderer.Component.KubeObjectListLayout` will fetch automatically items from the given store when component is mounted.
|
||||
Also, we can define needed sorting callbacks and search filters for the list:
|
||||
|
||||
```typescript
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
|
||||
const {
|
||||
Component: {
|
||||
TabLayout,
|
||||
KubeObjectListLayout,
|
||||
},
|
||||
} = Renderer;
|
||||
|
||||
enum sortBy {
|
||||
name = "name",
|
||||
namespace = "namespace",
|
||||
@ -181,8 +211,8 @@ export class CertificatePage extends React.Component<{ extension: LensRendererEx
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Component.TabLayout>
|
||||
<Component.KubeObjectListLayout
|
||||
<TabLayout>
|
||||
<KubeObjectListLayout
|
||||
className="Certicates" store={certificatesStore}
|
||||
sortingCallbacks={{
|
||||
[sortBy.name]: (certificate: Certificate) => certificate.getName(),
|
||||
@ -204,7 +234,7 @@ export class CertificatePage extends React.Component<{ extension: LensRendererEx
|
||||
certificate.spec.issuerRef.name
|
||||
]}
|
||||
/>
|
||||
</Component.TabLayout>
|
||||
</TabLayout>
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -219,7 +249,7 @@ First, we need to register our custom component to render details for the specif
|
||||
We will do this again in `CrdSampleExtension` class:
|
||||
|
||||
```typescript
|
||||
export default class CrdSampleExtension extends LensRendererExtension {
|
||||
export default class CrdSampleExtension extends Renderer.LensExtension {
|
||||
//...
|
||||
|
||||
kubeObjectDetailItems = [{
|
||||
@ -235,14 +265,22 @@ export default class CrdSampleExtension extends LensRendererExtension {
|
||||
Here we defined that `CertificateDetails` component will render the resource details.
|
||||
So, next we need to implement that component.
|
||||
Lens will inject `Certificate` object into our component so we just need to render some information out of it.
|
||||
We can use `Component.DrawerItem` component from Lens Extensions API to give the same look and feel as Lens is using elsewhere:
|
||||
We can use `Renderer.Component.DrawerItem` component from Lens Extensions API to give the same look and feel as Lens is using elsewhere:
|
||||
|
||||
```typescript
|
||||
import { Component, K8sApi } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import React from "react";
|
||||
import { Certificate } from "../certificate";
|
||||
|
||||
export interface CertificateDetailsProps extends Component.KubeObjectDetailsProps<Certificate>{
|
||||
const {
|
||||
Component: {
|
||||
KubeObjectDetailsProps,
|
||||
DrawerItem,
|
||||
Badge,
|
||||
}
|
||||
} = Renderer;
|
||||
|
||||
export interface CertificateDetailsProps extends KubeObjectDetailsProps<Certificate>{
|
||||
}
|
||||
|
||||
export class CertificateDetails extends React.Component<CertificateDetailsProps> {
|
||||
@ -252,29 +290,29 @@ export class CertificateDetails extends React.Component<CertificateDetailsProps>
|
||||
if (!certificate) return null;
|
||||
return (
|
||||
<div className="Certificate">
|
||||
<Component.DrawerItem name="Created">
|
||||
<DrawerItem name="Created">
|
||||
{certificate.getAge(true, false)} ago ({certificate.metadata.creationTimestamp })
|
||||
</Component.DrawerItem>
|
||||
<Component.DrawerItem name="DNS Names">
|
||||
</DrawerItem>
|
||||
<DrawerItem name="DNS Names">
|
||||
{certificate.spec.dnsNames.join(",")}
|
||||
</Component.DrawerItem>
|
||||
<Component.DrawerItem name="Secret">
|
||||
</DrawerItem>
|
||||
<DrawerItem name="Secret">
|
||||
{certificate.spec.secretName}
|
||||
</Component.DrawerItem>
|
||||
<Component.DrawerItem name="Status" className="status" labelsOnly>
|
||||
</DrawerItem>
|
||||
<DrawerItem name="Status" className="status" labelsOnly>
|
||||
{certificate.status.conditions.map((condition, index) => {
|
||||
const { type, reason, message, status } = condition;
|
||||
const kind = type || reason;
|
||||
if (!kind) return null;
|
||||
return (
|
||||
<Component.Badge
|
||||
<Badge
|
||||
key={kind + index} label={kind}
|
||||
className={"success "+kind.toLowerCase()}
|
||||
tooltip={message}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Component.DrawerItem>
|
||||
</DrawerItem>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -11,9 +11,9 @@ The Main Extension API allows you to access, configure, and customize Lens data,
|
||||
To create a main extension simply extend the `LensMainExtension` class:
|
||||
|
||||
```typescript
|
||||
import { LensMainExtension } from "@k8slens/extensions";
|
||||
import { Main } from "@k8slens/extensions";
|
||||
|
||||
export default class ExampleExtensionMain extends LensMainExtension {
|
||||
export default class ExampleExtensionMain extends Main.LensExtension {
|
||||
onActivate() {
|
||||
console.log('custom main process extension code started');
|
||||
}
|
||||
@ -39,34 +39,6 @@ The example above logs messages when the extension is enabled and disabled.
|
||||
To see standard output from the main process there must be a console connected to it.
|
||||
Achieve this by starting Lens from the command prompt.
|
||||
|
||||
The following example is a little more interesting.
|
||||
It accesses some Lens state data, and it periodically logs the name of the cluster that is currently active in Lens.
|
||||
|
||||
```typescript
|
||||
import { LensMainExtension, Store } from "@k8slens/extensions";
|
||||
|
||||
export default class ActiveClusterExtensionMain extends LensMainExtension {
|
||||
|
||||
timer: NodeJS.Timeout
|
||||
|
||||
onActivate() {
|
||||
console.log("Cluster logger activated");
|
||||
this.timer = setInterval(() => {
|
||||
if (!Store.ClusterStore.getInstance().active) {
|
||||
console.log("No active cluster");
|
||||
return;
|
||||
}
|
||||
console.log("active cluster is", Store.ClusterStore.getInstance().active.contextName)
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
onDeactivate() {
|
||||
clearInterval(this.timer)
|
||||
console.log("Cluster logger deactivated");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more details on accessing Lens state data, please see the [Stores](../stores) guide.
|
||||
|
||||
### `appMenus`
|
||||
@ -76,9 +48,9 @@ Note that this is the only UI feature that the Main Extension API allows you to
|
||||
The following example demonstrates adding an item to the **Help** menu.
|
||||
|
||||
``` typescript
|
||||
import { LensMainExtension } from "@k8slens/extensions";
|
||||
import { Main } from "@k8slens/extensions";
|
||||
|
||||
export default class SamplePageMainExtension extends LensMainExtension {
|
||||
export default class SamplePageMainExtension extends Main.LensExtension {
|
||||
appMenus = [
|
||||
{
|
||||
parentId: "help",
|
||||
@ -102,4 +74,4 @@ Valid values include: `"file"`, `"edit"`, `"view"`, and `"help"`.
|
||||
* `click()` is called when the menu item is selected.
|
||||
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 [`LensRendererExtension`](renderer-extension.md) class and can be defined in the process of extending it.
|
||||
Note that pages are associated with the [`Renderer.LensExtension`](renderer-extension.md) class and can be defined in the process of extending it.
|
||||
|
||||
@ -18,13 +18,13 @@ In other words, which handler is selected in either process is independent from
|
||||
Example of registering a handler:
|
||||
|
||||
```typescript
|
||||
import { LensMainExtension, Interface } from "@k8slens/extensions";
|
||||
import { Main, Common } from "@k8slens/extensions";
|
||||
|
||||
function rootHandler(params: Iterface.ProtocolRouteParams) {
|
||||
function rootHandler(params: Common.Types.ProtocolRouteParams) {
|
||||
console.log("routed to ExampleExtension", params);
|
||||
}
|
||||
|
||||
export default class ExampleExtensionMain extends LensMainExtension {
|
||||
export default class ExampleExtensionMain extends Main.LensExtension {
|
||||
protocolHandlers = [
|
||||
pathSchema: "/",
|
||||
handler: rootHandler,
|
||||
|
||||
@ -24,9 +24,9 @@ All UI elements are based on React components.
|
||||
To create a renderer extension, extend the `LensRendererExtension` class:
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
|
||||
export default class ExampleExtensionMain extends LensRendererExtension {
|
||||
export default class ExampleExtensionMain extends Renderer.LensExtension {
|
||||
onActivate() {
|
||||
console.log('custom renderer process extension code started');
|
||||
}
|
||||
@ -61,11 +61,11 @@ Use your extension to access Kubernetes resources in the active cluster with [`C
|
||||
Add a cluster page definition to a `LensRendererExtension` subclass with the following example:
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { ExampleIcon, ExamplePage } from "./page"
|
||||
import React from "react"
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
clusterPages = [
|
||||
{
|
||||
id: "hello",
|
||||
@ -88,7 +88,7 @@ It offers flexibility in defining the appearance and behavior of your page.
|
||||
`ExamplePage` in the example above can be defined in `page.tsx`:
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import React from "react"
|
||||
|
||||
export class ExamplePage extends React.Component<{ extension: LensRendererExtension }> {
|
||||
@ -116,11 +116,11 @@ Use `clusterPageMenus`, covered in the next section, to add cluster pages to the
|
||||
By expanding on the above example, you can add a cluster page menu item to the `ExampleExtension` definition:
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { ExampleIcon, ExamplePage } from "./page"
|
||||
import React from "react"
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
clusterPages = [
|
||||
{
|
||||
id: "hello",
|
||||
@ -157,14 +157,20 @@ When users click **Hello World**, the cluster dashboard will show the contents o
|
||||
This example requires the definition of another React-based component, `ExampleIcon`, which has been added to `page.tsx`, as follows:
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension, Component } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import React from "react"
|
||||
|
||||
export function ExampleIcon(props: Component.IconProps) {
|
||||
return <Component.Icon {...props} material="pages" tooltip={"Hi!"}/>
|
||||
type IconProps = Renderer.Component.IconProps;
|
||||
|
||||
const {
|
||||
Component: { Icon },
|
||||
} = Renderer;
|
||||
|
||||
export function ExampleIcon(props: IconProps) {
|
||||
return <Icon {...props} material="pages" tooltip={"Hi!"}/>
|
||||
}
|
||||
|
||||
export class ExamplePage extends React.Component<{ extension: LensRendererExtension }> {
|
||||
export class ExamplePage extends React.Component<{ extension: Renderer.LensExtension }> {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
@ -176,8 +182,8 @@ export class ExamplePage extends React.Component<{ extension: LensRendererExtens
|
||||
```
|
||||
|
||||
Lens includes various built-in components available for extension developers to use.
|
||||
One of these is the `Component.Icon`, introduced in `ExampleIcon`, which you can use to access any of the [icons](https://material.io/resources/icons/) available at [Material Design](https://material.io).
|
||||
The properties that `Component.Icon` uses are defined as follows:
|
||||
One of these is the `Renderer.Component.Icon`, introduced in `ExampleIcon`, which you can use to access any of the [icons](https://material.io/resources/icons/) available at [Material Design](https://material.io).
|
||||
The properties that `Renderer.Component.Icon` uses are defined as follows:
|
||||
|
||||
* `material` takes the name of the icon you want to use.
|
||||
* `tooltip` sets the text you want to appear when a user hovers over the icon.
|
||||
@ -187,11 +193,11 @@ The following example groups two sub menu items under one parent menu item:
|
||||
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { ExampleIcon, ExamplePage } from "./page"
|
||||
import React from "react"
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
clusterPages = [
|
||||
{
|
||||
id: "hello",
|
||||
@ -261,11 +267,11 @@ Unlike cluster pages, users can trigger global pages even when there is no activ
|
||||
The following example defines a `LensRendererExtension` subclass with a single global page definition:
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension } from '@k8slens/extensions';
|
||||
import { Renderer } from '@k8slens/extensions';
|
||||
import { HelpPage } from './page';
|
||||
import React from 'react';
|
||||
|
||||
export default class HelpExtension extends LensRendererExtension {
|
||||
export default class HelpExtension extends Renderer.LensExtension {
|
||||
globalPages = [
|
||||
{
|
||||
id: "help",
|
||||
@ -288,7 +294,7 @@ It offers flexibility in defining the appearance and behavior of your page.
|
||||
`HelpPage` in the example above can be defined in `page.tsx`:
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import React from "react"
|
||||
|
||||
export class HelpPage extends React.Component<{ extension: LensRendererExtension }> {
|
||||
@ -320,11 +326,11 @@ Global pages can be made available in the following ways:
|
||||
By expanding on the above example, you can add a global page menu item to the `HelpExtension` definition:
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { HelpIcon, HelpPage } from "./page"
|
||||
import React from "react"
|
||||
|
||||
export default class HelpExtension extends LensRendererExtension {
|
||||
export default class HelpExtension extends Renderer.LensExtension {
|
||||
globalPages = [
|
||||
{
|
||||
id: "help",
|
||||
@ -362,14 +368,20 @@ 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 { LensRendererExtension, Component } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import React from "react"
|
||||
|
||||
export function HelpIcon(props: Component.IconProps) {
|
||||
return <Component.Icon {...props} material="help"/>
|
||||
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: LensRendererExtension }> {
|
||||
export class HelpPage extends React.Component<{ extension: Renderer.LensExtension }> {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
@ -381,8 +393,8 @@ export class HelpPage extends React.Component<{ extension: LensRendererExtension
|
||||
```
|
||||
|
||||
Lens includes various built-in components available for extension developers to use.
|
||||
One of these is the `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 `Component.Icon` uses is defined as follows:
|
||||
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.
|
||||
|
||||
@ -401,11 +413,11 @@ They can be installed and uninstalled by the Lens user from the cluster **Settin
|
||||
The following example shows how to add a cluster feature as part of a `LensRendererExtension`:
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions"
|
||||
import { Renderer } from "@k8slens/extensions"
|
||||
import { ExampleFeature } from "./src/example-feature"
|
||||
import React from "react"
|
||||
|
||||
export default class ExampleFeatureExtension extends LensRendererExtension {
|
||||
export default class ExampleFeatureExtension extends Renderer.LensExtension {
|
||||
clusterFeatures = [
|
||||
{
|
||||
title: "Example Feature",
|
||||
@ -462,45 +474,69 @@ Consider using the following properties with `updateStatus()`:
|
||||
The following shows a very simple implementation of a `ClusterFeature`:
|
||||
|
||||
```typescript
|
||||
import { ClusterFeature, Store, K8sApi } from "@k8slens/extensions";
|
||||
import { Renderer, Common } from "@k8slens/extensions";
|
||||
import * as path from "path";
|
||||
|
||||
export class ExampleFeature extends ClusterFeature.Feature {
|
||||
const {
|
||||
K8sApi: {
|
||||
ResourceStack,
|
||||
forCluster,
|
||||
StorageClass,
|
||||
Namespace,
|
||||
}
|
||||
} = Renderer;
|
||||
|
||||
async install(cluster: Store.Cluster): Promise<void> {
|
||||
type ResourceStack = Renderer.K8sApi.ResourceStack;
|
||||
type Pod = Renderer.K8sApi.Pod;
|
||||
type KubernetesCluster = Common.Catalog.KubernetesCluster;
|
||||
|
||||
super.applyResources(cluster, path.join(__dirname, "../resources/"));
|
||||
export interface MetricsStatus {
|
||||
installed: boolean;
|
||||
canUpgrade: boolean;
|
||||
}
|
||||
|
||||
export class ExampleFeature {
|
||||
protected stack: ResourceStack;
|
||||
|
||||
constructor(protected cluster: KubernetesCluster) {
|
||||
this.stack = new ResourceStack(cluster, this.name);
|
||||
}
|
||||
|
||||
async upgrade(cluster: Store.Cluster): Promise<void> {
|
||||
return this.install(cluster);
|
||||
install(): Promise<string> {
|
||||
return this.stack.kubectlApplyFolder(path.join(__dirname, "../resources/"));
|
||||
}
|
||||
|
||||
async updateStatus(cluster: Store.Cluster): Promise<ClusterFeature.FeatureStatus> {
|
||||
upgrade(): Promise<string> {
|
||||
return this.install(config);
|
||||
}
|
||||
|
||||
async getStatus(): Promise<MetricsStatus> {
|
||||
const status: MetricsStatus = { installed: false, canUpgrade: false};
|
||||
|
||||
try {
|
||||
const pod = K8sApi.forCluster(cluster, K8sApi.Pod);
|
||||
const pod = forCluster(cluster, Pod);
|
||||
const examplePod = await pod.get({name: "example-pod", namespace: "default"});
|
||||
|
||||
if (examplePod?.kind) {
|
||||
this.status.installed = true;
|
||||
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
|
||||
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 {
|
||||
this.status.installed = false;
|
||||
this.status.canUpgrade = false;
|
||||
status.installed = false;
|
||||
status.canUpgrade = false;
|
||||
}
|
||||
} catch(e) {
|
||||
if (e?.error?.code === 404) {
|
||||
this.status.installed = false;
|
||||
this.status.canUpgrade = false;
|
||||
status.installed = false;
|
||||
status.canUpgrade = false;
|
||||
}
|
||||
}
|
||||
|
||||
return this.status;
|
||||
return status;
|
||||
}
|
||||
|
||||
async uninstall(cluster: Store.Cluster): Promise<void> {
|
||||
const podApi = K8sApi.forCluster(cluster, K8sApi.Pod);
|
||||
await podApi.delete({name: "example-pod", namespace: "default"});
|
||||
async uninstall(): Promise<string> {
|
||||
return this.stack.kubectlDeleteFolder(this.resourceFolder);
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -539,12 +575,12 @@ You can use Lens extensions to add custom preferences to the Preferences page, p
|
||||
The following example demonstrates adding a custom preference:
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { ExamplePreferenceHint, ExamplePreferenceInput } from "./src/example-preference";
|
||||
import { observable } from "mobx";
|
||||
import React from "react";
|
||||
|
||||
export default class ExampleRendererExtension extends LensRendererExtension {
|
||||
export default class ExampleRendererExtension extends Renderer.LensExtension {
|
||||
|
||||
@observable preference = { enabled: false };
|
||||
|
||||
@ -578,10 +614,16 @@ This is how `ExampleRendererExtension` handles the state of the preference input
|
||||
In this example `ExamplePreferenceInput`, `ExamplePreferenceHint`, and `ExamplePreferenceProps` are defined in `./src/example-preference.tsx` as follows:
|
||||
|
||||
```typescript
|
||||
import { Component } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
|
||||
const {
|
||||
Component: {
|
||||
Checkbox,
|
||||
},
|
||||
} = Renderer;
|
||||
|
||||
export class ExamplePreferenceProps {
|
||||
preference: {
|
||||
enabled: boolean;
|
||||
@ -594,7 +636,7 @@ export class ExamplePreferenceInput extends React.Component<ExamplePreferencePro
|
||||
render() {
|
||||
const { preference } = this.props;
|
||||
return (
|
||||
<Component.Checkbox
|
||||
<Checkbox
|
||||
label="I understand appPreferences"
|
||||
value={preference.enabled}
|
||||
onChange={v => { preference.enabled = v; }}
|
||||
@ -612,7 +654,7 @@ export class ExamplePreferenceHint extends React.Component {
|
||||
}
|
||||
```
|
||||
|
||||
`ExamplePreferenceInput` implements a simple checkbox using Lens's `Component.Checkbox` using the following properties:
|
||||
`ExamplePreferenceInput` implements a simple checkbox using Lens's `Renderer.Component.Checkbox` using the following properties:
|
||||
|
||||
* `label` sets the text that displays next to the checkbox.
|
||||
* `value` is initially set to `preference.enabled`.
|
||||
@ -645,11 +687,11 @@ The following example adds a `statusBarItems` definition and a `globalPages` def
|
||||
It configures the status bar item to navigate to the global page upon activation (normally a mouse click):
|
||||
|
||||
```typescript
|
||||
import { LensRendererExtension } from '@k8slens/extensions';
|
||||
import { Renderer } from '@k8slens/extensions';
|
||||
import { HelpIcon, HelpPage } from "./page"
|
||||
import React from 'react';
|
||||
|
||||
export default class HelpExtension extends LensRendererExtension {
|
||||
export default class HelpExtension extends Renderer.LensExtension {
|
||||
globalPages = [
|
||||
{
|
||||
id: "help",
|
||||
@ -703,16 +745,19 @@ The following example shows how to add a `kubeObjectMenuItems` for namespace res
|
||||
|
||||
```typescript
|
||||
import React from "react"
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { NamespaceMenuItem } from "./src/namespace-menu-item"
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
type KubeObjectMenuProps = Renderer.Component.KubeObjectMenuProps;
|
||||
type Namespace = Renderer.K8sApi.Namespace;
|
||||
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
kubeObjectMenuItems = [
|
||||
{
|
||||
kind: "Namespace",
|
||||
apiVersions: ["v1"],
|
||||
components: {
|
||||
MenuItem: (props: Component.KubeObjectMenuProps<K8sApi.Namespace>) => <NamespaceMenuItem {...props} />
|
||||
MenuItem: (props: KubeObjectMenuProps<Namespace>) => <NamespaceMenuItem {...props} />
|
||||
}
|
||||
}
|
||||
];
|
||||
@ -734,16 +779,28 @@ In this example a `NamespaceMenuItem` object is returned.
|
||||
|
||||
```typescript
|
||||
import React from "react";
|
||||
import { Component, K8sApi, Navigation} from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
|
||||
export function NamespaceMenuItem(props: Component.KubeObjectMenuProps<K8sApi.Namespace>) {
|
||||
const {
|
||||
Component: {
|
||||
terminalStore,
|
||||
MenuItem,
|
||||
Icon,
|
||||
},
|
||||
Navigation,
|
||||
} = Renderer;
|
||||
|
||||
type KubeObjectMenuProps = Renderer.Component.KubeObjectMenuProps;
|
||||
type Namespace = Renderer.K8sApi.Namespace;
|
||||
|
||||
export function NamespaceMenuItem(props: KubeObjectMenuProps<Namespace>) {
|
||||
const { object: namespace, toolbar } = props;
|
||||
if (!namespace) return null;
|
||||
|
||||
const namespaceName = namespace.getName();
|
||||
|
||||
const sendToTerminal = (command: string) => {
|
||||
Component.terminalStore.sendCommand(command, {
|
||||
terminalStore.sendCommand(command, {
|
||||
enter: true,
|
||||
newTab: true,
|
||||
});
|
||||
@ -755,21 +812,21 @@ export function NamespaceMenuItem(props: Component.KubeObjectMenuProps<K8sApi.Na
|
||||
};
|
||||
|
||||
return (
|
||||
<Component.MenuItem onClick={getPods}>
|
||||
<Component.Icon material="speaker_group" interactive={toolbar} title="Get pods in terminal"/>
|
||||
<span className="title">Get Pods</span>
|
||||
</Component.MenuItem>
|
||||
<MenuItem onClick={getPods}>
|
||||
<Icon material="speaker_group" interactive={toolbar} title="Get pods in terminal"/>
|
||||
<span className="title">Get Pods</span>
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
`NamespaceMenuItem` returns a `Component.MenuItem` which defines the menu item's appearance and its behavior when activated via the `onClick` property.
|
||||
`NamespaceMenuItem` returns a `Renderer.Component.MenuItem` which defines the menu item's appearance and its behavior when activated via the `onClick` property.
|
||||
In the example, `getPods()` opens a terminal tab and runs `kubectl` to get a list of pods running in the current namespace.
|
||||
|
||||
The name of the namespace is retrieved from `props` passed into `NamespaceMenuItem()`.
|
||||
`namespace` is the `props.object`, which is of type `K8sApi.Namespace`.
|
||||
`K8sApi.Namespace` is the API for accessing namespaces.
|
||||
`namespace` is the `props.object`, which is of type `Renderer.K8sApi.Namespace`.
|
||||
`Renderer.K8sApi.Namespace` is the API for accessing namespaces.
|
||||
The current namespace in this example is simply given by `namespace.getName()`.
|
||||
Thus, `kubeObjectMenuItems` afford convenient access to the specific resource selected by the user.
|
||||
|
||||
@ -783,18 +840,22 @@ These custom details appear on the details page for a specific resource, such as
|
||||
The following example shows how to use `kubeObjectDetailItems` to add a tabulated list of pods to the Namespace resource details page:
|
||||
|
||||
```typescript
|
||||
import React from "react"
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { NamespaceDetailsItem } from "./src/namespace-details-item"
|
||||
import React from "react";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { NamespaceDetailsItem } from "./src/namespace-details-item";
|
||||
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
type KubeObjectMenuProps = Renderer.Component.KubeObjectMenuProps;
|
||||
type KubeObjectDetailsProps = Renderer.Component.KubeObjectDetailsProps;
|
||||
type Namespace = Renderer.K8sApi.Namespace;
|
||||
|
||||
export default class ExampleExtension extends Renderer.LensExtension {
|
||||
kubeObjectDetailItems = [
|
||||
{
|
||||
kind: "Namespace",
|
||||
apiVersions: ["v1"],
|
||||
priority: 10,
|
||||
components: {
|
||||
Details: (props: Component.KubeObjectDetailsProps<K8sApi.Namespace>) => <NamespaceDetailsItem {...props} />
|
||||
Details: (props: KubeObjectDetailsProps<Namespace>) => <NamespaceDetailsItem {...props} />
|
||||
}
|
||||
}
|
||||
];
|
||||
@ -814,25 +875,39 @@ In this example a `NamespaceDetailsItem` object is returned.
|
||||
`NamespaceDetailsItem` is defined in `./src/namespace-details-item.tsx`:
|
||||
|
||||
``` typescript
|
||||
import { Component, K8sApi } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { PodsDetailsList } from "./pods-details-list";
|
||||
import React from "react";
|
||||
import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
@observer
|
||||
export class NamespaceDetailsItem extends React.Component<Component.KubeObjectDetailsProps<K8sApi.Namespace>> {
|
||||
const {
|
||||
K8sApi: {
|
||||
podsApi,
|
||||
},
|
||||
Component: {
|
||||
DrawerTitle,
|
||||
},
|
||||
} = Renderer;
|
||||
|
||||
@observable private pods: K8sApi.Pod[];
|
||||
type KubeObjectMenuProps = Renderer.Component.KubeObjectMenuProps;
|
||||
type Namespace = Renderer.K8sApi.Namespace;
|
||||
type Pod = Renderer.K8sApi.Pod;
|
||||
|
||||
@observer
|
||||
export class NamespaceDetailsItem extends React.Component<KubeObjectDetailsProps<Namespace>> {
|
||||
@observable private pods: Pod[];
|
||||
|
||||
async componentDidMount() {
|
||||
this.pods = await K8sApi.podsApi.list({namespace: this.props.object.getName()});
|
||||
const namespace = this.props.object.getName();
|
||||
|
||||
this.pods = await podsApi.list({ namespace });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Component.DrawerTitle title="Pods" />
|
||||
<DrawerTitle title="Pods" />
|
||||
<PodsDetailsList pods={this.pods}/>
|
||||
</div>
|
||||
)
|
||||
@ -840,14 +915,14 @@ export class NamespaceDetailsItem extends React.Component<Component.KubeObjectDe
|
||||
}
|
||||
```
|
||||
|
||||
Since `NamespaceDetailsItem` extends `React.Component<Component.KubeObjectDetailsProps<K8sApi.Namespace>>`, it can access the current namespace object (type `K8sApi.Namespace`) through `this.props.object`.
|
||||
Since `NamespaceDetailsItem` extends `React.Component<KubeObjectDetailsProps<Namespace>>`, it can access the current namespace object (type `Namespace`) through `this.props.object`.
|
||||
You can query this object for many details about the current namespace.
|
||||
In the example above, `componentDidMount()` gets the namespace's name using the `K8sApi.Namespace` `getName()` method.
|
||||
In the example above, `componentDidMount()` gets the namespace's name using the `Namespace` `getName()` method.
|
||||
Use the namespace's name to limit the list of pods only to those in the relevant namespace.
|
||||
To get this list of pods, this example uses the Kubernetes pods API `K8sApi.podsApi.list()` method.
|
||||
The `K8sApi.podsApi` is automatically configured for the active cluster.
|
||||
To get this list of pods, this example uses the Kubernetes pods API `podsApi.list()` method.
|
||||
The `podsApi` is automatically configured for the active cluster.
|
||||
|
||||
Note that `K8sApi.podsApi.list()` is an asynchronous method.
|
||||
Note that `podsApi.list()` is an asynchronous method.
|
||||
Getting the pods list should occur prior to rendering the `NamespaceDetailsItem`.
|
||||
It is a common technique in React development to await async calls in `componentDidMount()`.
|
||||
However, `componentDidMount()` is called right after the first call to `render()`.
|
||||
@ -856,51 +931,59 @@ Like in the [`appPreferences` guide](#apppreferences), [`mobx`](https://mobx.js.
|
||||
This is done simply by marking the `pods` field as an `observable` and the `NamespaceDetailsItem` class itself as an `observer`.
|
||||
|
||||
Finally, the `NamespaceDetailsItem` renders using the `render()` method.
|
||||
Details are placed in drawers, and using `Component.DrawerTitle` provides a separator from details above this one.
|
||||
Multiple details in a drawer can be placed in `<Component.DrawerItem>` elements for further separation, if desired.
|
||||
Details are placed in drawers, and using `Renderer.Component.DrawerTitle` provides a separator from details above this one.
|
||||
Multiple details in a drawer can be placed in `<Renderer.Component.DrawerItem>` elements for further separation, if desired.
|
||||
The rest of this example's details are defined in `PodsDetailsList`, found in `./pods-details-list.tsx`:
|
||||
|
||||
``` typescript
|
||||
import React from "react";
|
||||
import { Component, K8sApi } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
|
||||
const {
|
||||
Component: {
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
Table,
|
||||
},
|
||||
} = Renderer;
|
||||
|
||||
type Pod = Renderer.K8sApi.Pod;
|
||||
|
||||
interface Props {
|
||||
pods: K8sApi.Pod[];
|
||||
pods?: Pod[];
|
||||
}
|
||||
|
||||
export class PodsDetailsList extends React.Component<Props> {
|
||||
|
||||
getTableRow(index: number) {
|
||||
const {pods} = this.props;
|
||||
return (
|
||||
<Component.TableRow key={index} nowrap>
|
||||
<Component.TableCell className="podName">{pods[index].getName()}</Component.TableCell>
|
||||
<Component.TableCell className="podAge">{pods[index].getAge()}</Component.TableCell>
|
||||
<Component.TableCell className="podStatus">{pods[index].getStatus()}</Component.TableCell>
|
||||
</Component.TableRow>
|
||||
)
|
||||
}
|
||||
getTableRow = (pod: Pod) => {
|
||||
return (
|
||||
<TableRow key={index} nowrap>
|
||||
<TableCell className="podName">{pods[index].getName()}</TableCell>
|
||||
<TableCell className="podAge">{pods[index].getAge()}</TableCell>
|
||||
<TableCell className="podStatus">{pods[index].getStatus()}</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
};
|
||||
|
||||
render() {
|
||||
const {pods} = this.props
|
||||
if (!pods?.length) {
|
||||
return null;
|
||||
}
|
||||
const { pods } = this.props
|
||||
|
||||
return (
|
||||
<div >
|
||||
<Component.Table>
|
||||
<Component.TableHead>
|
||||
<Component.TableCell className="podName">Name</Component.TableCell>
|
||||
<Component.TableCell className="podAge">Age</Component.TableCell>
|
||||
<Component.TableCell className="podStatus">Status</Component.TableCell>
|
||||
</Component.TableHead>
|
||||
{
|
||||
pods.map((pod, index) => this.getTableRow(index))
|
||||
}
|
||||
</Component.Table>
|
||||
</div>
|
||||
)
|
||||
if (!pods?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableCell className="podName">Name</TableCell>
|
||||
<TableCell className="podAge">Age</TableCell>
|
||||
<TableCell className="podStatus">Status</TableCell>
|
||||
</TableHead>
|
||||
{ pods.map(this.getTableRow) }
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -909,9 +992,9 @@ export class PodsDetailsList extends React.Component<Props> {
|
||||
|
||||

|
||||
|
||||
Obtain the name, age, and status for each pod using the `K8sApi.Pod` methods.
|
||||
Construct the table using the `Component.Table` and related elements.
|
||||
Obtain the name, age, and status for each pod using the `Renderer.K8sApi.Pod` methods.
|
||||
Construct the table using the `Renderer.Component.Table` and related elements.
|
||||
|
||||
For each pod the name, age, and status are obtained using the `K8sApi.Pod` methods.
|
||||
The table is constructed using the `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.
|
||||
|
||||
@ -24,14 +24,14 @@ This is so that your data is kept up to date and not persisted longer than expec
|
||||
The following example code creates a store for the `appPreferences` guide example:
|
||||
|
||||
``` typescript
|
||||
import { Store } from "@k8slens/extensions";
|
||||
import { Common } from "@k8slens/extensions";
|
||||
import { observable, makeObservable } from "mobx";
|
||||
|
||||
export type ExamplePreferencesModel = {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
export class ExamplePreferencesStore extends Store.ExtensionStore<ExamplePreferencesModel> {
|
||||
export class ExamplePreferencesStore extends Common.Store.ExtensionStore<ExamplePreferencesModel> {
|
||||
|
||||
@observable enabled = false;
|
||||
|
||||
@ -87,10 +87,10 @@ The following example code, modified from the [`appPreferences`](../renderer-ext
|
||||
This can be done in `./main.ts`:
|
||||
|
||||
``` typescript
|
||||
import { LensMainExtension } from "@k8slens/extensions";
|
||||
import { Main } from "@k8slens/extensions";
|
||||
import { ExamplePreferencesStore } from "./src/example-preference-store";
|
||||
|
||||
export default class ExampleMainExtension extends LensMainExtension {
|
||||
export default class ExampleMainExtension extends Main.LensExtension {
|
||||
async onActivate() {
|
||||
await ExamplePreferencesStore.getInstanceOrCreate().loadExtension(this);
|
||||
}
|
||||
@ -102,12 +102,12 @@ Similarly, `ExamplePreferencesStore` must load in the renderer process where the
|
||||
This can be done in `./renderer.ts`:
|
||||
|
||||
``` typescript
|
||||
import { LensRendererExtension } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { ExamplePreferenceHint, ExamplePreferenceInput } from "./src/example-preference";
|
||||
import { ExamplePreferencesStore } from "./src/example-preference-store";
|
||||
import React from "react";
|
||||
|
||||
export default class ExampleRendererExtension extends LensRendererExtension {
|
||||
export default class ExampleRendererExtension extends Renderer.LensExtension {
|
||||
|
||||
async onActivate() {
|
||||
await ExamplePreferencesStore.getInstanceOrCreate().loadExtension(this);
|
||||
@ -130,17 +130,23 @@ Again, `ExamplePreferencesStore.getInstanceOrCreate().loadExtension(this)` is ca
|
||||
`ExamplePreferenceInput` is defined in `./src/example-preference.tsx`:
|
||||
|
||||
``` typescript
|
||||
import { Component } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { ExamplePreferencesStore } from "./example-preference-store";
|
||||
|
||||
const {
|
||||
Component: {
|
||||
Checkbox,
|
||||
},
|
||||
} = Renderer;
|
||||
|
||||
@observer
|
||||
export class ExamplePreferenceInput extends React.Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Component.Checkbox
|
||||
<Checkbox
|
||||
label="I understand appPreferences"
|
||||
value={ExamplePreferencesStore.getInstace().enabled}
|
||||
onChange={v => { ExamplePreferencesStore.getInstace().enabled = v; }}
|
||||
|
||||
@ -14,7 +14,13 @@ My component `GlobalPageMenuIcon`
|
||||
|
||||
```typescript
|
||||
import React from "react"
|
||||
import { Component: { Icon } } from "@k8slens/extensions";
|
||||
import { Renderer } from "@k8slens/extensions";
|
||||
|
||||
const {
|
||||
Component: {
|
||||
Icon,
|
||||
},
|
||||
} = Renderer;
|
||||
|
||||
const GlobalPageMenuIcon = ({ navigate }: { navigate?: () => void }): JSX.Element => (
|
||||
<Icon
|
||||
|
||||
@ -37,7 +37,7 @@ export const isPublishConfigured = Object.keys(packageInfo.build).includes("publ
|
||||
|
||||
export const productName = packageInfo.productName;
|
||||
export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`;
|
||||
export const publicPath = "/build/";
|
||||
export const publicPath = "/build/" as string;
|
||||
|
||||
// Webpack build paths
|
||||
export const contextDir = process.cwd();
|
||||
@ -60,13 +60,13 @@ defineGlobal("__static", {
|
||||
});
|
||||
|
||||
// Apis
|
||||
export const apiPrefix = "/api"; // local router apis
|
||||
export const apiKubePrefix = "/api-kube"; // k8s cluster apis
|
||||
export const apiPrefix = "/api" as string; // local router apis
|
||||
export const apiKubePrefix = "/api-kube" as string; // k8s cluster apis
|
||||
|
||||
// Links
|
||||
export const issuesTrackerUrl = "https://github.com/lensapp/lens/issues";
|
||||
export const slackUrl = "https://join.slack.com/t/k8slens/shared_invite/enQtOTc5NjAyNjYyOTk4LWU1NDQ0ZGFkOWJkNTRhYTc2YjVmZDdkM2FkNGM5MjhiYTRhMDU2NDQ1MzIyMDA4ZGZlNmExOTc0N2JmY2M3ZGI";
|
||||
export const supportUrl = "https://docs.k8slens.dev/latest/support/";
|
||||
export const issuesTrackerUrl = "https://github.com/lensapp/lens/issues" as string;
|
||||
export const slackUrl = "https://join.slack.com/t/k8slens/shared_invite/enQtOTc5NjAyNjYyOTk4LWU1NDQ0ZGFkOWJkNTRhYTc2YjVmZDdkM2FkNGM5MjhiYTRhMDU2NDQ1MzIyMDA4ZGZlNmExOTc0N2JmY2M3ZGI" as string;
|
||||
export const supportUrl = "https://docs.k8slens.dev/latest/support/" as string;
|
||||
|
||||
// This explicitly ignores the prerelease info on the package version
|
||||
const { major, minor, patch } = new SemVer(packageInfo.version);
|
||||
|
||||
@ -20,11 +20,13 @@
|
||||
*/
|
||||
|
||||
import * as Catalog from "./catalog";
|
||||
import * as Navigation from "./navigation";
|
||||
import { IpcMain as Ipc } from "../ipc/ipc-main";
|
||||
import { LensMainExtension as LensExtension } from "../lens-main-extension";
|
||||
|
||||
export {
|
||||
Catalog,
|
||||
Navigation,
|
||||
Ipc,
|
||||
LensExtension,
|
||||
};
|
||||
|
||||
26
src/extensions/main-api/navigation.ts
Normal file
26
src/extensions/main-api/navigation.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { WindowManager } from "../../main/window-manager";
|
||||
|
||||
export function navigate(url: string) {
|
||||
return WindowManager.getInstance().navigate(url);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user