From 136dac4a8ffa3f757437b1dc56c3dd1c1685198f Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 22 Feb 2021 14:44:21 +0200 Subject: [PATCH 01/12] - fix: don't reset selected "all namespaces" on page reload (#2185) - fix: don't reload namespaces on every page visit / NamespaceSelect.render() Signed-off-by: Roman --- .../+namespaces/namespace-select-filter.tsx | 2 +- .../components/+namespaces/namespace-select.tsx | 1 + .../components/+namespaces/namespace.store.ts | 12 ++++++------ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/renderer/components/+namespaces/namespace-select-filter.tsx b/src/renderer/components/+namespaces/namespace-select-filter.tsx index 453849e534..85a6299b17 100644 --- a/src/renderer/components/+namespaces/namespace-select-filter.tsx +++ b/src/renderer/components/+namespaces/namespace-select-filter.tsx @@ -56,7 +56,7 @@ export class NamespaceSelectFilter extends React.Component { if (namespace) { namespaceStore.toggleContext(namespace); } else { - namespaceStore.resetContext(); // "All namespaces" clicked, empty list considered as "all" + namespaceStore.toggleAll(false); // "All namespaces" clicked } } diff --git a/src/renderer/components/+namespaces/namespace-select.tsx b/src/renderer/components/+namespaces/namespace-select.tsx index 207a274d2c..775cace4d3 100644 --- a/src/renderer/components/+namespaces/namespace-select.tsx +++ b/src/renderer/components/+namespaces/namespace-select.tsx @@ -29,6 +29,7 @@ export class NamespaceSelect extends React.Component { disposeOnUnmount(this, [ kubeWatchApi.subscribeStores([namespaceStore], { preload: true, + loadOnce: true, // skip reloading namespaces on every render / page visit }) ]); } diff --git a/src/renderer/components/+namespaces/namespace.store.ts b/src/renderer/components/+namespaces/namespace.store.ts index 1f928fe2f3..ad271d1302 100644 --- a/src/renderer/components/+namespaces/namespace.store.ts +++ b/src/renderer/components/+namespaces/namespace.store.ts @@ -5,7 +5,7 @@ import { Namespace, namespacesApi } from "../../api/endpoints/namespaces.api"; import { createPageParam } from "../../navigation"; import { apiManager } from "../../api/api-manager"; -const storage = createStorage("context_namespaces", []); +const storage = createStorage("context_namespaces"); export const namespaceUrlParam = createPageParam({ name: "namespaces", @@ -74,11 +74,11 @@ export class NamespaceStore extends KubeObjectStore { @computed private get initialNamespaces(): string[] { const namespaces = new Set(this.allowedNamespaces); - const prevSelected = storage.get().filter(namespace => namespaces.has(namespace)); + const prevSelectedNamespaces = storage.get(); - // return previously saved namespaces from local-storage - if (prevSelected.length > 0) { - return prevSelected; + // return previously saved namespaces from local-storage (if any) + if (prevSelectedNamespaces) { + return prevSelectedNamespaces.filter(namespace => namespaces.has(namespace)); } // otherwise select "default" or first allowed namespace @@ -166,7 +166,7 @@ export class NamespaceStore extends KubeObjectStore { if (showAll) { this.setContext(this.allowedNamespaces); } else { - this.contextNs.clear(); + this.resetContext(); // empty context considered as "All namespaces" } } else { this.toggleAll(!this.hasAllContexts); From 73cf2893aaeb6715dda6be965816e743f559a06c Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 22 Feb 2021 12:18:48 -0500 Subject: [PATCH 02/12] fix RoleBindings Namespace and Bindings field not displaying the correct data (#2203) Signed-off-by: Sebastian Malton --- .../+user-management-roles-bindings/role-bindings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx b/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx index f55e781e0e..9a5e079894 100644 --- a/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx +++ b/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx @@ -50,8 +50,8 @@ export class RoleBindings extends React.Component { renderTableContents={(binding: RoleBinding) => [ binding.getName(), , - binding.getSubjectNames(), binding.getNs() || "-", + binding.getSubjectNames(), binding.getAge(), ]} addRemoveButtons={{ From 613540670945d1e346e4aa30fd9d5123696b9d76 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 22 Feb 2021 20:29:26 +0200 Subject: [PATCH 03/12] fix: allow scrolling in main-layout's content area (#2201) Signed-off-by: Roman --- src/renderer/components/layout/main-layout.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/renderer/components/layout/main-layout.scss b/src/renderer/components/layout/main-layout.scss index 92f1173b7a..4e456e357f 100755 --- a/src/renderer/components/layout/main-layout.scss +++ b/src/renderer/components/layout/main-layout.scss @@ -44,6 +44,11 @@ > main { display: contents; + + > * { + grid-area: main; + overflow: auto; + } } footer { From d3ff3c9cc183df0e84a81d31346942ce43d97fb2 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 22 Feb 2021 16:53:34 -0500 Subject: [PATCH 04/12] Fix auto update on quit with newer version (#2128) - Fix auto update on quit with newer version - If the user specifies that Lens should auto update on quit to a specific version, and before they quit the auto-updater finds a newer version. Then disregard the previous request to update - reset broadcast-ed version if failed to broadcast Signed-off-by: Sebastian Malton --- src/main/app-updater.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/app-updater.ts b/src/main/app-updater.ts index 5d9509c6e4..ed2bf4250b 100644 --- a/src/main/app-updater.ts +++ b/src/main/app-updater.ts @@ -5,6 +5,8 @@ import { delay } from "../common/utils"; import { areArgsUpdateAvailableToBackchannel, AutoUpdateLogPrefix, broadcastMessage, onceCorrect, UpdateAvailableChannel, UpdateAvailableToBackchannel } from "../common/ipc"; import { ipcMain } from "electron"; +let installVersion: null | string = null; + function handleAutoUpdateBackChannel(event: Electron.IpcMainEvent, ...[arg]: UpdateAvailableToBackchannel) { if (arg.doUpdate) { if (arg.now) { @@ -37,6 +39,22 @@ export function startUpdateChecking(interval = 1000 * 60 * 60 * 24): void { autoUpdater .on("update-available", (args: UpdateInfo) => { + if (autoUpdater.autoInstallOnAppQuit) { + // a previous auto-update loop was completed with YES+LATER, check if same version + if (installVersion === args.version) { + // same version, don't broadcast + return; + } + } + + /** + * This should be always set to false here because it is the reasonable + * default. Namely, if a don't auto update to a version that the user + * didn't ask for. + */ + autoUpdater.autoInstallOnAppQuit = false; + installVersion = args.version; + try { const backchannel = `auto-update:${args.version}`; @@ -53,6 +71,7 @@ export function startUpdateChecking(interval = 1000 * 60 * 60 * 24): void { broadcastMessage(UpdateAvailableChannel, backchannel, args); } catch (error) { logger.error(`${AutoUpdateLogPrefix}: broadcasting failed`, { error }); + installVersion = undefined; } }); From d6561f4fbadf2b10cebaf4072035fc34c15c0856 Mon Sep 17 00:00:00 2001 From: Steven Martin Date: Tue, 23 Feb 2021 07:05:44 -0500 Subject: [PATCH 05/12] Fix Heading spelling (#2202) Repositories was misspelled in Managing Helm Repositories Signed-off-by: stevenGravy --- docs/helm/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/helm/README.md b/docs/helm/README.md index b46bead65c..f07bced7f0 100644 --- a/docs/helm/README.md +++ b/docs/helm/README.md @@ -4,7 +4,7 @@ Lens has integration to Helm making it easy to install and manage Helm charts an ![Helm Charts](images/helm-charts.png) -## Managing Helm Reporistories +## Managing Helm Repositories Used Helm repositories are possible to configure in the [Preferences](/getting-started/preferences). Lens app will fetch available Helm repositories from the [Artifact HUB](https://artifacthub.io/) and automatically add `bitnami` repository by default if no other repositories are already configured. If any other repositories are needed to add, those can be added manually via command line. **Note!** Configured Helm repositories are added globally to user's computer, so other processes can see those as well. @@ -18,4 +18,4 @@ Lens will list all charts from configured Helm repositries on Apps section. To i To update a Helm release, you can open the release details and modify the release values and click "Save" button. To upgrade or downgrade the release, click "Upgrade" button in the release details. In the release editor you can select a new chart version and edit the release values if needed and then click "Upgrade" or "Upgrade and Close" button. ## Deleting a Helm Release -To delete existing Helm release open the release details and click trash can icon on the top of the panel. Deletion removes all Kubernetes resources created by the Helm release. **Note!** If the release included any persistent volumes, those are required to remove manually! \ No newline at end of file +To delete existing Helm release open the release details and click trash can icon on the top of the panel. Deletion removes all Kubernetes resources created by the Helm release. **Note!** If the release included any persistent volumes, those are required to remove manually! From fd27bd6bb804e82198e630fb76daf6519de06452 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 23 Feb 2021 08:13:46 -0500 Subject: [PATCH 06/12] Fix removal of langci from repo (#2204) - Move all strings of the form {`...`} to use just "" - Fix RoleBindingDetails not rendering the name of the role binding Signed-off-by: Sebastian Malton --- .../+apps-helm-charts/helm-chart-details.tsx | 10 +++++----- .../components/+apps-helm-charts/helm-charts.tsx | 2 +- .../+apps-releases/release-details.tsx | 10 +++++----- .../components/+apps-releases/release-menu.tsx | 2 +- .../components/+cluster/cluster-pie-charts.tsx | 6 +++--- .../+config-limit-ranges/limit-range-details.tsx | 6 +++--- .../+config-resource-quotas/add-quota-dialog.tsx | 10 +++++----- .../+config-secrets/add-secret-dialog.tsx | 8 ++++---- .../+config-secrets/secret-details.tsx | 4 ++-- .../+namespaces/add-namespace-dialog.tsx | 2 +- .../components/+namespaces/namespace-details.tsx | 2 +- .../+network-policies/network-policy-details.tsx | 2 +- .../+network-services/service-details.tsx | 4 ++-- .../+network-services/service-port-component.tsx | 2 +- .../+preferences/add-helm-repo-dialog.tsx | 12 ++++++------ .../components/+preferences/preferences.tsx | 2 +- .../+storage-classes/storage-class-details.tsx | 2 +- .../volume-claim-details.tsx | 2 +- .../add-role-binding-dialog.tsx | 6 +++--- .../role-binding-details.tsx | 4 ++-- .../+user-management-roles/add-role-dialog.tsx | 2 +- .../create-service-account-dialog.tsx | 2 +- .../components/+workloads-cronjobs/cronjobs.tsx | 6 +++--- .../+workloads-deployments/deployments.tsx | 4 ++-- .../+workloads-pods/pod-container-port.tsx | 2 +- .../+workloads-replicasets/replicasets.tsx | 2 +- .../+workloads-statefulsets/statefulsets.tsx | 2 +- src/renderer/components/dock/create-resource.tsx | 2 +- src/renderer/components/dock/dock-tab.tsx | 2 +- src/renderer/components/dock/edit-resource.tsx | 4 ++-- src/renderer/components/dock/install-chart.tsx | 16 ++++++++-------- src/renderer/components/dock/log-search.tsx | 4 ++-- src/renderer/components/dock/upgrade-chart.tsx | 4 ++-- .../components/error-boundary/error-boundary.tsx | 2 +- src/renderer/components/menu/menu-actions.tsx | 4 ++-- 35 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/renderer/components/+apps-helm-charts/helm-chart-details.tsx b/src/renderer/components/+apps-helm-charts/helm-chart-details.tsx index a33e9d2503..d31efc5438 100644 --- a/src/renderer/components/+apps-helm-charts/helm-chart-details.tsx +++ b/src/renderer/components/+apps-helm-charts/helm-chart-details.tsx @@ -82,9 +82,9 @@ export class HelmChartDetails extends Component {
{selectedChart.getDescription()} -
- + this.quotaName = v.toLowerCase()} className="box grow" @@ -156,7 +156,7 @@ export class AddQuotaDialog extends React.Component { this.namespace = value} @@ -167,14 +167,14 @@ export class AddQuotaDialog extends React.Component { this.quotaInputValue = v} onKeyDown={this.onInputQuota} @@ -183,7 +183,7 @@ export class AddQuotaDialog extends React.Component {
diff --git a/src/renderer/components/+config-secrets/add-secret-dialog.tsx b/src/renderer/components/+config-secrets/add-secret-dialog.tsx index 5071c908bf..2fae17d30d 100644 --- a/src/renderer/components/+config-secrets/add-secret-dialog.tsx +++ b/src/renderer/components/+config-secrets/add-secret-dialog.tsx @@ -133,7 +133,7 @@ export class AddSecretDialog extends React.Component { this.addField(field)} /> @@ -146,7 +146,7 @@ export class AddSecretDialog extends React.Component {
{ multiLine maxRows={5} required={required} className="value" - placeholder={`Value`} + placeholder="Value" value={value} onChange={v => item.value = v} /> { this.name = v} /> diff --git a/src/renderer/components/+config-secrets/secret-details.tsx b/src/renderer/components/+config-secrets/secret-details.tsx index 92a58141ac..ab7bc59e46 100644 --- a/src/renderer/components/+config-secrets/secret-details.tsx +++ b/src/renderer/components/+config-secrets/secret-details.tsx @@ -69,7 +69,7 @@ export class SecretDetails extends React.Component { {!isEmpty(this.data) && ( <> - + { Object.entries(this.data).map(([name, value]) => { const revealSecret = this.revealSecret[name]; @@ -107,7 +107,7 @@ export class SecretDetails extends React.Component { }
diff --git a/src/renderer/components/+network-services/service-port-component.tsx b/src/renderer/components/+network-services/service-port-component.tsx index 6946137bde..77070390e0 100644 --- a/src/renderer/components/+network-services/service-port-component.tsx +++ b/src/renderer/components/+network-services/service-port-component.tsx @@ -37,7 +37,7 @@ export class ServicePortComponent extends React.Component { return (
- this.portForward() }> + this.portForward() }> {port.toString()} {this.waiting && ( diff --git a/src/renderer/components/+preferences/add-helm-repo-dialog.tsx b/src/renderer/components/+preferences/add-helm-repo-dialog.tsx index f2c8388247..a0ede38ece 100644 --- a/src/renderer/components/+preferences/add-helm-repo-dialog.tsx +++ b/src/renderer/components/+preferences/add-helm-repo-dialog.tsx @@ -111,7 +111,7 @@ export class AddHelmRepoDialog extends React.Component { <> this.helmRepo.insecureSkipTlsVerify = v} /> @@ -120,12 +120,12 @@ export class AddHelmRepoDialog extends React.Component { {this.renderFileInput(`Cerificate file`, FileType.CertFile, AddHelmRepoDialog.certExtensions)} this.helmRepo.username = v} /> this.helmRepo.password = v} /> ); @@ -148,13 +148,13 @@ export class AddHelmRepoDialog extends React.Component {
this.helmRepo.name = v} /> this.helmRepo.url = v} /> @@ -162,7 +162,7 @@ export class AddHelmRepoDialog extends React.Component { More diff --git a/src/renderer/components/+preferences/preferences.tsx b/src/renderer/components/+preferences/preferences.tsx index 56e881d9f5..b0442f45b6 100644 --- a/src/renderer/components/+preferences/preferences.tsx +++ b/src/renderer/components/+preferences/preferences.tsx @@ -122,7 +122,7 @@ export class Preferences extends React.Component {

HTTP Proxy

this.httpProxy = v} onBlur={() => preferences.httpsProxy = this.httpProxy} diff --git a/src/renderer/components/+storage-classes/storage-class-details.tsx b/src/renderer/components/+storage-classes/storage-class-details.tsx index 95f09bc6bf..4ee8a197f2 100644 --- a/src/renderer/components/+storage-classes/storage-class-details.tsx +++ b/src/renderer/components/+storage-classes/storage-class-details.tsx @@ -45,7 +45,7 @@ export class StorageClassDetails extends React.Component { )} {parameters && ( <> - + { Object.entries(parameters).map(([name, value]) => ( diff --git a/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx b/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx index ea1ec71778..cbf8a9d4fc 100644 --- a/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx +++ b/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx @@ -71,7 +71,7 @@ export class PersistentVolumeClaimDetails extends React.Component { {volumeClaim.getStatus()} - + {volumeClaim.getMatchLabels().map(label => )} diff --git a/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx b/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx index 85d27243c2..e7a676365c 100644 --- a/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx +++ b/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx @@ -205,7 +205,7 @@ export class AddRoleBindingDialog extends React.Component { this.bindingName = v} @@ -239,7 +239,7 @@ export class AddRoleBindingDialog extends React.Component { this.roleName = v} diff --git a/src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx b/src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx index c56888f6f4..e9a27979cf 100644 --- a/src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx +++ b/src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx @@ -66,7 +66,7 @@ export class CreateServiceAccountDialog extends React.Component { this.name = v.toLowerCase()} /> diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx index 08b84c0671..5af1b5f248 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx @@ -87,7 +87,7 @@ export function CronJobMenu(props: KubeObjectMenuProps) { return ( <> CronJobTriggerDialog.open(object)}> - + Trigger @@ -106,7 +106,7 @@ export function CronJobMenu(props: KubeObjectMenuProps) { Resume CronJob {object.getName()}?

), })}> - + Resume @@ -124,7 +124,7 @@ export function CronJobMenu(props: KubeObjectMenuProps) { Suspend CronJob {object.getName()}?

), })}> - + Suspend } diff --git a/src/renderer/components/+workloads-deployments/deployments.tsx b/src/renderer/components/+workloads-deployments/deployments.tsx index 0147c238b9..5281307539 100644 --- a/src/renderer/components/+workloads-deployments/deployments.tsx +++ b/src/renderer/components/+workloads-deployments/deployments.tsx @@ -104,7 +104,7 @@ export function DeploymentMenu(props: KubeObjectMenuProps) { return ( <> DeploymentScaleDialog.open(object)}> - + Scale ConfirmDialog.open({ @@ -126,7 +126,7 @@ export function DeploymentMenu(props: KubeObjectMenuProps) {

), })}> - + Restart
diff --git a/src/renderer/components/+workloads-pods/pod-container-port.tsx b/src/renderer/components/+workloads-pods/pod-container-port.tsx index ce5c27c462..4f2124ec3d 100644 --- a/src/renderer/components/+workloads-pods/pod-container-port.tsx +++ b/src/renderer/components/+workloads-pods/pod-container-port.tsx @@ -43,7 +43,7 @@ export class PodContainerPort extends React.Component { return (
- this.portForward() }> + this.portForward() }> {text} {this.waiting && ( diff --git a/src/renderer/components/+workloads-replicasets/replicasets.tsx b/src/renderer/components/+workloads-replicasets/replicasets.tsx index fa6ee5cef4..160e5d1917 100644 --- a/src/renderer/components/+workloads-replicasets/replicasets.tsx +++ b/src/renderer/components/+workloads-replicasets/replicasets.tsx @@ -78,7 +78,7 @@ export function ReplicaSetMenu(props: KubeObjectMenuProps) { return ( <> ReplicaSetScaleDialog.open(object)}> - + Scale diff --git a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx index 7c91c9905c..1438e936c7 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx +++ b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx @@ -83,7 +83,7 @@ export function StatefulSetMenu(props: KubeObjectMenuProps) { return ( <> StatefulSetScaleDialog.open(object)}> - + Scale diff --git a/src/renderer/components/dock/create-resource.tsx b/src/renderer/components/dock/create-resource.tsx index 100ef6a3ae..8ee859d2cf 100644 --- a/src/renderer/components/dock/create-resource.tsx +++ b/src/renderer/components/dock/create-resource.tsx @@ -77,7 +77,7 @@ export class CreateResource extends React.Component { tabId={tabId} error={error} submit={create} - submitLabel={`Create`} + submitLabel="Create" showNotifications={false} /> { {!pinned && ( )} diff --git a/src/renderer/components/dock/edit-resource.tsx b/src/renderer/components/dock/edit-resource.tsx index e33e379620..104f8f1ea3 100644 --- a/src/renderer/components/dock/edit-resource.tsx +++ b/src/renderer/components/dock/edit-resource.tsx @@ -98,8 +98,8 @@ export class EditResource extends React.Component { tabId={tabId} error={error} submit={save} - submitLabel={`Save`} - submittingMessage={`Applying..`} + submitLabel="Save" + submittingMessage="Applying.." controls={(
Kind: diff --git a/src/renderer/components/dock/install-chart.tsx b/src/renderer/components/dock/install-chart.tsx index b433874a75..4f651f7a3a 100644 --- a/src/renderer/components/dock/install-chart.tsx +++ b/src/renderer/components/dock/install-chart.tsx @@ -125,17 +125,17 @@ export class InstallChart extends Component {
this.showNotes = false} logs={this.releaseDetails.log} @@ -148,7 +148,7 @@ export class InstallChart extends Component { const panelControls = (
Chart - + Version { controls={panelControls} error={this.error} submit={install} - submitLabel={`Install`} - submittingMessage={`Installing...`} + submitLabel="Install" + submittingMessage="Installing..." showSubmitClose={false} /> { /> diff --git a/src/renderer/components/dock/upgrade-chart.tsx b/src/renderer/components/dock/upgrade-chart.tsx index 41ce5d295e..c5253cb5b1 100644 --- a/src/renderer/components/dock/upgrade-chart.tsx +++ b/src/renderer/components/dock/upgrade-chart.tsx @@ -123,8 +123,8 @@ export class UpgradeChart extends React.Component { tabId={tabId} error={error} submit={upgrade} - submitLabel={`Upgrade`} - submittingMessage={`Updating..`} + submitLabel="Upgrade" + submittingMessage="Updating.." controls={controlsAndInfo} /> {
diff --git a/src/renderer/components/menu/menu-actions.tsx b/src/renderer/components/menu/menu-actions.tsx index 4e46935aa4..96a88bdf88 100644 --- a/src/renderer/components/menu/menu-actions.tsx +++ b/src/renderer/components/menu/menu-actions.tsx @@ -106,13 +106,13 @@ export class MenuActions extends React.Component { {children} {updateAction && ( - + Edit )} {removeAction && ( - + Remove )} From bae8c76a730528114002ac25361aa6f79789a549 Mon Sep 17 00:00:00 2001 From: Lauri Nevala Date: Tue, 23 Feb 2021 15:47:40 +0200 Subject: [PATCH 07/12] Display environment variables coming from secret in pod details (#2167) Signed-off-by: Lauri Nevala --- .../+workloads-pods/pod-container-env.tsx | 59 +++++++++++++++---- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/src/renderer/components/+workloads-pods/pod-container-env.tsx b/src/renderer/components/+workloads-pods/pod-container-env.tsx index 38af50a457..2e96b142f9 100644 --- a/src/renderer/components/+workloads-pods/pod-container-env.tsx +++ b/src/renderer/components/+workloads-pods/pod-container-env.tsx @@ -30,7 +30,11 @@ export const ContainerEnvironment = observer((props: Props) => { } }); envFrom && envFrom.forEach(item => { - const { configMapRef } = item; + const { configMapRef, secretRef } = item; + + if (secretRef && secretRef.name) { + secretsStore.load({ name: secretRef.name, namespace }); + } if (configMapRef && configMapRef.name) { configMapsStore.load({ name: configMapRef.name, namespace }); @@ -89,21 +93,54 @@ export const ContainerEnvironment = observer((props: Props) => { const renderEnvFrom = () => { const envVars = envFrom.map(vars => { - if (!vars.configMapRef || !vars.configMapRef.name) return; - const configMap = configMapsStore.getByName(vars.configMapRef.name, namespace); - - if (!configMap) return; - - return Object.entries(configMap.data).map(([name, value]) => ( -
- {name}: {value} -
- )); + if (vars.configMapRef?.name) { + return renderEnvFromConfigMap(vars.configMapRef.name); + } else if (vars.secretRef?.name ) { + return renderEnvFromSecret(vars.secretRef.name); + } }); return _.flatten(envVars); }; + const renderEnvFromConfigMap = (configMapName: string) => { + const configMap = configMapsStore.getByName(configMapName, namespace); + + if (!configMap) return; + + return Object.entries(configMap.data).map(([name, value]) => ( +
+ {name}: {value} +
+ )); + }; + + const renderEnvFromSecret = (secretName: string) => { + const secret = secretsStore.getByName(secretName, namespace); + + if (!secret) return; + + return Object.keys(secret.data).map(key => { + const secretKeyRef = { + name: secret.getName(), + key + }; + + const value = ( + + ); + + return ( +
+ {key}: {value} +
+ ); + }); + }; + return ( {env && renderEnv()} From cf7bed06f781c4000952f7f97415f60c131be348 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Wed, 24 Feb 2021 10:20:06 +0300 Subject: [PATCH 08/12] Fix: deprecated helm chart filtering (#2158) * Refactor of excludeDeprecated helm service method Signed-off-by: Alex Andreev * Pick first helm chart from the list on load Signed-off-by: Alex Andreev * Removing helm filtering in UI Signed-off-by: Alex Andreev * Cleaning up Signed-off-by: Alex Andreev * Cleaning up type definitions Signed-off-by: Alex Andreev * Adding sorting charts by version Signed-off-by: Alex Andreev * Adding tests for methods that manipute chart listing Signed-off-by: Alex Andreev * Cleaning up tests a bit Signed-off-by: Alex Andreev * Adding semver coercion before comparing versions Signed-off-by: Alex Andreev --- src/main/helm/__mocks__/helm-chart-manager.ts | 108 ++++++++++++++++++ src/main/helm/__tests__/helm-service.test.ts | 104 +++++++++++++++++ src/main/helm/helm-chart-manager.ts | 9 +- src/main/helm/helm-service.ts | 42 ++++--- src/renderer/api/endpoints/helm-charts.api.ts | 11 +- .../+apps-helm-charts/helm-charts.tsx | 3 - 6 files changed, 246 insertions(+), 31 deletions(-) create mode 100644 src/main/helm/__mocks__/helm-chart-manager.ts create mode 100644 src/main/helm/__tests__/helm-service.test.ts diff --git a/src/main/helm/__mocks__/helm-chart-manager.ts b/src/main/helm/__mocks__/helm-chart-manager.ts new file mode 100644 index 0000000000..e832a937cb --- /dev/null +++ b/src/main/helm/__mocks__/helm-chart-manager.ts @@ -0,0 +1,108 @@ +import { HelmRepo, HelmRepoManager } from "../helm-repo-manager"; + +export class HelmChartManager { + private cache: any = {}; + private repo: HelmRepo; + + constructor(repo: HelmRepo){ + this.cache = HelmRepoManager.cache; + this.repo = repo; + } + + public async charts(): Promise { + switch (this.repo.name) { + case "stable": + return Promise.resolve({ + "apm-server": [ + { + apiVersion: "3.0.0", + name: "apm-server", + version: "2.1.7", + repo: "stable", + digest: "test" + }, + { + apiVersion: "3.0.0", + name: "apm-server", + version: "2.1.6", + repo: "stable", + digest: "test" + } + ], + "redis": [ + { + apiVersion: "3.0.0", + name: "apm-server", + version: "1.0.0", + repo: "stable", + digest: "test" + }, + { + apiVersion: "3.0.0", + name: "apm-server", + version: "0.0.9", + repo: "stable", + digest: "test" + } + ] + }); + case "experiment": + return Promise.resolve({ + "fairwind": [ + { + apiVersion: "3.0.0", + name: "fairwind", + version: "0.0.1", + repo: "experiment", + digest: "test" + }, + { + apiVersion: "3.0.0", + name: "fairwind", + version: "0.0.2", + repo: "experiment", + digest: "test", + deprecated: true + } + ] + }); + case "bitnami": + return Promise.resolve({ + "hotdog": [ + { + apiVersion: "3.0.0", + name: "hotdog", + version: "1.0.1", + repo: "bitnami", + digest: "test" + }, + { + apiVersion: "3.0.0", + name: "hotdog", + version: "1.0.2", + repo: "bitnami", + digest: "test", + } + ], + "pretzel": [ + { + apiVersion: "3.0.0", + name: "pretzel", + version: "1.0", + repo: "bitnami", + digest: "test", + }, + { + apiVersion: "3.0.0", + name: "pretzel", + version: "1.0.1", + repo: "bitnami", + digest: "test" + } + ] + }); + default: + return Promise.resolve({}); + } + } +} diff --git a/src/main/helm/__tests__/helm-service.test.ts b/src/main/helm/__tests__/helm-service.test.ts new file mode 100644 index 0000000000..8c1e82ef0a --- /dev/null +++ b/src/main/helm/__tests__/helm-service.test.ts @@ -0,0 +1,104 @@ +import { helmService } from "../helm-service"; +import { repoManager } from "../helm-repo-manager"; + +jest.spyOn(repoManager, "init").mockImplementation(); + +jest.mock("../helm-chart-manager"); + +describe("Helm Service tests", () => { + test("list charts without deprecated ones", async () => { + jest.spyOn(repoManager, "repositories").mockImplementation(async () => { + return [ + { name: "stable", url: "stableurl" }, + { name: "experiment", url: "experimenturl" } + ]; + }); + + const charts = await helmService.listCharts(); + + expect(charts).toEqual({ + stable: { + "apm-server": [ + { + apiVersion: "3.0.0", + name: "apm-server", + version: "2.1.7", + repo: "stable", + digest: "test" + }, + { + apiVersion: "3.0.0", + name: "apm-server", + version: "2.1.6", + repo: "stable", + digest: "test" + } + ], + "redis": [ + { + apiVersion: "3.0.0", + name: "apm-server", + version: "1.0.0", + repo: "stable", + digest: "test" + }, + { + apiVersion: "3.0.0", + name: "apm-server", + version: "0.0.9", + repo: "stable", + digest: "test" + } + ] + }, + experiment: {} + }); + }); + + test("list charts sorted by version in descending order", async () => { + jest.spyOn(repoManager, "repositories").mockImplementation(async () => { + return [ + { name: "bitnami", url: "bitnamiurl" } + ]; + }); + + const charts = await helmService.listCharts(); + + expect(charts).toEqual({ + bitnami: { + "hotdog": [ + { + apiVersion: "3.0.0", + name: "hotdog", + version: "1.0.2", + repo: "bitnami", + digest: "test", + }, + { + apiVersion: "3.0.0", + name: "hotdog", + version: "1.0.1", + repo: "bitnami", + digest: "test" + }, + ], + "pretzel": [ + { + apiVersion: "3.0.0", + name: "pretzel", + version: "1.0.1", + repo: "bitnami", + digest: "test", + }, + { + apiVersion: "3.0.0", + name: "pretzel", + version: "1.0", + repo: "bitnami", + digest: "test" + } + ] + } + }); + }); +}); diff --git a/src/main/helm/helm-chart-manager.ts b/src/main/helm/helm-chart-manager.ts index cf4a8e5ace..69619a56d4 100644 --- a/src/main/helm/helm-chart-manager.ts +++ b/src/main/helm/helm-chart-manager.ts @@ -4,9 +4,10 @@ import { HelmRepo, HelmRepoManager } from "./helm-repo-manager"; import logger from "../logger"; import { promiseExec } from "../promise-exec"; import { helmCli } from "./helm-cli"; +import type { RepoHelmChartList } from "../../renderer/api/endpoints/helm-charts.api"; type CachedYaml = { - entries: any; // todo: types + entries: RepoHelmChartList }; export class HelmChartManager { @@ -24,15 +25,15 @@ export class HelmChartManager { return charts[name]; } - public async charts(): Promise { + public async charts(): Promise { try { const cachedYaml = await this.cachedYaml(); return cachedYaml["entries"]; } catch(error) { - logger.error(error); + logger.error("HELM-CHART-MANAGER]: failed to list charts", { error }); - return []; + return {}; } } diff --git a/src/main/helm/helm-service.ts b/src/main/helm/helm-service.ts index 1918268075..f7445cebd4 100644 --- a/src/main/helm/helm-service.ts +++ b/src/main/helm/helm-service.ts @@ -1,8 +1,10 @@ +import semver from "semver"; import { Cluster } from "../cluster"; import logger from "../logger"; import { repoManager } from "./helm-repo-manager"; import { HelmChartManager } from "./helm-chart-manager"; import { releaseManager } from "./helm-release-manager"; +import { HelmChartList, RepoHelmChartList } from "../../renderer/api/endpoints/helm-charts.api"; class HelmService { public async installChart(cluster: Cluster, data: { chart: string; values: {}; name: string; namespace: string; version: string }) { @@ -10,7 +12,7 @@ class HelmService { } public async listCharts() { - const charts: any = {}; + const charts: HelmChartList = {}; await repoManager.init(); const repositories = await repoManager.repositories(); @@ -18,14 +20,10 @@ class HelmService { for (const repo of repositories) { charts[repo.name] = {}; const manager = new HelmChartManager(repo); - let entries = await manager.charts(); + const sortedCharts = this.sortChartsByVersion(await manager.charts()); + const enabledCharts = this.excludeDeprecatedChartGroups(sortedCharts); - entries = this.excludeDeprecated(entries); - - for (const key in entries) { - entries[key] = entries[key][0]; - } - charts[repo.name] = entries; + charts[repo.name] = enabledCharts; } return charts; @@ -96,20 +94,30 @@ class HelmService { return { message: output }; } - protected excludeDeprecated(entries: any) { - for (const key in entries) { - entries[key] = entries[key].filter((entry: any) => { - if (Array.isArray(entry)) { - return entry[0]["deprecated"] != true; - } + private excludeDeprecatedChartGroups(chartGroups: RepoHelmChartList) { + const groups = new Map(Object.entries(chartGroups)); - return entry["deprecated"] != true; + for (const [chartName, charts] of groups) { + if (charts[0].deprecated) { + groups.delete(chartName); + } + } + + return Object.fromEntries(groups); + } + + private sortChartsByVersion(chartGroups: RepoHelmChartList) { + for (const key in chartGroups) { + chartGroups[key] = chartGroups[key].sort((first, second) => { + const firstVersion = semver.coerce(first.version || 0); + const secondVersion = semver.coerce(second.version || 0); + + return semver.compare(secondVersion, firstVersion); }); } - return entries; + return chartGroups; } - } export const helmService = new HelmService(); diff --git a/src/renderer/api/endpoints/helm-charts.api.ts b/src/renderer/api/endpoints/helm-charts.api.ts index 8beff01779..02b5b0dbee 100644 --- a/src/renderer/api/endpoints/helm-charts.api.ts +++ b/src/renderer/api/endpoints/helm-charts.api.ts @@ -3,11 +3,8 @@ import { apiBase } from "../index"; import { stringify } from "querystring"; import { autobind } from "../../utils"; -interface IHelmChartList { - [repo: string]: { - [name: string]: HelmChart; - }; -} +export type RepoHelmChartList = Record; +export type HelmChartList = Record; export interface IHelmChartDetails { readme: string; @@ -22,12 +19,12 @@ const endpoint = compile(`/v2/charts/:repo?/:name?`) as (params?: { export const helmChartsApi = { list() { return apiBase - .get(endpoint()) + .get(endpoint()) .then(data => { return Object .values(data) .reduce((allCharts, repoCharts) => allCharts.concat(Object.values(repoCharts)), []) - .map(HelmChart.create); + .map(([chart]) => HelmChart.create(chart)); }); }, diff --git a/src/renderer/components/+apps-helm-charts/helm-charts.tsx b/src/renderer/components/+apps-helm-charts/helm-charts.tsx index 22d5ba3a2f..8e4afe8d83 100644 --- a/src/renderer/components/+apps-helm-charts/helm-charts.tsx +++ b/src/renderer/components/+apps-helm-charts/helm-charts.tsx @@ -72,9 +72,6 @@ export class HelmCharts extends Component { (chart: HelmChart) => chart.getAppVersion(), (chart: HelmChart) => chart.getKeywords(), ]} - filterItems={[ - (items: HelmChart[]) => items.filter(item => !item.deprecated) - ]} customizeHeader={() => ( )} From a0c9dcad971ba899ee7b7834510e2916d2f44efd Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 24 Feb 2021 09:05:14 -0500 Subject: [PATCH 09/12] StatusBarRegistration's component field must be optional for backwards compatability (#2211) Signed-off-by: Sebastian Malton --- src/extensions/registries/status-bar-registry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/registries/status-bar-registry.ts b/src/extensions/registries/status-bar-registry.ts index 698c5f5f24..e0454fe77e 100644 --- a/src/extensions/registries/status-bar-registry.ts +++ b/src/extensions/registries/status-bar-registry.ts @@ -8,7 +8,7 @@ interface StatusBarComponents { } interface StatusBarRegistrationV2 { - components: StatusBarComponents; + components?: StatusBarComponents; // has to be optional for backwards compatability } export interface StatusBarRegistration extends StatusBarRegistrationV2 { From efd3c643de8b6988e561a6c5434370b864602e56 Mon Sep 17 00:00:00 2001 From: Lauri Nevala Date: Wed, 24 Feb 2021 16:10:47 +0200 Subject: [PATCH 10/12] Fix loading all namespaces for users with limited cluster access (#2217) Signed-off-by: Lauri Nevala --- src/renderer/api/kube-api.ts | 1 + src/renderer/components/+namespaces/namespace.store.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/renderer/api/kube-api.ts b/src/renderer/api/kube-api.ts index a880cc2406..448cd9da8f 100644 --- a/src/renderer/api/kube-api.ts +++ b/src/renderer/api/kube-api.ts @@ -272,6 +272,7 @@ export class KubeApi { } protected parseResponse(data: KubeJsonApiData | KubeJsonApiData[] | KubeJsonApiDataList, namespace?: string): any { + if (!data) return; const KubeObjectConstructor = this.objectConstructor; if (KubeObject.isJsonApiData(data)) { diff --git a/src/renderer/components/+namespaces/namespace.store.ts b/src/renderer/components/+namespaces/namespace.store.ts index ad271d1302..9995fbb7e5 100644 --- a/src/renderer/components/+namespaces/namespace.store.ts +++ b/src/renderer/components/+namespaces/namespace.store.ts @@ -120,7 +120,7 @@ export class NamespaceStore extends KubeObjectStore { protected async loadItems(params: KubeObjectStoreLoadingParams) { const { allowedNamespaces } = this; - let namespaces = await super.loadItems(params); + let namespaces = (await super.loadItems(params)) || []; namespaces = namespaces.filter(namespace => allowedNamespaces.includes(namespace.getName())); From ceeeb2473f58c77f5ee5a47e873737d7c3cf818d Mon Sep 17 00:00:00 2001 From: Rafed Ramzi <68276604+rafedramzi@users.noreply.github.com> Date: Thu, 18 Feb 2021 21:42:23 +0700 Subject: [PATCH 11/12] Fix Documentation: fix broken link in getting-started page (#2155) Signed-off-by: Rafed Ramzi --- docs/getting-started/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 704e59d97b..bfb990378d 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -5,7 +5,7 @@ Lens is lightweight and simple to install. You'll be up and running in just a fe ## System Requirements -Review the [System Requirements](/supporting/requirements/) to check if your computer configuration is supported. +Review the [System Requirements](../supporting/requirements.md) to check if your computer configuration is supported. ## macOS From 0a7ace772fb56318868d150f734b4d5a10b17f4f Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Wed, 24 Feb 2021 16:51:28 +0200 Subject: [PATCH 12/12] v4.1.3 Signed-off-by: Jari Kolehmainen --- package.json | 2 +- static/RELEASE_NOTES.md | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 99340c44ed..3d4c660e74 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "kontena-lens", "productName": "Lens", "description": "Lens - The Kubernetes IDE", - "version": "4.1.2", + "version": "4.1.3", "main": "static/build/main.js", "copyright": "© 2020, Mirantis, Inc.", "license": "MIT", diff --git a/static/RELEASE_NOTES.md b/static/RELEASE_NOTES.md index bb855fab08..60dcf8ec4e 100644 --- a/static/RELEASE_NOTES.md +++ b/static/RELEASE_NOTES.md @@ -2,7 +2,17 @@ Here you can find description of changes we've built into each release. While we try our best to make each upgrade automatic and as smooth as possible, there may be some cases where you might need to do something to ensure the application works smoothly. So please read through the release highlights! -## 4.1.2 (current version) +## 4.1.3 (current version) + +- Don't reset selected namespaces to defaults in case of "All namespaces" on page reload +- Fix loading all namespaces for users with limited cluster access +- Display environment variables coming from secret in pod details +- Fix deprecated helm chart filtering +- Fix RoleBindings Namespace and Bindings field not displaying the correct data +- Fix RoleBindingDetails not rendering the name of the role binding +- Fix auto update on quit with newer version + +## 4.1.2 **Upgrade note:** Where have all my pods gone? Namespaced Kubernetes resources are now initially shown only for the "default" namespace. Use the namespaces selector to add more.