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

Merge branch 'master' into weblink-add-remove

This commit is contained in:
Jari Kolehmainen 2021-06-17 18:02:25 +03:00 committed by GitHub
commit 93d602a905
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 177 additions and 122 deletions

View File

@ -19,27 +19,26 @@ jobs:
node_12.x: node_12.x:
node_version: 12.x node_version: 12.x
steps: steps:
- displayName: Set the tag name as an environment variable - powershell: |
powershell: |
$CI_BUILD_TAG = git describe --tags $CI_BUILD_TAG = git describe --tags
Write-Output ("##vso[task.setvariable variable=CI_BUILD_TAG;]$CI_BUILD_TAG") Write-Output ("##vso[task.setvariable variable=CI_BUILD_TAG;]$CI_BUILD_TAG")
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
displayName: Set the tag name as an environment variable
- displayName: Install Node.js - task: NodeTool@0
task: NodeTool@0
inputs: inputs:
versionSpec: $(node_version) versionSpec: $(node_version)
displayName: Install Node.js
- displayName: Cache Yarn packages - task: Cache@2
task: Cache@2
inputs: inputs:
key: 'yarn | "$(Agent.OS)"" | yarn.lock' key: 'yarn | "$(Agent.OS)"" | yarn.lock'
restoreKeys: | restoreKeys: |
yarn | "$(Agent.OS)" yarn | "$(Agent.OS)"
path: $(YARN_CACHE_FOLDER) path: $(YARN_CACHE_FOLDER)
displayName: Cache Yarn packages
- displayName: Customize config - bash: |
bash: |
set -e set -e
git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay
rm -rf .lens-ide-overlay/.git rm -rf .lens-ide-overlay/.git
@ -47,15 +46,15 @@ jobs:
jq -s '.[0] * .[1]' package.json package.ide.json > package.custom.json && mv package.custom.json package.json jq -s '.[0] * .[1]' package.json package.ide.json > package.custom.json && mv package.custom.json package.json
env: env:
GH_TOKEN: $(LENS_IDE_GH_TOKEN) GH_TOKEN: $(LENS_IDE_GH_TOKEN)
displayName: Customize config
- displayName: Install dependencies - script: make node_modules
script: make node_modules displayName: Install dependencies
- displayName: Generate npm package - script: make build-npm
script: make build-npm displayName: Generate npm package
- displayName: Build - script: make build
script: make build
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
env: env:
WIN_CSC_LINK: $(WIN_CSC_LINK) WIN_CSC_LINK: $(WIN_CSC_LINK)
@ -63,6 +62,7 @@ jobs:
AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID) AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID)
AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY) AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)
BUILD_NUMBER: $(Build.BuildNumber) BUILD_NUMBER: $(Build.BuildNumber)
displayName: Build
- job: macOS - job: macOS
pool: pool:
@ -72,25 +72,24 @@ jobs:
node_12.x: node_12.x:
node_version: 12.x node_version: 12.x
steps: steps:
- displayName: Set the tag name as an environment variable - script: CI_BUILD_TAG=`git describe --tags` && echo "##vso[task.setvariable variable=CI_BUILD_TAG]$CI_BUILD_TAG"
script: CI_BUILD_TAG=`git describe --tags` && echo "##vso[task.setvariable variable=CI_BUILD_TAG]$CI_BUILD_TAG"
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
displayName: Set the tag name as an environment variable
- displayName: Install Node.js - task: NodeTool@0
task: NodeTool@0
inputs: inputs:
versionSpec: $(node_version) versionSpec: $(node_version)
displayName: Install Node.js
- displayName: Cache Yarn packages - task: Cache@2
task: Cache@2
inputs: inputs:
key: 'yarn | "$(Agent.OS)" | yarn.lock' key: 'yarn | "$(Agent.OS)" | yarn.lock'
restoreKeys: | restoreKeys: |
yarn | "$(Agent.OS)" yarn | "$(Agent.OS)"
path: $(YARN_CACHE_FOLDER) path: $(YARN_CACHE_FOLDER)
displayName: Cache Yarn packages
- displayName: Customize config - bash: |
bash: |
set -e set -e
git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay
rm -rf .lens-ide-overlay/.git rm -rf .lens-ide-overlay/.git
@ -98,15 +97,15 @@ jobs:
jq -s '.[0] * .[1]' package.json package.ide.json > package.custom.json && mv package.custom.json package.json jq -s '.[0] * .[1]' package.json package.ide.json > package.custom.json && mv package.custom.json package.json
env: env:
GH_TOKEN: $(LENS_IDE_GH_TOKEN) GH_TOKEN: $(LENS_IDE_GH_TOKEN)
displayName: Customize config
- displayName: Install dependencies - script: make node_modules
script: make node_modules displayName: Install dependencies
- displayName: Generate npm package - script: make build-npm
script: make build-npm displayName: Generate npm package
- displayName: Build - script: make build
script: make build
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
env: env:
APPLEID: $(APPLEID) APPLEID: $(APPLEID)
@ -116,6 +115,7 @@ jobs:
AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID) AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID)
AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY) AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)
BUILD_NUMBER: $(Build.BuildNumber) BUILD_NUMBER: $(Build.BuildNumber)
displayName: Build
- job: Linux - job: Linux
pool: pool:
@ -125,25 +125,24 @@ jobs:
node_12.x: node_12.x:
node_version: 12.x node_version: 12.x
steps: steps:
- displayName: Set the tag name as an environment variable - script: CI_BUILD_TAG=`git describe --tags` && echo "##vso[task.setvariable variable=CI_BUILD_TAG]$CI_BUILD_TAG"
script: CI_BUILD_TAG=`git describe --tags` && echo "##vso[task.setvariable variable=CI_BUILD_TAG]$CI_BUILD_TAG"
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
displayName: Set the tag name as an environment variable
- displayName: Install Node.js - task: NodeTool@0
task: NodeTool@0
inputs: inputs:
versionSpec: $(node_version) versionSpec: $(node_version)
displayName: Install Node.js
- displayName: Cache Yarn packages - task: Cache@2
task: Cache@2
inputs: inputs:
key: 'yarn | "$(Agent.OS)" | yarn.lock' key: 'yarn | "$(Agent.OS)" | yarn.lock'
restoreKeys: | restoreKeys: |
yarn | "$(Agent.OS)" yarn | "$(Agent.OS)"
path: $(YARN_CACHE_FOLDER) path: $(YARN_CACHE_FOLDER)
displayName: Cache Yarn packages
- displayName: Customize config - bash: |
bash: |
set -e set -e
git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay
rm -rf .lens-ide-overlay/.git rm -rf .lens-ide-overlay/.git
@ -151,15 +150,15 @@ jobs:
jq -s '.[0] * .[1]' package.json package.ide.json > package.custom.json && mv package.custom.json package.json jq -s '.[0] * .[1]' package.json package.ide.json > package.custom.json && mv package.custom.json package.json
env: env:
GH_TOKEN: $(LENS_IDE_GH_TOKEN) GH_TOKEN: $(LENS_IDE_GH_TOKEN)
displayName: Customize config
- displayName: Install dependencies - script: make node_modules
script: make node_modules displayName: Install dependencies
- displayName: Generate npm package - script: make build-npm
script: make build-npm displayName: Generate npm package
- displayName: Setup snapcraft - bash: |
bash: |
sudo chown root:root / sudo chown root:root /
sudo apt-get update && sudo apt-get install -y snapd sudo apt-get update && sudo apt-get install -y snapd
sudo snap install snapcraft --classic sudo snap install snapcraft --classic
@ -168,11 +167,12 @@ jobs:
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
env: env:
SNAP_LOGIN: $(SNAP_LOGIN) SNAP_LOGIN: $(SNAP_LOGIN)
displayName: Setup snapcraft
- displayName: Build - script: make build
script: make build
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
env: env:
AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID) AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID)
AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY) AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)
BUILD_NUMBER: $(Build.BuildNumber) BUILD_NUMBER: $(Build.BuildNumber)
displayName: Build

