From e5f613ead780f654c885cf460f42b4b0fd04e096 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Thu, 15 Apr 2021 14:02:13 +0300 Subject: [PATCH 1/6] Exporting search input component (#2521) Signed-off-by: Alex Andreev --- src/extensions/renderer-api/components.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/extensions/renderer-api/components.ts b/src/extensions/renderer-api/components.ts index 62a3687fdb..1a48fc54e3 100644 --- a/src/extensions/renderer-api/components.ts +++ b/src/extensions/renderer-api/components.ts @@ -1,5 +1,6 @@ // Common UI components export * from "../../renderer/components/layout/sub-title"; +export * from "../../renderer/components/input/search-input"; // layouts export * from "../../renderer/components/layout/page-layout"; From 95ebb52f5dc30e7158dc4c7c01a5f8c33ee08895 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Thu, 15 Apr 2021 14:40:49 +0300 Subject: [PATCH 2/6] Test also push to branches on gh actions (#2532) Signed-off-by: Jari Kolehmainen --- .github/workflows/test.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7f3959eb8..29373e017d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,11 @@ name: Test on: - - pull_request + pull_request: + branches: + - "*" + push: + branches: + - master jobs: build: name: Test From 1ac5588fab94da94af799fc7ed939cca7b92b8e6 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Thu, 15 Apr 2021 15:16:56 +0300 Subject: [PATCH 3/6] Allow to control catalog entity menu item visibility based on source (#2499) * allow to control catalog entity menu item visibility based on source Signed-off-by: Jari Kolehmainen * fix Signed-off-by: Jari Kolehmainen --- src/common/catalog-entities/kubernetes-cluster.ts | 2 ++ src/common/catalog-entity.ts | 1 + src/renderer/components/+catalog/catalog.tsx | 3 ++- src/renderer/components/hotbar/hotbar-icon.tsx | 3 ++- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts index 3354b9ba61..d0760dcdf1 100644 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ b/src/common/catalog-entities/kubernetes-cluster.ts @@ -49,11 +49,13 @@ export class KubernetesCluster implements CatalogEntity { { icon: "settings", title: "Settings", + onlyVisibleForSource: "local", onClick: async () => context.navigate(`/cluster/${this.metadata.uid}/settings`) }, { icon: "delete", title: "Delete", + onlyVisibleForSource: "local", onClick: async () => clusterStore.removeById(this.metadata.uid), confirm: { message: `Remove Kubernetes Cluster "${this.metadata.name} from Lens?` diff --git a/src/common/catalog-entity.ts b/src/common/catalog-entity.ts index ad3f0e591e..19d83d5437 100644 --- a/src/common/catalog-entity.ts +++ b/src/common/catalog-entity.ts @@ -45,6 +45,7 @@ export interface CatalogEntityActionContext { export type CatalogEntityContextMenu = { icon: string; title: string; + onlyVisibleForSource?: string; // show only if empty or if matches with entity source onClick: () => Promise; confirm?: { message: string; diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index 5cd0e918c1..a73ba86c28 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -127,6 +127,7 @@ export class Catalog extends React.Component { @autobind() renderItemMenu(item: CatalogEntityItem) { + const menuItems = this.contextMenu.menuItems.filter((menuItem) => !menuItem.onlyVisibleForSource || menuItem.onlyVisibleForSource === item.entity.metadata.source); const onOpen = async () => { await item.onContextMenuOpen(this.contextMenu); }; @@ -139,7 +140,7 @@ export class Catalog extends React.Component { this.removeFromHotbar(item) }> Remove from Hotbar - { this.contextMenu.menuItems.map((menuItem, index) => { + { menuItems.map((menuItem, index) => { return ( this.onMenuItemClick(menuItem)}> {menuItem.title} diff --git a/src/renderer/components/hotbar/hotbar-icon.tsx b/src/renderer/components/hotbar/hotbar-icon.tsx index 201bc2df02..29902bc6a0 100644 --- a/src/renderer/components/hotbar/hotbar-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-icon.tsx @@ -100,6 +100,7 @@ export class HotbarIcon extends React.Component { await entity.onContextMenuOpen(this.contextMenu); this.toggleMenu(); }; + const menuItems = this.contextMenu?.menuItems.filter((menuItem) => !menuItem.onlyVisibleForSource || menuItem.onlyVisibleForSource === entity.metadata.source); return (
@@ -117,7 +118,7 @@ export class HotbarIcon extends React.Component { this.removeFromHotbar(entity) }> Remove from Hotbar - { this.contextMenu && this.contextMenu.menuItems.map((menuItem) => { + { this.contextMenu && menuItems.map((menuItem) => { return ( this.onMenuItemClick(menuItem) }> {menuItem.title} From ec9c47752f652aeca89e77ffe5836e3e4513f780 Mon Sep 17 00:00:00 2001 From: pashevskii <53330707+pashevskii@users.noreply.github.com> Date: Thu, 15 Apr 2021 16:19:54 +0400 Subject: [PATCH 4/6] YAML Templates in Create Resource dock tab (#2327) Co-authored-by: Pavel Ashevskiy --- package.json | 1 + .../components/dock/create-resource.scss | 2 +- .../components/dock/create-resource.store.ts | 48 +++++ .../components/dock/create-resource.tsx | 49 +++++ templates/create-resource/ClusterRole.yaml | 198 ++++++++++++++++++ .../create-resource/ClusterRoleBinding.yaml | 12 ++ templates/create-resource/ConfigMap.yaml | 17 ++ templates/create-resource/CronJob.yaml | 19 ++ templates/create-resource/DeamonSet.yaml | 44 ++++ templates/create-resource/Deployment.yaml | 21 ++ templates/create-resource/Ingress.yaml | 17 ++ templates/create-resource/Job.yaml | 13 ++ templates/create-resource/NetworkPolicy.yaml | 34 +++ .../create-resource/PersistentVolume.yaml | 18 ++ .../PersistentVolumeClaim.yaml | 17 ++ templates/create-resource/ReplicaSet.yaml | 21 ++ .../ReplicationController.yaml | 19 ++ templates/create-resource/Role.yaml | 9 + templates/create-resource/RoleBinding.yaml | 17 ++ templates/create-resource/Secret.yaml | 8 + templates/create-resource/Service.yaml | 11 + templates/create-resource/StatefulSet.yaml | 34 +++ yarn.lock | 47 ++++- 23 files changed, 673 insertions(+), 3 deletions(-) create mode 100644 templates/create-resource/ClusterRole.yaml create mode 100644 templates/create-resource/ClusterRoleBinding.yaml create mode 100644 templates/create-resource/ConfigMap.yaml create mode 100644 templates/create-resource/CronJob.yaml create mode 100644 templates/create-resource/DeamonSet.yaml create mode 100644 templates/create-resource/Deployment.yaml create mode 100644 templates/create-resource/Ingress.yaml create mode 100644 templates/create-resource/Job.yaml create mode 100644 templates/create-resource/NetworkPolicy.yaml create mode 100644 templates/create-resource/PersistentVolume.yaml create mode 100644 templates/create-resource/PersistentVolumeClaim.yaml create mode 100644 templates/create-resource/ReplicaSet.yaml create mode 100644 templates/create-resource/ReplicationController.yaml create mode 100644 templates/create-resource/Role.yaml create mode 100644 templates/create-resource/RoleBinding.yaml create mode 100644 templates/create-resource/Secret.yaml create mode 100644 templates/create-resource/Service.yaml create mode 100644 templates/create-resource/StatefulSet.yaml diff --git a/package.json b/package.json index bb82612ea2..e80f47352d 100644 --- a/package.json +++ b/package.json @@ -206,6 +206,7 @@ "electron-devtools-installer": "^3.1.1", "electron-updater": "^4.3.1", "electron-window-state": "^5.0.3", + "filehound": "^1.17.4", "filenamify": "^4.1.0", "fs-extra": "^9.0.1", "handlebars": "^4.7.6", diff --git a/src/renderer/components/dock/create-resource.scss b/src/renderer/components/dock/create-resource.scss index 027b37763d..48231378c9 100644 --- a/src/renderer/components/dock/create-resource.scss +++ b/src/renderer/components/dock/create-resource.scss @@ -1,2 +1,2 @@ .CreateResource { -} \ No newline at end of file +} diff --git a/src/renderer/components/dock/create-resource.store.ts b/src/renderer/components/dock/create-resource.store.ts index 8933ddf4d0..bdc1f4c139 100644 --- a/src/renderer/components/dock/create-resource.store.ts +++ b/src/renderer/components/dock/create-resource.store.ts @@ -1,13 +1,61 @@ +import fs from "fs-extra"; +import path from "path"; +import os from "os"; +import groupBy from "lodash/groupBy"; +import filehound from "filehound"; +import { watch } from "chokidar"; import { autobind } from "../../utils"; import { DockTabStore } from "./dock-tab.store"; import { dockStore, IDockTab, TabKind } from "./dock.store"; @autobind() export class CreateResourceStore extends DockTabStore { + constructor() { super({ storageKey: "create_resource" }); + fs.ensureDirSync(this.userTemplatesFolder); + } + + get lensTemplatesFolder():string { + return path.resolve(__static, "../templates/create-resource"); + } + + get userTemplatesFolder():string { + return path.join(os.homedir(), ".k8slens", "templates"); + } + + async getTemplates(templatesPath: string, defaultGroup: string) { + const templates = await filehound.create().path(templatesPath).ext(["yaml", "json"]).depth(1).find(); + + return templates ? this.groupTemplates(templates, templatesPath, defaultGroup) : {}; + } + + groupTemplates(templates: string[], templatesPath: string, defaultGroup: string) { + return groupBy(templates,(v:string) => + path.relative(templatesPath,v).split(path.sep).length>1 + ? path.parse(path.relative(templatesPath,v)).dir + : defaultGroup); + } + + async getMergedTemplates() { + const userTemplates = await this.getTemplates(this.userTemplatesFolder, "ungrouped"); + const lensTemplates = await this.getTemplates(this.lensTemplatesFolder, "lens"); + + return {...userTemplates,...lensTemplates}; + } + + async watchUserTemplates(callback: ()=> void){ + watch(this.userTemplatesFolder, { + depth: 1, + ignoreInitial: true, + awaitWriteFinish: { + stabilityThreshold: 500 + } + }).on("all", () => { + callback(); + }); } } diff --git a/src/renderer/components/dock/create-resource.tsx b/src/renderer/components/dock/create-resource.tsx index 01e6002309..29e2bf60e7 100644 --- a/src/renderer/components/dock/create-resource.tsx +++ b/src/renderer/components/dock/create-resource.tsx @@ -1,6 +1,9 @@ import "./create-resource.scss"; import React from "react"; +import path from "path"; +import fs from "fs-extra"; +import {Select, GroupSelectOption, SelectOption} from "../select"; import jsYaml from "js-yaml"; import { observable } from "mobx"; import { observer } from "mobx-react"; @@ -20,7 +23,25 @@ interface Props { @observer export class CreateResource extends React.Component { + @observable currentTemplates:Map = new Map(); @observable error = ""; + @observable templates:GroupSelectOption[] = []; + + componentDidMount() { + createResourceStore.getMergedTemplates().then(v => this.updateGroupSelectOptions(v)); + createResourceStore.watchUserTemplates(() => createResourceStore.getMergedTemplates().then(v => this.updateGroupSelectOptions(v))); + } + + updateGroupSelectOptions(templates :Record) { + this.templates = Object.entries(templates) + .map(([name, grouping]) => this.convertToGroup(name, grouping)); + } + + convertToGroup(group:string, items:string[]):GroupSelectOption { + const options = items.map(v => ({label: path.parse(v).name, value: v})); + + return {label: group, options}; + } get tabId() { return this.props.tab.id; @@ -30,11 +51,20 @@ export class CreateResource extends React.Component { return createResourceStore.getData(this.tabId); } + get currentTemplate() { + return this.currentTemplates.get(this.tabId) ?? null; + } + onChange = (value: string, error?: string) => { createResourceStore.setData(this.tabId, value); this.error = error; }; + onSelectTemplate = (item: SelectOption) => { + this.currentTemplates.set(this.tabId, item); + fs.readFile(item.value,"utf8").then(v => createResourceStore.setData(this.tabId,v)); + }; + create = async () => { if (this.error) return; if (!this.data.trim()) return; // do not save when field is empty @@ -67,6 +97,24 @@ export class CreateResource extends React.Component { return successMessage; }; + renderControls(){ + return ( +
+