From b8994a453fca70b28fe9dcf0580b59a61fdd44e1 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 28 Aug 2020 08:43:51 +0300 Subject: [PATCH] Allow user to select Kubeconfig from filesystem (#740) * Allow user to select Kubeconfig from filesystem, fix #738 Signed-off-by: Roman Co-authored-by: Jari Kolehmainen Co-authored-by: Alex Andreev --- locales/en/messages.po | 220 ++++++++++- locales/fi/messages.po | 218 ++++++++++- locales/ru/messages.po | 218 ++++++++++- src/common/cluster-store.ts | 35 +- src/common/cluster-store_test.ts | 13 +- src/common/kube-helpers.ts | 31 +- src/common/user-store.ts | 41 +- src/common/utils/saveToAppFiles.ts | 11 + src/migrations/cluster-store/3.6.0-beta.1.ts | 8 +- .../components/+add-cluster/add-cluster.scss | 36 +- .../components/+add-cluster/add-cluster.tsx | 368 +++++++++++++----- .../components/ace-editor/ace-editor.tsx | 16 +- .../cluster-manager/clusters-menu.tsx | 6 +- src/renderer/components/dock/dock.scss | 8 +- src/renderer/components/input/file-input.tsx | 66 ++++ src/renderer/components/input/index.ts | 1 + src/renderer/components/input/input.tsx | 26 +- .../components/layout/main-layout.scss | 4 - .../components/layout/main-layout.tsx | 5 +- .../components/layout/wizard-layout.tsx | 14 +- .../notifications/notifications.tsx | 7 +- src/renderer/components/select/select.tsx | 1 + src/renderer/components/tabs/tabs.scss | 6 +- src/renderer/components/tabs/tabs.tsx | 17 +- 24 files changed, 1113 insertions(+), 263 deletions(-) create mode 100644 src/common/utils/saveToAppFiles.ts create mode 100644 src/renderer/components/input/file-input.tsx diff --git a/locales/en/messages.po b/locales/en/messages.po index 8859dde6a8..8ae622d54c 100644 --- a/locales/en/messages.po +++ b/locales/en/messages.po @@ -37,6 +37,10 @@ msgstr "(empty) (Allowing the specific traffic to all pods in this namespace)" #~ msgid "(new)" #~ msgstr "(new)" +#: src/renderer/components/+add-cluster/add-cluster.tsx:213 +#~ msgid "* Choose how to import clusters: from selected kube-config file or by manually pasting kube-config's content as a text" +#~ msgstr "* Choose how to import clusters: from selected kube-config file or by manually pasting kube-config's content as a text" + #: src/renderer/components/item-object-list/item-list-layout.tsx:224 msgid "<0>Filtered: {itemsCount} / {allItemsCount}" msgstr "<0>Filtered: {itemsCount} / {allItemsCount}" @@ -83,8 +87,8 @@ msgstr "Account Name" msgid "Active" msgstr "Active" -#: src/renderer/components/+add-cluster/add-cluster.tsx:171 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:116 +#: src/renderer/components/+add-cluster/add-cluster.tsx:303 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:118 msgid "Add Cluster" msgstr "Add Cluster" @@ -108,7 +112,7 @@ msgstr "Add bindings to {name}" #~ msgid "Add cluster" #~ msgstr "Add cluster" -#: src/renderer/components/+add-cluster/add-cluster.tsx:192 +#: src/renderer/components/+add-cluster/add-cluster.tsx:320 msgid "Add cluster(s)" msgstr "Add cluster(s)" @@ -128,6 +132,10 @@ msgstr "Add field" #~ msgid "Added repos:" #~ msgstr "Added repos:" +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#~ msgid "Adding clusters: <0>{0}" +#~ msgstr "Adding clusters: <0>{0}" + #: src/renderer/components/+preferences/preferences.tsx:103 msgid "Adding helm branch <0>{0} has failed: {1}" msgstr "Adding helm branch <0>{0} has failed: {1}" @@ -285,7 +293,7 @@ msgstr "Are you sure you want to drain <0>{nodeName}?" msgid "Arguments" msgstr "Arguments" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:106 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:108 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." @@ -315,6 +323,10 @@ msgstr "Binding targets" msgid "Bindings" msgstr "Bindings" +#: src/renderer/components/+add-cluster/add-cluster.tsx:251 +msgid "Browse" +msgstr "Browse" + #: src/renderer/components/error-boundary/error-boundary.tsx:37 #~ msgid "Build version" #~ msgstr "Build version" @@ -440,6 +452,18 @@ msgstr "Charts" #~ msgid "Checking update" #~ msgstr "Checking update" +#: src/renderer/components/+add-cluster/add-cluster.tsx:218 +#~ msgid "Choose how to import clusters: from selected kube-config file or by manually pasting kube-config's content as a text" +#~ msgstr "Choose how to import clusters: from selected kube-config file or by manually pasting kube-config's content as a text" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:218 +#~ msgid "Choose how to import clusters: from selected kube-config file or from manually pasted configuration contents" +#~ msgstr "Choose how to import clusters: from selected kube-config file or from manually pasted configuration contents" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:218 +#~ msgid "Choose how to import clusters: from selected kube-config file or from pasted yaml configuration" +#~ msgstr "Choose how to import clusters: from selected kube-config file or from pasted yaml configuration" + #: src/renderer/components/+storage-volumes/volume-details.tsx:68 #: src/renderer/components/+storage-volumes/volumes.tsx:43 msgid "Claim" @@ -451,7 +475,7 @@ msgstr "Claim Name" #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:243 #: src/renderer/components/dialog/logs-dialog.tsx:39 -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:94 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:93 msgid "Close" msgstr "Close" @@ -522,7 +546,7 @@ msgstr "Conditions" msgid "Config Maps" msgstr "Config Maps" -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:55 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:54 msgid "Config copied to clipboard" msgstr "Config copied to clipboard" @@ -580,6 +604,14 @@ msgstr "Containers" msgid "Context" msgstr "Context" +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#~ msgid "Contexts: <0>{0}" +#~ msgstr "Contexts: <0>{0}" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:249 +#~ msgid "Contexts: {0}" +#~ msgstr "Contexts: {0}" + #: src/renderer/components/+workloads-pods/pods.tsx:79 #: src/renderer/components/kube-object/kube-object-meta.tsx:39 msgid "Controlled By" @@ -594,7 +626,7 @@ msgid "Conversion" msgstr "Conversion" #: src/renderer/components/dialog/logs-dialog.tsx:36 -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:88 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:87 msgid "Copy to clipboard" msgstr "Copy to clipboard" @@ -710,9 +742,9 @@ msgstr "Currently applied filters:" msgid "Custom Resources" msgstr "Custom Resources" -#: src/renderer/components/+add-cluster/add-cluster.tsx:116 -msgid "Custom.." -msgstr "Custom.." +#: src/renderer/components/+add-cluster/add-cluster.tsx:155 +#~ msgid "Custom.." +#~ msgstr "Custom.." #: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:95 msgid "DNS Provider" @@ -811,7 +843,7 @@ msgstr "Domains" msgid "Download Mirror" msgstr "Download Mirror" -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:91 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:90 msgid "Download file" msgstr "Download file" @@ -883,6 +915,10 @@ msgstr "Environment" msgid "Error stack" msgstr "Error stack" +#: src/renderer/components/+add-cluster/add-cluster.tsx:109 +msgid "Error while adding cluster(s): {0}" +msgstr "Error while adding cluster(s): {0}" + #: src/renderer/components/+events/events.tsx:56 #: src/renderer/components/+events/kube-event-details.tsx:34 #: src/renderer/components/+events/kube-event-details.tsx:39 @@ -1201,7 +1237,7 @@ msgstr "Kind" msgid "Kubeconfig" msgstr "Kubeconfig" -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:85 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:84 msgid "Kubeconfig File" msgstr "Kubeconfig File" @@ -1586,6 +1622,14 @@ msgstr "No" msgid "No Nodes Available." msgstr "No Nodes Available." +#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +#~ 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 +msgid "No contexts available or they have been added already" +msgstr "No contexts available or they have been added already" + #: src/renderer/components/item-object-list/page-filters-select.tsx:84 msgid "No filters available." msgstr "No filters available." @@ -1714,6 +1758,10 @@ msgstr "Parallelism" msgid "Parameters" msgstr "Parameters" +#: src/renderer/components/+add-cluster/add-cluster.tsx:245 +msgid "Paste as text" +msgstr "Paste as text" + #: src/renderer/components/+custom-resources/certmanager.k8s.io/issuer-details.tsx:94 #: src/renderer/components/+custom-resources/certmanager.k8s.io/issuer-details.tsx:102 #: src/renderer/components/+network-ingresses/ingress-details.tsx:42 @@ -1734,9 +1782,29 @@ msgstr "Persistent Volume Claims" msgid "Persistent Volumes" msgstr "Persistent Volumes" +#: src/renderer/components/+add-cluster/add-cluster.tsx:72 +msgid "Please select at least one cluster context" +msgstr "Please select at least one cluster context" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:146 +#~ msgid "Please select at least one context to add a cluster" +#~ msgstr "Please select at least one context to add a cluster" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:106 +#~ msgid "Please select kube-config's context" +#~ msgstr "Please select kube-config's context" + #: src/renderer/components/+add-cluster/add-cluster.tsx:63 -msgid "Please select kubeconfig" -msgstr "Please select kubeconfig" +#~ msgid "Please select kubeconfig" +#~ msgstr "Please select kubeconfig" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:64 +#~ msgid "Please select kubeconfig context" +#~ msgstr "Please select kubeconfig context" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:106 +#~ msgid "Please select kubeconfig's context" +#~ msgstr "Please select kubeconfig's context" #: src/renderer/components/+workloads-pods/pod-menu.tsx:50 msgid "Pod" @@ -1823,6 +1891,34 @@ msgstr "Private Key Secret" msgid "Privileged" msgstr "Privileged" +#: src/renderer/components/+add-cluster/add-cluster.tsx:264 +#~ msgid "Pro-Tip: paste kubeconfig (text/yaml) to get available contexts" +#~ msgstr "Pro-Tip: paste kubeconfig (text/yaml) to get available contexts" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:264 +#~ 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 +msgid "Pro-Tip: paste kubeconfig to get available contexts" +msgstr "Pro-Tip: paste kubeconfig to get available contexts" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:264 +#~ 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 +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" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:225 +#~ msgid "Pro-tip: you can also drag-n-drop kube-config file in the left-side area" +#~ msgstr "Pro-tip: you can also drag-n-drop kube-config file in the left-side area" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:229 +#~ msgid "Pro-tip: you can also drag-n-drop kube-config file to this area" +#~ msgstr "Pro-tip: you can also drag-n-drop kube-config file to this area" + #: src/renderer/components/+storage-classes/storage-class-details.tsx:28 #: src/renderer/components/+storage-classes/storage-classes.tsx:35 msgid "Provisioner" @@ -1832,7 +1928,7 @@ msgstr "Provisioner" 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:176 +#: src/renderer/components/+add-cluster/add-cluster.tsx:308 msgid "Proxy settings" msgstr "Proxy settings" @@ -2013,6 +2109,7 @@ msgstr "Required Drop Capabilities" msgid "Required field" msgstr "Required field" +#: src/renderer/components/+add-cluster/add-cluster.tsx:250 #: src/renderer/components/item-object-list/page-filters-list.tsx:31 msgid "Reset" msgstr "Reset" @@ -2021,6 +2118,18 @@ msgstr "Reset" msgid "Reset filters?" msgstr "Reset filters?" +#: src/renderer/components/+add-cluster/add-cluster.tsx:65 +#~ msgid "Resetting config to {0}" +#~ msgstr "Resetting config to {0}" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:68 +#~ msgid "Resetting kube-config to current {0}" +#~ msgstr "Resetting kube-config to current {0}" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:68 +#~ msgid "Resetting kube-config to default: {kubeConfigDefaultPath}" +#~ msgstr "Resetting kube-config to default: {kubeConfigDefaultPath}" + #: src/renderer/components/+custom-resources/crd-details.tsx:44 #: src/renderer/components/+custom-resources/crd-list.tsx:73 msgid "Resource" @@ -2216,13 +2325,59 @@ msgstr "Secret type" msgid "Secrets" msgstr "Secrets" +#: src/renderer/components/+add-cluster/add-cluster.tsx:253 +#~ msgid "Select a context" +#~ msgstr "Select a context" + #: src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx:134 msgid "Select a quota.." msgstr "Select a quota.." #: src/renderer/components/+add-cluster/add-cluster.tsx:173 -msgid "Select kubeconfig" -msgstr "Select kubeconfig" +#~ msgid "Select context" +#~ msgstr "Select context" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:245 +#~ 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:272 +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 +#~ msgid "Select custom kube-config file" +#~ msgstr "Select custom kube-config file" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 +msgid "Select custom kubeconfig file" +msgstr "Select custom kubeconfig file" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:212 +#~ msgid "Select file" +#~ msgstr "Select file" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:221 +#~ msgid "Select kube-config file" +#~ msgstr "Select kube-config file" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:173 +#~ msgid "Select kubeconfig" +#~ msgstr "Select kubeconfig" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +msgid "Select kubeconfig file" +msgstr "Select kubeconfig file" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:224 +#~ msgid "Select or drop file" +#~ msgstr "Select or drop file" #: src/renderer/components/+preferences/preferences.tsx:88 #~ msgid "Select repository" @@ -2236,6 +2391,22 @@ msgstr "Select role.." msgid "Select service accounts" msgstr "Select service accounts" +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#~ msgid "Selected clusters: <0>{0}" +#~ msgstr "Selected clusters: <0>{0}" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#~ msgid "Selected contexts ({0}): <0>{1}" +#~ msgstr "Selected contexts ({0}): <0>{1}" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:271 +msgid "Selected contexts: <0>{0}" +msgstr "Selected contexts: <0>{0}" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:246 +#~ msgid "Selected contexts: {0}" +#~ msgstr "Selected contexts: {0}" + #: src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx:27 #: src/renderer/components/+network-services/service-details.tsx:37 #: src/renderer/components/+network-services/services.tsx:50 @@ -2412,6 +2583,10 @@ msgstr "Submitting.." msgid "Subsets" msgstr "Subsets" +#: src/renderer/components/+add-cluster/add-cluster.tsx:102 +msgid "Successfully imported <0>{0} cluster(s)" +msgstr "Successfully imported <0>{0} cluster(s)" + #: src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx:128 msgid "Supplemental Groups" msgstr "Supplemental Groups" @@ -2458,7 +2633,7 @@ msgstr "There are no logs available." msgid "This field is required" msgstr "This field is required" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:104 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:106 msgid "This is the quick launch menu." msgstr "This is the quick launch menu." @@ -2572,6 +2747,11 @@ msgstr "Upgrade version" msgid "Usage" msgstr "Usage" +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 +msgid "Use configuration" +msgstr "Use configuration" + #: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:190 msgid "Use same name for RoleBinding" msgstr "Use same name for RoleBinding" @@ -2735,7 +2915,7 @@ msgstr "listKind" msgid "never" msgstr "never" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:119 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:121 msgid "new" msgstr "new" @@ -2787,7 +2967,7 @@ msgstr "{0} total, {1} available" msgid "{0} unavailable" msgstr "{0} unavailable" -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:129 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:128 msgid "{accountName} kubeconfig" msgstr "{accountName} kubeconfig" diff --git a/locales/fi/messages.po b/locales/fi/messages.po index 464a49bf9e..f64a50f272 100644 --- a/locales/fi/messages.po +++ b/locales/fi/messages.po @@ -37,6 +37,10 @@ msgstr "" #~ msgid "(new)" #~ msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:213 +#~ msgid "* Choose how to import clusters: from selected kube-config file or by manually pasting kube-config's content as a text" +#~ msgstr "" + #: src/renderer/components/item-object-list/item-list-layout.tsx:224 msgid "<0>Filtered: {itemsCount} / {allItemsCount}" msgstr "" @@ -83,8 +87,8 @@ msgstr "" msgid "Active" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:171 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:116 +#: src/renderer/components/+add-cluster/add-cluster.tsx:303 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:118 msgid "Add Cluster" msgstr "" @@ -108,7 +112,7 @@ msgstr "" #~ msgid "Add cluster" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:192 +#: src/renderer/components/+add-cluster/add-cluster.tsx:320 msgid "Add cluster(s)" msgstr "" @@ -128,6 +132,10 @@ msgstr "" #~ msgid "Added repos:" #~ msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#~ msgid "Adding clusters: <0>{0}" +#~ msgstr "" + #: src/renderer/components/+preferences/preferences.tsx:103 msgid "Adding helm branch <0>{0} has failed: {1}" msgstr "" @@ -285,7 +293,7 @@ msgstr "" msgid "Arguments" msgstr "" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:106 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:108 msgid "Associate clusters and choose the ones you want to access via quick launch menu by clicking the + button." msgstr "" @@ -315,6 +323,10 @@ msgstr "" msgid "Bindings" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:251 +msgid "Browse" +msgstr "" + #: src/renderer/components/error-boundary/error-boundary.tsx:37 #~ msgid "Build version" #~ msgstr "" @@ -436,6 +448,18 @@ msgstr "" msgid "Charts" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:218 +#~ msgid "Choose how to import clusters: from selected kube-config file or by manually pasting kube-config's content as a text" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:218 +#~ msgid "Choose how to import clusters: from selected kube-config file or from manually pasted configuration contents" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:218 +#~ msgid "Choose how to import clusters: from selected kube-config file or from pasted yaml configuration" +#~ msgstr "" + #: src/renderer/components/+storage-volumes/volume-details.tsx:68 #: src/renderer/components/+storage-volumes/volumes.tsx:43 msgid "Claim" @@ -447,7 +471,7 @@ msgstr "" #: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:243 #: src/renderer/components/dialog/logs-dialog.tsx:39 -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:94 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:93 msgid "Close" msgstr "" @@ -518,7 +542,7 @@ msgstr "" msgid "Config Maps" msgstr "" -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:55 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:54 msgid "Config copied to clipboard" msgstr "" @@ -576,6 +600,14 @@ msgstr "" msgid "Context" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#~ msgid "Contexts: <0>{0}" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:249 +#~ msgid "Contexts: {0}" +#~ msgstr "" + #: src/renderer/components/+workloads-pods/pods.tsx:79 #: src/renderer/components/kube-object/kube-object-meta.tsx:39 msgid "Controlled By" @@ -590,7 +622,7 @@ msgid "Conversion" msgstr "" #: src/renderer/components/dialog/logs-dialog.tsx:36 -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:88 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:87 msgid "Copy to clipboard" msgstr "" @@ -706,9 +738,9 @@ msgstr "" msgid "Custom Resources" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:116 -msgid "Custom.." -msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:155 +#~ msgid "Custom.." +#~ msgstr "" #: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:95 msgid "DNS Provider" @@ -807,7 +839,7 @@ msgstr "" msgid "Download Mirror" msgstr "" -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:91 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:90 msgid "Download file" msgstr "" @@ -879,6 +911,10 @@ msgstr "" msgid "Error stack" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:109 +msgid "Error while adding cluster(s): {0}" +msgstr "" + #: src/renderer/components/+events/events.tsx:56 #: src/renderer/components/+events/kube-event-details.tsx:34 #: src/renderer/components/+events/kube-event-details.tsx:39 @@ -1192,7 +1228,7 @@ msgstr "" msgid "Kubeconfig" msgstr "" -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:85 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:84 msgid "Kubeconfig File" msgstr "" @@ -1569,6 +1605,14 @@ msgstr "" msgid "No Nodes Available." msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +#~ msgid "No contexts available or they already added" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +msgid "No contexts available or they have been added already" +msgstr "" + #: src/renderer/components/item-object-list/page-filters-select.tsx:84 msgid "No filters available." msgstr "" @@ -1697,6 +1741,10 @@ msgstr "" msgid "Parameters" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:245 +msgid "Paste as text" +msgstr "" + #: src/renderer/components/+custom-resources/certmanager.k8s.io/issuer-details.tsx:94 #: src/renderer/components/+custom-resources/certmanager.k8s.io/issuer-details.tsx:102 #: src/renderer/components/+network-ingresses/ingress-details.tsx:42 @@ -1717,10 +1765,30 @@ msgstr "" msgid "Persistent Volumes" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:63 -msgid "Please select kubeconfig" +#: src/renderer/components/+add-cluster/add-cluster.tsx:72 +msgid "Please select at least one cluster context" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:146 +#~ msgid "Please select at least one context to add a cluster" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:106 +#~ msgid "Please select kube-config's context" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 +#~ msgid "Please select kubeconfig" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:64 +#~ msgid "Please select kubeconfig context" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:106 +#~ msgid "Please select kubeconfig's context" +#~ msgstr "" + #: src/renderer/components/+workloads-pods/pod-menu.tsx:50 msgid "Pod" msgstr "" @@ -1806,6 +1874,34 @@ msgstr "" msgid "Privileged" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:264 +#~ msgid "Pro-Tip: paste kubeconfig (text/yaml) to get available contexts" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:264 +#~ msgid "Pro-Tip: paste kubeconfig to collect available contexts" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:263 +msgid "Pro-Tip: paste kubeconfig to get available contexts" +msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:264 +#~ msgid "Pro-Tip: paste kubeconfig to parse available contexts" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:254 +msgid "Pro-Tip: you can also drag-n-drop kubeconfig file to this area" +msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:225 +#~ msgid "Pro-tip: you can also drag-n-drop kube-config file in the left-side area" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:229 +#~ msgid "Pro-tip: you can also drag-n-drop kube-config file to this area" +#~ msgstr "" + #: src/renderer/components/+storage-classes/storage-class-details.tsx:28 #: src/renderer/components/+storage-classes/storage-classes.tsx:35 msgid "Provisioner" @@ -1815,7 +1911,7 @@ msgstr "" msgid "Proxy is used only for non-cluster communication." msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:176 +#: src/renderer/components/+add-cluster/add-cluster.tsx:308 msgid "Proxy settings" msgstr "" @@ -1996,6 +2092,7 @@ msgstr "" msgid "Required field" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:250 #: src/renderer/components/item-object-list/page-filters-list.tsx:31 msgid "Reset" msgstr "" @@ -2004,6 +2101,18 @@ msgstr "" msgid "Reset filters?" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:65 +#~ msgid "Resetting config to {0}" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:68 +#~ msgid "Resetting kube-config to current {0}" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:68 +#~ msgid "Resetting kube-config to default: {kubeConfigDefaultPath}" +#~ msgstr "" + #: src/renderer/components/+custom-resources/crd-details.tsx:44 #: src/renderer/components/+custom-resources/crd-list.tsx:73 msgid "Resource" @@ -2199,14 +2308,60 @@ msgstr "" msgid "Secrets" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:253 +#~ msgid "Select a context" +#~ msgstr "" + #: src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx:134 msgid "Select a quota.." msgstr "" #: src/renderer/components/+add-cluster/add-cluster.tsx:173 -msgid "Select kubeconfig" +#~ msgid "Select context" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:245 +#~ 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:272 +msgid "Select contexts (available: {0})" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:76 +#: src/renderer/components/+add-cluster/add-cluster.tsx:76 +#~ msgid "Select custom kube-config file" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 +msgid "Select custom kubeconfig file" +msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:212 +#~ msgid "Select file" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:221 +#~ msgid "Select kube-config file" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:173 +#~ msgid "Select kubeconfig" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +msgid "Select kubeconfig file" +msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:224 +#~ msgid "Select or drop file" +#~ msgstr "" + #: src/renderer/components/+preferences/preferences.tsx:88 #~ msgid "Select repository" #~ msgstr "" @@ -2219,6 +2374,22 @@ msgstr "" msgid "Select service accounts" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#~ msgid "Selected clusters: <0>{0}" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#~ msgid "Selected contexts ({0}): <0>{1}" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:271 +msgid "Selected contexts: <0>{0}" +msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:246 +#~ msgid "Selected contexts: {0}" +#~ msgstr "" + #: src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx:27 #: src/renderer/components/+network-services/service-details.tsx:37 #: src/renderer/components/+network-services/services.tsx:50 @@ -2395,6 +2566,10 @@ msgstr "" msgid "Subsets" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:102 +msgid "Successfully imported <0>{0} cluster(s)" +msgstr "" + #: src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx:128 msgid "Supplemental Groups" msgstr "" @@ -2441,7 +2616,7 @@ msgstr "" msgid "This field is required" msgstr "" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:104 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:106 msgid "This is the quick launch menu." msgstr "" @@ -2555,6 +2730,11 @@ msgstr "" msgid "Usage" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 +msgid "Use configuration" +msgstr "" + #: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:190 msgid "Use same name for RoleBinding" msgstr "" @@ -2718,7 +2898,7 @@ msgstr "" msgid "never" msgstr "" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:119 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:121 msgid "new" msgstr "" @@ -2770,7 +2950,7 @@ msgstr "" msgid "{0} unavailable" msgstr "" -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:129 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:128 msgid "{accountName} kubeconfig" msgstr "" diff --git a/locales/ru/messages.po b/locales/ru/messages.po index 0200d47b72..e3a021d837 100644 --- a/locales/ru/messages.po +++ b/locales/ru/messages.po @@ -38,6 +38,10 @@ msgstr "(Пусто) (Допускается трафик ко всем пода #~ msgid "(new)" #~ msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:213 +#~ msgid "* Choose how to import clusters: from selected kube-config file or by manually pasting kube-config's content as a text" +#~ msgstr "" + #: src/renderer/components/item-object-list/item-list-layout.tsx:224 msgid "<0>Filtered: {itemsCount} / {allItemsCount}" msgstr "<0>Отфильтровано: {itemsCount} / {allItemsCount}" @@ -84,8 +88,8 @@ msgstr "Название аккаунта" msgid "Active" msgstr "Активный" -#: src/renderer/components/+add-cluster/add-cluster.tsx:171 -#: src/renderer/components/cluster-manager/clusters-menu.tsx:116 +#: src/renderer/components/+add-cluster/add-cluster.tsx:303 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:118 msgid "Add Cluster" msgstr "" @@ -109,7 +113,7 @@ msgstr "Добавить привязки к {name}" #~ msgid "Add cluster" #~ msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:192 +#: src/renderer/components/+add-cluster/add-cluster.tsx:320 msgid "Add cluster(s)" msgstr "" @@ -129,6 +133,10 @@ msgstr "Добавить поле" #~ msgid "Added repos:" #~ msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#~ msgid "Adding clusters: <0>{0}" +#~ msgstr "" + #: src/renderer/components/+preferences/preferences.tsx:103 msgid "Adding helm branch <0>{0} has failed: {1}" msgstr "" @@ -286,7 +294,7 @@ msgstr "Выполнить команду drain для ноды <0>{nodeName}{0}" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:249 +#~ msgid "Contexts: {0}" +#~ msgstr "" + #: src/renderer/components/+workloads-pods/pods.tsx:79 #: src/renderer/components/kube-object/kube-object-meta.tsx:39 msgid "Controlled By" @@ -595,7 +627,7 @@ msgid "Conversion" msgstr "" #: src/renderer/components/dialog/logs-dialog.tsx:36 -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:88 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:87 msgid "Copy to clipboard" msgstr "Копировать" @@ -711,9 +743,9 @@ msgstr "Текущие фильтры:" msgid "Custom Resources" msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:116 -msgid "Custom.." -msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:155 +#~ msgid "Custom.." +#~ msgstr "" #: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:95 msgid "DNS Provider" @@ -812,7 +844,7 @@ msgstr "Домены" msgid "Download Mirror" msgstr "" -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:91 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:90 msgid "Download file" msgstr "Скачать файл" @@ -884,6 +916,10 @@ msgstr "Среда" msgid "Error stack" msgstr "Стэк ошибки" +#: src/renderer/components/+add-cluster/add-cluster.tsx:109 +msgid "Error while adding cluster(s): {0}" +msgstr "" + #: src/renderer/components/+events/events.tsx:56 #: src/renderer/components/+events/kube-event-details.tsx:34 #: src/renderer/components/+events/kube-event-details.tsx:39 @@ -1202,7 +1238,7 @@ msgstr "Тип" msgid "Kubeconfig" msgstr "Файл конфигурации" -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:85 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:84 msgid "Kubeconfig File" msgstr "Файл конфигурации" @@ -1587,6 +1623,14 @@ msgstr "Нет" msgid "No Nodes Available." msgstr "Нет доступных нод." +#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +#~ msgid "No contexts available or they already added" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:275 +msgid "No contexts available or they have been added already" +msgstr "" + #: src/renderer/components/item-object-list/page-filters-select.tsx:84 msgid "No filters available." msgstr "Нет доступных фильтров." @@ -1715,6 +1759,10 @@ msgstr "Параллелизм" msgid "Parameters" msgstr "Параметры" +#: src/renderer/components/+add-cluster/add-cluster.tsx:245 +msgid "Paste as text" +msgstr "" + #: src/renderer/components/+custom-resources/certmanager.k8s.io/issuer-details.tsx:94 #: src/renderer/components/+custom-resources/certmanager.k8s.io/issuer-details.tsx:102 #: src/renderer/components/+network-ingresses/ingress-details.tsx:42 @@ -1735,10 +1783,30 @@ msgstr "Persistent Volume Claims" msgid "Persistent Volumes" msgstr "Persistent Volumes" -#: src/renderer/components/+add-cluster/add-cluster.tsx:63 -msgid "Please select kubeconfig" +#: src/renderer/components/+add-cluster/add-cluster.tsx:72 +msgid "Please select at least one cluster context" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:146 +#~ msgid "Please select at least one context to add a cluster" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:106 +#~ msgid "Please select kube-config's context" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 +#~ msgid "Please select kubeconfig" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:64 +#~ msgid "Please select kubeconfig context" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:106 +#~ msgid "Please select kubeconfig's context" +#~ msgstr "" + #: src/renderer/components/+workloads-pods/pod-menu.tsx:50 msgid "Pod" msgstr "" @@ -1824,6 +1892,34 @@ msgstr "Секрет приватного ключа" msgid "Privileged" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:264 +#~ msgid "Pro-Tip: paste kubeconfig (text/yaml) to get available contexts" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:264 +#~ msgid "Pro-Tip: paste kubeconfig to collect available contexts" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:263 +msgid "Pro-Tip: paste kubeconfig to get available contexts" +msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:264 +#~ msgid "Pro-Tip: paste kubeconfig to parse available contexts" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:254 +msgid "Pro-Tip: you can also drag-n-drop kubeconfig file to this area" +msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:225 +#~ msgid "Pro-tip: you can also drag-n-drop kube-config file in the left-side area" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:229 +#~ msgid "Pro-tip: you can also drag-n-drop kube-config file to this area" +#~ msgstr "" + #: src/renderer/components/+storage-classes/storage-class-details.tsx:28 #: src/renderer/components/+storage-classes/storage-classes.tsx:35 msgid "Provisioner" @@ -1833,7 +1929,7 @@ msgstr "Комиссия" msgid "Proxy is used only for non-cluster communication." msgstr "" -#: src/renderer/components/+add-cluster/add-cluster.tsx:176 +#: src/renderer/components/+add-cluster/add-cluster.tsx:308 msgid "Proxy settings" msgstr "" @@ -2014,6 +2110,7 @@ msgstr "" msgid "Required field" msgstr "Обязательное поле" +#: src/renderer/components/+add-cluster/add-cluster.tsx:250 #: src/renderer/components/item-object-list/page-filters-list.tsx:31 msgid "Reset" msgstr "Сбросить" @@ -2022,6 +2119,18 @@ msgstr "Сбросить" msgid "Reset filters?" msgstr "Сбросить фильтры?" +#: src/renderer/components/+add-cluster/add-cluster.tsx:65 +#~ msgid "Resetting config to {0}" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:68 +#~ msgid "Resetting kube-config to current {0}" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:68 +#~ msgid "Resetting kube-config to default: {kubeConfigDefaultPath}" +#~ msgstr "" + #: src/renderer/components/+custom-resources/crd-details.tsx:44 #: src/renderer/components/+custom-resources/crd-list.tsx:73 msgid "Resource" @@ -2217,14 +2326,60 @@ msgstr "Тип секрета" msgid "Secrets" msgstr "Secrets" +#: src/renderer/components/+add-cluster/add-cluster.tsx:253 +#~ msgid "Select a context" +#~ msgstr "" + #: src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx:134 msgid "Select a quota.." msgstr "Выберите квоту..." #: src/renderer/components/+add-cluster/add-cluster.tsx:173 -msgid "Select kubeconfig" +#~ msgid "Select context" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:245 +#~ 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:272 +msgid "Select contexts (available: {0})" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:76 +#: src/renderer/components/+add-cluster/add-cluster.tsx:76 +#~ msgid "Select custom kube-config file" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 +#: src/renderer/components/+add-cluster/add-cluster.tsx:62 +msgid "Select custom kubeconfig file" +msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:212 +#~ msgid "Select file" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:221 +#~ msgid "Select kube-config file" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:173 +#~ msgid "Select kubeconfig" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +msgid "Select kubeconfig file" +msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:224 +#~ msgid "Select or drop file" +#~ msgstr "" + #: src/renderer/components/+preferences/preferences.tsx:88 #~ msgid "Select repository" #~ msgstr "" @@ -2237,6 +2392,22 @@ msgstr "Выбрать роль.." msgid "Select service accounts" msgstr "Выбрать сервисные аккаунты" +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#~ msgid "Selected clusters: <0>{0}" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:244 +#~ msgid "Selected contexts ({0}): <0>{1}" +#~ msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:271 +msgid "Selected contexts: <0>{0}" +msgstr "" + +#: src/renderer/components/+add-cluster/add-cluster.tsx:246 +#~ msgid "Selected contexts: {0}" +#~ msgstr "" + #: src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx:27 #: src/renderer/components/+network-services/service-details.tsx:37 #: src/renderer/components/+network-services/services.tsx:50 @@ -2413,6 +2584,10 @@ msgstr "Применение.." msgid "Subsets" msgstr "" +#: src/renderer/components/+add-cluster/add-cluster.tsx:102 +msgid "Successfully imported <0>{0} cluster(s)" +msgstr "" + #: src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx:128 msgid "Supplemental Groups" msgstr "" @@ -2459,7 +2634,7 @@ msgstr "Логи отсутствуют." msgid "This field is required" msgstr "Это обязательное поле" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:104 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:106 msgid "This is the quick launch menu." msgstr "" @@ -2573,6 +2748,11 @@ msgstr "Обновить версию" msgid "Usage" msgstr "Использование" +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 +#: src/renderer/components/+add-cluster/add-cluster.tsx:63 +msgid "Use configuration" +msgstr "" + #: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:190 msgid "Use same name for RoleBinding" msgstr "Использовать тоже имя для привязки ролей" @@ -2736,7 +2916,7 @@ msgstr "" msgid "never" msgstr "" -#: src/renderer/components/cluster-manager/clusters-menu.tsx:119 +#: src/renderer/components/cluster-manager/clusters-menu.tsx:121 msgid "new" msgstr "" @@ -2788,7 +2968,7 @@ msgstr "{0} всего, {1} доступно" msgid "{0} unavailable" msgstr "{0} недоступно" -#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:129 +#: src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx:128 msgid "{accountName} kubeconfig" msgstr "{accountName} конфигурация" diff --git a/src/common/cluster-store.ts b/src/common/cluster-store.ts index 6e9930f5ad..7b9de86fef 100644 --- a/src/common/cluster-store.ts +++ b/src/common/cluster-store.ts @@ -1,5 +1,6 @@ import type { WorkspaceId } from "./workspace-store"; -import { ipcRenderer } from "electron"; +import path from "path"; +import { app, ipcRenderer, remote } from "electron"; import { unlink } from "fs-extra"; import { action, computed, observable, toJS } from "mobx"; import { BaseStore } from "./base-store"; @@ -7,6 +8,9 @@ import { Cluster, ClusterState } from "../main/cluster"; import migrations from "../migrations/cluster-store" import logger from "../main/logger"; import { tracker } from "./tracker"; +import { dumpConfigYaml } from "./kube-helpers"; +import { saveToAppFiles } from "./utils/saveToAppFiles"; +import { KubeConfig } from "@kubernetes/client-node"; export interface ClusterIconUpload { clusterId: string; @@ -49,6 +53,17 @@ export interface ClusterPreferences { } export class ClusterStore extends BaseStore { + static getCustomKubeConfigPath(clusterId: ClusterId): string { + return path.resolve((app || remote.app).getPath("userData"), "kubeconfigs", clusterId); + } + + static embedCustomKubeConfig(clusterId: ClusterId, kubeConfig: KubeConfig | string): string { + const filePath = ClusterStore.getCustomKubeConfigPath(clusterId); + const fileContents = typeof kubeConfig == "string" ? kubeConfig : dumpConfigYaml(kubeConfig); + saveToAppFiles(filePath, fileContents); + return filePath; + } + private constructor() { super({ configName: "lens-cluster-store", @@ -81,6 +96,7 @@ export class ClusterStore extends BaseStore { return this.activeClusterId === id; } + @action setActive(id: ClusterId) { this.activeClusterId = id; } @@ -102,12 +118,12 @@ export class ClusterStore extends BaseStore { } @action - async addCluster(model: ClusterModel, activate = true): Promise { - tracker.event("cluster", "add"); - const cluster = new Cluster(model); - this.clusters.set(model.id, cluster); - if (activate) this.activeClusterId = model.id; - return cluster; + addCluster(...models: ClusterModel[]) { + models.forEach(model => { + tracker.event("cluster", "add"); + const cluster = new Cluster(model); + this.clusters.set(model.id, cluster); + }) } @action @@ -119,7 +135,10 @@ export class ClusterStore extends BaseStore { if (this.activeClusterId === clusterId) { this.activeClusterId = null; } - unlink(cluster.kubeConfigPath).catch(() => null); + // remove only custom kubeconfigs (pasted as text) + if (cluster.kubeConfigPath == ClusterStore.getCustomKubeConfigPath(clusterId)) { + unlink(cluster.kubeConfigPath).catch(() => null); + } } } diff --git a/src/common/cluster-store_test.ts b/src/common/cluster-store_test.ts index 6e5b99dbaf..fd27d0da43 100644 --- a/src/common/cluster-store_test.ts +++ b/src/common/cluster-store_test.ts @@ -4,7 +4,6 @@ import yaml from "js-yaml"; import { Cluster } from "../main/cluster"; import { ClusterStore } from "./cluster-store"; import { workspaceStore } from "./workspace-store"; -import { saveConfigToAppFiles } from "./kube-helpers"; const testDataIcon = fs.readFileSync("test-data/cluster-store-migration-icon.png") @@ -37,7 +36,7 @@ describe("empty config", () => { icon: "data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5", clusterName: "minikube" }, - kubeConfigPath: saveConfigToAppFiles("foo", "fancy foo config"), + kubeConfigPath: ClusterStore.embedCustomKubeConfig("foo", "fancy foo config"), workspace: workspaceStore.currentWorkspaceId }); clusterStore.addCluster(cluster); @@ -58,7 +57,7 @@ describe("empty config", () => { preferences: { clusterName: "prod" }, - kubeConfigPath: saveConfigToAppFiles("prod", "fancy config"), + kubeConfigPath: ClusterStore.embedCustomKubeConfig("prod", "fancy config"), workspace: "workstation" }); const devCluster = new Cluster({ @@ -66,7 +65,7 @@ describe("empty config", () => { preferences: { clusterName: "dev" }, - kubeConfigPath: saveConfigToAppFiles("dev", "fancy config"), + kubeConfigPath: ClusterStore.embedCustomKubeConfig("dev", "fancy config"), workspace: "workstation" }); clusterStore.addCluster(prodCluster); @@ -84,17 +83,13 @@ describe("empty config", () => { expect(wsClusters[1].id).toBe("dev"); }) - it("checks if last added cluster becomes active", () => { - expect(clusterStore.activeCluster.id).toBe("dev"); - }) - it("sets active cluster", () => { clusterStore.setActive("foo"); expect(clusterStore.activeCluster.id).toBe("foo"); }) it("check if cluster's kubeconfig file saved", () => { - const file = saveConfigToAppFiles("boo", "kubeconfig"); + const file = ClusterStore.embedCustomKubeConfig("boo", "kubeconfig"); expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig"); }) diff --git a/src/common/kube-helpers.ts b/src/common/kube-helpers.ts index 26e73db4a8..18f472243b 100644 --- a/src/common/kube-helpers.ts +++ b/src/common/kube-helpers.ts @@ -1,11 +1,12 @@ -import { app, remote } from "electron"; import { KubeConfig, V1Node, V1Pod } from "@kubernetes/client-node" -import fse, { ensureDirSync, readFile, writeFileSync } from "fs-extra"; +import fse from "fs-extra"; import path from "path" import os from "os" import yaml from "js-yaml" import logger from "../main/logger"; +export const kubeConfigDefaultPath = path.join(os.homedir(), '.kube', 'config'); + function resolveTilde(filePath: string) { if (filePath[0] === "~" && (filePath[1] === "/" || filePath.length === 1)) { return filePath.replace("~", os.homedir()); @@ -139,29 +140,3 @@ export function getNodeWarningConditions(node: V1Node) { c.status.toLowerCase() === "true" && c.type !== "Ready" && c.type !== "HostUpgrades" ) } - -// Write kubeconfigs to "embedded" store, i.e. "/Users/ixrock/Library/Application Support/Lens/kubeconfigs" -export function saveConfigToAppFiles(clusterId: string, kubeConfig: KubeConfig | string): string { - const userData = (app || remote.app).getPath("userData"); - const kubeConfigFile = path.join(userData, `kubeconfigs/${clusterId}`) - const kubeConfigContents = typeof kubeConfig == "string" ? kubeConfig : dumpConfigYaml(kubeConfig); - - ensureDirSync(path.dirname(kubeConfigFile)); - writeFileSync(kubeConfigFile, kubeConfigContents); - return kubeConfigFile; -} - -export async function getKubeConfigLocal(): Promise { - try { - const configFile = path.join(os.homedir(), '.kube', 'config'); - const file = await readFile(configFile, "utf8"); - const obj = yaml.safeLoad(file); - if (obj.contexts) { - obj.contexts = obj.contexts.filter((ctx: any) => ctx?.context?.cluster && ctx?.name) - } - return yaml.safeDump(obj); - } catch (err) { - logger.debug(`Cannot read local kube-config: ${err}`) - return ""; - } -} diff --git a/src/common/user-store.ts b/src/common/user-store.ts index d88ee204cb..6f3e2605be 100644 --- a/src/common/user-store.ts +++ b/src/common/user-store.ts @@ -1,13 +1,16 @@ import type { ThemeId } from "../renderer/theme.store"; import semver from "semver" +import { readFile } from "fs-extra" import { action, observable, reaction, toJS } from "mobx"; import { BaseStore } from "./base-store"; import migrations from "../migrations/user-store" import { getAppVersion } from "./utils/app-version"; -import { getKubeConfigLocal, loadConfig } from "./kube-helpers"; +import { kubeConfigDefaultPath, loadConfig } from "./kube-helpers"; import { tracker } from "./tracker"; +import logger from "../main/logger"; export interface UserStoreModel { + kubeConfigPath: string; lastSeenAppVersion: string; seenContexts: string[]; preferences: UserPreferences; @@ -37,10 +40,11 @@ export class UserStore extends BaseStore { // refresh new contexts this.whenLoaded.then(this.refreshNewContexts); - reaction(() => this.seenContexts.size, this.refreshNewContexts); + reaction(() => this.kubeConfigPath, this.refreshNewContexts); } @observable lastSeenAppVersion = "0.0.0" + @observable kubeConfigPath = kubeConfigDefaultPath; // used in add-cluster page for providing context @observable seenContexts = observable.set(); @observable newContexts = observable.set(); @@ -55,6 +59,11 @@ export class UserStore extends BaseStore { return semver.gt(getAppVersion(), this.lastSeenAppVersion); } + @action + resetKubeConfigPath() { + this.kubeConfigPath = kubeConfigDefaultPath; + } + @action resetTheme() { this.preferences.colorTheme = UserStore.defaultTheme; @@ -67,14 +76,18 @@ export class UserStore extends BaseStore { } protected refreshNewContexts = async () => { - const kubeConfig = await getKubeConfigLocal(); - if (kubeConfig) { - this.newContexts.clear(); - const localContexts = loadConfig(kubeConfig).getContexts(); - localContexts - .filter(ctx => ctx.cluster) - .filter(ctx => !this.seenContexts.has(ctx.name)) - .forEach(ctx => this.newContexts.add(ctx.name)); + try { + const kubeConfig = await readFile(this.kubeConfigPath, "utf8"); + if (kubeConfig) { + this.newContexts.clear(); + loadConfig(kubeConfig).getContexts() + .filter(ctx => ctx.cluster) + .filter(ctx => !this.seenContexts.has(ctx.name)) + .forEach(ctx => this.newContexts.add(ctx.name)); + } + } catch (err) { + logger.error(err); + this.resetKubeConfigPath(); } } @@ -86,17 +99,21 @@ export class UserStore extends BaseStore { } @action - protected fromStore(data: Partial = {}) { - const { lastSeenAppVersion, seenContexts = [], preferences } = data + protected async fromStore(data: Partial = {}) { + const { lastSeenAppVersion, seenContexts = [], preferences, kubeConfigPath } = data if (lastSeenAppVersion) { this.lastSeenAppVersion = lastSeenAppVersion; } + if (kubeConfigPath) { + this.kubeConfigPath = kubeConfigPath; + } this.seenContexts.replace(seenContexts); Object.assign(this.preferences, preferences); } toJSON(): UserStoreModel { const model: UserStoreModel = { + kubeConfigPath: this.kubeConfigPath, lastSeenAppVersion: this.lastSeenAppVersion, seenContexts: Array.from(this.seenContexts), preferences: this.preferences, diff --git a/src/common/utils/saveToAppFiles.ts b/src/common/utils/saveToAppFiles.ts new file mode 100644 index 0000000000..b0b3ff8d7a --- /dev/null +++ b/src/common/utils/saveToAppFiles.ts @@ -0,0 +1,11 @@ +// Save file to electron app directory (e.g. "/Users/$USER/Library/Application Support/Lens" for MacOS) +import path from "path"; +import { app, remote } from "electron"; +import { ensureDirSync, writeFileSync } from "fs-extra"; + +export function saveToAppFiles(filePath: string, contents: any): string { + const absPath = path.resolve((app || remote.app).getPath("userData"), filePath); + ensureDirSync(path.dirname(absPath)); + writeFileSync(absPath, contents); + return absPath; +} diff --git a/src/migrations/cluster-store/3.6.0-beta.1.ts b/src/migrations/cluster-store/3.6.0-beta.1.ts index db39cb741d..18ce07a638 100644 --- a/src/migrations/cluster-store/3.6.0-beta.1.ts +++ b/src/migrations/cluster-store/3.6.0-beta.1.ts @@ -5,8 +5,8 @@ import path from "path" import { app, remote } from "electron" import { migration } from "../migration-wrapper"; import fse from "fs-extra" -import { ClusterModel } from "../../common/cluster-store"; -import { loadConfig, saveConfigToAppFiles } from "../../common/kube-helpers"; +import { ClusterModel, ClusterStore } from "../../common/cluster-store"; +import { loadConfig } from "../../common/kube-helpers"; import makeSynchronous from "make-synchronous" const AsyncFunction = Object.getPrototypeOf(async function () { return }).constructor; @@ -17,7 +17,7 @@ export default migration({ version: "3.6.0-beta.1", run(store, printLog) { const userDataPath = (app || remote.app).getPath("userData") - const kubeConfigBase = path.join(userDataPath, "kubeconfigs") + const kubeConfigBase = ClusterStore.getCustomKubeConfigPath(""); const storedClusters: ClusterModel[] = store.get("clusters") || []; if (!storedClusters.length) return; @@ -31,7 +31,7 @@ export default migration({ */ try { // take the embedded kubeconfig and dump it into a file - cluster.kubeConfigPath = saveConfigToAppFiles(cluster.id, cluster.kubeConfig) + cluster.kubeConfigPath = ClusterStore.embedCustomKubeConfig(cluster.id, cluster.kubeConfig); cluster.contextName = loadConfig(cluster.kubeConfigPath).getCurrentContext(); delete cluster.kubeConfig; diff --git a/src/renderer/components/+add-cluster/add-cluster.scss b/src/renderer/components/+add-cluster/add-cluster.scss index 17b6a09d82..90977fa78b 100644 --- a/src/renderer/components/+add-cluster/add-cluster.scss +++ b/src/renderer/components/+add-cluster/add-cluster.scss @@ -1,5 +1,39 @@ .AddCluster { + .droppable { + box-shadow: 0 0 0 5px inset $primary; + + > * { + pointer-events: none; + } + } + + .hint { + margin-top: -$padding; + color: $textColorSecondary; + + > * { + vertical-align: middle; + } + } + + .AceEditor { + min-height: 200px; + max-height: 400px; + } + .Select { + .kube-context { + --flex-gap: #{$padding}; + } + + // todo: extract to component, merge with namespace-select.scss + &__placeholder { + width: 100%; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + &__control { box-shadow: 0 0 0 1px $borderFaintColor; } @@ -8,4 +42,4 @@ code { color: $pink-400; } -} \ No newline at end of file +} diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx index 0672a1a91a..9245926029 100644 --- a/src/renderer/components/+add-cluster/add-cluster.tsx +++ b/src/renderer/components/+add-cluster/add-cluster.tsx @@ -1,112 +1,163 @@ import "./add-cluster.scss" +import os from "os"; import React, { Fragment } from "react"; import { observer } from "mobx-react"; -import { computed, observable } from "mobx"; +import { action, observable, runInAction } from "mobx"; +import { remote } from "electron"; import { KubeConfig } from "@kubernetes/client-node"; -import { t, Trans } from "@lingui/macro"; import { _i18n } from "../../i18n"; +import { t, Trans } from "@lingui/macro"; import { Select, SelectOption } from "../select"; import { Input } from "../input"; import { AceEditor } from "../ace-editor"; import { Button } from "../button"; import { Icon } from "../icon"; import { WizardLayout } from "../layout/wizard-layout"; -import { getKubeConfigLocal, loadConfig, saveConfigToAppFiles, splitConfig, validateConfig } from "../../../common/kube-helpers"; -import { clusterStore } from "../../../common/cluster-store"; +import { kubeConfigDefaultPath, loadConfig, splitConfig, validateConfig } from "../../../common/kube-helpers"; +import { ClusterModel, ClusterStore, clusterStore } from "../../../common/cluster-store"; import { workspaceStore } from "../../../common/workspace-store"; import { v4 as uuid } from "uuid" import { navigate } from "../../navigation"; import { userStore } from "../../../common/user-store"; import { clusterViewURL } from "../cluster-manager/cluster-view.route"; +import { cssNames } from "../../utils"; +import { Notifications } from "../notifications"; +import { Tab, Tabs } from "../tabs"; + +enum KubeConfigSourceTab { + FILE = "file", + TEXT = "text" +} @observer export class AddCluster extends React.Component { - readonly custom: any = "custom" - @observable.ref clusterConfig: KubeConfig; - @observable.ref kubeConfig: KubeConfig; // local ~/.kube/config (if available) + @observable.ref kubeConfigLocal: KubeConfig; @observable.ref error: React.ReactNode; + @observable kubeContexts = observable.map(); // available contexts from kubeconfig-file or user-input + @observable selectedContexts = observable.array(); + @observable sourceTab = KubeConfigSourceTab.FILE; + @observable kubeConfigPath = ""; + @observable customConfig = "" + @observable proxyServer = "" @observable isWaiting = false @observable showSettings = false - @observable proxyServer = "" - @observable customConfig = "" + @observable dropAreaActive = false; - async componentDidMount() { - const kubeConfig: string = await getKubeConfigLocal() - if (kubeConfig) { - this.kubeConfig = loadConfig(kubeConfig) - } + componentDidMount() { + this.setKubeConfig(userStore.kubeConfigPath); } componentWillUnmount() { userStore.markNewContextsAsSeen(); } - @computed get isCustom() { - return this.clusterConfig === this.custom; - } - - @computed get clusterOptions() { - const options: SelectOption[] = []; - if (this.kubeConfig) { - splitConfig(this.kubeConfig).forEach(kubeConfig => { - const context = kubeConfig.currentContext; - const hasContext = clusterStore.hasContext(context); - if (!hasContext) { - options.push({ - value: kubeConfig, - label: context, - }); - } - }) - } - options.push({ - label: Custom.., - value: this.custom, - }); - return options; - } - - protected formatClusterContextLabel = ({ value, label }: SelectOption) => { - if (value instanceof KubeConfig) { - const context = value.currentContext; - const isNew = userStore.newContexts.has(context); - const className = `${context} kube-context flex gaps align-center` - return ( -
- {context} - {isNew && } -
- ) - } - return label; - }; - - addCluster = async () => { - const { clusterConfig, customConfig, proxyServer } = this; - const clusterId = uuid(); - this.isWaiting = true - this.error = "" + @action + setKubeConfig(filePath: string, { throwError = false } = {}) { try { - const config = this.isCustom ? loadConfig(customConfig) : clusterConfig; - if (!config) { - this.error = Please select kubeconfig + this.kubeConfigLocal = loadConfig(filePath); + validateConfig(this.kubeConfigLocal); + this.refreshContexts(); + this.kubeConfigPath = filePath; + userStore.kubeConfigPath = filePath; // save to store + } catch (err) { + Notifications.error( +
Can't setup {filePath} as kubeconfig: {String(err)}
+ ); + if (throwError) { + throw err; + } + } + } + + @action + refreshContexts() { + this.selectedContexts.clear(); + this.kubeContexts.clear(); + + switch (this.sourceTab) { + case KubeConfigSourceTab.FILE: + const contexts = this.getContexts(this.kubeConfigLocal); + this.kubeContexts.replace(contexts); + break; + + case KubeConfigSourceTab.TEXT: + try { + this.error = "" + const contexts = this.getContexts(loadConfig(this.customConfig || "{}")); + this.kubeContexts.replace(contexts); + } catch (err) { + this.error = String(err); + } + break; + } + } + + getContexts(config: KubeConfig): Map { + const contexts = new Map(); + splitConfig(config).forEach(config => { + const isExists = clusterStore.hasContext(config.currentContext); + if (!isExists) { + contexts.set(config.currentContext, config); + } + }) + return contexts + } + + selectKubeConfigDialog = async () => { + const { dialog, BrowserWindow } = remote; + const { canceled, filePaths } = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), { + defaultPath: this.kubeConfigPath, + properties: ["openFile", "showHiddenFiles"], + message: _i18n._(t`Select custom kubeconfig file`), + buttonLabel: _i18n._(t`Use configuration`), + }); + if (!canceled && filePaths.length) { + this.setKubeConfig(filePaths[0]); + } + } + + addClusters = () => { + try { + if (!this.selectedContexts.length) { + this.error = Please select at least one cluster context return; } - validateConfig(config); - await clusterStore.addCluster({ - id: clusterId, - kubeConfigPath: saveConfigToAppFiles(clusterId, config), - workspace: workspaceStore.currentWorkspaceId, - contextName: config.currentContext, - preferences: { - clusterName: config.currentContext, - httpsProxy: proxyServer || undefined, - }, + this.error = "" + this.isWaiting = true + const newClusters: ClusterModel[] = this.selectedContexts.map(context => { + const clusterId = uuid(); + const kubeConfig = this.kubeContexts.get(context); + const kubeConfigPath = this.sourceTab === KubeConfigSourceTab.FILE + ? this.kubeConfigPath // save link to original kubeconfig in file-system + : ClusterStore.embedCustomKubeConfig(clusterId, kubeConfig); // save in app-files folder + return { + id: clusterId, + kubeConfigPath: kubeConfigPath, + workspace: workspaceStore.currentWorkspaceId, + contextName: kubeConfig.currentContext, + preferences: { + clusterName: kubeConfig.currentContext, + httpsProxy: this.proxyServer || undefined, + }, + } }); - navigate(clusterViewURL({ params: { clusterId } })) + runInAction(() => { + clusterStore.addCluster(...newClusters); + if (newClusters.length === 1) { + const clusterId = newClusters[0].id; + clusterStore.setActive(clusterId); + navigate(clusterViewURL({ params: { clusterId } })); + } else { + Notifications.ok( + Successfully imported {newClusters.length} cluster(s) + ); + } + }) + this.refreshContexts(); } catch (err) { this.error = String(err); + Notifications.error(Error while adding cluster(s): {this.error}); } finally { this.isWaiting = false; } @@ -163,19 +214,156 @@ export class AddCluster extends React.Component { ) } + renderKubeConfigSource() { + return ( + <> + + Select kubeconfig file} + active={this.sourceTab == KubeConfigSourceTab.FILE}/> + Paste as text} + active={this.sourceTab == KubeConfigSourceTab.TEXT} + /> + + {this.sourceTab === KubeConfigSourceTab.FILE && ( + <> +
+ this.kubeConfigPath = v} + onBlur={this.onKubeConfigInputBlur} + /> + {this.kubeConfigPath !== kubeConfigDefaultPath && ( + this.setKubeConfig(kubeConfigDefaultPath)} + tooltip={Reset} + /> + )} + Browse} + /> +
+ + Pro-Tip: you can also drag-n-drop kubeconfig file to this area + + + )} + {this.sourceTab === KubeConfigSourceTab.TEXT && ( + <> + { + this.customConfig = value; + this.refreshContexts(); + }} + /> + + Pro-Tip: paste kubeconfig to get available contexts + + + )} + + ) + } + + renderContextSelector() { + const allContexts = Array.from(this.kubeContexts.keys()); + const placeholder = this.selectedContexts.length > 0 + ? Selected contexts: {this.selectedContexts.length} + : Select contexts; + return ( + <> + Select kubeconfig} - value={this.clusterConfig} - options={this.clusterOptions} - onChange={({ value }: SelectOption) => this.clusterConfig = value} - formatOptionLabel={this.formatClusterContextLabel} - id="kubecontext-select" - /> + {this.renderKubeConfigSource()} + {this.renderContextSelector()} )} - {this.isCustom && ( -
-

Kubeconfig:

- this.customConfig = value} - /> -
- )} {this.error && (
{this.error}
)} @@ -214,7 +390,7 @@ export class AddCluster extends React.Component {