View File

@ -1,6 +1,6 @@
{ {
"name": "kube-object-event-status", "name": "kube-object-event-status",
"version": "0.1.0", "version": "0.0.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "lens-metrics-cluster-feature", "name": "lens-metrics-cluster-feature",
"version": "0.1.0", "version": "0.0.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -81,7 +81,7 @@ export class MetricsSettings extends React.Component<Props> {
@computed get isTogglable() { @computed get isTogglable() {
if (this.inProgress) return false; if (this.inProgress) return false;
if (!this.props.cluster.status.active) return false; if (this.props.cluster.status.phase !== "connected") return false;
if (this.canUpgrade) return false; if (this.canUpgrade) return false;
if (!this.isActiveMetricsProvider) return false; if (!this.isActiveMetricsProvider) return false;
@ -205,7 +205,7 @@ export class MetricsSettings extends React.Component<Props> {
render() { render() {
return ( return (
<> <>
{ !this.props.cluster.status.active && ( { this.props.cluster.status.phase !== "connected" && (
<section> <section>
<p style={ {color: "var(--colorError)"} }> <p style={ {color: "var(--colorError)"} }>
Lens Metrics settings requires established connection to the cluster. Lens Metrics settings requires established connection to the cluster.
@ -225,7 +225,7 @@ export class MetricsSettings extends React.Component<Props> {
control={ control={
<Switcher <Switcher
disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable} disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable}
checked={!!this.featureStates.prometheus && this.props.cluster.status.active} checked={!!this.featureStates.prometheus && this.props.cluster.status.phase == "connected"}
onChange={v => this.togglePrometheus(v.target.checked)} onChange={v => this.togglePrometheus(v.target.checked)}
name="prometheus" name="prometheus"
/> />
@ -243,7 +243,7 @@ export class MetricsSettings extends React.Component<Props> {
control={ control={
<Switcher <Switcher
disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable} disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable}
checked={!!this.featureStates.kubeStateMetrics && this.props.cluster.status.active} checked={!!this.featureStates.kubeStateMetrics && this.props.cluster.status.phase == "connected"}
onChange={v => this.toggleKubeStateMetrics(v.target.checked)} onChange={v => this.toggleKubeStateMetrics(v.target.checked)}
name="node-exporter" name="node-exporter"
/> />
@ -262,7 +262,7 @@ export class MetricsSettings extends React.Component<Props> {
control={ control={
<Switcher <Switcher
disabled={this.featureStates.nodeExporter === undefined || !this.isTogglable} disabled={this.featureStates.nodeExporter === undefined || !this.isTogglable}
checked={!!this.featureStates.nodeExporter && this.props.cluster.status.active} checked={!!this.featureStates.nodeExporter && this.props.cluster.status.phase == "connected"}
onChange={v => this.toggleNodeExporter(v.target.checked)} onChange={v => this.toggleNodeExporter(v.target.checked)}
name="node-exporter" name="node-exporter"
/> />

View File

@ -1,6 +1,6 @@
{ {
"name": "lens-node-menu", "name": "lens-node-menu",
"version": "0.1.0", "version": "0.0.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "lens-pod-menu", "name": "lens-pod-menu",
"version": "0.1.0", "version": "0.0.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -7,9 +7,6 @@ repo_name: GitHub
repo_url: https://github.com/lensapp/lens repo_url: https://github.com/lensapp/lens
copyright: Copyright &copy; 2021 <a href="https://mirantis.com/">Mirantis Inc.</a> - All rights reserved. copyright: Copyright &copy; 2021 <a href="https://mirantis.com/">Mirantis Inc.</a> - All rights reserved.
edit_uri: "" edit_uri: ""
google_analytics:
- UA-159377374-2
- auto
nav: nav:
- Overview: README.md - Overview: README.md
- Getting Started: - Getting Started:
@ -86,4 +83,7 @@ extra:
link: https://k8slens.dev/ link: https://k8slens.dev/
name: Lens Website name: Lens Website
version: version:
method: mike provider: mike
analytics:
provider: google
property: UA-159377374-2

View File

@ -1,8 +1,8 @@
# Direct dependencies # Direct dependencies
mkdocs>=1.1 mkdocs>=1.2
Pygments>=2.4 Pygments>=2.4
markdown>=3.2 markdown>=3.2
pymdown-extensions>=7.0 pymdown-extensions>=7.0
mkdocs-material-extensions>=1.0 mkdocs-material-extensions>=1.0
mkdocs-git-revision-date-localized-plugin>=0.7.3 mkdocs-git-revision-date-localized-plugin>=0.7.3
mkdocs-material>=6.1.0 mkdocs-material>=7.1.8

View File

@ -3,7 +3,7 @@
"productName": "OpenLens", "productName": "OpenLens",
"description": "OpenLens - Open Source IDE for Kubernetes", "description": "OpenLens - Open Source IDE for Kubernetes",
"homepage": "https://github.com/lensapp/lens", "homepage": "https://github.com/lensapp/lens",
"version": "5.0.0-beta.8", "version": "5.0.0-beta.9",
"main": "static/build/main.js", "main": "static/build/main.js",
"copyright": "© 2021 OpenLens Authors", "copyright": "© 2021 OpenLens Authors",
"license": "MIT", "license": "MIT",
@ -39,7 +39,7 @@
"lint": "yarn run eslint --ext js,ts,tsx --max-warnings=0 .", "lint": "yarn run eslint --ext js,ts,tsx --max-warnings=0 .",
"lint:fix": "yarn run lint --fix", "lint:fix": "yarn run lint --fix",
"mkdocs-serve-local": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -it -p 8000:8000 -v ${PWD}:/docs mkdocs-serve-local:latest", "mkdocs-serve-local": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -it -p 8000:8000 -v ${PWD}:/docs mkdocs-serve-local:latest",
"verify-docs": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -v ${PWD}:/docs mkdocs-serve-local:latest build", "verify-docs": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -v ${PWD}:/docs mkdocs-serve-local:latest build --strict",
"typedocs-extensions-api": "yarn run typedoc src/extensions/extension-api.ts", "typedocs-extensions-api": "yarn run typedoc src/extensions/extension-api.ts",
"version-checkout": "cat package.json | jq '.version' -r | xargs printf \"release/v%s\" | xargs git checkout -b", "version-checkout": "cat package.json | jq '.version' -r | xargs printf \"release/v%s\" | xargs git checkout -b",
"version-commit": "cat package.json | jq '.version' -r | xargs printf \"release v%s\" | git commit --no-edit -s -F -", "version-commit": "cat package.json | jq '.version' -r | xargs printf \"release v%s\" | git commit --no-edit -s -F -",

View File

@ -21,9 +21,9 @@
import { observable } from "mobx"; import { observable } from "mobx";
export type ClusterFrameInfo = { export interface ClusterFrameInfo {
frameId: number; frameId: number;
processId: number processId: number
}; }
export const clusterFrameMap = observable.map<string, ClusterFrameInfo>(); export const clusterFrameMap = observable.map<string, ClusterFrameInfo>();

View File

@ -140,3 +140,19 @@ export function* filterMapStrict<T, U>(src: Iterable<T>, fn: (from: T) => U | nu
} }
} }
} }
/**
* Iterate through `src` until `match` returns a truthy value
* @param src A type that can be iterated over
* @param match A function that should return a truthy value for the item that you want to find
* @returns The first entry that `match` returns a truthy value for, or `undefined`
*/
export function find<T>(src: Iterable<T>, match: (i: T) => any): T | undefined {
for (const from of src) {
if (match(from)) {
return from;
}
}
return void 0;
}

View File

@ -24,6 +24,6 @@ export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from ".
export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../registries/kube-object-menu-registry"; export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../registries/kube-object-menu-registry";
export type { KubeObjectStatusRegistration } from "../registries/kube-object-status-registry"; export type { KubeObjectStatusRegistration } from "../registries/kube-object-status-registry";
export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, PageComponents, PageTarget } from "../registries/page-registry"; export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, PageComponents, PageTarget } from "../registries/page-registry";
export type { PageMenuRegistration, ClusterPageMenuRegistration, PageMenuComponents } from "../registries/page-menu-registry"; export type { ClusterPageMenuRegistration, ClusterPageMenuComponents } from "../registries/page-menu-registry";
export type { StatusBarRegistration } from "../registries/status-bar-registry"; export type { StatusBarRegistration } from "../registries/status-bar-registry";
export type { ProtocolHandlerRegistration, RouteParams as ProtocolRouteParams, RouteHandler as ProtocolRouteHandler } from "../registries/protocol-handler"; export type { ProtocolHandlerRegistration, RouteParams as ProtocolRouteParams, RouteHandler as ProtocolRouteHandler } from "../registries/protocol-handler";

View File

@ -355,6 +355,10 @@ export class ExtensionLoader extends Singleton {
return this.extensions.get(extId); return this.extensions.get(extId);
} }
getInstanceById<E extends LensExtension>(extId: LensExtensionId): E {
return this.instances.get(extId) as E;
}
toJSON(): Map<LensExtensionId, InstalledExtension> { toJSON(): Map<LensExtensionId, InstalledExtension> {
return toJS(this.extensions); return toJS(this.extensions);
} }

View File

@ -21,7 +21,6 @@
import { LensExtension } from "./lens-extension"; import { LensExtension } from "./lens-extension";
import { WindowManager } from "../main/window-manager"; import { WindowManager } from "../main/window-manager";
import { getExtensionPageUrl } from "./registries/page-registry";
import { catalogEntityRegistry } from "../main/catalog"; import { catalogEntityRegistry } from "../main/catalog";
import type { CatalogEntity } from "../common/catalog"; import type { CatalogEntity } from "../common/catalog";
import type { IObservableArray } from "mobx"; import type { IObservableArray } from "mobx";
@ -30,15 +29,8 @@ import type { MenuRegistration } from "./registries";
export class LensMainExtension extends LensExtension { export class LensMainExtension extends LensExtension {
appMenus: MenuRegistration[] = []; appMenus: MenuRegistration[] = [];
async navigate<P extends object>(pageId?: string, params?: P, frameId?: number) { async navigate(pageId?: string, params?: Record<string, any>, frameId?: number) {
const windowManager = WindowManager.getInstance(); return WindowManager.getInstance().navigateExtension(this.id, pageId, params, frameId);
const pageUrl = getExtensionPageUrl({
extensionId: this.name,
pageId,
params: params ?? {}, // compile to url with params
});
await windowManager.navigate(pageUrl, frameId);
} }
addCatalogSource(id: string, source: IObservableArray<CatalogEntity>) { addCatalogSource(id: string, source: IObservableArray<CatalogEntity>) {

View File

@ -19,33 +19,26 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
import type { import type * as registries from "./registries";
AppPreferenceRegistration, CatalogEntityDetailRegistration, ClusterPageMenuRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration,
KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, WelcomeMenuRegistration, WorkloadsOverviewDetailRegistration,
} from "./registries";
import type { Cluster } from "../main/cluster"; import type { Cluster } from "../main/cluster";
import { LensExtension } from "./lens-extension"; import { LensExtension } from "./lens-extension";
import { getExtensionPageUrl } from "./registries/page-registry"; import { getExtensionPageUrl } from "./registries/page-registry";
import type { CommandRegistration } from "./registries/command-registry";
import type { EntitySettingRegistration } from "./registries/entity-setting-registry";
import type { TopBarRegistration } from "./registries/topbar-registry";
export class LensRendererExtension extends LensExtension { export class LensRendererExtension extends LensExtension {
globalPages: PageRegistration[] = []; globalPages: registries.PageRegistration[] = [];
clusterPages: PageRegistration[] = []; clusterPages: registries.PageRegistration[] = [];
globalPageMenus: PageMenuRegistration[] = []; clusterPageMenus: registries.ClusterPageMenuRegistration[] = [];
clusterPageMenus: ClusterPageMenuRegistration[] = []; kubeObjectStatusTexts: registries.KubeObjectStatusRegistration[] = [];
kubeObjectStatusTexts: KubeObjectStatusRegistration[] = []; appPreferences: registries.AppPreferenceRegistration[] = [];
appPreferences: AppPreferenceRegistration[] = []; entitySettings: registries.EntitySettingRegistration[] = [];
entitySettings: EntitySettingRegistration[] = []; statusBarItems: registries.StatusBarRegistration[] = [];
statusBarItems: StatusBarRegistration[] = []; kubeObjectDetailItems: registries.KubeObjectDetailRegistration[] = [];
kubeObjectDetailItems: KubeObjectDetailRegistration[] = []; kubeObjectMenuItems: registries.KubeObjectMenuRegistration[] = [];
kubeObjectMenuItems: KubeObjectMenuRegistration[] = []; kubeWorkloadsOverviewItems: registries.WorkloadsOverviewDetailRegistration[] = [];
kubeWorkloadsOverviewItems: WorkloadsOverviewDetailRegistration[] = []; commands: registries.CommandRegistration[] = [];
commands: CommandRegistration[] = []; welcomeMenus: registries.WelcomeMenuRegistration[] = [];
welcomeMenus: WelcomeMenuRegistration[] = []; catalogEntityDetailItems: registries.CatalogEntityDetailRegistration[] = [];
catalogEntityDetailItems: CatalogEntityDetailRegistration[] = []; topBarItems: registries.TopBarRegistration[] = [];
topBarItems: TopBarRegistration[] = [];
async navigate<P extends object>(pageId?: string, params?: P) { async navigate<P extends object>(pageId?: string, params?: P) {
const { navigate } = await import("../renderer/navigation"); const { navigate } = await import("../renderer/navigation");

View File

@ -26,23 +26,20 @@ import type { PageTarget, RegisteredPage } from "./page-registry";
import type { LensExtension } from "../lens-extension"; import type { LensExtension } from "../lens-extension";
import { BaseRegistry } from "./base-registry"; import { BaseRegistry } from "./base-registry";
export interface PageMenuRegistration { export interface ClusterPageMenuRegistration {
target?: PageTarget;
title: React.ReactNode;
components: PageMenuComponents;
}
export interface ClusterPageMenuRegistration extends PageMenuRegistration {
id?: string; id?: string;
parentId?: string; parentId?: string;
target?: PageTarget;
title: React.ReactNode;
components: ClusterPageMenuComponents;
} }
export interface PageMenuComponents { export interface ClusterPageMenuComponents {
Icon: React.ComponentType<IconProps>; Icon: React.ComponentType<IconProps>;
} }
export class PageMenuRegistry<T extends PageMenuRegistration> extends BaseRegistry<T> { export class ClusterPageMenuRegistry extends BaseRegistry<ClusterPageMenuRegistration> {
add(items: T[], ext: LensExtension) { add(items: ClusterPageMenuRegistration[], ext: LensExtension) {
const normalizedItems = items.map(menuItem => { const normalizedItems = items.map(menuItem => {
menuItem.target = { menuItem.target = {
extensionId: ext.name, extensionId: ext.name,
@ -54,9 +51,7 @@ export class PageMenuRegistry<T extends PageMenuRegistration> extends BaseRegist
return super.add(normalizedItems); return super.add(normalizedItems);
} }
}
export class ClusterPageMenuRegistry extends PageMenuRegistry<ClusterPageMenuRegistration> {
getRootItems() { getRootItems() {
return this.getItems().filter((item) => !item.parentId); return this.getItems().filter((item) => !item.parentId);
} }

View File

@ -27,7 +27,7 @@ import { appEventBus } from "../common/event-bus";
import { ipcMainOn } from "../common/ipc"; import { ipcMainOn } from "../common/ipc";
import { initMenu } from "./menu"; import { initMenu } from "./menu";
import { initTray } from "./tray"; import { initTray } from "./tray";
import { delay, Singleton } from "../common/utils"; import { delay, iter, Singleton } from "../common/utils";
import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames"; import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames";
import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
import logger from "./logger"; import logger from "./logger";
@ -38,6 +38,12 @@ function isHideable(window: BrowserWindow | null): boolean {
return Boolean(window && !window.isDestroyed()); return Boolean(window && !window.isDestroyed());
} }
export interface SendToViewArgs {
channel: string;
frameInfo?: ClusterFrameInfo;
data?: any[];
}
export class WindowManager extends Singleton { export class WindowManager extends Singleton {
protected mainWindow: BrowserWindow; protected mainWindow: BrowserWindow;
protected splashWindow: BrowserWindow; protected splashWindow: BrowserWindow;
@ -175,7 +181,7 @@ export class WindowManager extends Singleton {
return this.mainWindow; return this.mainWindow;
} }
sendToView({ channel, frameInfo, data = [] }: { channel: string, frameInfo?: ClusterFrameInfo, data?: any[] }) { private sendToView({ channel, frameInfo, data = [] }: SendToViewArgs) {
if (frameInfo) { if (frameInfo) {
this.mainWindow.webContents.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...data); this.mainWindow.webContents.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...data);
} else { } else {
@ -183,10 +189,22 @@ export class WindowManager extends Singleton {
} }
} }
async navigateExtension(extId: string, pageId?: string, params?: Record<string, any>, frameId?: number) {
await this.ensureMainWindow();
const frameInfo = iter.find(clusterFrameMap.values(), frameInfo => frameInfo.frameId === frameId);
this.sendToView({
channel: "extension:navigate",
frameInfo,
data: [extId, pageId, params],
});
}
async navigate(url: string, frameId?: number) { async navigate(url: string, frameId?: number) {
await this.ensureMainWindow(); await this.ensureMainWindow();
const frameInfo = Array.from(clusterFrameMap.values()).find((frameInfo) => frameInfo.frameId === frameId); const frameInfo = iter.find(clusterFrameMap.values(), frameInfo => frameInfo.frameId === frameId);
const channel = frameInfo const channel = frameInfo
? IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER ? IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER
: IpcRendererNavigationEvents.NAVIGATE_IN_APP; : IpcRendererNavigationEvents.NAVIGATE_IN_APP;

View File

@ -79,6 +79,7 @@ export async function bootstrap(App: AppComponent) {
initializers.initWelcomeMenuRegistry(); initializers.initWelcomeMenuRegistry();
initializers.initWorkloadsOverviewDetailRegistry(); initializers.initWorkloadsOverviewDetailRegistry();
initializers.initCatalog(); initializers.initCatalog();
initializers.initIpcRendererListeners();
ExtensionLoader.createInstance().init(); ExtensionLoader.createInstance().init();
ExtensionDiscovery.createInstance().init(); ExtensionDiscovery.createInstance().init();

View File

@ -85,8 +85,8 @@ export class EntitySettings extends React.Component<Props> {
<> <>
<h2>{this.entity.metadata.name}</h2> <h2>{this.entity.metadata.name}</h2>
<Tabs className="flex column" scrollable={false} onChange={this.onTabChange} value={this.activeTab}> <Tabs className="flex column" scrollable={false} onChange={this.onTabChange} value={this.activeTab}>
{ groups.map((group) => ( { groups.map((group, groupIndex) => (
<> <React.Fragment key={`group-${groupIndex}`}>
<div className="header">{group[0]}</div> <div className="header">{group[0]}</div>
{ group[1].map((setting, index) => ( { group[1].map((setting, index) => (
<Tab <Tab
@ -96,7 +96,7 @@ export class EntitySettings extends React.Component<Props> {
data-testid={`${setting.id}-tab`} data-testid={`${setting.id}-tab`}
/> />
))} ))}
</> </React.Fragment>
))} ))}
</Tabs> </Tabs>
</> </>

View File

@ -112,7 +112,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
protected async loadItems(params: KubeObjectStoreLoadingParams) { protected async loadItems(params: KubeObjectStoreLoadingParams) {
const { allowedNamespaces } = this; const { allowedNamespaces } = this;
let namespaces = (await super.loadItems(params)) || []; let namespaces = await super.loadItems(params).catch(() => []);
namespaces = namespaces.filter(namespace => allowedNamespaces.includes(namespace.getName())); namespaces = namespaces.filter(namespace => allowedNamespaces.includes(namespace.getName()));

View File

@ -91,17 +91,15 @@ export class RoleBindingDialog extends React.Component<Props> {
const serviceAccounts = Array.from(this.selectedAccounts, sa => ({ const serviceAccounts = Array.from(this.selectedAccounts, sa => ({
name: sa.getName(), name: sa.getName(),
kind: "ServiceAccount" as const, kind: "ServiceAccount" as const,
namespace: this.bindingNamespace, namespace: sa.getNs(),
})); }));
const users = Array.from(this.selectedUsers, user => ({ const users = Array.from(this.selectedUsers, user => ({
name: user, name: user,
kind: "User" as const, kind: "User" as const
namespace: this.bindingNamespace,
})); }));
const groups = Array.from(this.selectedGroups, group => ({ const groups = Array.from(this.selectedGroups, group => ({
name: group, name: group,
kind: "Group" as const, kind: "Group" as const
namespace: this.bindingNamespace,
})); }));
return [ return [

View File

@ -35,6 +35,10 @@
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
> * {
z-index: 1;
}
} }
.HotbarMenu { .HotbarMenu {

View File

@ -27,3 +27,4 @@ export * from "./registries";
export * from "./welcome-menu-registry"; export * from "./welcome-menu-registry";
export * from "./workloads-overview-detail-registry"; export * from "./workloads-overview-detail-registry";
export * from "./catalog"; export * from "./catalog";
export * from "./ipc";

View File

@ -0,0 +1,30 @@
/**
* 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 { ipcRendererOn } from "../../common/ipc";
import { ExtensionLoader } from "../../extensions/extension-loader";
import type { LensRendererExtension } from "../../extensions/lens-renderer-extension";
export function initIpcRendererListeners() {
ipcRendererOn("extension:navigate", (event, extId: string, pageId ?: string, params?: Record<string, any>) => {
ExtensionLoader.getInstance().getInstanceById<LensRendererExtension>(extId).navigate(pageId, params);
});
}

View File

@ -32,7 +32,6 @@ export function initRegistries() {
registries.KubeObjectDetailRegistry.createInstance(); registries.KubeObjectDetailRegistry.createInstance();
registries.KubeObjectMenuRegistry.createInstance(); registries.KubeObjectMenuRegistry.createInstance();
registries.KubeObjectStatusRegistry.createInstance(); registries.KubeObjectStatusRegistry.createInstance();
registries.PageMenuRegistry.createInstance();
registries.StatusBarRegistry.createInstance(); registries.StatusBarRegistry.createInstance();
registries.WelcomeMenuRegistry.createInstance(); registries.WelcomeMenuRegistry.createInstance();
registries.WorkloadsOverviewDetailRegistry.createInstance(); registries.WorkloadsOverviewDetailRegistry.createInstance();

View File

@ -280,7 +280,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
async update(item: T, data: Partial<T>): Promise<T> { async update(item: T, data: Partial<T>): Promise<T> {
const newItem = await item.update<T>(data); const newItem = await item.update<T>(data);
ensureObjectSelfLink(this.api, newItem); ensureObjectSelfLink(this.api, newItem);
const index = this.items.findIndex(item => item.getId() === newItem.getId()); const index = this.items.findIndex(item => item.getId() === newItem.getId());
@ -332,6 +332,10 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
} }
private watchNamespace(namespace: string, abortController: AbortController) { private watchNamespace(namespace: string, abortController: AbortController) {
if (!this.api.getResourceVersion(namespace)) {
return;
}
let timedRetry: NodeJS.Timeout; let timedRetry: NodeJS.Timeout;
const watch = () => this.api.watch({ const watch = () => this.api.watch({
namespace, namespace,