diff --git a/integration/specs/app_spec.ts b/integration/specs/app_spec.ts
index 02024d1647..5cde34afb5 100644
--- a/integration/specs/app_spec.ts
+++ b/integration/specs/app_spec.ts
@@ -1,99 +1,473 @@
+/*
+ Cluster tests are run if there is a pre-existing minikube cluster. Before running cluster tests the TEST_NAMESPACE
+ namespace is removed, if it exists, from the minikube cluster. Resources are created as part of the cluster tests in the
+ TEST_NAMESPACE namespace. This is done to minimize destructive impact of the cluster tests on an existing minikube
+ cluster and vice versa.
+*/
import { Application } from "spectron"
import * as util from "../helpers/utils"
import { spawnSync } from "child_process"
-jest.setTimeout(30000)
+const describeif = (condition : boolean) => condition ? describe : describe.skip
+const itif = (condition : boolean) => condition ? it : it.skip
-const BACKSPACE = "\uE003"
+jest.setTimeout(60000)
-describe("app start", () => {
+describe("Lens integration tests", () => {
+ const TEST_NAMESPACE = "integration-tests"
+
+ const BACKSPACE = "\uE003"
let app: Application
+
+ const appStart = async () => {
+ app = util.setup()
+ await app.start()
+ // Wait for splash screen to be closed
+ while (await app.client.getWindowCount() > 1);
+ await app.client.windowByIndex(0)
+ await app.client.waitUntilWindowLoaded()
+ }
+
const clickWhatsNew = async (app: Application) => {
await app.client.waitUntilTextExists("h1", "What's new")
await app.client.click("button.primary")
await app.client.waitUntilTextExists("h1", "Welcome")
}
+ describe("app start", () => {
+ beforeAll(appStart, 20000)
+
+ afterAll(async () => {
+ if (app && app.isRunning()) {
+ return util.tearDown(app)
+ }
+ })
+
+ it('shows "whats new"', async () => {
+ await clickWhatsNew(app)
+ })
+
+ // Todo figure out how to access main menu to get these to work
+ it.skip('shows "add cluster"', async () => {
+ await app.client.keys(['Shift', 'Meta', 'A'])
+ await app.client.waitUntilTextExists("h2", "Add Cluster")
+ await app.client.keys(['Shift', 'Meta'])
+ })
+
+ it.skip('shows "preferences"', async () => {
+ await app.client.keys(['Meta', ','])
+ await app.client.waitUntilTextExists("h2", "Preferences")
+ await app.client.keys('Meta')
+ })
+
+ it.skip('quits Lens"', async () => {
+ await app.client.keys(['Meta', 'Q'])
+ await app.client.keys('Meta')
+ })
+ })
+
+ const minikubeReady = (): boolean => {
+ // determine if minikube is running
+ let status = spawnSync("minikube status", { shell: true })
+ if (status.status !== 0) {
+ console.warn("minikube not running")
+ return false
+ }
+
+ // Remove TEST_NAMESPACE if it already exists
+ status = spawnSync(`minikube kubectl -- get namespace ${TEST_NAMESPACE}`, { shell: true })
+ if (status.status === 0) {
+ console.warn(`Removing existing ${TEST_NAMESPACE} namespace`)
+ status = spawnSync(`minikube kubectl -- delete namespace ${TEST_NAMESPACE}`, { shell: true })
+ if (status.status !== 0) {
+ console.warn(`Error removing ${TEST_NAMESPACE} namespace: ${status.stderr.toString()}`)
+ return false
+ }
+ console.log(status.stdout.toString())
+ }
+ return true
+ }
+ const ready = minikubeReady()
+
const addMinikubeCluster = async (app: Application) => {
await app.client.click("div.add-cluster")
await app.client.waitUntilTextExists("div", "Select kubeconfig file")
- await app.client.click("button.primary")
+ await app.client.click("div.Select__control") // show the context drop-down list
+ await app.client.waitUntilTextExists("div", "minikube")
+ if (!await app.client.$("button.primary").isEnabled()) {
+ await app.client.click("div.minikube") // select minikube context
+ } // else the only context, which must be 'minikube', is automatically selected
+ await app.client.click("div.Select__control") // hide the context drop-down list (it might be obscuring the Add cluster(s) button)
+ await app.client.click("button.primary") // add minikube cluster
}
const waitForMinikubeDashboard = async (app: Application) => {
await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started")
- await app.client.getWindowCount()
await app.client.waitForExist(`iframe[name="minikube"]`)
await app.client.frame("minikube")
await app.client.waitUntilTextExists("span.link-text", "Cluster")
}
- beforeEach(async () => {
- app = util.setup()
- await app.start()
- await app.client.waitUntilWindowLoaded()
- // Wait for splash screen to be closed
- while (await app.client.getWindowCount() > 1);
- await app.client.windowByIndex(0)
- await app.client.waitUntilWindowLoaded()
- }, 20000)
+ describeif(ready)("cluster tests", () => {
+ let clusterAdded = false
- it('shows "whats new"', async () => {
- await clickWhatsNew(app)
- })
-
- it('allows to add a cluster', async () => {
- const status = spawnSync("minikube status", { shell: true })
- if (status.status !== 0) {
- console.warn("minikube not running, skipping test")
- return
+ const addCluster = async () => {
+ await clickWhatsNew(app)
+ await addMinikubeCluster(app)
+ await waitForMinikubeDashboard(app)
+ await app.client.click('a[href="/nodes"]')
+ await app.client.waitUntilTextExists("div.TableCell", "Ready")
}
- await clickWhatsNew(app)
- await addMinikubeCluster(app)
- await waitForMinikubeDashboard(app)
- await app.client.click('a[href="/nodes"]')
- await app.client.waitUntilTextExists("div.TableCell", "Ready")
- })
- it('allows to create a pod', async () => {
- const status = spawnSync("minikube status", { shell: true })
- if (status.status !== 0) {
- console.warn("minikube not running, skipping test")
- return
- }
- await clickWhatsNew(app)
- await addMinikubeCluster(app)
- await waitForMinikubeDashboard(app)
- await app.client.click(".sidebar-nav #workloads span.link-text")
- await app.client.waitUntilTextExists('a[href="/pods"]', "Pods")
- await app.client.click('a[href="/pods"]')
- await app.client.waitUntilTextExists("div.TableCell", "kube-apiserver")
- await app.client.click('.Icon.new-dock-tab')
- await app.client.waitUntilTextExists("li.MenuItem.create-resource-tab", "Create resource")
- await app.client.click("li.MenuItem.create-resource-tab")
- await app.client.waitForVisible(".CreateResource div.ace_content")
- // Write pod manifest to editor
- await app.client.keys("apiVersion: v1\n")
- await app.client.keys("kind: Pod\n")
- await app.client.keys("metadata:\n")
- await app.client.keys(" name: nginx\n")
- await app.client.keys(BACKSPACE + "spec:\n")
- await app.client.keys(" containers:\n")
- await app.client.keys("- name: nginx\n")
- await app.client.keys(" image: nginx:alpine\n")
- // Create deployent
- await app.client.waitForEnabled("button.Button=Create & Close")
- await app.client.click("button.Button=Create & Close")
- // Wait until first bits of pod appears on dashboard
- await app.client.waitForExist(".name=nginx")
- // Open pod details
- await app.client.click(".name=nginx")
- await app.client.waitUntilTextExists("div.drawer-title-text", "Pod: nginx")
- })
+ describe("cluster add", () => {
+ beforeAll(appStart, 20000)
- afterEach(async () => {
- if (app && app.isRunning()) {
- return util.tearDown(app)
+ afterAll(async () => {
+ if (app && app.isRunning()) {
+ return util.tearDown(app)
+ }
+ })
+
+ it('allows to add a cluster', async () => {
+ await addCluster()
+ clusterAdded = true
+ })
+ })
+
+ const appStartAddCluster = async () => {
+ if (clusterAdded) {
+ await appStart()
+ await addCluster()
+ }
}
- })
+
+ describe("cluster pages", () => {
+
+ beforeAll(appStartAddCluster, 40000)
+
+ afterAll(async () => {
+ if (app && app.isRunning()) {
+ return util.tearDown(app)
+ }
+ })
+
+ const tests : {
+ drawer?: string
+ drawerId?: string
+ pages: {
+ name: string,
+ href: string,
+ expectedSelector: string,
+ expectedText: string
+ }[]
+ }[] = [
+ {
+ drawer: "",
+ drawerId: "",
+ pages: [ {
+ name: "Cluster",
+ href: "cluster",
+ expectedSelector: "div.ClusterNoMetrics p",
+ expectedText: "Metrics are not available due"
+ }]
+ },
+ {
+ drawer: "",
+ drawerId: "",
+ pages: [ {
+ name: "Nodes",
+ href: "nodes",
+ expectedSelector: "h5.title",
+ expectedText: "Nodes"
+ }]
+ },
+ {
+ drawer: "Workloads",
+ drawerId: "workloads",
+ pages: [ {
+ name: "Overview",
+ href: "workloads",
+ expectedSelector: "h5.box",
+ expectedText: "Overview"
+ },
+ {
+ name: "Pods",
+ href: "pods",
+ expectedSelector: "h5.title",
+ expectedText: "Pods"
+ },
+ {
+ name: "Deployments",
+ href: "deployments",
+ expectedSelector: "h5.title",
+ expectedText: "Deployments"
+ },
+ {
+ name: "DaemonSets",
+ href: "daemonsets",
+ expectedSelector: "h5.title",
+ expectedText: "Daemon Sets"
+ },
+ {
+ name: "StatefulSets",
+ href: "statefulsets",
+ expectedSelector: "h5.title",
+ expectedText: "Stateful Sets"
+ },
+ {
+ name: "Jobs",
+ href: "jobs",
+ expectedSelector: "h5.title",
+ expectedText: "Jobs"
+ },
+ {
+ name: "CronJobs",
+ href: "cronjobs",
+ expectedSelector: "h5.title",
+ expectedText: "Cron Jobs"
+ } ]
+ },
+ {
+ drawer: "Configuration",
+ drawerId: "config",
+ pages: [ {
+ name: "ConfigMaps",
+ href: "configmaps",
+ expectedSelector: "h5.title",
+ expectedText: "Config Maps"
+ },
+ {
+ name: "Secrets",
+ href: "secrets",
+ expectedSelector: "h5.title",
+ expectedText: "Secrets"
+ },
+ {
+ name: "Resource Quotas",
+ href: "resourcequotas",
+ expectedSelector: "h5.title",
+ expectedText: "Resource Quotas"
+ },
+ {
+ name: "HPA",
+ href: "hpa",
+ expectedSelector: "h5.title",
+ expectedText: "Horizontal Pod Autoscalers"
+ },
+ {
+ name: "Pod Disruption Budgets",
+ href: "poddisruptionbudgets",
+ expectedSelector: "h5.title",
+ expectedText: "Pod Disruption Budgets"
+ } ]
+ },
+ {
+ drawer: "Network",
+ drawerId: "networks",
+ pages: [ {
+ name: "Services",
+ href: "services",
+ expectedSelector: "h5.title",
+ expectedText: "Services"
+ },
+ {
+ name: "Endpoints",
+ href: "endpoints",
+ expectedSelector: "h5.title",
+ expectedText: "Endpoints"
+ },
+ {
+ name: "Ingresses",
+ href: "ingresses",
+ expectedSelector: "h5.title",
+ expectedText: "Ingresses"
+ },
+ {
+ name: "Network Policies",
+ href: "network-policies",
+ expectedSelector: "h5.title",
+ expectedText: "Network Policies"
+ } ]
+ },
+ {
+ drawer: "Storage",
+ drawerId: "storage",
+ pages: [ {
+ name: "Persistent Volume Claims",
+ href: "persistent-volume-claims",
+ expectedSelector: "h5.title",
+ expectedText: "Persistent Volume Claims"
+ },
+ {
+ name: "Persistent Volumes",
+ href: "persistent-volumes",
+ expectedSelector: "h5.title",
+ expectedText: "Persistent Volumes"
+ },
+ {
+ name: "Storage Classes",
+ href: "storage-classes",
+ expectedSelector: "h5.title",
+ expectedText: "Storage Classes"
+ } ]
+ },
+ {
+ drawer: "",
+ drawerId: "",
+ pages: [ {
+ name: "Namespaces",
+ href: "namespaces",
+ expectedSelector: "h5.title",
+ expectedText: "Namespaces"
+ }]
+ },
+ {
+ drawer: "",
+ drawerId: "",
+ pages: [ {
+ name: "Events",
+ href: "events",
+ expectedSelector: "h5.title",
+ expectedText: "Events"
+ }]
+ },
+ {
+ drawer: "Apps",
+ drawerId: "apps",
+ pages: [ {
+ name: "Charts",
+ href: "apps/charts",
+ expectedSelector: "div.HelmCharts input",
+ expectedText: ""
+ },
+ {
+ name: "Releases",
+ href: "apps/releases",
+ expectedSelector: "h5.title",
+ expectedText: "Releases"
+ } ]
+ },
+ {
+ drawer: "Access Control",
+ drawerId: "users",
+ pages: [ {
+ name: "Service Accounts",
+ href: "service-accounts",
+ expectedSelector: "h5.title",
+ expectedText: "Service Accounts"
+ },
+ {
+ name: "Role Bindings",
+ href: "role-bindings",
+ expectedSelector: "h5.title",
+ expectedText: "Role Bindings"
+ },
+ {
+ name: "Roles",
+ href: "roles",
+ expectedSelector: "h5.title",
+ expectedText: "Roles"
+ },
+ {
+ name: "Pod Security Policies",
+ href: "pod-security-policies",
+ expectedSelector: "h5.title",
+ expectedText: "Pod Security Policies"
+ } ]
+ },
+ {
+ drawer: "Custom Resources",
+ drawerId: "custom-resources",
+ pages: [ {
+ name: "Definitions",
+ href: "crd/definitions",
+ expectedSelector: "h5.title",
+ expectedText: "Custom Resources"
+ } ]
+ },
+ ];
+ tests.forEach(({ drawer = "", drawerId = "", pages }) => {
+ if (drawer !== "") {
+ it(`shows ${drawer} drawer`, async () => {
+ expect(clusterAdded).toBe(true)
+ await app.client.click(`.sidebar-nav #${drawerId} span.link-text`)
+ await app.client.waitUntilTextExists(`a[href="/${pages[0].href}"]`, pages[0].name)
+ })
+ }
+ pages.forEach(({name, href, expectedSelector, expectedText}) => {
+ it(`shows ${drawer}->${name} page`, async () => {
+ expect(clusterAdded).toBe(true)
+ await app.client.click(`a[href="/${href}"]`)
+ await app.client.waitUntilTextExists(expectedSelector, expectedText)
+ })
+ })
+ if (drawer !== "") {
+ // hide the drawer
+ it(`hides ${drawer} drawer`, async () => {
+ expect(clusterAdded).toBe(true)
+ await app.client.click(`.sidebar-nav #${drawerId} span.link-text`)
+ await expect(app.client.waitUntilTextExists(`a[href="/${pages[0].href}"]`, pages[0].name, 100)).rejects.toThrow()
+ })
+ }
+ })
+ })
+
+ describe("cluster operations", () => {
+ beforeEach(appStartAddCluster, 40000)
+
+ afterEach(async () => {
+ if (app && app.isRunning()) {
+ return util.tearDown(app)
+ }
+ })
+
+ it('shows default namespace', async () => {
+ expect(clusterAdded).toBe(true)
+ await app.client.click('a[href="/namespaces"]')
+ await app.client.waitUntilTextExists("div.TableCell", "default")
+ await app.client.waitUntilTextExists("div.TableCell", "kube-system")
+ })
+
+ it(`creates ${TEST_NAMESPACE} namespace`, async () => {
+ expect(clusterAdded).toBe(true)
+ await app.client.click('a[href="/namespaces"]')
+ await app.client.waitUntilTextExists("div.TableCell", "default")
+ await app.client.waitUntilTextExists("div.TableCell", "kube-system")
+ await app.client.click("button.add-button")
+ await app.client.waitUntilTextExists("div.AddNamespaceDialog", "Create Namespace")
+ await app.client.keys(`${TEST_NAMESPACE}\n`)
+ await app.client.waitForExist(`.name=${TEST_NAMESPACE}`)
+ })
+
+ it(`creates a pod in ${TEST_NAMESPACE} namespace`, async () => {
+ expect(clusterAdded).toBe(true)
+ await app.client.click(".sidebar-nav #workloads span.link-text")
+ await app.client.waitUntilTextExists('a[href="/pods"]', "Pods")
+ await app.client.click('a[href="/pods"]')
+ await app.client.waitUntilTextExists("div.TableCell", "kube-apiserver")
+ await app.client.click('.Icon.new-dock-tab')
+ await app.client.waitUntilTextExists("li.MenuItem.create-resource-tab", "Create resource")
+ await app.client.click("li.MenuItem.create-resource-tab")
+ await app.client.waitForVisible(".CreateResource div.ace_content")
+ // Write pod manifest to editor
+ await app.client.keys("apiVersion: v1\n")
+ await app.client.keys("kind: Pod\n")
+ await app.client.keys("metadata:\n")
+ await app.client.keys(" name: nginx-create-pod-test\n")
+ await app.client.keys(`namespace: ${TEST_NAMESPACE}\n`)
+ await app.client.keys(BACKSPACE + "spec:\n")
+ await app.client.keys(" containers:\n")
+ await app.client.keys("- name: nginx-create-pod-test\n")
+ await app.client.keys(" image: nginx:alpine\n")
+ // Create deployment
+ await app.client.waitForEnabled("button.Button=Create & Close")
+ await app.client.click("button.Button=Create & Close")
+ // Wait until first bits of pod appears on dashboard
+ await app.client.waitForExist(".name=nginx-create-pod-test")
+ // Open pod details
+ await app.client.click(".name=nginx-create-pod-test")
+ await app.client.waitUntilTextExists("div.drawer-title-text", "Pod: nginx-create-pod-test")
+ })
+ })
+ })
})
diff --git a/locales/en/messages.po b/locales/en/messages.po
index 8ae622d54c..092161d0cf 100644
--- a/locales/en/messages.po
+++ b/locales/en/messages.po
@@ -25,7 +25,7 @@ msgstr ""
msgid "(as a percentage of request)"
msgstr "(as a percentage of request)"
-#: src/renderer/components/+workspaces/workspaces.tsx:108
+#: src/renderer/components/+workspaces/workspaces.tsx:121
msgid "(current)"
msgstr "(current)"
@@ -57,11 +57,11 @@ msgstr "<0>{0}0> successfully created"
#~ msgid "A HTTP proxy server URL (format: http://
:)"
#~ msgstr "A HTTP proxy server URL (format: http://:)"
-#: src/renderer/components/input/input.validators.ts:40
+#: src/renderer/components/input/input.validators.ts:46
msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics."
msgstr "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics."
-#: src/renderer/components/+workspaces/workspaces.tsx:84
+#: src/renderer/components/+workspaces/workspaces.tsx:93
msgid "A single workspaces contains a list of clusters and their full configuration."
msgstr "A single workspaces contains a list of clusters and their full configuration."
@@ -87,8 +87,8 @@ msgstr "Account Name"
msgid "Active"
msgstr "Active"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:303
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:118
+#: src/renderer/components/+add-cluster/add-cluster.tsx:288
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
msgid "Add Cluster"
msgstr "Add Cluster"
@@ -100,7 +100,7 @@ msgstr "Add Namespace"
msgid "Add RoleBinding"
msgstr "Add RoleBinding"
-#: src/renderer/components/+workspaces/workspaces.tsx:125
+#: src/renderer/components/+workspaces/workspaces.tsx:138
msgid "Add Workspace"
msgstr "Add Workspace"
@@ -112,7 +112,7 @@ msgstr "Add bindings to {name}"
#~ msgid "Add cluster"
#~ msgstr "Add cluster"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:320
+#: src/renderer/components/+add-cluster/add-cluster.tsx:305
msgid "Add cluster(s)"
msgstr "Add cluster(s)"
@@ -136,7 +136,7 @@ msgstr "Add field"
#~ msgid "Adding clusters: <0>{0}0>"
#~ msgstr "Adding clusters: <0>{0}0>"
-#: src/renderer/components/+preferences/preferences.tsx:103
+#: src/renderer/components/+preferences/preferences.tsx:111
msgid "Adding helm branch <0>{0}0> has failed: {1}"
msgstr "Adding helm branch <0>{0}0> has failed: {1}"
@@ -191,7 +191,7 @@ msgstr "Affinities"
msgid "Age"
msgstr "Age"
-#: src/renderer/components/+workspaces/workspaces.tsx:64
+#: src/renderer/components/+workspaces/workspaces.tsx:65
msgid "All clusters within workspace will be cleared as well"
msgstr "All clusters within workspace will be cleared as well"
@@ -219,11 +219,11 @@ msgstr "Allocatable"
msgid "Allow Privilege Escalation"
msgstr "Allow Privilege Escalation"
-#: src/renderer/components/+preferences/preferences.tsx:162
+#: src/renderer/components/+preferences/preferences.tsx:169
msgid "Allow telemetry & usage tracking"
msgstr "Allow telemetry & usage tracking"
-#: src/renderer/components/+preferences/preferences.tsx:154
+#: src/renderer/components/+preferences/preferences.tsx:161
msgid "Allow untrusted Certificate Authorities"
msgstr "Allow untrusted Certificate Authorities"
@@ -281,7 +281,7 @@ msgstr "Applying.."
msgid "Apps"
msgstr "Apps"
-#: src/renderer/components/+workspaces/workspaces.tsx:61
+#: src/renderer/components/+workspaces/workspaces.tsx:62
msgid "Are you sure you want remove workspace <0>{0}0>?"
msgstr "Are you sure you want remove workspace <0>{0}0>?"
@@ -293,7 +293,7 @@ msgstr "Are you sure you want to drain <0>{nodeName}0>?"
msgid "Arguments"
msgstr "Arguments"
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:108
+#: src/renderer/components/+landing-page/landing-page.tsx:27
msgid "Associate clusters and choose the ones you want to access via quick launch menu by clicking the + button."
msgstr "Associate clusters and choose the ones you want to access via quick launch menu by clicking the + button."
@@ -323,7 +323,7 @@ msgstr "Binding targets"
msgid "Bindings"
msgstr "Bindings"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:251
+#: src/renderer/components/+add-cluster/add-cluster.tsx:236
msgid "Browse"
msgstr "Browse"
@@ -402,7 +402,7 @@ msgstr "CPU requests"
msgid "CPU:"
msgstr "CPU:"
-#: src/renderer/components/+workspaces/workspaces.tsx:119
+#: src/renderer/components/+workspaces/workspaces.tsx:133
#: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44
#: src/renderer/components/dock/info-panel.tsx:97
#: src/renderer/components/wizard/wizard.tsx:130
@@ -422,7 +422,7 @@ msgstr "Cancel"
msgid "Capacity"
msgstr "Capacity"
-#: src/renderer/components/+preferences/preferences.tsx:153
+#: src/renderer/components/+preferences/preferences.tsx:160
msgid "Certificate Trust"
msgstr "Certificate Trust"
@@ -501,7 +501,7 @@ msgstr "Cluster IP"
msgid "Cluster Issuers"
msgstr "Cluster Issuers"
-#: src/renderer/components/+preferences/preferences.tsx:126
+#: src/renderer/components/+preferences/preferences.tsx:134
msgid "Color Theme"
msgstr "Color Theme"
@@ -712,7 +712,6 @@ msgid "Cron Jobs"
msgstr "Cron Jobs"
#: src/renderer/components/+workloads/workloads.tsx:77
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:67
msgid "CronJobs"
msgstr "CronJobs"
@@ -759,7 +758,6 @@ msgid "Daemon Sets"
msgstr "Daemon Sets"
#: src/renderer/components/+workloads/workloads.tsx:53
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:57
msgid "DaemonSets"
msgstr "DaemonSets"
@@ -784,11 +782,15 @@ msgstr "Default Add Capabilities"
msgid "Default Runtime Class Name"
msgstr "Default Runtime Class Name"
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:30
+msgid "Default:"
+msgstr "Default:"
+
#: src/renderer/components/+custom-resources/custom-resources.tsx:22
msgid "Definitions"
msgstr "Definitions"
-#: src/renderer/components/+workspaces/workspaces.tsx:113
+#: src/renderer/components/+workspaces/workspaces.tsx:126
#: src/renderer/components/menu/menu-actions.tsx:84
msgid "Delete"
msgstr "Delete"
@@ -799,12 +801,11 @@ msgstr "Deploy Revisions"
#: src/renderer/components/+workloads/workloads.tsx:45
#: src/renderer/components/+workloads-deployments/deployments.tsx:57
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:47
msgid "Deployments"
msgstr "Deployments"
#: src/renderer/components/+apps-helm-charts/helm-charts.tsx:65
-#: src/renderer/components/+workspaces/workspaces.tsx:118
+#: src/renderer/components/+workspaces/workspaces.tsx:131
msgid "Description"
msgstr "Description"
@@ -817,7 +818,7 @@ msgstr "Desired Healthy"
msgid "Desired number of replicas"
msgstr "Desired number of replicas"
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:64
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:65
msgid "Disconnect"
msgstr "Disconnect"
@@ -831,7 +832,7 @@ msgstr "Disk"
msgid "Disk:"
msgstr "Disk:"
-#: src/renderer/components/+preferences/preferences.tsx:158
+#: src/renderer/components/+preferences/preferences.tsx:165
msgid "Does not affect cluster communications!"
msgstr "Does not affect cluster communications!"
@@ -840,14 +841,22 @@ msgid "Domains"
msgstr "Domains"
#: src/renderer/components/+preferences/preferences.tsx:129
-msgid "Download Mirror"
-msgstr "Download Mirror"
+#~ msgid "Download Mirror"
+#~ msgstr "Download Mirror"
#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:90
msgid "Download file"
msgstr "Download file"
-#: src/renderer/components/+preferences/preferences.tsx:130
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:39
+msgid "Download kubectl binaries"
+msgstr "Download kubectl binaries"
+
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:37
+msgid "Download kubectl binaries matching to Kubernetes cluster verison."
+msgstr "Download kubectl binaries matching to Kubernetes cluster verison."
+
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:41
msgid "Download mirror for kubectl"
msgstr "Download mirror for kubectl"
@@ -873,7 +882,7 @@ msgstr "Duration"
msgid "E-mail"
msgstr "E-mail"
-#: src/renderer/components/+workspaces/workspaces.tsx:112
+#: src/renderer/components/+workspaces/workspaces.tsx:125
#: src/renderer/components/menu/menu-actions.tsx:80
#: src/renderer/components/menu/menu-actions.tsx:81
msgid "Edit"
@@ -1000,7 +1009,7 @@ msgstr "From <0>{from}0> to <1>{to}1>"
msgid "Fs Group"
msgstr "Fs Group"
-#: src/renderer/components/+landing-page/landing-page.tsx:23
+#: src/renderer/components/+landing-page/landing-page.tsx:37
msgid "Get started by associating one or more clusters to Lens."
msgstr "Get started by associating one or more clusters to Lens."
@@ -1022,7 +1031,7 @@ msgstr "Groups"
msgid "HPA"
msgstr "HPA"
-#: src/renderer/components/+preferences/preferences.tsx:147
+#: src/renderer/components/+preferences/preferences.tsx:137
msgid "HTTP Proxy"
msgstr "HTTP Proxy"
@@ -1030,7 +1039,7 @@ msgstr "HTTP Proxy"
#~ msgid "HTTP Proxy server. Used for communicating with Kubernetes API."
#~ msgstr "HTTP Proxy server. Used for communicating with Kubernetes API."
-#: src/renderer/components/+preferences/preferences.tsx:132
+#: src/renderer/components/+preferences/preferences.tsx:145
msgid "Helm"
msgstr "Helm"
@@ -1050,7 +1059,7 @@ msgstr "Helm Install: {repo}/{name}"
msgid "Helm Upgrade: {0}"
msgstr "Helm Upgrade: {0}"
-#: src/renderer/components/+preferences/preferences.tsx:47
+#: src/renderer/components/+preferences/preferences.tsx:51
msgid "Helm branch <0>{0}0> already in use"
msgstr "Helm branch <0>{0}0> already in use"
@@ -1157,11 +1166,11 @@ msgstr "Installation complete!"
msgid "Installing..."
msgstr "Installing..."
-#: src/renderer/components/input/input.validators.ts:44
+#: src/renderer/components/input/input.validators.ts:50
msgid "Invalid account ID"
msgstr "Invalid account ID"
-#: src/renderer/components/input/input.validators.ts:15
+#: src/renderer/components/input/input.validators.ts:16
msgid "Invalid number"
msgstr "Invalid number"
@@ -1197,7 +1206,6 @@ msgstr "Job name"
#: src/renderer/components/+workloads/workloads.tsx:69
#: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:62
#: src/renderer/components/+workloads-jobs/jobs.tsx:36
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:62
msgid "Jobs"
msgstr "Jobs"
@@ -1241,6 +1249,10 @@ msgstr "Kubeconfig"
msgid "Kubeconfig File"
msgstr "Kubeconfig File"
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:35
+msgid "Kubectl Binary"
+msgstr "Kubectl Binary"
+
#: src/renderer/components/+nodes/node-details.tsx:98
msgid "Kubelet version"
msgstr "Kubelet version"
@@ -1357,7 +1369,7 @@ msgstr "Max Pods"
msgid "Max Unavailable"
msgstr "Max Unavailable"
-#: src/renderer/components/input/input.validators.ts:35
+#: src/renderer/components/input/input.validators.ts:41
msgid "Maximum length is {maxLength}"
msgstr "Maximum length is {maxLength}"
@@ -1433,7 +1445,7 @@ msgstr "Min Pods"
msgid "Minimize"
msgstr "Minimize"
-#: src/renderer/components/input/input.validators.ts:30
+#: src/renderer/components/input/input.validators.ts:36
msgid "Minimum length is {minLength}"
msgstr "Minimum length is {minLength}"
@@ -1497,7 +1509,7 @@ msgstr "Mounts"
#: src/renderer/components/+workloads-pods/pods.tsx:74
#: src/renderer/components/+workloads-replicasets/replicasets.tsx:50
#: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40
-#: src/renderer/components/+workspaces/workspaces.tsx:117
+#: src/renderer/components/+workspaces/workspaces.tsx:130
#: src/renderer/components/dock/edit-resource.tsx:90
#: src/renderer/components/kube-object/kube-object-meta.tsx:20
msgid "Name"
@@ -1565,7 +1577,7 @@ msgstr "Namespaces"
msgid "Namespaces: {0}"
msgstr "Namespaces: {0}"
-#: src/renderer/components/+preferences/preferences.tsx:157
+#: src/renderer/components/+preferences/preferences.tsx:164
msgid "Needed with some corporate proxies that do certificate re-writing."
msgstr "Needed with some corporate proxies that do certificate re-writing."
@@ -1626,7 +1638,7 @@ msgstr "No Nodes Available."
#~ msgid "No contexts available or they already added"
#~ msgstr "No contexts available or they already added"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:275
+#: src/renderer/components/+add-cluster/add-cluster.tsx:260
msgid "No contexts available or they have been added already"
msgstr "No contexts available or they have been added already"
@@ -1742,7 +1754,7 @@ msgid "Organization"
msgstr "Organization"
#: src/renderer/components/+workloads/workloads.tsx:29
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:35
+#: src/renderer/components/+workloads-overview/overview-statuses.tsx:45
msgid "Overview"
msgstr "Overview"
@@ -1758,7 +1770,7 @@ msgstr "Parallelism"
msgid "Parameters"
msgstr "Parameters"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:245
+#: src/renderer/components/+add-cluster/add-cluster.tsx:230
msgid "Paste as text"
msgstr "Paste as text"
@@ -1848,7 +1860,6 @@ msgstr "Pod shell"
#: src/renderer/components/+workloads/workloads.tsx:37
#: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:47
#: src/renderer/components/+workloads-deployments/deployments.tsx:60
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:42
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:89
#: src/renderer/components/+workloads-pods/pods.tsx:73
#: src/renderer/components/+workloads-replicasets/replicasets.tsx:52
@@ -1899,7 +1910,7 @@ msgstr "Privileged"
#~ msgid "Pro-Tip: paste kubeconfig to collect available contexts"
#~ msgstr "Pro-Tip: paste kubeconfig to collect available contexts"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:263
+#: src/renderer/components/+add-cluster/add-cluster.tsx:248
msgid "Pro-Tip: paste kubeconfig to get available contexts"
msgstr "Pro-Tip: paste kubeconfig to get available contexts"
@@ -1907,7 +1918,7 @@ msgstr "Pro-Tip: paste kubeconfig to get available contexts"
#~ msgid "Pro-Tip: paste kubeconfig to parse available contexts"
#~ msgstr "Pro-Tip: paste kubeconfig to parse available contexts"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:254
+#: src/renderer/components/+add-cluster/add-cluster.tsx:239
msgid "Pro-Tip: you can also drag-n-drop kubeconfig file to this area"
msgstr "Pro-Tip: you can also drag-n-drop kubeconfig file to this area"
@@ -1924,11 +1935,11 @@ msgstr "Pro-Tip: you can also drag-n-drop kubeconfig file to this area"
msgid "Provisioner"
msgstr "Provisioner"
-#: src/renderer/components/+preferences/preferences.tsx:150
+#: src/renderer/components/+preferences/preferences.tsx:140
msgid "Proxy is used only for non-cluster communication."
msgstr "Proxy is used only for non-cluster communication."
-#: src/renderer/components/+add-cluster/add-cluster.tsx:308
+#: src/renderer/components/+add-cluster/add-cluster.tsx:293
msgid "Proxy settings"
msgstr "Proxy settings"
@@ -2008,10 +2019,10 @@ msgstr "Release: {0}"
msgid "Releases"
msgstr "Releases"
-#: src/renderer/components/+preferences/preferences.tsx:139
+#: src/renderer/components/+preferences/preferences.tsx:152
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:74
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:80
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:76
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:82
#: src/renderer/components/item-object-list/item-list-layout.tsx:179
#: src/renderer/components/menu/menu-actions.tsx:49
#: src/renderer/components/menu/menu-actions.tsx:85
@@ -2022,7 +2033,7 @@ msgstr "Remove"
msgid "Remove <0>{releaseNames}0>?"
msgstr "Remove <0>{releaseNames}0>?"
-#: src/renderer/components/+workspaces/workspaces.tsx:51
+#: src/renderer/components/+workspaces/workspaces.tsx:52
msgid "Remove Workspace"
msgstr "Remove Workspace"
@@ -2050,7 +2061,7 @@ msgstr "Remove selected items ({0})"
msgid "Remove {resourceKind} <0>{resourceName}0>?"
msgstr "Remove {resourceKind} <0>{resourceName}0>?"
-#: src/renderer/components/+preferences/preferences.tsx:114
+#: src/renderer/components/+preferences/preferences.tsx:122
msgid "Removing helm branch <0>{0}0> has failed: {1}"
msgstr "Removing helm branch <0>{0}0> has failed: {1}"
@@ -2074,7 +2085,7 @@ msgstr "Replicas"
msgid "Repo/Name"
msgstr "Repo/Name"
-#: src/renderer/components/+preferences/preferences.tsx:133
+#: src/renderer/components/+preferences/preferences.tsx:146
msgid "Repositories"
msgstr "Repositories"
@@ -2109,7 +2120,7 @@ msgstr "Required Drop Capabilities"
msgid "Required field"
msgstr "Required field"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:250
+#: src/renderer/components/+add-cluster/add-cluster.tsx:235
#: src/renderer/components/item-object-list/page-filters-list.tsx:31
msgid "Reset"
msgstr "Reset"
@@ -2252,7 +2263,7 @@ msgstr "Runtime Class"
#: src/renderer/components/+config-maps/config-map-details.tsx:78
#: src/renderer/components/+config-secrets/secret-details.tsx:97
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216
-#: src/renderer/components/+workspaces/workspaces.tsx:120
+#: src/renderer/components/+workspaces/workspaces.tsx:132
#: src/renderer/components/dock/edit-resource.tsx:88
msgid "Save"
msgstr "Save"
@@ -2341,13 +2352,13 @@ msgstr "Select a quota.."
#~ msgid "Select context(s)"
#~ msgstr "Select context(s)"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:272
-#~ msgid "Select contexts"
-#~ msgstr "Select contexts"
+#: src/renderer/components/+add-cluster/add-cluster.tsx:257
+msgid "Select contexts"
+msgstr "Select contexts"
#: src/renderer/components/+add-cluster/add-cluster.tsx:272
-msgid "Select contexts (available: {0})"
-msgstr "Select contexts (available: {0})"
+#~ msgid "Select contexts (available: {0})"
+#~ msgstr "Select contexts (available: {0})"
#: src/renderer/components/+add-cluster/add-cluster.tsx:76
#: src/renderer/components/+add-cluster/add-cluster.tsx:76
@@ -2371,7 +2382,7 @@ msgstr "Select custom kubeconfig file"
#~ msgid "Select kubeconfig"
#~ msgstr "Select kubeconfig"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:244
+#: src/renderer/components/+add-cluster/add-cluster.tsx:229
msgid "Select kubeconfig file"
msgstr "Select kubeconfig file"
@@ -2399,7 +2410,7 @@ msgstr "Select service accounts"
#~ msgid "Selected contexts ({0}): <0>{1}0>"
#~ msgstr "Selected contexts ({0}): <0>{1}0>"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:271
+#: src/renderer/components/+add-cluster/add-cluster.tsx:256
msgid "Selected contexts: <0>{0}0>"
msgstr "Selected contexts: <0>{0}0>"
@@ -2503,7 +2514,6 @@ msgid "Stateful Sets"
msgstr "Stateful Sets"
#: src/renderer/components/+workloads/workloads.tsx:61
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:52
msgid "StatefulSets"
msgstr "StatefulSets"
@@ -2605,11 +2615,11 @@ msgstr "TLS"
msgid "Taints"
msgstr "Taints"
-#: src/renderer/components/+preferences/preferences.tsx:161
+#: src/renderer/components/+preferences/preferences.tsx:168
msgid "Telemetry & Usage Tracking"
msgstr "Telemetry & Usage Tracking"
-#: src/renderer/components/+preferences/preferences.tsx:164
+#: src/renderer/components/+preferences/preferences.tsx:171
msgid "Telemetry & usage data is collected to continuously improve the Lens experience."
msgstr "Telemetry & usage data is collected to continuously improve the Lens experience."
@@ -2629,15 +2639,19 @@ msgstr "There are no logs available for container."
msgid "There are no logs available."
msgstr "There are no logs available."
-#: src/renderer/components/input/input.validators.ts:5
+#: src/renderer/components/input/input.validators.ts:6
msgid "This field is required"
msgstr "This field is required"
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:106
+#: src/renderer/components/input/input.validators.ts:31
+msgid "This field must be a valid path"
+msgstr "This field must be a valid path"
+
+#: src/renderer/components/+landing-page/landing-page.tsx:25
msgid "This is the quick launch menu."
msgstr "This is the quick launch menu."
-#: src/renderer/components/+preferences/preferences.tsx:156
+#: src/renderer/components/+preferences/preferences.tsx:163
msgid "This will make Lens to trust ANY certificate authority without any validations."
msgstr "This will make Lens to trust ANY certificate authority without any validations."
@@ -2661,13 +2675,13 @@ msgstr "Tolerations"
msgid "Transmit"
msgstr "Transmit"
-#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:107
+#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:106
#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:79
#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:80
msgid "Trigger"
msgstr "Trigger"
-#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:103
+#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:102
msgid "Trigger CronJob <0>{cronjobName}0>"
msgstr "Trigger CronJob <0>{cronjobName}0>"
@@ -2690,7 +2704,7 @@ msgstr "Trigger CronJob <0>{cronjobName}0>"
msgid "Type"
msgstr "Type"
-#: src/renderer/components/+preferences/preferences.tsx:148
+#: src/renderer/components/+preferences/preferences.tsx:138
msgid "Type HTTP proxy url (example: http://proxy.acme.org:8080)"
msgstr "Type HTTP proxy url (example: http://proxy.acme.org:8080)"
@@ -2827,11 +2841,11 @@ msgstr "Waiting services to be running"
msgid "Warnings: {0}"
msgstr "Warnings: {0}"
-#: src/renderer/components/+landing-page/landing-page.tsx:20
+#: src/renderer/components/+landing-page/landing-page.tsx:34
msgid "Welcome!"
msgstr "Welcome!"
-#: src/renderer/components/+workspaces/workspaces.tsx:79
+#: src/renderer/components/+workspaces/workspaces.tsx:88
msgid "What is a Workspace?"
msgstr "What is a Workspace?"
@@ -2844,19 +2858,19 @@ msgid "Workloads"
msgstr "Workloads"
#: src/renderer/components/+workspaces/workspace-menu.tsx:39
-#: src/renderer/components/+workspaces/workspaces.tsx:91
+#: src/renderer/components/+workspaces/workspaces.tsx:100
msgid "Workspaces"
msgstr "Workspaces"
-#: src/renderer/components/+workspaces/workspaces.tsx:81
+#: src/renderer/components/+workspaces/workspaces.tsx:90
msgid "Workspaces are used to organize number of clusters into logical groups."
msgstr "Workspaces are used to organize number of clusters into logical groups."
-#: src/renderer/components/input/input.validators.ts:10
+#: src/renderer/components/input/input.validators.ts:11
msgid "Wrong email format"
msgstr "Wrong email format"
-#: src/renderer/components/input/input.validators.ts:25
+#: src/renderer/components/input/input.validators.ts:26
msgid "Wrong url format"
msgstr "Wrong url format"
@@ -2915,7 +2929,7 @@ msgstr "listKind"
msgid "never"
msgstr "never"
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:121
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:133
msgid "new"
msgstr "new"
diff --git a/locales/fi/messages.po b/locales/fi/messages.po
index f64a50f272..363b9a49cc 100644
--- a/locales/fi/messages.po
+++ b/locales/fi/messages.po
@@ -25,7 +25,7 @@ msgstr ""
msgid "(as a percentage of request)"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:108
+#: src/renderer/components/+workspaces/workspaces.tsx:121
msgid "(current)"
msgstr ""
@@ -57,11 +57,11 @@ msgstr ""
#~ msgid "A HTTP proxy server URL (format: http://:)"
#~ msgstr ""
-#: src/renderer/components/input/input.validators.ts:40
+#: src/renderer/components/input/input.validators.ts:46
msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics."
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:84
+#: src/renderer/components/+workspaces/workspaces.tsx:93
msgid "A single workspaces contains a list of clusters and their full configuration."
msgstr ""
@@ -87,8 +87,8 @@ msgstr ""
msgid "Active"
msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:303
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:118
+#: src/renderer/components/+add-cluster/add-cluster.tsx:288
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
msgid "Add Cluster"
msgstr ""
@@ -100,7 +100,7 @@ msgstr ""
msgid "Add RoleBinding"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:125
+#: src/renderer/components/+workspaces/workspaces.tsx:138
msgid "Add Workspace"
msgstr ""
@@ -112,7 +112,7 @@ msgstr ""
#~ msgid "Add cluster"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:320
+#: src/renderer/components/+add-cluster/add-cluster.tsx:305
msgid "Add cluster(s)"
msgstr ""
@@ -136,7 +136,7 @@ msgstr ""
#~ msgid "Adding clusters: <0>{0}0>"
#~ msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:103
+#: src/renderer/components/+preferences/preferences.tsx:111
msgid "Adding helm branch <0>{0}0> has failed: {1}"
msgstr ""
@@ -191,7 +191,7 @@ msgstr ""
msgid "Age"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:64
+#: src/renderer/components/+workspaces/workspaces.tsx:65
msgid "All clusters within workspace will be cleared as well"
msgstr ""
@@ -219,11 +219,11 @@ msgstr ""
msgid "Allow Privilege Escalation"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:162
+#: src/renderer/components/+preferences/preferences.tsx:169
msgid "Allow telemetry & usage tracking"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:154
+#: src/renderer/components/+preferences/preferences.tsx:161
msgid "Allow untrusted Certificate Authorities"
msgstr ""
@@ -281,7 +281,7 @@ msgstr ""
msgid "Apps"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:61
+#: src/renderer/components/+workspaces/workspaces.tsx:62
msgid "Are you sure you want remove workspace <0>{0}0>?"
msgstr ""
@@ -293,7 +293,7 @@ msgstr ""
msgid "Arguments"
msgstr ""
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:108
+#: src/renderer/components/+landing-page/landing-page.tsx:27
msgid "Associate clusters and choose the ones you want to access via quick launch menu by clicking the + button."
msgstr ""
@@ -323,7 +323,7 @@ msgstr ""
msgid "Bindings"
msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:251
+#: src/renderer/components/+add-cluster/add-cluster.tsx:236
msgid "Browse"
msgstr ""
@@ -402,7 +402,7 @@ msgstr ""
msgid "CPU:"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:119
+#: src/renderer/components/+workspaces/workspaces.tsx:133
#: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44
#: src/renderer/components/dock/info-panel.tsx:97
#: src/renderer/components/wizard/wizard.tsx:130
@@ -422,7 +422,7 @@ msgstr ""
msgid "Capacity"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:153
+#: src/renderer/components/+preferences/preferences.tsx:160
msgid "Certificate Trust"
msgstr ""
@@ -497,7 +497,7 @@ msgstr ""
msgid "Cluster Issuers"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:126
+#: src/renderer/components/+preferences/preferences.tsx:134
msgid "Color Theme"
msgstr ""
@@ -708,7 +708,6 @@ msgid "Cron Jobs"
msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:77
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:67
msgid "CronJobs"
msgstr ""
@@ -755,7 +754,6 @@ msgid "Daemon Sets"
msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:53
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:57
msgid "DaemonSets"
msgstr ""
@@ -780,11 +778,15 @@ msgstr ""
msgid "Default Runtime Class Name"
msgstr ""
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:30
+msgid "Default:"
+msgstr ""
+
#: src/renderer/components/+custom-resources/custom-resources.tsx:22
msgid "Definitions"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:113
+#: src/renderer/components/+workspaces/workspaces.tsx:126
#: src/renderer/components/menu/menu-actions.tsx:84
msgid "Delete"
msgstr ""
@@ -795,12 +797,11 @@ msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:45
#: src/renderer/components/+workloads-deployments/deployments.tsx:57
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:47
msgid "Deployments"
msgstr ""
#: src/renderer/components/+apps-helm-charts/helm-charts.tsx:65
-#: src/renderer/components/+workspaces/workspaces.tsx:118
+#: src/renderer/components/+workspaces/workspaces.tsx:131
msgid "Description"
msgstr ""
@@ -813,7 +814,7 @@ msgstr ""
msgid "Desired number of replicas"
msgstr ""
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:64
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:65
msgid "Disconnect"
msgstr ""
@@ -827,7 +828,7 @@ msgstr ""
msgid "Disk:"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:158
+#: src/renderer/components/+preferences/preferences.tsx:165
msgid "Does not affect cluster communications!"
msgstr ""
@@ -836,14 +837,22 @@ msgid "Domains"
msgstr ""
#: src/renderer/components/+preferences/preferences.tsx:129
-msgid "Download Mirror"
-msgstr ""
+#~ msgid "Download Mirror"
+#~ msgstr ""
#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:90
msgid "Download file"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:130
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:39
+msgid "Download kubectl binaries"
+msgstr ""
+
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:37
+msgid "Download kubectl binaries matching to Kubernetes cluster verison."
+msgstr ""
+
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:41
msgid "Download mirror for kubectl"
msgstr ""
@@ -869,7 +878,7 @@ msgstr ""
msgid "E-mail"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:112
+#: src/renderer/components/+workspaces/workspaces.tsx:125
#: src/renderer/components/menu/menu-actions.tsx:80
#: src/renderer/components/menu/menu-actions.tsx:81
msgid "Edit"
@@ -991,7 +1000,7 @@ msgstr ""
msgid "Fs Group"
msgstr ""
-#: src/renderer/components/+landing-page/landing-page.tsx:23
+#: src/renderer/components/+landing-page/landing-page.tsx:37
msgid "Get started by associating one or more clusters to Lens."
msgstr ""
@@ -1013,7 +1022,7 @@ msgstr ""
msgid "HPA"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:147
+#: src/renderer/components/+preferences/preferences.tsx:137
msgid "HTTP Proxy"
msgstr ""
@@ -1021,7 +1030,7 @@ msgstr ""
#~ msgid "HTTP Proxy server. Used for communicating with Kubernetes API."
#~ msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:132
+#: src/renderer/components/+preferences/preferences.tsx:145
msgid "Helm"
msgstr ""
@@ -1041,7 +1050,7 @@ msgstr ""
msgid "Helm Upgrade: {0}"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:47
+#: src/renderer/components/+preferences/preferences.tsx:51
msgid "Helm branch <0>{0}0> already in use"
msgstr ""
@@ -1148,11 +1157,11 @@ msgstr ""
msgid "Installing..."
msgstr ""
-#: src/renderer/components/input/input.validators.ts:44
+#: src/renderer/components/input/input.validators.ts:50
msgid "Invalid account ID"
msgstr ""
-#: src/renderer/components/input/input.validators.ts:15
+#: src/renderer/components/input/input.validators.ts:16
msgid "Invalid number"
msgstr ""
@@ -1188,7 +1197,6 @@ msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:69
#: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:62
#: src/renderer/components/+workloads-jobs/jobs.tsx:36
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:62
msgid "Jobs"
msgstr ""
@@ -1232,6 +1240,10 @@ msgstr ""
msgid "Kubeconfig File"
msgstr ""
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:35
+msgid "Kubectl Binary"
+msgstr ""
+
#: src/renderer/components/+nodes/node-details.tsx:98
msgid "Kubelet version"
msgstr ""
@@ -1348,7 +1360,7 @@ msgstr ""
msgid "Max Unavailable"
msgstr ""
-#: src/renderer/components/input/input.validators.ts:35
+#: src/renderer/components/input/input.validators.ts:41
msgid "Maximum length is {maxLength}"
msgstr ""
@@ -1424,7 +1436,7 @@ msgstr ""
msgid "Minimize"
msgstr ""
-#: src/renderer/components/input/input.validators.ts:30
+#: src/renderer/components/input/input.validators.ts:36
msgid "Minimum length is {minLength}"
msgstr ""
@@ -1488,7 +1500,7 @@ msgstr ""
#: src/renderer/components/+workloads-pods/pods.tsx:74
#: src/renderer/components/+workloads-replicasets/replicasets.tsx:50
#: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40
-#: src/renderer/components/+workspaces/workspaces.tsx:117
+#: src/renderer/components/+workspaces/workspaces.tsx:130
#: src/renderer/components/dock/edit-resource.tsx:90
#: src/renderer/components/kube-object/kube-object-meta.tsx:20
msgid "Name"
@@ -1556,7 +1568,7 @@ msgstr ""
msgid "Namespaces: {0}"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:157
+#: src/renderer/components/+preferences/preferences.tsx:164
msgid "Needed with some corporate proxies that do certificate re-writing."
msgstr ""
@@ -1609,7 +1621,7 @@ msgstr ""
#~ msgid "No contexts available or they already added"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:275
+#: src/renderer/components/+add-cluster/add-cluster.tsx:260
msgid "No contexts available or they have been added already"
msgstr ""
@@ -1725,7 +1737,7 @@ msgid "Organization"
msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:29
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:35
+#: src/renderer/components/+workloads-overview/overview-statuses.tsx:45
msgid "Overview"
msgstr ""
@@ -1741,7 +1753,7 @@ msgstr ""
msgid "Parameters"
msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:245
+#: src/renderer/components/+add-cluster/add-cluster.tsx:230
msgid "Paste as text"
msgstr ""
@@ -1831,7 +1843,6 @@ msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:37
#: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:47
#: src/renderer/components/+workloads-deployments/deployments.tsx:60
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:42
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:89
#: src/renderer/components/+workloads-pods/pods.tsx:73
#: src/renderer/components/+workloads-replicasets/replicasets.tsx:52
@@ -1882,7 +1893,7 @@ msgstr ""
#~ msgid "Pro-Tip: paste kubeconfig to collect available contexts"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:263
+#: src/renderer/components/+add-cluster/add-cluster.tsx:248
msgid "Pro-Tip: paste kubeconfig to get available contexts"
msgstr ""
@@ -1890,7 +1901,7 @@ msgstr ""
#~ msgid "Pro-Tip: paste kubeconfig to parse available contexts"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:254
+#: src/renderer/components/+add-cluster/add-cluster.tsx:239
msgid "Pro-Tip: you can also drag-n-drop kubeconfig file to this area"
msgstr ""
@@ -1907,11 +1918,11 @@ msgstr ""
msgid "Provisioner"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:150
+#: src/renderer/components/+preferences/preferences.tsx:140
msgid "Proxy is used only for non-cluster communication."
msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:308
+#: src/renderer/components/+add-cluster/add-cluster.tsx:293
msgid "Proxy settings"
msgstr ""
@@ -1991,10 +2002,10 @@ msgstr ""
msgid "Releases"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:139
+#: src/renderer/components/+preferences/preferences.tsx:152
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:74
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:80
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:76
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:82
#: src/renderer/components/item-object-list/item-list-layout.tsx:179
#: src/renderer/components/menu/menu-actions.tsx:49
#: src/renderer/components/menu/menu-actions.tsx:85
@@ -2005,7 +2016,7 @@ msgstr ""
msgid "Remove <0>{releaseNames}0>?"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:51
+#: src/renderer/components/+workspaces/workspaces.tsx:52
msgid "Remove Workspace"
msgstr ""
@@ -2033,7 +2044,7 @@ msgstr ""
msgid "Remove {resourceKind} <0>{resourceName}0>?"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:114
+#: src/renderer/components/+preferences/preferences.tsx:122
msgid "Removing helm branch <0>{0}0> has failed: {1}"
msgstr ""
@@ -2057,7 +2068,7 @@ msgstr ""
msgid "Repo/Name"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:133
+#: src/renderer/components/+preferences/preferences.tsx:146
msgid "Repositories"
msgstr ""
@@ -2092,7 +2103,7 @@ msgstr ""
msgid "Required field"
msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:250
+#: src/renderer/components/+add-cluster/add-cluster.tsx:235
#: src/renderer/components/item-object-list/page-filters-list.tsx:31
msgid "Reset"
msgstr ""
@@ -2235,7 +2246,7 @@ msgstr ""
#: src/renderer/components/+config-maps/config-map-details.tsx:78
#: src/renderer/components/+config-secrets/secret-details.tsx:97
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216
-#: src/renderer/components/+workspaces/workspaces.tsx:120
+#: src/renderer/components/+workspaces/workspaces.tsx:132
#: src/renderer/components/dock/edit-resource.tsx:88
msgid "Save"
msgstr ""
@@ -2324,13 +2335,13 @@ msgstr ""
#~ msgid "Select context(s)"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:272
-#~ msgid "Select contexts"
-#~ msgstr ""
+#: src/renderer/components/+add-cluster/add-cluster.tsx:257
+msgid "Select contexts"
+msgstr ""
#: src/renderer/components/+add-cluster/add-cluster.tsx:272
-msgid "Select contexts (available: {0})"
-msgstr ""
+#~ msgid "Select contexts (available: {0})"
+#~ msgstr ""
#: src/renderer/components/+add-cluster/add-cluster.tsx:76
#: src/renderer/components/+add-cluster/add-cluster.tsx:76
@@ -2354,7 +2365,7 @@ msgstr ""
#~ msgid "Select kubeconfig"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:244
+#: src/renderer/components/+add-cluster/add-cluster.tsx:229
msgid "Select kubeconfig file"
msgstr ""
@@ -2382,7 +2393,7 @@ msgstr ""
#~ msgid "Selected contexts ({0}): <0>{1}0>"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:271
+#: src/renderer/components/+add-cluster/add-cluster.tsx:256
msgid "Selected contexts: <0>{0}0>"
msgstr ""
@@ -2486,7 +2497,6 @@ msgid "Stateful Sets"
msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:61
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:52
msgid "StatefulSets"
msgstr ""
@@ -2588,11 +2598,11 @@ msgstr ""
msgid "Taints"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:161
+#: src/renderer/components/+preferences/preferences.tsx:168
msgid "Telemetry & Usage Tracking"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:164
+#: src/renderer/components/+preferences/preferences.tsx:171
msgid "Telemetry & usage data is collected to continuously improve the Lens experience."
msgstr ""
@@ -2612,15 +2622,19 @@ msgstr ""
msgid "There are no logs available."
msgstr ""
-#: src/renderer/components/input/input.validators.ts:5
+#: src/renderer/components/input/input.validators.ts:6
msgid "This field is required"
msgstr ""
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:106
+#: src/renderer/components/input/input.validators.ts:31
+msgid "This field must be a valid path"
+msgstr ""
+
+#: src/renderer/components/+landing-page/landing-page.tsx:25
msgid "This is the quick launch menu."
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:156
+#: src/renderer/components/+preferences/preferences.tsx:163
msgid "This will make Lens to trust ANY certificate authority without any validations."
msgstr ""
@@ -2644,13 +2658,13 @@ msgstr ""
msgid "Transmit"
msgstr ""
-#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:107
+#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:106
#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:79
#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:80
msgid "Trigger"
msgstr ""
-#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:103
+#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:102
msgid "Trigger CronJob <0>{cronjobName}0>"
msgstr ""
@@ -2673,7 +2687,7 @@ msgstr ""
msgid "Type"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:148
+#: src/renderer/components/+preferences/preferences.tsx:138
msgid "Type HTTP proxy url (example: http://proxy.acme.org:8080)"
msgstr ""
@@ -2810,11 +2824,11 @@ msgstr ""
msgid "Warnings: {0}"
msgstr ""
-#: src/renderer/components/+landing-page/landing-page.tsx:20
+#: src/renderer/components/+landing-page/landing-page.tsx:34
msgid "Welcome!"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:79
+#: src/renderer/components/+workspaces/workspaces.tsx:88
msgid "What is a Workspace?"
msgstr ""
@@ -2827,19 +2841,19 @@ msgid "Workloads"
msgstr ""
#: src/renderer/components/+workspaces/workspace-menu.tsx:39
-#: src/renderer/components/+workspaces/workspaces.tsx:91
+#: src/renderer/components/+workspaces/workspaces.tsx:100
msgid "Workspaces"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:81
+#: src/renderer/components/+workspaces/workspaces.tsx:90
msgid "Workspaces are used to organize number of clusters into logical groups."
msgstr ""
-#: src/renderer/components/input/input.validators.ts:10
+#: src/renderer/components/input/input.validators.ts:11
msgid "Wrong email format"
msgstr ""
-#: src/renderer/components/input/input.validators.ts:25
+#: src/renderer/components/input/input.validators.ts:26
msgid "Wrong url format"
msgstr ""
@@ -2898,7 +2912,7 @@ msgstr ""
msgid "never"
msgstr ""
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:121
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:133
msgid "new"
msgstr ""
diff --git a/locales/ru/messages.po b/locales/ru/messages.po
index e3a021d837..ea3d5a9731 100644
--- a/locales/ru/messages.po
+++ b/locales/ru/messages.po
@@ -26,7 +26,7 @@ msgstr ""
msgid "(as a percentage of request)"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:108
+#: src/renderer/components/+workspaces/workspaces.tsx:121
msgid "(current)"
msgstr ""
@@ -58,11 +58,11 @@ msgstr ""
#~ msgid "A HTTP proxy server URL (format: http://:)"
#~ msgstr ""
-#: src/renderer/components/input/input.validators.ts:40
+#: src/renderer/components/input/input.validators.ts:46
msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics."
msgstr "Это поле может содержать только латинские буквы в нижнем регистре, номера и дефис."
-#: src/renderer/components/+workspaces/workspaces.tsx:84
+#: src/renderer/components/+workspaces/workspaces.tsx:93
msgid "A single workspaces contains a list of clusters and their full configuration."
msgstr ""
@@ -88,8 +88,8 @@ msgstr "Название аккаунта"
msgid "Active"
msgstr "Активный"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:303
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:118
+#: src/renderer/components/+add-cluster/add-cluster.tsx:288
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
msgid "Add Cluster"
msgstr ""
@@ -101,7 +101,7 @@ msgstr "Добавить Namespace"
msgid "Add RoleBinding"
msgstr "Добавить привязку ролей"
-#: src/renderer/components/+workspaces/workspaces.tsx:125
+#: src/renderer/components/+workspaces/workspaces.tsx:138
msgid "Add Workspace"
msgstr ""
@@ -113,7 +113,7 @@ msgstr "Добавить привязки к {name}"
#~ msgid "Add cluster"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:320
+#: src/renderer/components/+add-cluster/add-cluster.tsx:305
msgid "Add cluster(s)"
msgstr ""
@@ -137,7 +137,7 @@ msgstr "Добавить поле"
#~ msgid "Adding clusters: <0>{0}0>"
#~ msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:103
+#: src/renderer/components/+preferences/preferences.tsx:111
msgid "Adding helm branch <0>{0}0> has failed: {1}"
msgstr ""
@@ -192,7 +192,7 @@ msgstr "Аффинитеты"
msgid "Age"
msgstr "Возраст"
-#: src/renderer/components/+workspaces/workspaces.tsx:64
+#: src/renderer/components/+workspaces/workspaces.tsx:65
msgid "All clusters within workspace will be cleared as well"
msgstr ""
@@ -220,11 +220,11 @@ msgstr ""
msgid "Allow Privilege Escalation"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:162
+#: src/renderer/components/+preferences/preferences.tsx:169
msgid "Allow telemetry & usage tracking"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:154
+#: src/renderer/components/+preferences/preferences.tsx:161
msgid "Allow untrusted Certificate Authorities"
msgstr ""
@@ -282,7 +282,7 @@ msgstr "Применение.."
msgid "Apps"
msgstr "Приложения"
-#: src/renderer/components/+workspaces/workspaces.tsx:61
+#: src/renderer/components/+workspaces/workspaces.tsx:62
msgid "Are you sure you want remove workspace <0>{0}0>?"
msgstr ""
@@ -294,7 +294,7 @@ msgstr "Выполнить команду drain для ноды <0>{nodeName}0
msgid "Arguments"
msgstr "Аргументы"
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:108
+#: src/renderer/components/+landing-page/landing-page.tsx:27
msgid "Associate clusters and choose the ones you want to access via quick launch menu by clicking the + button."
msgstr ""
@@ -324,7 +324,7 @@ msgstr "Цели привязки"
msgid "Bindings"
msgstr "Привязки"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:251
+#: src/renderer/components/+add-cluster/add-cluster.tsx:236
msgid "Browse"
msgstr ""
@@ -403,7 +403,7 @@ msgstr "Запросы к процессору"
msgid "CPU:"
msgstr "CPU:"
-#: src/renderer/components/+workspaces/workspaces.tsx:119
+#: src/renderer/components/+workspaces/workspaces.tsx:133
#: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44
#: src/renderer/components/dock/info-panel.tsx:97
#: src/renderer/components/wizard/wizard.tsx:130
@@ -423,7 +423,7 @@ msgstr "Отмена"
msgid "Capacity"
msgstr "Емкость"
-#: src/renderer/components/+preferences/preferences.tsx:153
+#: src/renderer/components/+preferences/preferences.tsx:160
msgid "Certificate Trust"
msgstr ""
@@ -502,7 +502,7 @@ msgstr "IP-адрес кластера"
msgid "Cluster Issuers"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:126
+#: src/renderer/components/+preferences/preferences.tsx:134
msgid "Color Theme"
msgstr ""
@@ -713,7 +713,6 @@ msgid "Cron Jobs"
msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:77
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:67
msgid "CronJobs"
msgstr "CronJobs"
@@ -760,7 +759,6 @@ msgid "Daemon Sets"
msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:53
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:57
msgid "DaemonSets"
msgstr "DaemonSets"
@@ -785,11 +783,15 @@ msgstr ""
msgid "Default Runtime Class Name"
msgstr ""
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:30
+msgid "Default:"
+msgstr ""
+
#: src/renderer/components/+custom-resources/custom-resources.tsx:22
msgid "Definitions"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:113
+#: src/renderer/components/+workspaces/workspaces.tsx:126
#: src/renderer/components/menu/menu-actions.tsx:84
msgid "Delete"
msgstr "Удалить"
@@ -800,12 +802,11 @@ msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:45
#: src/renderer/components/+workloads-deployments/deployments.tsx:57
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:47
msgid "Deployments"
msgstr "Deployments"
#: src/renderer/components/+apps-helm-charts/helm-charts.tsx:65
-#: src/renderer/components/+workspaces/workspaces.tsx:118
+#: src/renderer/components/+workspaces/workspaces.tsx:131
msgid "Description"
msgstr "Описание"
@@ -818,7 +819,7 @@ msgstr ""
msgid "Desired number of replicas"
msgstr "Нужный уровень реплик"
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:64
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:65
msgid "Disconnect"
msgstr ""
@@ -832,7 +833,7 @@ msgstr "Диск"
msgid "Disk:"
msgstr "Диск:"
-#: src/renderer/components/+preferences/preferences.tsx:158
+#: src/renderer/components/+preferences/preferences.tsx:165
msgid "Does not affect cluster communications!"
msgstr ""
@@ -841,14 +842,22 @@ msgid "Domains"
msgstr "Домены"
#: src/renderer/components/+preferences/preferences.tsx:129
-msgid "Download Mirror"
-msgstr ""
+#~ msgid "Download Mirror"
+#~ msgstr ""
#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:90
msgid "Download file"
msgstr "Скачать файл"
-#: src/renderer/components/+preferences/preferences.tsx:130
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:39
+msgid "Download kubectl binaries"
+msgstr ""
+
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:37
+msgid "Download kubectl binaries matching to Kubernetes cluster verison."
+msgstr ""
+
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:41
msgid "Download mirror for kubectl"
msgstr ""
@@ -874,7 +883,7 @@ msgstr "Продолжительность"
msgid "E-mail"
msgstr "Эл. почта"
-#: src/renderer/components/+workspaces/workspaces.tsx:112
+#: src/renderer/components/+workspaces/workspaces.tsx:125
#: src/renderer/components/menu/menu-actions.tsx:80
#: src/renderer/components/menu/menu-actions.tsx:81
msgid "Edit"
@@ -1001,7 +1010,7 @@ msgstr "От <0>{from}0> до <1>{to}1>"
msgid "Fs Group"
msgstr ""
-#: src/renderer/components/+landing-page/landing-page.tsx:23
+#: src/renderer/components/+landing-page/landing-page.tsx:37
msgid "Get started by associating one or more clusters to Lens."
msgstr ""
@@ -1023,7 +1032,7 @@ msgstr "Группы"
msgid "HPA"
msgstr "HPA"
-#: src/renderer/components/+preferences/preferences.tsx:147
+#: src/renderer/components/+preferences/preferences.tsx:137
msgid "HTTP Proxy"
msgstr ""
@@ -1031,7 +1040,7 @@ msgstr ""
#~ msgid "HTTP Proxy server. Used for communicating with Kubernetes API."
#~ msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:132
+#: src/renderer/components/+preferences/preferences.tsx:145
msgid "Helm"
msgstr ""
@@ -1051,7 +1060,7 @@ msgstr "Helm установка: {repo}/{name}"
msgid "Helm Upgrade: {0}"
msgstr "Helm обновление: {0}"
-#: src/renderer/components/+preferences/preferences.tsx:47
+#: src/renderer/components/+preferences/preferences.tsx:51
msgid "Helm branch <0>{0}0> already in use"
msgstr ""
@@ -1158,11 +1167,11 @@ msgstr "Установка завершена!"
msgid "Installing..."
msgstr "Установка.."
-#: src/renderer/components/input/input.validators.ts:44
+#: src/renderer/components/input/input.validators.ts:50
msgid "Invalid account ID"
msgstr "Неверный ID аккаунта"
-#: src/renderer/components/input/input.validators.ts:15
+#: src/renderer/components/input/input.validators.ts:16
msgid "Invalid number"
msgstr "Неверный номер"
@@ -1198,7 +1207,6 @@ msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:69
#: src/renderer/components/+workloads-cronjobs/cronjob-details.tsx:62
#: src/renderer/components/+workloads-jobs/jobs.tsx:36
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:62
msgid "Jobs"
msgstr "Jobs"
@@ -1242,6 +1250,10 @@ msgstr "Файл конфигурации"
msgid "Kubeconfig File"
msgstr "Файл конфигурации"
+#: src/renderer/components/+preferences/kubectl-binaries.tsx:35
+msgid "Kubectl Binary"
+msgstr ""
+
#: src/renderer/components/+nodes/node-details.tsx:98
msgid "Kubelet version"
msgstr "Версия Kubelet"
@@ -1358,7 +1370,7 @@ msgstr "Макс. подов"
msgid "Max Unavailable"
msgstr ""
-#: src/renderer/components/input/input.validators.ts:35
+#: src/renderer/components/input/input.validators.ts:41
msgid "Maximum length is {maxLength}"
msgstr "Максимальная длина {maxLength}"
@@ -1434,7 +1446,7 @@ msgstr "Мин. подов"
msgid "Minimize"
msgstr "Минимизировать"
-#: src/renderer/components/input/input.validators.ts:30
+#: src/renderer/components/input/input.validators.ts:36
msgid "Minimum length is {minLength}"
msgstr "Минимальная длина {minLength}"
@@ -1498,7 +1510,7 @@ msgstr "Установки"
#: src/renderer/components/+workloads-pods/pods.tsx:74
#: src/renderer/components/+workloads-replicasets/replicasets.tsx:50
#: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40
-#: src/renderer/components/+workspaces/workspaces.tsx:117
+#: src/renderer/components/+workspaces/workspaces.tsx:130
#: src/renderer/components/dock/edit-resource.tsx:90
#: src/renderer/components/kube-object/kube-object-meta.tsx:20
msgid "Name"
@@ -1566,7 +1578,7 @@ msgstr "Namespaces"
msgid "Namespaces: {0}"
msgstr "Namespaces: {0}"
-#: src/renderer/components/+preferences/preferences.tsx:157
+#: src/renderer/components/+preferences/preferences.tsx:164
msgid "Needed with some corporate proxies that do certificate re-writing."
msgstr ""
@@ -1627,7 +1639,7 @@ msgstr "Нет доступных нод."
#~ msgid "No contexts available or they already added"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:275
+#: src/renderer/components/+add-cluster/add-cluster.tsx:260
msgid "No contexts available or they have been added already"
msgstr ""
@@ -1743,7 +1755,7 @@ msgid "Organization"
msgstr "Организация"
#: src/renderer/components/+workloads/workloads.tsx:29
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:35
+#: src/renderer/components/+workloads-overview/overview-statuses.tsx:45
msgid "Overview"
msgstr "Обзор"
@@ -1759,7 +1771,7 @@ msgstr "Параллелизм"
msgid "Parameters"
msgstr "Параметры"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:245
+#: src/renderer/components/+add-cluster/add-cluster.tsx:230
msgid "Paste as text"
msgstr ""
@@ -1849,7 +1861,6 @@ msgstr "Командная строка пода"
#: src/renderer/components/+workloads/workloads.tsx:37
#: src/renderer/components/+workloads-daemonsets/daemonsets.tsx:47
#: src/renderer/components/+workloads-deployments/deployments.tsx:60
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:42
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:89
#: src/renderer/components/+workloads-pods/pods.tsx:73
#: src/renderer/components/+workloads-replicasets/replicasets.tsx:52
@@ -1900,7 +1911,7 @@ msgstr ""
#~ msgid "Pro-Tip: paste kubeconfig to collect available contexts"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:263
+#: src/renderer/components/+add-cluster/add-cluster.tsx:248
msgid "Pro-Tip: paste kubeconfig to get available contexts"
msgstr ""
@@ -1908,7 +1919,7 @@ msgstr ""
#~ msgid "Pro-Tip: paste kubeconfig to parse available contexts"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:254
+#: src/renderer/components/+add-cluster/add-cluster.tsx:239
msgid "Pro-Tip: you can also drag-n-drop kubeconfig file to this area"
msgstr ""
@@ -1925,11 +1936,11 @@ msgstr ""
msgid "Provisioner"
msgstr "Комиссия"
-#: src/renderer/components/+preferences/preferences.tsx:150
+#: src/renderer/components/+preferences/preferences.tsx:140
msgid "Proxy is used only for non-cluster communication."
msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:308
+#: src/renderer/components/+add-cluster/add-cluster.tsx:293
msgid "Proxy settings"
msgstr ""
@@ -2009,10 +2020,10 @@ msgstr "Установка: {0}"
msgid "Releases"
msgstr "Релизы"
-#: src/renderer/components/+preferences/preferences.tsx:139
+#: src/renderer/components/+preferences/preferences.tsx:152
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:60
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:74
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:80
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:76
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:82
#: src/renderer/components/item-object-list/item-list-layout.tsx:179
#: src/renderer/components/menu/menu-actions.tsx:49
#: src/renderer/components/menu/menu-actions.tsx:85
@@ -2023,7 +2034,7 @@ msgstr "Удалить"
msgid "Remove <0>{releaseNames}0>?"
msgstr "Удалить <0>{releaseNames}0>?"
-#: src/renderer/components/+workspaces/workspaces.tsx:51
+#: src/renderer/components/+workspaces/workspaces.tsx:52
msgid "Remove Workspace"
msgstr ""
@@ -2051,7 +2062,7 @@ msgstr "Удалить выбранные элементы ({0})"
msgid "Remove {resourceKind} <0>{resourceName}0>?"
msgstr "Удалить {resourceKind} <0>{resourceName}0>?"
-#: src/renderer/components/+preferences/preferences.tsx:114
+#: src/renderer/components/+preferences/preferences.tsx:122
msgid "Removing helm branch <0>{0}0> has failed: {1}"
msgstr ""
@@ -2075,7 +2086,7 @@ msgstr "Реплики"
msgid "Repo/Name"
msgstr "Репозиторий/Имя"
-#: src/renderer/components/+preferences/preferences.tsx:133
+#: src/renderer/components/+preferences/preferences.tsx:146
msgid "Repositories"
msgstr ""
@@ -2110,7 +2121,7 @@ msgstr ""
msgid "Required field"
msgstr "Обязательное поле"
-#: src/renderer/components/+add-cluster/add-cluster.tsx:250
+#: src/renderer/components/+add-cluster/add-cluster.tsx:235
#: src/renderer/components/item-object-list/page-filters-list.tsx:31
msgid "Reset"
msgstr "Сбросить"
@@ -2253,7 +2264,7 @@ msgstr ""
#: src/renderer/components/+config-maps/config-map-details.tsx:78
#: src/renderer/components/+config-secrets/secret-details.tsx:97
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216
-#: src/renderer/components/+workspaces/workspaces.tsx:120
+#: src/renderer/components/+workspaces/workspaces.tsx:132
#: src/renderer/components/dock/edit-resource.tsx:88
msgid "Save"
msgstr "Сохранить"
@@ -2342,13 +2353,13 @@ msgstr "Выберите квоту..."
#~ msgid "Select context(s)"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:272
-#~ msgid "Select contexts"
-#~ msgstr ""
+#: src/renderer/components/+add-cluster/add-cluster.tsx:257
+msgid "Select contexts"
+msgstr ""
#: src/renderer/components/+add-cluster/add-cluster.tsx:272
-msgid "Select contexts (available: {0})"
-msgstr ""
+#~ msgid "Select contexts (available: {0})"
+#~ msgstr ""
#: src/renderer/components/+add-cluster/add-cluster.tsx:76
#: src/renderer/components/+add-cluster/add-cluster.tsx:76
@@ -2372,7 +2383,7 @@ msgstr ""
#~ msgid "Select kubeconfig"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:244
+#: src/renderer/components/+add-cluster/add-cluster.tsx:229
msgid "Select kubeconfig file"
msgstr ""
@@ -2400,7 +2411,7 @@ msgstr "Выбрать сервисные аккаунты"
#~ msgid "Selected contexts ({0}): <0>{1}0>"
#~ msgstr ""
-#: src/renderer/components/+add-cluster/add-cluster.tsx:271
+#: src/renderer/components/+add-cluster/add-cluster.tsx:256
msgid "Selected contexts: <0>{0}0>"
msgstr ""
@@ -2504,7 +2515,6 @@ msgid "Stateful Sets"
msgstr ""
#: src/renderer/components/+workloads/workloads.tsx:61
-#: src/renderer/components/+workloads-overview/overview-statuses.tsx:52
msgid "StatefulSets"
msgstr "StatefulSets"
@@ -2606,11 +2616,11 @@ msgstr "TLS"
msgid "Taints"
msgstr "Метки блокировки"
-#: src/renderer/components/+preferences/preferences.tsx:161
+#: src/renderer/components/+preferences/preferences.tsx:168
msgid "Telemetry & Usage Tracking"
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:164
+#: src/renderer/components/+preferences/preferences.tsx:171
msgid "Telemetry & usage data is collected to continuously improve the Lens experience."
msgstr ""
@@ -2630,15 +2640,19 @@ msgstr "Для контейнера нет логов."
msgid "There are no logs available."
msgstr "Логи отсутствуют."
-#: src/renderer/components/input/input.validators.ts:5
+#: src/renderer/components/input/input.validators.ts:6
msgid "This field is required"
msgstr "Это обязательное поле"
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:106
+#: src/renderer/components/input/input.validators.ts:31
+msgid "This field must be a valid path"
+msgstr ""
+
+#: src/renderer/components/+landing-page/landing-page.tsx:25
msgid "This is the quick launch menu."
msgstr ""
-#: src/renderer/components/+preferences/preferences.tsx:156
+#: src/renderer/components/+preferences/preferences.tsx:163
msgid "This will make Lens to trust ANY certificate authority without any validations."
msgstr ""
@@ -2662,13 +2676,13 @@ msgstr "Толерантности"
msgid "Transmit"
msgstr "Транзит"
-#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:107
+#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:106
#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:79
#: src/renderer/components/+workloads-cronjobs/cronjobs.tsx:80
msgid "Trigger"
msgstr ""
-#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:103
+#: src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx:102
msgid "Trigger CronJob <0>{cronjobName}0>"
msgstr ""
@@ -2691,7 +2705,7 @@ msgstr ""
msgid "Type"
msgstr "Тип"
-#: src/renderer/components/+preferences/preferences.tsx:148
+#: src/renderer/components/+preferences/preferences.tsx:138
msgid "Type HTTP proxy url (example: http://proxy.acme.org:8080)"
msgstr ""
@@ -2828,11 +2842,11 @@ msgstr "Ожидание запуска сервисов"
msgid "Warnings: {0}"
msgstr "Предупреждения: {0}"
-#: src/renderer/components/+landing-page/landing-page.tsx:20
+#: src/renderer/components/+landing-page/landing-page.tsx:34
msgid "Welcome!"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:79
+#: src/renderer/components/+workspaces/workspaces.tsx:88
msgid "What is a Workspace?"
msgstr ""
@@ -2845,19 +2859,19 @@ msgid "Workloads"
msgstr "Ресурсы"
#: src/renderer/components/+workspaces/workspace-menu.tsx:39
-#: src/renderer/components/+workspaces/workspaces.tsx:91
+#: src/renderer/components/+workspaces/workspaces.tsx:100
msgid "Workspaces"
msgstr ""
-#: src/renderer/components/+workspaces/workspaces.tsx:81
+#: src/renderer/components/+workspaces/workspaces.tsx:90
msgid "Workspaces are used to organize number of clusters into logical groups."
msgstr ""
-#: src/renderer/components/input/input.validators.ts:10
+#: src/renderer/components/input/input.validators.ts:11
msgid "Wrong email format"
msgstr "Неверный формат электронной почты"
-#: src/renderer/components/input/input.validators.ts:25
+#: src/renderer/components/input/input.validators.ts:26
msgid "Wrong url format"
msgstr "Неверный url формат"
@@ -2916,7 +2930,7 @@ msgstr ""
msgid "never"
msgstr ""
-#: src/renderer/components/cluster-manager/clusters-menu.tsx:121
+#: src/renderer/components/cluster-manager/clusters-menu.tsx:133
msgid "new"
msgstr ""
diff --git a/package.json b/package.json
index 0a8204576a..aa526d50c3 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "kontena-lens",
"productName": "Lens",
"description": "Lens - The Kubernetes IDE",
- "version": "3.6.4",
+ "version": "3.6.5-rc.1",
"main": "static/build/main.js",
"copyright": "© 2020, Mirantis, Inc.",
"license": "MIT",
@@ -39,7 +39,7 @@
},
"config": {
"bundledKubectlVersion": "1.17.11",
- "bundledHelmVersion": "3.3.1"
+ "bundledHelmVersion": "3.3.4"
},
"engines": {
"node": ">=12.0 <13.0"
diff --git a/src/common/base-store.ts b/src/common/base-store.ts
index 0b77a370b1..f476965736 100644
--- a/src/common/base-store.ts
+++ b/src/common/base-store.ts
@@ -93,6 +93,10 @@ export class BaseStore extends Singleton {
}
}
+ unregisterIpcListener() {
+ ipcRenderer.removeAllListeners(this.syncChannel)
+ }
+
disableSync() {
this.syncDisposers.forEach(dispose => dispose());
this.syncDisposers.length = 0;
diff --git a/src/common/cluster-ipc.ts b/src/common/cluster-ipc.ts
index f48ce0f9c4..e11a232ea3 100644
--- a/src/common/cluster-ipc.ts
+++ b/src/common/cluster-ipc.ts
@@ -9,7 +9,7 @@ export const clusterIpc = {
const cluster = clusterStore.getById(clusterId);
if (cluster) {
if (frameId) cluster.frameId = frameId; // save cluster's webFrame.routingId to be able to send push-updates
- return cluster.activate(true);
+ return cluster.activate();
}
},
}),
@@ -58,4 +58,4 @@ export const clusterIpc = {
return clusterStore.getById(clusterId)?.upgradeFeature(feature, config)
}
}),
-}
\ No newline at end of file
+}
diff --git a/src/common/cluster-store.ts b/src/common/cluster-store.ts
index 82e4d10198..4ab64f668b 100644
--- a/src/common/cluster-store.ts
+++ b/src/common/cluster-store.ts
@@ -1,6 +1,6 @@
import type { WorkspaceId } from "./workspace-store";
import path from "path";
-import { app, ipcRenderer, remote } from "electron";
+import { app, ipcRenderer, remote, webFrame, webContents } from "electron";
import { unlink } from "fs-extra";
import { action, computed, observable, toJS } from "mobx";
import { BaseStore } from "./base-store";
@@ -73,20 +73,27 @@ export class ClusterStore extends BaseStore {
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
migrations: migrations,
});
- if (ipcRenderer) {
- ipcRenderer.on("cluster:state", (event, model: ClusterState) => {
- this.applyWithoutSync(() => {
- logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host}`, model);
- this.getById(model.id)?.updateModel(model);
- })
- })
- }
}
@observable activeClusterId: ClusterId;
@observable removedClusters = observable.map();
@observable clusters = observable.map();
+ registerIpcListener() {
+ logger.info(`[CLUSTER-STORE] start to listen (${webFrame.routingId})`)
+ ipcRenderer.on("cluster:state", (event, model: ClusterState) => {
+ this.applyWithoutSync(() => {
+ logger.silly(`[CLUSTER-STORE]: received push-state at ${location.host} (${webFrame.routingId})`, model);
+ this.getById(model.id)?.updateModel(model);
+ })
+ })
+ }
+
+ unregisterIpcListener() {
+ super.unregisterIpcListener()
+ ipcRenderer.removeAllListeners("cluster:state")
+ }
+
@computed get activeCluster(): Cluster | null {
return this.getById(this.activeClusterId);
}
@@ -122,10 +129,6 @@ export class ClusterStore extends BaseStore {
return this.clusters.size > 0;
}
- hasContext(name: string) {
- return this.clustersList.some(cluster => cluster.contextName === name);
- }
-
getById(id: ClusterId): Cluster {
return this.clusters.get(id);
}
diff --git a/src/common/cluster-store_test.ts b/src/common/cluster-store_test.ts
index d7d6df4d2a..3c0369def3 100644
--- a/src/common/cluster-store_test.ts
+++ b/src/common/cluster-store_test.ts
@@ -146,14 +146,22 @@ describe("config with existing clusters", () => {
id: 'cluster1',
kubeConfig: 'foo',
contextName: 'foo',
- preferences: { terminalCWD: '/foo' }
+ preferences: { terminalCWD: '/foo' },
+ workspace: 'default'
},
{
id: 'cluster2',
kubeConfig: 'foo2',
contextName: 'foo2',
preferences: { terminalCWD: '/foo2' }
- }
+ },
+ {
+ id: 'cluster3',
+ kubeConfig: 'foo',
+ contextName: 'foo',
+ preferences: { terminalCWD: '/foo' },
+ workspace: 'foo'
+ },
]
})
}
@@ -183,10 +191,12 @@ describe("config with existing clusters", () => {
it("allows getting all of the clusters", async () => {
const storedClusters = clusterStore.clustersList;
+ expect(storedClusters.length).toBe(3)
expect(storedClusters[0].id).toBe('cluster1')
expect(storedClusters[0].preferences.terminalCWD).toBe('/foo')
expect(storedClusters[1].id).toBe('cluster2')
expect(storedClusters[1].preferences.terminalCWD).toBe('/foo2')
+ expect(storedClusters[2].id).toBe('cluster3')
})
})
diff --git a/src/common/prometheus-providers.ts b/src/common/prometheus-providers.ts
index 87f081b94e..2c6e6fac8e 100644
--- a/src/common/prometheus-providers.ts
+++ b/src/common/prometheus-providers.ts
@@ -1,9 +1,10 @@
import { PrometheusLens } from "../main/prometheus/lens";
import { PrometheusHelm } from "../main/prometheus/helm";
import { PrometheusOperator } from "../main/prometheus/operator";
+import { PrometheusStacklight } from "../main/prometheus/stacklight";
import { PrometheusProviderRegistry } from "../main/prometheus/provider-registry";
-[PrometheusLens, PrometheusHelm, PrometheusOperator].forEach(providerClass => {
+[PrometheusLens, PrometheusHelm, PrometheusOperator, PrometheusStacklight].forEach(providerClass => {
const provider = new providerClass()
PrometheusProviderRegistry.registerProvider(provider.id, provider)
});
diff --git a/src/common/user-store.ts b/src/common/user-store.ts
index a0cfe09fd5..f7d3ade005 100644
--- a/src/common/user-store.ts
+++ b/src/common/user-store.ts
@@ -59,8 +59,6 @@ export class UserStore extends BaseStore {
colorTheme: UserStore.defaultTheme,
downloadMirror: "default",
downloadKubectlBinaries: true, // Download kubectl binaries matching cluster version
- downloadBinariesPath: this.getDefaultKubectlPath(),
- kubectlBinariesPath: ""
};
get isNewVersion() {
diff --git a/src/main/cluster.ts b/src/main/cluster.ts
index 9fc9172296..12acb793ab 100644
--- a/src/main/cluster.ts
+++ b/src/main/cluster.ts
@@ -56,10 +56,10 @@ export class Cluster implements ClusterModel {
@observable kubeConfigPath: string;
@observable apiUrl: string; // cluster server url
@observable kubeProxyUrl: string; // lens-proxy to kube-api url
- @observable online: boolean;
- @observable accessible: boolean;
- @observable ready: boolean;
- @observable disconnected: boolean;
+ @observable online = false;
+ @observable accessible = false;
+ @observable ready = false;
+ @observable disconnected = true;
@observable failureReason: string;
@observable nodes = 0;
@observable version: string;
@@ -124,13 +124,14 @@ export class Cluster implements ClusterModel {
this.eventDisposers.length = 0;
}
- async activate(init = false) {
+ @action
+ async activate() {
logger.info(`[CLUSTER]: activate`, this.getMeta());
await this.whenInitialized;
if (!this.eventDisposers.length) {
this.bindEvents();
}
- if (this.disconnected || (!init && !this.accessible)) {
+ if (this.disconnected || !this.accessible) {
await this.reconnect();
}
await this.refreshConnectionStatus()
@@ -143,6 +144,7 @@ export class Cluster implements ClusterModel {
return this.pushState();
}
+ @action
async reconnect() {
logger.info(`[CLUSTER]: reconnect`, this.getMeta());
this.contextHandler.stopServer();
diff --git a/src/main/kubectl.ts b/src/main/kubectl.ts
index afb7a89111..672d93cf65 100644
--- a/src/main/kubectl.ts
+++ b/src/main/kubectl.ts
@@ -36,7 +36,7 @@ const packageMirrors: Map = new Map([
let bundledPath: string
const initScriptVersionString = "# lens-initscript v3\n"
-if (isDevelopment || isTestEnv) {
+if (isDevelopment || isTestEnv) {
const platformName = isWindows ? "windows" : process.platform
bundledPath = path.join(process.cwd(), "binaries", "client", platformName, process.arch, "kubectl")
} else {
@@ -110,7 +110,11 @@ export class Kubectl {
}
protected getDownloadDir() {
- return userStore.preferences?.downloadBinariesPath || Kubectl.kubectlDir
+ if (userStore.preferences?.downloadBinariesPath) {
+ return path.join(userStore.preferences.downloadBinariesPath, "kubectl")
+ }
+
+ return Kubectl.kubectlDir
}
public async getPath(bundled = false): Promise {
@@ -214,7 +218,7 @@ export class Kubectl {
});
isValid = !await this.checkBinary(this.path, false)
}
- if(!isValid) {
+ if (!isValid) {
logger.debug(`Releasing lock for ${this.kubectlVersion}`)
release()
return false
@@ -279,6 +283,12 @@ export class Kubectl {
bashScript += "fi\n"
bashScript += `export PATH="${helmPath}:${kubectlPath}:$PATH"\n`
bashScript += "export KUBECONFIG=\"$tempkubeconfig\"\n"
+
+ bashScript += "NO_PROXY=\",${NO_PROXY:-localhost},\"\n"
+ bashScript += "NO_PROXY=\"${NO_PROXY//,localhost,/,}\"\n"
+ bashScript += "NO_PROXY=\"${NO_PROXY//,127.0.0.1,/,}\"\n"
+ bashScript += "NO_PROXY=\"localhost,127.0.0.1${NO_PROXY%,}\"\n"
+ bashScript += "export NO_PROXY\n"
bashScript += "unset tempkubeconfig\n"
await fsPromises.writeFile(bashScriptPath, bashScript.toString(), { mode: 0o644 })
@@ -304,6 +314,11 @@ export class Kubectl {
zshScript += "d=${d/#:/}\n"
zshScript += "export PATH=\"$helmpath:$kubectlpath:${d/%:/}\"\n"
zshScript += "export KUBECONFIG=\"$tempkubeconfig\"\n"
+ zshScript += "NO_PROXY=\",${NO_PROXY:-localhost},\"\n"
+ zshScript += "NO_PROXY=\"${NO_PROXY//,localhost,/,}\"\n"
+ zshScript += "NO_PROXY=\"${NO_PROXY//,127.0.0.1,/,}\"\n"
+ zshScript += "NO_PROXY=\"localhost,127.0.0.1${NO_PROXY%,}\"\n"
+ zshScript += "export NO_PROXY\n"
zshScript += "unset tempkubeconfig\n"
zshScript += "unset OLD_ZDOTDIR\n"
await fsPromises.writeFile(zshScriptPath, zshScript.toString(), { mode: 0o644 })
diff --git a/src/main/lens-proxy.ts b/src/main/lens-proxy.ts
index 765b3f4d1a..dfb7de7867 100644
--- a/src/main/lens-proxy.ts
+++ b/src/main/lens-proxy.ts
@@ -44,9 +44,7 @@ export class LensProxy {
const spdyProxy = spdy.createServer({
spdy: {
plain: true,
- connection: {
- autoSpdy31: true
- }
+ protocols: ["http/1.1", "spdy/3.1"]
}
}, (req: http.IncomingMessage, res: http.ServerResponse) => {
this.handleRequest(proxy, req, res)
@@ -55,11 +53,7 @@ export class LensProxy {
if (req.url.startsWith(`${apiPrefix}?`)) {
this.handleWsUpgrade(req, socket, head)
} else {
- if (req.headers.upgrade?.startsWith("SPDY")) {
- this.handleSpdyProxy(proxy, req, socket, head)
- } else {
- socket.end()
- }
+ this.handleProxyUpgrade(proxy, req, socket, head)
}
})
spdyProxy.on("error", (err) => {
@@ -68,17 +62,45 @@ export class LensProxy {
return spdyProxy
}
- protected async handleSpdyProxy(proxy: httpProxy, req: http.IncomingMessage, socket: net.Socket, head: Buffer) {
+ protected async handleProxyUpgrade(proxy: httpProxy, req: http.IncomingMessage, socket: net.Socket, head: Buffer) {
const cluster = this.clusterManager.getClusterForRequest(req)
if (cluster) {
const proxyUrl = await cluster.contextHandler.resolveAuthProxyUrl() + req.url.replace(apiKubePrefix, "")
const apiUrl = url.parse(cluster.apiUrl)
- const res = new http.ServerResponse(req)
- res.assignSocket(socket)
- res.setHeader("Location", proxyUrl)
- res.setHeader("Host", apiUrl.hostname)
- res.statusCode = 302
- res.end()
+ const pUrl = url.parse(proxyUrl)
+ const connectOpts = { port: parseInt(pUrl.port), host: pUrl.hostname }
+ const proxySocket = new net.Socket()
+ proxySocket.connect(connectOpts, () => {
+ proxySocket.write(`${req.method} ${pUrl.path} HTTP/1.1\r\n`)
+ proxySocket.write(`Host: ${apiUrl.host}\r\n`)
+ for (let i = 0; i < req.rawHeaders.length; i += 2) {
+ const key = req.rawHeaders[i]
+ if (key !== "Host" && key !== "Authorization") {
+ proxySocket.write(`${req.rawHeaders[i]}: ${req.rawHeaders[i+1]}\r\n`)
+ }
+ }
+ proxySocket.write("\r\n")
+ proxySocket.write(head)
+ })
+ proxySocket.on('data', function (chunk) {
+ socket.write(chunk)
+ })
+ proxySocket.on('end', function () {
+ socket.end()
+ })
+ proxySocket.on('error', function (err) {
+ socket.write("HTTP/" + req.httpVersion + " 500 Connection error\r\n\r\n");
+ socket.end()
+ })
+ socket.on('data', function (chunk) {
+ proxySocket.write(chunk)
+ })
+ socket.on('end', function () {
+ proxySocket.end()
+ })
+ socket.on('error', function () {
+ proxySocket.end()
+ })
}
}
@@ -134,7 +156,6 @@ export class LensProxy {
protected async handleRequest(proxy: httpProxy, req: http.IncomingMessage, res: http.ServerResponse) {
const cluster = this.clusterManager.getClusterForRequest(req)
if (cluster) {
- await cluster.contextHandler.ensureServer();
const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler)
if (proxyTarget) {
// allow to fetch apis in "clusterId.localhost:port" from "localhost:port"
diff --git a/src/main/prometheus/operator.ts b/src/main/prometheus/operator.ts
index 37efa64b98..3d335ff554 100644
--- a/src/main/prometheus/operator.ts
+++ b/src/main/prometheus/operator.ts
@@ -37,19 +37,19 @@ export class PrometheusOperator implements PrometheusProvider {
memoryUsage: `
sum(
node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)
- ) by (node)
+ )
`.replace(/_bytes/g, `_bytes * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}`),
- memoryRequests: `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="memory"}) by (component)`,
- memoryLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="memory"}) by (component)`,
- memoryCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="memory"}) by (component)`,
- cpuUsage: `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])* on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}) by (node)`,
- cpuRequests:`sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
- cpuLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
- cpuCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
+ memoryRequests: `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="memory"})`,
+ memoryLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="memory"})`,
+ memoryCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="memory"})`,
+ cpuUsage: `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])* on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`,
+ cpuRequests:`sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"})`,
+ cpuLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="cpu"})`,
+ cpuCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="cpu"})`,
podUsage: `sum(kubelet_running_pod_count{node=~"${opts.nodes}"})`,
- podCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="pods"}) by (component)`,
- fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}) by (node)`,
- fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"} - node_filesystem_avail_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}) by (node)`
+ podCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="pods"})`,
+ fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`,
+ fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"} - node_filesystem_avail_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`
}
case 'nodes':
return {
@@ -62,10 +62,10 @@ export class PrometheusOperator implements PrometheusProvider {
}
case 'pods':
return {
- cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
+ cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",image!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`,
cpuLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`,
- memoryUsage: `sum(container_memory_working_set_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`,
+ memoryUsage: `sum(container_memory_working_set_bytes{container!="POD",container!="",image!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`,
memoryRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`,
memoryLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`,
fsUsage: `sum(container_fs_usage_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`,
diff --git a/src/main/prometheus/stacklight.ts b/src/main/prometheus/stacklight.ts
new file mode 100644
index 0000000000..4cb946c81d
--- /dev/null
+++ b/src/main/prometheus/stacklight.ts
@@ -0,0 +1,83 @@
+import { PrometheusProvider, PrometheusQueryOpts, PrometheusQuery, PrometheusService } from "./provider-registry";
+import { CoreV1Api } from "@kubernetes/client-node";
+import logger from "../logger"
+
+export class PrometheusStacklight implements PrometheusProvider {
+ id = "stacklight"
+ name = "Stacklight"
+ rateAccuracy = "1m"
+
+ public async getPrometheusService(client: CoreV1Api): Promise {
+ try {
+ const resp = await client.readNamespacedService("prometheus-server", "stacklight")
+ const service = resp.body
+ return {
+ id: this.id,
+ namespace: service.metadata.namespace,
+ service: service.metadata.name,
+ port: service.spec.ports[0].port
+ }
+ } catch(error) {
+ logger.warn(`PrometheusLens: failed to list services: ${error.response.body.message}`)
+ }
+ }
+
+ public getQueries(opts: PrometheusQueryOpts): PrometheusQuery {
+ switch(opts.category) {
+ case 'cluster':
+ return {
+ memoryUsage: `
+ sum(
+ node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)
+ ) by (kubernetes_name)
+ `.replace(/_bytes/g, `_bytes{node=~"${opts.nodes}"}`),
+ memoryRequests: `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="memory"}) by (component)`,
+ memoryLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="memory"}) by (component)`,
+ memoryCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="memory"}) by (component)`,
+ cpuUsage: `sum(rate(node_cpu_seconds_total{node=~"${opts.nodes}", mode=~"user|system"}[${this.rateAccuracy}]))`,
+ cpuRequests:`sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
+ cpuLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
+ cpuCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
+ podUsage: `sum(kubelet_running_pod_count{instance=~"${opts.nodes}"})`,
+ podCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="pods"}) by (component)`,
+ fsSize: `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)`,
+ fsUsage: `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"} - node_filesystem_avail_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)`
+ }
+ case 'nodes':
+ return {
+ memoryUsage: `sum (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (node)`,
+ memoryCapacity: `sum(kube_node_status_capacity{resource="memory"}) by (node)`,
+ cpuUsage: `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])) by(node)`,
+ cpuCapacity: `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`,
+ fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"}) by (node)`,
+ fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) by (node)`
+ }
+ case 'pods':
+ return {
+ cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
+ cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`,
+ cpuLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`,
+ memoryUsage: `sum(container_memory_working_set_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`,
+ memoryRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`,
+ memoryLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`,
+ fsUsage: `sum(container_fs_usage_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`,
+ networkReceive: `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
+ networkTransmit: `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`
+ }
+ case 'pvc':
+ return {
+ diskUsage: `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`,
+ diskCapacity: `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`
+ }
+ case 'ingress':
+ const bytesSent = (ingress: string, statuses: string) =>
+ `sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}", status=~"${statuses}"}[${this.rateAccuracy}])) by (ingress)`
+ return {
+ bytesSentSuccess: bytesSent(opts.igress, "^2\\\\d*"),
+ bytesSentFailure: bytesSent(opts.ingres, "^5\\\\d*"),
+ requestDurationSeconds: `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}"}[${this.rateAccuracy}])) by (ingress)`,
+ responseDurationSeconds: `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}"}[${this.rateAccuracy}])) by (ingress)`
+ }
+ }
+ }
+}
diff --git a/src/main/shell-session.ts b/src/main/shell-session.ts
index 1d9d722f57..962074c803 100644
--- a/src/main/shell-session.ts
+++ b/src/main/shell-session.ts
@@ -125,6 +125,8 @@ export class ShellSession extends EventEmitter {
if (this.preferences.httpsProxy) {
env["HTTPS_PROXY"] = this.preferences.httpsProxy
}
+ const no_proxy = ["localhost", "127.0.0.1", env["NO_PROXY"]]
+ env["NO_PROXY"] = no_proxy.filter(address => !!address).join()
if (env.DEBUG) { // do not pass debug option to bash
delete env["DEBUG"]
}
diff --git a/src/migrations/cluster-store/index.ts b/src/migrations/cluster-store/index.ts
index d178d7106b..f35e8f6c9c 100644
--- a/src/migrations/cluster-store/index.ts
+++ b/src/migrations/cluster-store/index.ts
@@ -7,6 +7,7 @@ import version260Beta3 from "./2.6.0-beta.3"
import version270Beta0 from "./2.7.0-beta.0"
import version270Beta1 from "./2.7.0-beta.1"
import version360Beta1 from "./3.6.0-beta.1"
+import snap from "./snap"
export default {
...version200Beta2,
@@ -16,4 +17,5 @@ export default {
...version270Beta0,
...version270Beta1,
...version360Beta1,
+ ...snap
}
\ No newline at end of file
diff --git a/src/migrations/cluster-store/snap.ts b/src/migrations/cluster-store/snap.ts
new file mode 100644
index 0000000000..a377ba4268
--- /dev/null
+++ b/src/migrations/cluster-store/snap.ts
@@ -0,0 +1,33 @@
+// Fix embedded kubeconfig paths under snap config
+
+import { migration } from "../migration-wrapper";
+import { ClusterModel, ClusterStore } from "../../common/cluster-store";
+import { getAppVersion } from "../../common/utils/app-version";
+import fs from "fs"
+
+export default migration({
+ version: getAppVersion(), // Run always after upgrade
+ run(store, printLog) {
+ if (!process.env["SNAP"]) return;
+
+ printLog("Migrating embedded kubeconfig paths")
+ const storedClusters: ClusterModel[] = store.get("clusters") || [];
+ if (!storedClusters.length) return;
+
+ printLog("Number of clusters to migrate: ", storedClusters.length)
+ const migratedClusters = storedClusters
+ .map(cluster => {
+ /**
+ * replace snap version with 'current' in kubeconfig path
+ */
+ if (!fs.existsSync(cluster.kubeConfigPath)) {
+ const kubeconfigPath = cluster.kubeConfigPath.replace(/\/snap\/kontena-lens\/[0-9]*\//, "/snap/kontena-lens/current/")
+ cluster.kubeConfigPath = kubeconfigPath
+ }
+ return cluster;
+ })
+
+
+ store.set("clusters", migratedClusters)
+ }
+})
diff --git a/src/renderer/api/endpoints/cert-manager.api.ts b/src/renderer/api/endpoints/cert-manager.api.ts
index bfeb2692cb..3e35000ee1 100644
--- a/src/renderer/api/endpoints/cert-manager.api.ts
+++ b/src/renderer/api/endpoints/cert-manager.api.ts
@@ -223,6 +223,7 @@ export class Issuer extends KubeObject {
}
getConditions() {
+ if (!this.status?.conditions) return [];
const { conditions = [] } = this.status;
return conditions.map(condition => {
const { message, reason, lastTransitionTime, status } = condition;
diff --git a/src/renderer/api/endpoints/crd.api.ts b/src/renderer/api/endpoints/crd.api.ts
index ebacf832ca..444d59497d 100644
--- a/src/renderer/api/endpoints/crd.api.ts
+++ b/src/renderer/api/endpoints/crd.api.ts
@@ -9,12 +9,12 @@ type AdditionalPrinterColumnsCommon = {
description: string;
}
-type AdditionalPrinterColumnsV1 = AdditionalPrinterColumnsCommon & {
- jsonPath: string;
+type AdditionalPrinterColumnsV1 = AdditionalPrinterColumnsCommon & {
+ jsonPath: string;
}
-type AdditionalPrinterColumnsV1Beta = AdditionalPrinterColumnsCommon & {
- JSONPath: string;
+type AdditionalPrinterColumnsV1Beta = AdditionalPrinterColumnsCommon & {
+ JSONPath: string;
}
export class CustomResourceDefinition extends KubeObject {
@@ -50,7 +50,7 @@ export class CustomResourceDefinition extends KubeObject {
message: string;
reason: string;
status: string;
- type: string;
+ type?: string;
}[];
acceptedNames: {
plural: string;
@@ -132,7 +132,7 @@ export class CustomResourceDefinition extends KubeObject {
}
getConditions() {
- if (!this.status.conditions) return [];
+ if (!this.status?.conditions) return [];
return this.status.conditions.map(condition => {
const { message, reason, lastTransitionTime, status } = condition;
return {
diff --git a/src/renderer/api/kube-object.ts b/src/renderer/api/kube-object.ts
index abb92dfe58..5e712df9f2 100644
--- a/src/renderer/api/kube-object.ts
+++ b/src/renderer/api/kube-object.ts
@@ -115,12 +115,12 @@ export class KubeObject implements ItemObject {
return KubeObject.stringifyLabels(this.metadata.labels);
}
- getAnnotations(): string[] {
+ getAnnotations(filter = false): string[] {
const labels = KubeObject.stringifyLabels(this.metadata.annotations);
- return labels.filter(label => {
+ return filter ? labels.filter(label => {
const skip = resourceApplierApi.annotations.some(key => label.startsWith(key));
return !skip;
- })
+ }) : labels;
}
getOwnerRefs() {
@@ -138,7 +138,7 @@ export class KubeObject implements ItemObject {
getNs(),
getId(),
...getLabels(),
- ...getAnnotations(),
+ ...getAnnotations(true),
]
}
diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx
index 29670ba99c..ac4ac55935 100644
--- a/src/renderer/bootstrap.tsx
+++ b/src/renderer/bootstrap.tsx
@@ -1,6 +1,6 @@
import "./components/app.scss"
import React from "react";
-import { render } from "react-dom";
+import { render, unmountComponentAtNode } from "react-dom";
import { isMac } from "../common/vars";
import { userStore } from "../common/user-store";
import { workspaceStore } from "../common/workspace-store";
@@ -30,12 +30,27 @@ export async function bootstrap(App: AppComponent) {
themeStore.init(),
]);
+ // Register additional store listeners
+ clusterStore.registerIpcListener();
+
// init app's dependencies if any
if (App.init) {
await App.init();
extensionStore.autoEnableOnLoad(getLensRuntime);
}
- render(, rootElem);
+ window.addEventListener("message", (ev: MessageEvent) => {
+ if (ev.data === "teardown") {
+ userStore.unregisterIpcListener()
+ workspaceStore.unregisterIpcListener()
+ clusterStore.unregisterIpcListener()
+ unmountComponentAtNode(rootElem)
+ window.location.href = "about:blank"
+ }
+ })
+ render(<>
+ {isMac && }
+
+ >, rootElem);
}
// run
diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx
index 624df4c1bf..a0414541d8 100644
--- a/src/renderer/components/+add-cluster/add-cluster.tsx
+++ b/src/renderer/components/+add-cluster/add-cluster.tsx
@@ -99,10 +99,7 @@ export class AddCluster extends React.Component {
getContexts(config: KubeConfig): Map {
const contexts = new Map();
splitConfig(config).forEach(config => {
- const isExists = clusterStore.hasContext(config.currentContext);
- if (!isExists) {
- contexts.set(config.currentContext, config);
- }
+ contexts.set(config.currentContext, config);
})
return contexts
}
diff --git a/src/renderer/components/+apps-helm-charts/helm-chart-details.scss b/src/renderer/components/+apps-helm-charts/helm-chart-details.scss
index 212e9d723a..c436dfa50f 100644
--- a/src/renderer/components/+apps-helm-charts/helm-chart-details.scss
+++ b/src/renderer/components/+apps-helm-charts/helm-chart-details.scss
@@ -49,4 +49,4 @@
.chart-description {
margin-top: $margin * 2;
}
-}
\ No newline at end of file
+}
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 32c6cd6a3e..4ae8995f11 100644
--- a/src/renderer/components/+apps-helm-charts/helm-chart-details.tsx
+++ b/src/renderer/components/+apps-helm-charts/helm-chart-details.tsx
@@ -3,8 +3,8 @@ import "./helm-chart-details.scss";
import React, { Component } from "react";
import { HelmChart, helmChartsApi } from "../../api/endpoints/helm-charts.api";
import { t, Trans } from "@lingui/macro";
-import { autorun, observable } from "mobx";
-import { disposeOnUnmount, observer } from "mobx-react";
+import { observable, toJS } from "mobx";
+import { observer } from "mobx-react";
import { Drawer, DrawerItem } from "../drawer";
import { autobind, stopPropagation } from "../../utils";
import { MarkdownViewer } from "../markdown-viewer";
@@ -25,39 +25,41 @@ interface Props {
export class HelmChartDetails extends Component {
@observable chartVersions: HelmChart[];
@observable selectedChart: HelmChart;
- @observable description: string = null;
+ @observable readme: string = null;
+ @observable error: string = null;
private chartPromise: CancelablePromise<{ readme: string; versions: HelmChart[] }>;
- @disposeOnUnmount
- chartSelector = autorun(async () => {
- if (!this.props.chart) return;
- this.chartVersions = null;
- this.selectedChart = null;
- this.description = null;
- this.loadChartData();
- this.chartPromise.then(data => {
- this.description = data.readme;
- this.chartVersions = data.versions;
- this.selectedChart = data.versions[0];
- });
- });
+ async componentDidMount() {
+ const { chart: { name, repo, version } } = this.props
- loadChartData(version?: string) {
- const { chart: { name, repo } } = this.props;
- if (this.chartPromise) this.chartPromise.cancel();
- this.chartPromise = helmChartsApi.get(repo, name, version);
+ try {
+ const { readme, versions } = await (this.chartPromise = helmChartsApi.get(repo, name, version))
+ this.readme = readme
+ this.chartVersions = versions
+ this.selectedChart = versions[0]
+ } catch (error) {
+ this.error = error
+ }
+ }
+
+ componentWillUnmount() {
+ this.chartPromise?.cancel();
}
@autobind()
- onVersionChange(opt: SelectOption) {
- const version = opt.value;
+ async onVersionChange({ value: version }: SelectOption) {
this.selectedChart = this.chartVersions.find(chart => chart.version === version);
- this.description = null;
- this.loadChartData(version);
- this.chartPromise.then(data => {
- this.description = data.readme
- });
+ this.readme = null;
+
+ try {
+ this.chartPromise?.cancel();
+ const { chart: { name, repo } } = this.props;
+ const { readme } = await (this.chartPromise = helmChartsApi.get(repo, name, version))
+ this.readme = readme;
+ } catch (error) {
+ this.error = error;
+ }
}
@autobind()
@@ -79,7 +81,7 @@ export class HelmChartDetails extends Component {
{selectedChart.getDescription()}
-
{selectedChart.getMaintainers().map(({ name, email, url }) =>
- {name}
+ {name}
)}
{selectedChart.getKeywords().length > 0 && (
- {selectedChart.getKeywords().map(key => )}
+ {selectedChart.getKeywords().map(key => )}
)}
@@ -108,14 +110,35 @@ export class HelmChartDetails extends Component {
);
}
+ renderReadme() {
+ if (this.readme === null) {
+ return
+ }
+
+ return (
+
+
+
+ )
+ }
+
renderContent() {
- if (this.selectedChart === null || this.description === null) return ;
+ if (!this.selectedChart) {
+ return ;
+ }
+
+ if (this.error) {
+ return (
+
+ )
+ }
+
return (
{this.renderIntroduction()}
-
-
-
+ {this.renderReadme()}
);
}
@@ -135,4 +158,4 @@ export class HelmChartDetails extends Component {
);
}
-}
\ No newline at end of file
+}
diff --git a/src/renderer/components/+apps-helm-charts/helm-charts.tsx b/src/renderer/components/+apps-helm-charts/helm-charts.tsx
index 1e517847f9..7948cf950e 100644
--- a/src/renderer/components/+apps-helm-charts/helm-charts.tsx
+++ b/src/renderer/components/+apps-helm-charts/helm-charts.tsx
@@ -72,7 +72,7 @@ export class HelmCharts extends Component {
(items: HelmChart[]) => items.filter(item => !item.deprecated)
]}
customizeHeader={() => (
-
+
)}
renderTableHeader={[
{ className: "icon" },
@@ -99,11 +99,13 @@ export class HelmCharts extends Component {
detailsItem={this.selectedChart}
onDetails={this.showDetails}
/>
-
+ {this.selectedChart && (
+
+ )}
>
);
}
-}
\ No newline at end of file
+}
diff --git a/src/renderer/components/+custom-resources/crd-resource-details.tsx b/src/renderer/components/+custom-resources/crd-resource-details.tsx
index 201d5e6e4a..ac64bea683 100644
--- a/src/renderer/components/+custom-resources/crd-resource-details.tsx
+++ b/src/renderer/components/+custom-resources/crd-resource-details.tsx
@@ -13,8 +13,9 @@ import { apiManager } from "../../api/api-manager";
import { crdStore } from "./crd.store";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { Input } from "../input";
+import { CustomResourceDefinition } from "../../api/endpoints/crd.api";
-interface Props extends KubeObjectDetailsProps {
+interface Props extends KubeObjectDetailsProps {
}
function CrdColumnValue({ value }: { value: any[] | {} | string }) {
@@ -66,12 +67,14 @@ export class CrdResourceDetails extends React.Component {
})}
{showStatus && (
Status} className="status" labelsOnly>
- {object.status.conditions.map((condition: { type: string; message: string; status: string }) => {
- const { type, message, status } = condition;
+ {object.status.conditions.map((condition, index) => {
+ const { type, reason, message, status } = condition;
+ const kind = type || reason;
+ if (!kind) return null;
return (
);
diff --git a/src/renderer/components/+namespaces/namespace-select.tsx b/src/renderer/components/+namespaces/namespace-select.tsx
index 4d4dcc22da..7657a25345 100644
--- a/src/renderer/components/+namespaces/namespace-select.tsx
+++ b/src/renderer/components/+namespaces/namespace-select.tsx
@@ -58,7 +58,7 @@ export class NamespaceSelect extends React.Component {
const { value, label } = option;
return label || (
<>
- {showIcons && }
+ {showIcons && }
{value}
>
);
@@ -91,14 +91,15 @@ export class NamespaceSelectFilter extends React.Component {
closeMenuOnSelect={false}
isOptionSelected={() => false}
controlShouldRenderValue={false}
- onChange={({ value: namespace }: SelectOption) => toggleContext(namespace)}
+ isMulti
+ onChange={([{ value }]: SelectOption[]) => toggleContext(value)}
formatOptionLabel={({ value: namespace }: SelectOption) => {
const isSelected = hasContext(namespace);
return (
-
+
{namespace}
- {isSelected && }
+ {isSelected && }
)
}}
diff --git a/src/renderer/components/+preferences/kubectl-binaries.tsx b/src/renderer/components/+preferences/kubectl-binaries.tsx
index 15b01251f4..c57ab1b8a2 100644
--- a/src/renderer/components/+preferences/kubectl-binaries.tsx
+++ b/src/renderer/components/+preferences/kubectl-binaries.tsx
@@ -18,44 +18,22 @@ export const KubectlBinaries = observer(({ preferences }: { preferences: UserPre
{ value: "china", label: "China (Azure)" },
]
-
const save = () => {
preferences.downloadBinariesPath = downloadPath;
preferences.kubectlBinariesPath = binariesPath;
}
- const renderPath = () => {
- if (preferences.downloadKubectlBinaries) {
- return null;
- }
- return (
- <>
-
-
-
- Default:{" "}{Kubectl.bundledKubectlPath}
-
- >
- );
- }
-
return (
<>
Kubectl Binary
-
- Download kubectl binaries matching to Kubernetes cluster verison.
-
Download kubectl binaries}
value={preferences.downloadKubectlBinaries}
onChange={downloadKubectlBinaries => preferences.downloadKubectlBinaries = downloadKubectlBinaries}
/>
+
+ Download kubectl binaries matching to Kubernetes cluster version.
+