mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into extensions-api
This commit is contained in:
commit
de3849d22c
@ -1,69 +1,446 @@
|
||||
/*
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
describe("app start", () => {
|
||||
beforeAll(appStart, 20000)
|
||||
|
||||
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")
|
||||
afterAll(async () => {
|
||||
if (app && app.isRunning()) {
|
||||
return util.tearDown(app)
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
it('shows "whats new"', async () => {
|
||||
await clickWhatsNew(app)
|
||||
})
|
||||
|
||||
it('allows to add a cluster', async () => {
|
||||
const status = spawnSync("minikube status", { shell: true })
|
||||
// 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, skipping test")
|
||||
return
|
||||
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("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.waitForExist(`iframe[name="minikube"]`)
|
||||
await app.client.frame("minikube")
|
||||
await app.client.waitUntilTextExists("span.link-text", "Cluster")
|
||||
}
|
||||
|
||||
describeif(ready)("cluster tests", () => {
|
||||
let clusterAdded = false
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
describe("cluster add", () => {
|
||||
beforeAll(appStart, 20000)
|
||||
|
||||
afterAll(async () => {
|
||||
if (app && app.isRunning()) {
|
||||
return util.tearDown(app)
|
||||
}
|
||||
})
|
||||
|
||||
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
|
||||
it('allows to add a cluster', async () => {
|
||||
await addCluster()
|
||||
clusterAdded = true
|
||||
})
|
||||
})
|
||||
|
||||
const appStartAddCluster = async () => {
|
||||
if (clusterAdded) {
|
||||
await appStart()
|
||||
await addCluster()
|
||||
}
|
||||
await clickWhatsNew(app)
|
||||
await addMinikubeCluster(app)
|
||||
await waitForMinikubeDashboard(app)
|
||||
}
|
||||
|
||||
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"]')
|
||||
@ -76,24 +453,21 @@ describe("app start", () => {
|
||||
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(" 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\n")
|
||||
await app.client.keys("- name: nginx-create-pod-test\n")
|
||||
await app.client.keys(" image: nginx:alpine\n")
|
||||
// Create deployent
|
||||
// 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")
|
||||
await app.client.waitForExist(".name=nginx-create-pod-test")
|
||||
// Open pod details
|
||||
await app.client.click(".name=nginx")
|
||||
await app.client.waitUntilTextExists("div.drawer-title-text", "Pod: nginx")
|
||||
await app.client.click(".name=nginx-create-pod-test")
|
||||
await app.client.waitUntilTextExists("div.drawer-title-text", "Pod: nginx-create-pod-test")
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
if (app && app.isRunning()) {
|
||||
return util.tearDown(app)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@ -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://<address>:<port>)"
|
||||
#~ msgstr "A HTTP proxy server URL (format: http://<address>:<port>)"
|
||||
|
||||
#: 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"
|
||||
|
||||
|
||||
@ -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://<address>:<port>)"
|
||||
#~ 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 ""
|
||||
|
||||
|
||||
@ -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://<address>:<port>)"
|
||||
#~ 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 ""
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -93,6 +93,10 @@ export class BaseStore<T = any> extends Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
unregisterIpcListener() {
|
||||
ipcRenderer.removeAllListeners(this.syncChannel)
|
||||
}
|
||||
|
||||
disableSync() {
|
||||
this.syncDisposers.forEach(dispose => dispose());
|
||||
this.syncDisposers.length = 0;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
@ -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<ClusterStoreModel> {
|
||||
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<ClusterId, Cluster>();
|
||||
@observable clusters = observable.map<ClusterId, Cluster>();
|
||||
|
||||
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<ClusterStoreModel> {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -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)
|
||||
});
|
||||
|
||||
@ -59,8 +59,6 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
colorTheme: UserStore.defaultTheme,
|
||||
downloadMirror: "default",
|
||||
downloadKubectlBinaries: true, // Download kubectl binaries matching cluster version
|
||||
downloadBinariesPath: this.getDefaultKubectlPath(),
|
||||
kubectlBinariesPath: ""
|
||||
};
|
||||
|
||||
get isNewVersion() {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -36,7 +36,7 @@ const packageMirrors: Map<string, string> = 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<string> {
|
||||
@ -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 })
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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})`,
|
||||
|
||||
83
src/main/prometheus/stacklight.ts
Normal file
83
src/main/prometheus/stacklight.ts
Normal file
@ -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<PrometheusService> {
|
||||
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)`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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"]
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
33
src/migrations/cluster-store/snap.ts
Normal file
33
src/migrations/cluster-store/snap.ts
Normal file
@ -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)
|
||||
}
|
||||
})
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@ -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(<App/>, 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 && <div id="draggable-top" />}
|
||||
<App />
|
||||
</>, rootElem);
|
||||
}
|
||||
|
||||
// run
|
||||
|
||||
@ -99,10 +99,7 @@ export class AddCluster extends React.Component {
|
||||
getContexts(config: KubeConfig): Map<string, KubeConfig> {
|
||||
const contexts = new Map();
|
||||
splitConfig(config).forEach(config => {
|
||||
const isExists = clusterStore.hasContext(config.currentContext);
|
||||
if (!isExists) {
|
||||
contexts.set(config.currentContext, config);
|
||||
}
|
||||
})
|
||||
return contexts
|
||||
}
|
||||
|
||||
@ -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<Props> {
|
||||
@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<Props> {
|
||||
<div className="intro-contents box grow">
|
||||
<div className="description flex align-center justify-space-between">
|
||||
{selectedChart.getDescription()}
|
||||
<Button primary label={_i18n._(t`Install`)} onClick={this.install}/>
|
||||
<Button primary label={_i18n._(t`Install`)} onClick={this.install} />
|
||||
</div>
|
||||
<DrawerItem name={_i18n._(t`Version`)} className="version" onClick={stopPropagation}>
|
||||
<Select
|
||||
@ -95,12 +97,12 @@ export class HelmChartDetails extends Component<Props> {
|
||||
</DrawerItem>
|
||||
<DrawerItem name={_i18n._(t`Maintainers`)} className="maintainers">
|
||||
{selectedChart.getMaintainers().map(({ name, email, url }) =>
|
||||
<a key={name} href={url ? url : `mailto:${email}`} target="_blank">{name}</a>
|
||||
<a key={name} href={url || `mailto:${email}`} target="_blank">{name}</a>
|
||||
)}
|
||||
</DrawerItem>
|
||||
{selectedChart.getKeywords().length > 0 && (
|
||||
<DrawerItem name={_i18n._(t`Keywords`)} labelsOnly>
|
||||
{selectedChart.getKeywords().map(key => <Badge key={key} label={key}/>)}
|
||||
{selectedChart.getKeywords().map(key => <Badge key={key} label={key} />)}
|
||||
</DrawerItem>
|
||||
)}
|
||||
</div>
|
||||
@ -108,14 +110,35 @@ export class HelmChartDetails extends Component<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
renderReadme() {
|
||||
if (this.readme === null) {
|
||||
return <Spinner center />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="chart-description">
|
||||
<MarkdownViewer markdown={this.readme} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
if (this.selectedChart === null || this.description === null) return <Spinner center/>;
|
||||
if (!this.selectedChart) {
|
||||
return <Spinner center />;
|
||||
}
|
||||
|
||||
if (this.error) {
|
||||
return (
|
||||
<div className="box grow">
|
||||
<p className="error">{this.error}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="box grow">
|
||||
{this.renderIntroduction()}
|
||||
<div className="chart-description">
|
||||
<MarkdownViewer markdown={this.description}/>
|
||||
</div>
|
||||
{this.renderReadme()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ export class HelmCharts extends Component<Props> {
|
||||
(items: HelmChart[]) => items.filter(item => !item.deprecated)
|
||||
]}
|
||||
customizeHeader={() => (
|
||||
<SearchInput placeholder={_i18n._(t`Search Helm Charts`)}/>
|
||||
<SearchInput placeholder={_i18n._(t`Search Helm Charts`)} />
|
||||
)}
|
||||
renderTableHeader={[
|
||||
{ className: "icon" },
|
||||
@ -99,10 +99,12 @@ export class HelmCharts extends Component<Props> {
|
||||
detailsItem={this.selectedChart}
|
||||
onDetails={this.showDetails}
|
||||
/>
|
||||
{this.selectedChart && (
|
||||
<HelmChartDetails
|
||||
chart={this.selectedChart}
|
||||
hideDetails={this.hideDetails}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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<CustomResourceDefinition> {
|
||||
}
|
||||
|
||||
function CrdColumnValue({ value }: { value: any[] | {} | string }) {
|
||||
@ -66,12 +67,14 @@ export class CrdResourceDetails extends React.Component<Props> {
|
||||
})}
|
||||
{showStatus && (
|
||||
<DrawerItem name={<Trans>Status</Trans>} 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 (
|
||||
<Badge
|
||||
key={type} label={type}
|
||||
className={cssNames({ disabled: status === "False" }, type.toLowerCase())}
|
||||
key={kind + index} label={kind}
|
||||
className={cssNames({ disabled: status === "False" }, kind.toLowerCase())}
|
||||
tooltip={message}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -58,7 +58,7 @@ export class NamespaceSelect extends React.Component<Props> {
|
||||
const { value, label } = option;
|
||||
return label || (
|
||||
<>
|
||||
{showIcons && <Icon small material="layers"/>}
|
||||
{showIcons && <Icon small material="layers" />}
|
||||
{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 (
|
||||
<div className="flex gaps align-center">
|
||||
<FilterIcon type={FilterType.NAMESPACE}/>
|
||||
<FilterIcon type={FilterType.NAMESPACE} />
|
||||
<span>{namespace}</span>
|
||||
{isSelected && <Icon small material="check" className="box right"/>}
|
||||
{isSelected && <Icon small material="check" className="box right" />}
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
|
||||
@ -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 (
|
||||
<>
|
||||
<SubTitle title="Path to Kubectl binary"/>
|
||||
<Input
|
||||
theme="round-black"
|
||||
value={binariesPath}
|
||||
validators={isPath}
|
||||
onChange={setBinariesPath}
|
||||
onBlur={save}
|
||||
/>
|
||||
<small className="hint">
|
||||
<Trans>Default:</Trans>{" "}{Kubectl.bundledKubectlPath}
|
||||
</small>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2><Trans>Kubectl Binary</Trans></h2>
|
||||
<small className="hint">
|
||||
<Trans>Download kubectl binaries matching to Kubernetes cluster verison.</Trans>
|
||||
</small>
|
||||
<Checkbox
|
||||
label={<Trans>Download kubectl binaries</Trans>}
|
||||
value={preferences.downloadKubectlBinaries}
|
||||
onChange={downloadKubectlBinaries => preferences.downloadKubectlBinaries = downloadKubectlBinaries}
|
||||
/>
|
||||
<small className="hint">
|
||||
<Trans>Download kubectl binaries matching to Kubernetes cluster version.</Trans>
|
||||
</small>
|
||||
<SubTitle title="Download mirror" />
|
||||
<Select
|
||||
placeholder={<Trans>Download mirror for kubectl</Trans>}
|
||||
@ -64,20 +42,32 @@ export const KubectlBinaries = observer(({ preferences }: { preferences: UserPre
|
||||
onChange={({ value }: SelectOption) => preferences.downloadMirror = value}
|
||||
disabled={!preferences.downloadKubectlBinaries}
|
||||
/>
|
||||
<SubTitle title="Directory for binaries"/>
|
||||
<SubTitle title="Directory for binaries" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
value={downloadPath}
|
||||
placeholder={`Directory to download binaries into`}
|
||||
placeholder={userStore.getDefaultKubectlPath()}
|
||||
validators={isPath}
|
||||
onChange={setDownloadPath}
|
||||
onBlur={save}
|
||||
disabled={!preferences.downloadKubectlBinaries}
|
||||
/>
|
||||
<small>
|
||||
Default: {userStore.getDefaultKubectlPath()}
|
||||
<small className="hint">
|
||||
The directory to download binaries into.
|
||||
</small>
|
||||
<SubTitle title="Path to Kubectl binary" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
placeholder={Kubectl.bundledKubectlPath}
|
||||
value={binariesPath}
|
||||
validators={isPath}
|
||||
onChange={setBinariesPath}
|
||||
onBlur={save}
|
||||
disabled={preferences.downloadKubectlBinaries}
|
||||
/>
|
||||
<small className="hint">
|
||||
<Trans>The path to the kubectl binary on the system.</Trans>
|
||||
</small>
|
||||
{renderPath()}
|
||||
</>
|
||||
);
|
||||
});
|
||||
@ -11,11 +11,9 @@ export class DaemonSetStore extends KubeObjectStore<DaemonSet> {
|
||||
|
||||
@observable metrics: IPodMetrics = null;
|
||||
|
||||
loadMetrics(daemonSet: DaemonSet) {
|
||||
async loadMetrics(daemonSet: DaemonSet) {
|
||||
const pods = this.getChildPods(daemonSet);
|
||||
return podsApi.getMetrics(pods, daemonSet.getNs(), "").then(metrics =>
|
||||
this.metrics = metrics
|
||||
);
|
||||
this.metrics = await podsApi.getMetrics(pods, daemonSet.getNs(), "");
|
||||
}
|
||||
|
||||
getChildPods(daemonSet: DaemonSet): Pod[] {
|
||||
|
||||
@ -16,11 +16,9 @@ export class DeploymentStore extends KubeObjectStore<Deployment> {
|
||||
], "desc");
|
||||
}
|
||||
|
||||
loadMetrics(deployment: Deployment) {
|
||||
async loadMetrics(deployment: Deployment) {
|
||||
const pods = this.getChildPods(deployment);
|
||||
return podsApi.getMetrics(pods, deployment.getNs(), "").then(metrics =>
|
||||
this.metrics = metrics
|
||||
);
|
||||
this.metrics = await podsApi.getMetrics(pods, deployment.getNs(), "");
|
||||
}
|
||||
|
||||
getStatuses(deployments?: Deployment[]) {
|
||||
|
||||
@ -5,72 +5,54 @@ import { observer } from "mobx-react";
|
||||
import { Trans } from "@lingui/macro";
|
||||
import { OverviewWorkloadStatus } from "./overview-workload-status";
|
||||
import { Link } from "react-router-dom";
|
||||
import { cronJobsURL, daemonSetsURL, deploymentsURL, jobsURL, podsURL, statefulSetsURL } from "../+workloads";
|
||||
import { podsStore } from "../+workloads-pods/pods.store";
|
||||
import { deploymentStore } from "../+workloads-deployments/deployments.store";
|
||||
import { daemonSetStore } from "../+workloads-daemonsets/daemonsets.store";
|
||||
import { statefulSetStore } from "../+workloads-statefulsets/statefulset.store";
|
||||
import { jobStore } from "../+workloads-jobs/job.store";
|
||||
import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
|
||||
import { workloadURL, workloadStores } from "../+workloads";
|
||||
import { namespaceStore } from "../+namespaces/namespace.store";
|
||||
import { PageFiltersList } from "../item-object-list/page-filters-list";
|
||||
import { NamespaceSelectFilter } from "../+namespaces/namespace-select";
|
||||
import { isAllowedResource } from "../../../common/rbac";
|
||||
import { isAllowedResource, KubeResource } from "../../../common/rbac";
|
||||
import { ResourceNames } from "../../../renderer/utils/rbac";
|
||||
import { autobind } from "../../utils";
|
||||
import { _i18n } from "../../i18n";
|
||||
|
||||
const resources: KubeResource[] = [
|
||||
"pods",
|
||||
"deployments",
|
||||
"statefulsets",
|
||||
"daemonsets",
|
||||
"jobs",
|
||||
"cronjobs",
|
||||
]
|
||||
|
||||
@observer
|
||||
export class OverviewStatuses extends React.Component {
|
||||
@autobind()
|
||||
renderWorkload(resource: KubeResource): React.ReactElement {
|
||||
const store = workloadStores[resource];
|
||||
const items = store.getAllByNs(namespaceStore.contextNs)
|
||||
return (
|
||||
<div className="workload" key={resource}>
|
||||
<div className="title">
|
||||
<Link to={workloadURL[resource]()}>{ResourceNames[resource]} ({items.length})</Link>
|
||||
</div>
|
||||
<OverviewWorkloadStatus status={store.getStatuses(items)} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { contextNs } = namespaceStore;
|
||||
const pods = isAllowedResource("pods") ? podsStore.getAllByNs(contextNs) : [];
|
||||
const deployments = isAllowedResource("deployments") ? deploymentStore.getAllByNs(contextNs) : [];
|
||||
const statefulSets = isAllowedResource("statefulsets") ? statefulSetStore.getAllByNs(contextNs) : [];
|
||||
const daemonSets = isAllowedResource("daemonsets") ? daemonSetStore.getAllByNs(contextNs) : [];
|
||||
const jobs = isAllowedResource("jobs") ? jobStore.getAllByNs(contextNs) : [];
|
||||
const cronJobs = isAllowedResource("cronjobs") ? cronJobStore.getAllByNs(contextNs) : [];
|
||||
const workloads = resources
|
||||
.filter(isAllowedResource)
|
||||
.map(this.renderWorkload);
|
||||
|
||||
return (
|
||||
<div className="OverviewStatuses">
|
||||
<div className="header flex gaps align-center">
|
||||
<h5 className="box grow"><Trans>Overview</Trans></h5>
|
||||
<NamespaceSelectFilter/>
|
||||
<NamespaceSelectFilter />
|
||||
</div>
|
||||
<PageFiltersList/>
|
||||
<PageFiltersList />
|
||||
<div className="workloads">
|
||||
{isAllowedResource("pods") &&
|
||||
<div className="workload">
|
||||
<div className="title"><Link to={podsURL()}><Trans>Pods</Trans> ({pods.length})</Link></div>
|
||||
<OverviewWorkloadStatus status={podsStore.getStatuses(pods)}/>
|
||||
</div>
|
||||
}
|
||||
{isAllowedResource("deployments") &&
|
||||
<div className="workload">
|
||||
<div className="title"><Link to={deploymentsURL()}><Trans>Deployments</Trans> ({deployments.length})</Link></div>
|
||||
<OverviewWorkloadStatus status={deploymentStore.getStatuses(deployments)}/>
|
||||
</div>
|
||||
}
|
||||
{isAllowedResource("statefulsets") &&
|
||||
<div className="workload">
|
||||
<div className="title"><Link to={statefulSetsURL()}><Trans>StatefulSets</Trans> ({statefulSets.length})</Link></div>
|
||||
<OverviewWorkloadStatus status={statefulSetStore.getStatuses(statefulSets)}/>
|
||||
</div>
|
||||
}
|
||||
{isAllowedResource("daemonsets") &&
|
||||
<div className="workload">
|
||||
<div className="title"><Link to={daemonSetsURL()}><Trans>DaemonSets</Trans> ({daemonSets.length})</Link></div>
|
||||
<OverviewWorkloadStatus status={daemonSetStore.getStatuses(daemonSets)}/>
|
||||
</div>
|
||||
}
|
||||
{isAllowedResource("jobs") &&
|
||||
<div className="workload">
|
||||
<div className="title"><Link to={jobsURL()}><Trans>Jobs</Trans> ({jobs.length})</Link></div>
|
||||
<OverviewWorkloadStatus status={jobStore.getStatuses(jobs)}/>
|
||||
</div>
|
||||
}
|
||||
{isAllowedResource("cronjobs") &&
|
||||
<div className="workload">
|
||||
<div className="title"><Link to={cronJobsURL()}><Trans>CronJobs</Trans> ({cronJobs.length})</Link></div>
|
||||
<OverviewWorkloadStatus status={cronJobStore.getStatuses(cronJobs)}/>
|
||||
</div>
|
||||
}
|
||||
{workloads}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -113,7 +113,8 @@ const SecretKey = (props: SecretKeyProps) => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [secret, setSecret] = useState<Secret>()
|
||||
|
||||
const showKey = async () => {
|
||||
const showKey = async (evt: React.MouseEvent) => {
|
||||
evt.preventDefault()
|
||||
setLoading(true)
|
||||
const secret = await secretsStore.load({ name, namespace });
|
||||
setLoading(false)
|
||||
|
||||
@ -10,11 +10,9 @@ export class ReplicaSetStore extends KubeObjectStore<ReplicaSet> {
|
||||
api = replicaSetApi
|
||||
@observable metrics: IPodMetrics = null;
|
||||
|
||||
loadMetrics(replicaSet: ReplicaSet) {
|
||||
async loadMetrics(replicaSet: ReplicaSet) {
|
||||
const pods = this.getChildPods(replicaSet);
|
||||
return podsApi.getMetrics(pods, replicaSet.getNs(), "").then(metrics =>
|
||||
this.metrics = metrics
|
||||
);
|
||||
this.metrics = await podsApi.getMetrics(pods, replicaSet.getNs(), "");
|
||||
}
|
||||
|
||||
getChildPods(replicaSet: ReplicaSet) {
|
||||
|
||||
@ -10,11 +10,9 @@ export class StatefulSetStore extends KubeObjectStore<StatefulSet> {
|
||||
api = statefulSetApi
|
||||
@observable metrics: IPodMetrics = null;
|
||||
|
||||
loadMetrics(statefulSet: StatefulSet) {
|
||||
async loadMetrics(statefulSet: StatefulSet) {
|
||||
const pods = this.getChildPods(statefulSet);
|
||||
return podsApi.getMetrics(pods, statefulSet.getNs(), "").then(metrics =>
|
||||
this.metrics = metrics
|
||||
);
|
||||
this.metrics = await podsApi.getMetrics(pods, statefulSet.getNs(), "");
|
||||
}
|
||||
|
||||
getChildPods(statefulSet: StatefulSet) {
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export * from "./workloads.route"
|
||||
export * from "./workloads"
|
||||
|
||||
export * from "./workloads.stores"
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { RouteProps } from "react-router"
|
||||
import { Workloads } from "./workloads";
|
||||
import { buildURL, IURLParams } from "../../navigation";
|
||||
import { KubeResource } from "../../../common/rbac";
|
||||
|
||||
export const workloadsRoute: RouteProps = {
|
||||
get path() {
|
||||
@ -62,3 +63,12 @@ export const daemonSetsURL = buildURL<IDaemonSetsRouteParams>(daemonSetsRoute.pa
|
||||
export const statefulSetsURL = buildURL<IStatefulSetsRouteParams>(statefulSetsRoute.path)
|
||||
export const jobsURL = buildURL<IJobsRouteParams>(jobsRoute.path)
|
||||
export const cronJobsURL = buildURL<ICronJobsRouteParams>(cronJobsRoute.path)
|
||||
|
||||
export const workloadURL: Partial<Record<KubeResource, ReturnType<typeof buildURL>>> = {
|
||||
"pods": podsURL,
|
||||
"deployments": deploymentsURL,
|
||||
"daemonsets": daemonSetsURL,
|
||||
"statefulsets": statefulSetsURL,
|
||||
"jobs": jobsURL,
|
||||
"cronjobs": cronJobsURL,
|
||||
}
|
||||
|
||||
17
src/renderer/components/+workloads/workloads.stores.ts
Normal file
17
src/renderer/components/+workloads/workloads.stores.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { KubeObjectStore } from "../../kube-object.store";
|
||||
import { podsStore } from "../+workloads-pods/pods.store";
|
||||
import { deploymentStore } from "../+workloads-deployments/deployments.store";
|
||||
import { daemonSetStore } from "../+workloads-daemonsets/daemonsets.store";
|
||||
import { statefulSetStore } from "../+workloads-statefulsets/statefulset.store";
|
||||
import { jobStore } from "../+workloads-jobs/job.store";
|
||||
import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
|
||||
import { KubeResource } from "../../../common/rbac";
|
||||
|
||||
export const workloadStores: Partial<Record<KubeResource, KubeObjectStore>> = {
|
||||
"pods": podsStore,
|
||||
"deployments": deploymentStore,
|
||||
"daemonsets": daemonSetStore,
|
||||
"statefulsets": statefulSetStore,
|
||||
"jobs": jobStore,
|
||||
"cronjobs": cronJobStore,
|
||||
}
|
||||
@ -13,6 +13,7 @@
|
||||
:root {
|
||||
--mainBackground: #1e2124;
|
||||
--main-layout-header: 40px;
|
||||
--drag-region-height: 22px
|
||||
}
|
||||
|
||||
::selection {
|
||||
@ -47,7 +48,7 @@ html, body {
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: var(--main-layout-header);
|
||||
height: var(--drag-region-height);
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@ -165,7 +165,7 @@ export const memoryOptions: ChartOptions = {
|
||||
}
|
||||
return bytesToUnits(parseInt(value));
|
||||
}
|
||||
return `${value}`;
|
||||
return bytesToUnits(value);
|
||||
},
|
||||
stepSize: 1
|
||||
}
|
||||
|
||||
@ -55,25 +55,24 @@ export class ClusterManager extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="ClusterManager">
|
||||
<div id="draggable-top"/>
|
||||
<main>
|
||||
<div id="lens-views"/>
|
||||
<div id="lens-views" />
|
||||
<Switch>
|
||||
<Route component={LandingPage} {...landingRoute}/>
|
||||
<Route component={Preferences} {...preferencesRoute}/>
|
||||
<Route component={Workspaces} {...workspacesRoute}/>
|
||||
<Route component={AddCluster} {...addClusterRoute}/>
|
||||
<Route component={ClusterView} {...clusterViewRoute}/>
|
||||
<Route component={ClusterSettings} {...clusterSettingsRoute}/>
|
||||
<Route component={LandingPage} {...landingRoute} />
|
||||
<Route component={Preferences} {...preferencesRoute} />
|
||||
<Route component={Workspaces} {...workspacesRoute} />
|
||||
<Route component={AddCluster} {...addClusterRoute} />
|
||||
<Route component={ClusterView} {...clusterViewRoute} />
|
||||
<Route component={ClusterSettings} {...clusterSettingsRoute} />
|
||||
<Route component={Extensions} {...extensionsRoute}/>
|
||||
{dynamicPages.globalPages.map(({ path, components: { Page } }) => {
|
||||
return <Route key={path} path={path} component={Page}/>
|
||||
})}
|
||||
<Redirect exact to={this.startUrl}/>
|
||||
<Redirect exact to={this.startUrl} />
|
||||
</Switch>
|
||||
</main>
|
||||
<ClustersMenu/>
|
||||
<BottomBar/>
|
||||
<ClustersMenu />
|
||||
<BottomBar />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -19,8 +19,11 @@ export async function initView(clusterId: ClusterId) {
|
||||
if (!clusterId || lensViews.has(clusterId)) {
|
||||
return;
|
||||
}
|
||||
logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`)
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
if (!cluster) {
|
||||
return;
|
||||
}
|
||||
logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`)
|
||||
const parentElem = document.getElementById("lens-views");
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.name = cluster.contextName;
|
||||
@ -42,9 +45,9 @@ export async function autoCleanOnRemove(clusterId: ClusterId, iframe: HTMLIFrame
|
||||
// Keep frame in DOM to avoid possible bugs when same cluster re-created after being removed.
|
||||
// In that case for some reasons `webFrame.routingId` returns some previous frameId (usage in app.tsx)
|
||||
// Issue: https://github.com/lensapp/lens/issues/811
|
||||
iframe.dataset.meta = `${iframe.name} was removed at ${new Date().toLocaleString()}`;
|
||||
iframe.removeAttribute("src")
|
||||
iframe.dataset.meta = `${iframe.name} was removed at ${new Date().toLocaleString()}`
|
||||
iframe.removeAttribute("name")
|
||||
iframe.contentWindow.postMessage("teardown", "*")
|
||||
}
|
||||
|
||||
export function refreshViews() {
|
||||
|
||||
@ -5,6 +5,7 @@ import { t } from "@lingui/macro";
|
||||
import { HelmChart, helmChartsApi } from "../../api/endpoints/helm-charts.api";
|
||||
import { IReleaseUpdateDetails } from "../../api/endpoints/helm-releases.api";
|
||||
import { _i18n } from "../../i18n";
|
||||
import { Notifications } from "../notifications";
|
||||
|
||||
export interface IChartInstallData {
|
||||
name: string;
|
||||
@ -27,45 +28,50 @@ export class InstallChartStore extends DockTabStore<IChartInstallData> {
|
||||
});
|
||||
autorun(() => {
|
||||
const { selectedTab, isOpen } = dockStore;
|
||||
if (!isInstallChartTab(selectedTab)) return;
|
||||
if (isOpen) {
|
||||
this.loadData();
|
||||
if (isInstallChartTab(selectedTab) && isOpen) {
|
||||
this.loadData()
|
||||
.catch(err => Notifications.error(String(err)))
|
||||
}
|
||||
}, { delay: 250 })
|
||||
}
|
||||
|
||||
@action
|
||||
async loadData(tabId = dockStore.selectedTabId) {
|
||||
const { values } = this.getData(tabId);
|
||||
const versions = this.versions.getData(tabId);
|
||||
return Promise.all([
|
||||
!versions && this.loadVersions(tabId),
|
||||
!values && this.loadValues(tabId),
|
||||
])
|
||||
const promises = []
|
||||
|
||||
if (!this.getData(tabId).values) {
|
||||
promises.push(this.loadValues(tabId))
|
||||
}
|
||||
|
||||
if (!this.versions.getData(tabId)) {
|
||||
promises.push(this.loadVersions(tabId))
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
}
|
||||
|
||||
@action
|
||||
async loadVersions(tabId: TabId) {
|
||||
const { repo, name } = this.getData(tabId);
|
||||
const { repo, name, version } = this.getData(tabId);
|
||||
this.versions.clearData(tabId); // reset
|
||||
const charts = await helmChartsApi.get(repo, name);
|
||||
const charts = await helmChartsApi.get(repo, name, version);
|
||||
const versions = charts.versions.map(chartVersion => chartVersion.version);
|
||||
this.versions.setData(tabId, versions);
|
||||
}
|
||||
|
||||
@action
|
||||
async loadValues(tabId: TabId) {
|
||||
const data = this.getData(tabId);
|
||||
const { repo, name, version } = data;
|
||||
let values = "";
|
||||
const fetchValues = async (retry = 1, maxRetries = 3) => {
|
||||
values = await helmChartsApi.getValues(repo, name, version);
|
||||
if (values || retry == maxRetries) return;
|
||||
await fetchValues(retry + 1);
|
||||
};
|
||||
this.setData(tabId, { ...data, values: undefined }); // reset
|
||||
await fetchValues();
|
||||
this.setData(tabId, { ...data, values });
|
||||
const data = this.getData(tabId)
|
||||
const { repo, name, version } = data
|
||||
|
||||
// This loop is for "retrying" the "getValues" call
|
||||
for (const _ of Array(3)) {
|
||||
const values = await helmChartsApi.getValues(repo, name, version)
|
||||
if (values) {
|
||||
this.setData(tabId, { ...data, values })
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -107,15 +107,15 @@ export class InstallChart extends Component<Props> {
|
||||
|
||||
render() {
|
||||
const { tabId, chartData, values, versions, install } = this;
|
||||
if (!chartData || chartData.values === undefined || !versions) {
|
||||
return <Spinner center/>;
|
||||
if (chartData?.values === undefined || !versions) {
|
||||
return <Spinner center />;
|
||||
}
|
||||
|
||||
if (this.releaseDetails) {
|
||||
return (
|
||||
<div className="InstallChartDone flex column gaps align-center justify-center">
|
||||
<p>
|
||||
<Icon material="check" big sticker/>
|
||||
<Icon material="check" big sticker />
|
||||
</p>
|
||||
<p><Trans>Installation complete!</Trans></p>
|
||||
<div className="flex gaps align-center">
|
||||
@ -144,7 +144,7 @@ export class InstallChart extends Component<Props> {
|
||||
const panelControls = (
|
||||
<div className="install-controls flex gaps align-center">
|
||||
<span><Trans>Chart</Trans></span>
|
||||
<Badge label={`${repo}/${name}`} title={_i18n._(t`Repo/Name`)}/>
|
||||
<Badge label={`${repo}/${name}`} title={_i18n._(t`Repo/Name`)} />
|
||||
<span><Trans>Version</Trans></span>
|
||||
<Select
|
||||
className="chart-version"
|
||||
|
||||
@ -282,7 +282,6 @@ export class Input extends React.Component<InputProps, State> {
|
||||
onKeyDown: this.onKeyDown,
|
||||
rows: multiLine ? (rows || 1) : null,
|
||||
ref: this.bindRef,
|
||||
type: "text",
|
||||
spellCheck: "false",
|
||||
});
|
||||
|
||||
|
||||
@ -116,6 +116,6 @@ export function openServiceAccountKubeConfig(account: ServiceAccount) {
|
||||
const namespace = account.getNs()
|
||||
KubeConfigDialog.open({
|
||||
title: <Trans>{accountName} kubeconfig</Trans>,
|
||||
loader: () => apiBase.get(`/kubeconfig/service-account/${namespace}/${account}`)
|
||||
loader: () => apiBase.get(`/kubeconfig/service-account/${namespace}/${accountName}`)
|
||||
})
|
||||
}
|
||||
@ -8,10 +8,16 @@ import { JsonApiErrorParsed } from "../../api/json-api";
|
||||
export type IMessageId = string | number;
|
||||
export type IMessage = React.ReactNode | React.ReactNode[] | JsonApiErrorParsed;
|
||||
|
||||
export enum NotificationStatus {
|
||||
OK = "ok",
|
||||
ERROR = "error",
|
||||
INFO = "info",
|
||||
}
|
||||
|
||||
export interface INotification {
|
||||
id?: IMessageId;
|
||||
message: IMessage;
|
||||
status?: "ok" | "error" | "info";
|
||||
status?: NotificationStatus;
|
||||
timeout?: number; // auto-hiding timeout in milliseconds, 0 = no hide
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import { reaction } from "mobx";
|
||||
import { disposeOnUnmount, observer } from "mobx-react"
|
||||
import { JsonApiErrorParsed } from "../../api/json-api";
|
||||
import { cssNames, prevDefault } from "../../utils";
|
||||
import { IMessage, INotification, notificationsStore } from "./notifications.store";
|
||||
import { IMessage, INotification, notificationsStore, NotificationStatus } from "./notifications.store";
|
||||
import { Animate } from "../animate";
|
||||
import { Icon } from "../icon"
|
||||
|
||||
@ -17,7 +17,7 @@ export class Notifications extends React.Component {
|
||||
notificationsStore.add({
|
||||
message: message,
|
||||
timeout: 2500,
|
||||
status: "ok"
|
||||
status: NotificationStatus.OK
|
||||
})
|
||||
}
|
||||
|
||||
@ -25,13 +25,13 @@ export class Notifications extends React.Component {
|
||||
notificationsStore.add({
|
||||
message: message,
|
||||
timeout: 10000,
|
||||
status: "error"
|
||||
status: NotificationStatus.ERROR
|
||||
});
|
||||
}
|
||||
|
||||
static info(message: IMessage, customOpts: Partial<INotification> = {}) {
|
||||
return notificationsStore.add({
|
||||
status: "info",
|
||||
status: NotificationStatus.INFO,
|
||||
timeout: 0,
|
||||
message: message,
|
||||
...customOpts,
|
||||
@ -78,7 +78,7 @@ export class Notifications extends React.Component {
|
||||
onMouseLeave={() => addAutoHideTimer(notification)}
|
||||
onMouseEnter={() => removeAutoHideTimer(notification)}>
|
||||
<div className="box center">
|
||||
<Icon material="info_outline"/>
|
||||
<Icon material="info_outline" />
|
||||
</div>
|
||||
<div className="message box grow">{msgText}</div>
|
||||
<div className="box center">
|
||||
|
||||
@ -32,6 +32,7 @@ html {
|
||||
background: transparent;
|
||||
min-height: 0;
|
||||
box-shadow: 0 0 0 1px $halfGray;
|
||||
cursor: pointer;
|
||||
|
||||
&--is-focused {
|
||||
box-shadow: 0 0 0 2px $primary;
|
||||
@ -88,6 +89,7 @@ html {
|
||||
|
||||
&__option {
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
|
||||
&:active {
|
||||
background: $primary;
|
||||
|
||||
@ -19,6 +19,8 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
||||
kubeWatchApi.addListener(this, this.onWatchApiEvent);
|
||||
}
|
||||
|
||||
getStatuses?(items: T[]): Record<string, number>;
|
||||
|
||||
getAllByNs(namespace: string | string[], strict = false): T[] {
|
||||
const namespaces: string[] = [].concat(namespace);
|
||||
if (namespaces.length) {
|
||||
|
||||
28
src/renderer/utils/rbac.ts
Normal file
28
src/renderer/utils/rbac.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { KubeResource } from "../../common/rbac";
|
||||
import { _i18n } from "../i18n";
|
||||
|
||||
export const ResourceNames: Record<KubeResource, string> = {
|
||||
"namespaces": _i18n._("Namespaces"),
|
||||
"nodes": _i18n._("Nodes"),
|
||||
"events": _i18n._("Events"),
|
||||
"resourcequotas": _i18n._("Resource Quotas"),
|
||||
"services": _i18n._("Services"),
|
||||
"secrets": _i18n._("Secrets"),
|
||||
"configmaps": _i18n._("Config Maps"),
|
||||
"ingresses": _i18n._("Ingresses"),
|
||||
"networkpolicies": _i18n._("Network Policies"),
|
||||
"persistentvolumes": _i18n._("Persistent Volumes"),
|
||||
"storageclasses": _i18n._("Storage Classes"),
|
||||
"pods": _i18n._("Pods"),
|
||||
"daemonsets": _i18n._("Daemon Sets"),
|
||||
"deployments": _i18n._("Deployments"),
|
||||
"statefulsets": _i18n._("Stateful Sets"),
|
||||
"replicasets": _i18n._("Replica Sets"),
|
||||
"jobs": _i18n._("Jobs"),
|
||||
"cronjobs": _i18n._("Cron Jobs"),
|
||||
"endpoints": _i18n._("Endpoints"),
|
||||
"customresourcedefinitions": _i18n._("Custom Resource Definitions"),
|
||||
"horizontalpodautoscalers": _i18n._("Horizontal Pod Autoscalers"),
|
||||
"podsecuritypolicies": _i18n._("Pod Security Policies"),
|
||||
"poddisruptionbudgets": _i18n._("Pod Disruption Budgets"),
|
||||
}
|
||||
@ -2,7 +2,26 @@
|
||||
|
||||
Here you can find description of changes we've built into each release. While we try our best to make each upgrade automatic and as smooth as possible, there may be some cases where you might need to do something to ensure the application works smoothly. So please read through the release highlights!
|
||||
|
||||
## 3.6.4 (current version)
|
||||
## 3.6.5-rc.1 (current version)
|
||||
- Fix Notifications not to block items not visually under them from being interacted with
|
||||
- Fix side bar not to scroll after clicking on lower menu item
|
||||
- Auto-select context if only one context is present in pasted Kubeconfig
|
||||
- Fix background image of What's New page on white theme
|
||||
- Reduce height on draggable-top and only render it on macos
|
||||
- Download dir option is now consistent with other settings
|
||||
- Allow to add the same cluster multiple times
|
||||
- Convert bytes in memory chart properly
|
||||
- Fix empty dashboard screen after cluster is removed and added multiple times in a row to application
|
||||
- Dropdowns have pointer cursor now
|
||||
- Proxy kubectl exec requests properly
|
||||
- Pass always chart version information when dealing with helm commands
|
||||
- Fix app crash when conditions are not yet present in CRD objects
|
||||
- Fix kubeconfig generating for service account
|
||||
- Update bundled Helm binary to version 3.3.4
|
||||
- Fix clusters' kubeconfig paths that point to snap config dir to use current snap config path
|
||||
|
||||
|
||||
## 3.6.4
|
||||
- Fix: deleted namespace does not get auto unselected
|
||||
- Get focus to dock tab (terminal & resource editor) content after resize
|
||||
- Downloading kubectl binary does not block dashboard opening anymore
|
||||
|
||||
Loading…
Reference in New Issue
Block a user