mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
more refactorings & fixes on kubeconfig paths
Signed-off-by: Jussi Nummelin <jussi.nummelin@gmail.com>
This commit is contained in:
parent
c46a5f6f20
commit
7178d0534a
@ -1,4 +1,7 @@
|
|||||||
|
import { app, remote } from "electron"
|
||||||
import ElectronStore from "electron-store"
|
import ElectronStore from "electron-store"
|
||||||
|
import { ensureDirSync, writeFileSync } from "fs-extra"
|
||||||
|
import * as path from "path"
|
||||||
import { Cluster, ClusterBaseInfo } from "../main/cluster";
|
import { Cluster, ClusterBaseInfo } from "../main/cluster";
|
||||||
import * as version200Beta2 from "../migrations/cluster-store/2.0.0-beta.2"
|
import * as version200Beta2 from "../migrations/cluster-store/2.0.0-beta.2"
|
||||||
import * as version241 from "../migrations/cluster-store/2.4.1"
|
import * as version241 from "../migrations/cluster-store/2.4.1"
|
||||||
@ -117,4 +120,17 @@ export class ClusterStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const clusterStore = ClusterStore.getInstance();
|
export const clusterStore: ClusterStore = ClusterStore.getInstance();
|
||||||
|
|
||||||
|
// Writes kubeconfigs to "embedded" store, i.e. .../Lens/kubeconfigs/
|
||||||
|
export function writeEmbeddedKubeConfig(clusterId: string, kubeConfig: string): string {
|
||||||
|
// This can be called from main & renderer
|
||||||
|
const a = (app || remote.app)
|
||||||
|
const kubeConfigBase = path.join(a.getPath("userData"), "kubeconfigs")
|
||||||
|
ensureDirSync(kubeConfigBase)
|
||||||
|
|
||||||
|
const kubeConfigFile = path.join(kubeConfigBase, clusterId)
|
||||||
|
writeFileSync(kubeConfigFile, kubeConfig)
|
||||||
|
|
||||||
|
return kubeConfigFile
|
||||||
|
}
|
||||||
|
|||||||
@ -86,7 +86,6 @@ export class Cluster implements ClusterInfo {
|
|||||||
constructor(clusterInfo: ClusterBaseInfo) {
|
constructor(clusterInfo: ClusterBaseInfo) {
|
||||||
if (clusterInfo) Object.assign(this, clusterInfo)
|
if (clusterInfo) Object.assign(this, clusterInfo)
|
||||||
if (!this.preferences) this.preferences = {}
|
if (!this.preferences) this.preferences = {}
|
||||||
this.kubeconfigManager = new KubeconfigManager(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public proxyKubeconfigPath() {
|
public proxyKubeconfigPath() {
|
||||||
@ -95,7 +94,9 @@ export class Cluster implements ClusterInfo {
|
|||||||
|
|
||||||
public async init(kc: KubeConfig) {
|
public async init(kc: KubeConfig) {
|
||||||
this.contextHandler = new ContextHandler(kc, this)
|
this.contextHandler = new ContextHandler(kc, this)
|
||||||
//this.contextName = kc.currentContext
|
await this.contextHandler.init() // So we get the proxy port reserved
|
||||||
|
this.kubeconfigManager = new KubeconfigManager(this)
|
||||||
|
|
||||||
this.url = this.contextHandler.url
|
this.url = this.contextHandler.url
|
||||||
this.apiUrl = kc.getCurrentCluster().server
|
this.apiUrl = kc.getCurrentCluster().server
|
||||||
}
|
}
|
||||||
@ -139,14 +140,6 @@ export class Cluster implements ClusterInfo {
|
|||||||
this.eventCount = await this.getEventCount();
|
this.eventCount = await this.getEventCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
// public updateKubeconfig(kubeconfig: string) {
|
|
||||||
// const storedCluster = clusterStore.getCluster(this.id)
|
|
||||||
// if (!storedCluster) { return }
|
|
||||||
|
|
||||||
// this.kubeConfig = kubeconfig
|
|
||||||
// this.save()
|
|
||||||
// }
|
|
||||||
|
|
||||||
public getPrometheusApiPrefix() {
|
public getPrometheusApiPrefix() {
|
||||||
if (!this.preferences.prometheus?.prefix) {
|
if (!this.preferences.prometheus?.prefix) {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@ -22,14 +22,14 @@ export class ContextHandler {
|
|||||||
|
|
||||||
protected apiTarget: ServerOptions
|
protected apiTarget: ServerOptions
|
||||||
protected proxyTarget: ServerOptions
|
protected proxyTarget: ServerOptions
|
||||||
protected clusterUrl: url.UrlWithStringQuery
|
public clusterUrl: url.UrlWithStringQuery
|
||||||
protected proxyServer: KubeAuthProxy
|
public proxyServer: KubeAuthProxy
|
||||||
|
|
||||||
protected clientCert: string
|
protected clientCert: string
|
||||||
protected clientKey: string
|
protected clientKey: string
|
||||||
protected secureApiConnection = true
|
protected secureApiConnection = true
|
||||||
protected defaultNamespace: string
|
protected defaultNamespace: string
|
||||||
protected proxyPort: number
|
public proxyPort: number
|
||||||
protected kubernetesApi: string
|
protected kubernetesApi: string
|
||||||
protected prometheusProvider: string
|
protected prometheusProvider: string
|
||||||
protected prometheusPath: string
|
protected prometheusPath: string
|
||||||
@ -38,21 +38,6 @@ export class ContextHandler {
|
|||||||
constructor(kc: KubeConfig, cluster: Cluster) {
|
constructor(kc: KubeConfig, cluster: Cluster) {
|
||||||
this.id = cluster.id
|
this.id = cluster.id
|
||||||
this.kc = kc
|
this.kc = kc
|
||||||
logger.info(`**** ctxHandler.kc: ${JSON.stringify(kc, null, 2)}`)
|
|
||||||
// this.kc.users = [
|
|
||||||
// {
|
|
||||||
// name: kc.getCurrentUser().name,
|
|
||||||
// token: this.id
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// this.kc.contexts = [
|
|
||||||
// {
|
|
||||||
// name: kc.currentContext,
|
|
||||||
// cluster: kc.getCurrentCluster().name,
|
|
||||||
// user: kc.getCurrentUser().name,
|
|
||||||
// namespace: kc.getContextObject(kc.currentContext).namespace
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
this.kc.setCurrentContext(cluster.contextName)
|
this.kc.setCurrentContext(cluster.contextName)
|
||||||
|
|
||||||
this.cluster = cluster
|
this.cluster = cluster
|
||||||
@ -61,16 +46,14 @@ export class ContextHandler {
|
|||||||
this.defaultNamespace = kc.getContextObject(kc.currentContext).namespace
|
this.defaultNamespace = kc.getContextObject(kc.currentContext).namespace
|
||||||
this.url = `http://${this.id}.localhost:${cluster.port}/`
|
this.url = `http://${this.id}.localhost:${cluster.port}/`
|
||||||
this.kubernetesApi = `http://127.0.0.1:${cluster.port}/${this.id}`
|
this.kubernetesApi = `http://127.0.0.1:${cluster.port}/${this.id}`
|
||||||
// this.kc.clusters = [
|
|
||||||
// {
|
|
||||||
// name: kc.getCurrentCluster().name,
|
|
||||||
// server: this.kubernetesApi,
|
|
||||||
// skipTLSVerify: true
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
this.setClusterPreferences(cluster.preferences)
|
this.setClusterPreferences(cluster.preferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async init() {
|
||||||
|
await this.resolveProxyPort()
|
||||||
|
}
|
||||||
|
|
||||||
public setClusterPreferences(clusterPreferences?: ClusterPreferences) {
|
public setClusterPreferences(clusterPreferences?: ClusterPreferences) {
|
||||||
this.prometheusProvider = clusterPreferences.prometheusProvider?.type
|
this.prometheusProvider = clusterPreferences.prometheusProvider?.type
|
||||||
|
|
||||||
|
|||||||
@ -20,19 +20,18 @@ export class KubeconfigManager {
|
|||||||
return this.tempFile
|
return this.tempFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new "temporary" kubeconfig that point to the kubectl-proxy.
|
||||||
|
* This way any user of the config does not need to know anything about the auth etc. details.
|
||||||
|
*/
|
||||||
protected createTemporaryKubeconfig(): string {
|
protected createTemporaryKubeconfig(): string {
|
||||||
ensureDir(this.configDir)
|
ensureDir(this.configDir)
|
||||||
const path = `${this.configDir}/${randomFileName("kubeconfig")}`
|
const path = `${this.configDir}/${randomFileName("kubeconfig")}`
|
||||||
logger.debug('Creating temporary kubeconfig: ' + path)
|
|
||||||
logger.debug(`cluster url: ${this.cluster.url}`)
|
|
||||||
logger.debug(`cluster api url: ${this.cluster.apiUrl}`)
|
|
||||||
|
|
||||||
// TODO Make the names come from actual cluster.name etc.
|
|
||||||
let kc = {
|
let kc = {
|
||||||
clusters: [
|
clusters: [
|
||||||
{
|
{
|
||||||
name: this.cluster.contextName,
|
name: this.cluster.contextName,
|
||||||
server: `http://127.0.0.1:${this.cluster.port}/${this.cluster.id}`
|
server: `http://127.0.0.1:${this.cluster.contextHandler.proxyPort}`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
users: [
|
users: [
|
||||||
@ -44,6 +43,7 @@ export class KubeconfigManager {
|
|||||||
{
|
{
|
||||||
name: this.cluster.contextName,
|
name: this.cluster.contextName,
|
||||||
cluster: this.cluster.contextName,
|
cluster: this.cluster.contextName,
|
||||||
|
namespace: this.cluster.contextHandler.kc.getContextObject(this.cluster.contextName).namespace,
|
||||||
user: "proxy"
|
user: "proxy"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
import { app } from "electron"
|
import { app } from "electron"
|
||||||
import { ensureDirSync, writeFileSync } from "fs-extra"
|
import { ensureDirSync, writeFileSync } from "fs-extra"
|
||||||
import * as path from "path"
|
import * as path from "path"
|
||||||
|
import { KubeConfig } from "@kubernetes/client-node";
|
||||||
|
import * as clusterStore from "../../cluster-store"
|
||||||
|
|
||||||
export function migration(store: any) {
|
export function migration(store: any) {
|
||||||
console.log("CLUSTER STORE, MIGRATION: 3.5.0-beta.1");
|
console.log("CLUSTER STORE, MIGRATION: 3.5.0-beta.1");
|
||||||
@ -11,21 +13,23 @@ export function migration(store: any) {
|
|||||||
ensureDirSync(kubeConfigBase)
|
ensureDirSync(kubeConfigBase)
|
||||||
let storedClusters = store.get("clusters") as any[]
|
let storedClusters = store.get("clusters") as any[]
|
||||||
if (!storedClusters) return
|
if (!storedClusters) return
|
||||||
|
|
||||||
console.log("num clusters to migrate: ", storedClusters.length)
|
console.log("num clusters to migrate: ", storedClusters.length)
|
||||||
for (let cluster of storedClusters ) {
|
for (let cluster of storedClusters ) {
|
||||||
|
// TODO Should probably guard this, not to make the whole migration fail if one cluster fails!?
|
||||||
// take the embedded kubeconfig and dump it into a file
|
// take the embedded kubeconfig and dump it into a file
|
||||||
const kubeConfigFile = path.join(kubeConfigBase, cluster.id)
|
const kubeConfigFile = clusterStore.writeEmbeddedKubeConfig(cluster.id, cluster.kubeConfig)
|
||||||
writeFileSync(kubeConfigFile, cluster.kubeConfig)
|
|
||||||
delete cluster.kubeConfig
|
|
||||||
cluster.kubeConfigPath = kubeConfigFile
|
cluster.kubeConfigPath = kubeConfigFile
|
||||||
// TODO Need to parse the context name from the config
|
|
||||||
|
const kc = new KubeConfig()
|
||||||
|
kc.loadFromFile(cluster.kubeConfigPath)
|
||||||
// "overwrite" the cluster configs
|
cluster.contextName = kc.getCurrentContext()
|
||||||
|
|
||||||
|
delete cluster.kubeConfig
|
||||||
clusters.push(cluster)
|
clusters.push(cluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "overwrite" the cluster configs
|
||||||
if (clusters.length > 0) {
|
if (clusters.length > 0) {
|
||||||
store.set("clusters", clusters)
|
store.set("clusters", clusters)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,15 @@
|
|||||||
<b-form-group
|
<b-form-group
|
||||||
label="Choose config:"
|
label="Choose config:"
|
||||||
>
|
>
|
||||||
|
<b-form-file
|
||||||
|
v-model="file"
|
||||||
|
:state="Boolean(file)"
|
||||||
|
placeholder="Choose a file or drop it here..."
|
||||||
|
drop-placeholder="Drop file here..."
|
||||||
|
@input="reloadKubeContexts()"
|
||||||
|
></b-form-file>
|
||||||
|
<div class="mt-3">Selected file: {{ file ? file.name : '' }}</div>
|
||||||
|
|
||||||
<b-form-select
|
<b-form-select
|
||||||
id="kubecontext-select"
|
id="kubecontext-select"
|
||||||
v-model="kubecontext"
|
v-model="kubecontext"
|
||||||
@ -114,6 +123,9 @@ import * as k8s from "@kubernetes/client-node"
|
|||||||
import { dumpConfigYaml } from "../../../main/k8s"
|
import { dumpConfigYaml } from "../../../main/k8s"
|
||||||
import ClustersMixin from "@/_vue/mixins/ClustersMixin";
|
import ClustersMixin from "@/_vue/mixins/ClustersMixin";
|
||||||
import * as path from "path"
|
import * as path from "path"
|
||||||
|
import fs from 'fs'
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import * as clusterStore from "../../common/cluster-store"
|
||||||
|
|
||||||
class ClusterAccessError extends Error {}
|
class ClusterAccessError extends Error {}
|
||||||
|
|
||||||
@ -126,6 +138,8 @@ export default {
|
|||||||
},
|
},
|
||||||
data(){
|
data(){
|
||||||
return {
|
return {
|
||||||
|
file: null,
|
||||||
|
filepath: null,
|
||||||
clusterconfig: "",
|
clusterconfig: "",
|
||||||
httpsProxy: "",
|
httpsProxy: "",
|
||||||
kubecontext: "",
|
kubecontext: "",
|
||||||
@ -137,7 +151,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted: function() {
|
mounted: function() {
|
||||||
this.$store.dispatch("reloadAvailableKubeContexts");
|
const kubeConfigPath = path.join(process.env.HOME, '.kube', 'config')
|
||||||
|
this.filepath = kubeConfigPath
|
||||||
|
this.file = new File(fs.readFileSync(this.filepath), this.filepath)
|
||||||
|
this.$store.dispatch("reloadAvailableKubeContexts", this.filepath);
|
||||||
this.seenContexts = JSON.parse(JSON.stringify(this.$store.getters.seenContexts)) // clone seenContexts from store
|
this.seenContexts = JSON.parse(JSON.stringify(this.$store.getters.seenContexts)) // clone seenContexts from store
|
||||||
this.storeSeenContexts()
|
this.storeSeenContexts()
|
||||||
},
|
},
|
||||||
@ -164,6 +181,10 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
reloadKubeContexts() {
|
||||||
|
this.filepath = this.file.path
|
||||||
|
this.$store.dispatch("reloadAvailableKubeContexts", this.file.path);
|
||||||
|
},
|
||||||
isNewContext(context) {
|
isNewContext(context) {
|
||||||
return this.newContexts.indexOf(context) > -1
|
return this.newContexts.indexOf(context) > -1
|
||||||
},
|
},
|
||||||
@ -197,12 +218,15 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const kc = new k8s.KubeConfig();
|
const kc = new k8s.KubeConfig();
|
||||||
kc.loadFromString(this.clusterconfig); // throws TypeError if we cannot parse kubeconfig
|
kc.loadFromString(this.clusterconfig); // throws TypeError if we cannot parse kubeconfig
|
||||||
|
const clusterId = uuidv4();
|
||||||
// TODO Remove hardcoded path
|
// We need to store the kubeconfig to "app-home"/
|
||||||
const configPath = path.join(process.env.HOME, '.kube', 'config');
|
if (this.kubecontext === "custom") {
|
||||||
|
// TODO Call "writeEmbeddedKubeConfig"
|
||||||
|
this.filepath = clusterStore.writeEmbeddedKubeConfig(clusterId, this.clusterconfig)
|
||||||
|
}
|
||||||
const clusterInfo = {
|
const clusterInfo = {
|
||||||
kubeConfigPath: configPath,
|
id: clusterId,
|
||||||
|
kubeConfigPath: this.filepath,
|
||||||
contextName: kc.currentContext,
|
contextName: kc.currentContext,
|
||||||
preferences: {
|
preferences: {
|
||||||
clusterName: kc.currentContext
|
clusterName: kc.currentContext
|
||||||
@ -212,6 +236,7 @@ export default {
|
|||||||
if (this.httpsProxy) {
|
if (this.httpsProxy) {
|
||||||
clusterInfo.preferences.httpsProxy = this.httpsProxy
|
clusterInfo.preferences.httpsProxy = this.httpsProxy
|
||||||
}
|
}
|
||||||
|
console.log("sending clusterInfo:", clusterInfo)
|
||||||
let res = await this.$store.dispatch('addCluster', clusterInfo)
|
let res = await this.$store.dispatch('addCluster', clusterInfo)
|
||||||
console.log("addCluster result:", res)
|
console.log("addCluster result:", res)
|
||||||
if(!res){
|
if(!res){
|
||||||
|
|||||||
@ -6,10 +6,10 @@ const state = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
reloadAvailableKubeContexts: ({commit}) => {
|
reloadAvailableKubeContexts({commit}, file) {
|
||||||
let kc = new k8s.KubeConfig();
|
let kc = new k8s.KubeConfig();
|
||||||
try {
|
try {
|
||||||
kc.loadFromDefault();
|
kc.loadFromFile(file);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to read default kubeconfig: " + error.message);
|
console.error("Failed to read default kubeconfig: " + error.message);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user