mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
kubeconfig-manager.ts fixes / refactoring
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
40da87c0ef
commit
e7fc6b4a35
@ -5,8 +5,7 @@ export enum ClusterIpcMessage {
|
|||||||
CLUSTER_STOP = "cluster-stop",
|
CLUSTER_STOP = "cluster-stop",
|
||||||
CLUSTER_REMOVE = "cluster-remove",
|
CLUSTER_REMOVE = "cluster-remove",
|
||||||
CLUSTER_REMOVE_WORKSPACE = "cluster-remove-all-from-workspace",
|
CLUSTER_REMOVE_WORKSPACE = "cluster-remove-all-from-workspace",
|
||||||
CLUSTER_EVENTS = "cluster-events-count",
|
CLUSTER_REFRESH = "cluster-refresh", // todo: remove, possibly won't needed
|
||||||
CLUSTER_REFRESH = "cluster-refresh",
|
|
||||||
FEATURE_INSTALL = "cluster-feature-install",
|
FEATURE_INSTALL = "cluster-feature-install",
|
||||||
FEATURE_UPGRADE = "cluster-feature-upgrade",
|
FEATURE_UPGRADE = "cluster-feature-upgrade",
|
||||||
FEATURE_REMOVE = "cluster-feature-remove",
|
FEATURE_REMOVE = "cluster-feature-remove",
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// Creates system valid random filename
|
// Create random filename
|
||||||
|
|
||||||
export function randomFileName(name: string) {
|
export function randomFileName({ prefix = "", suffix = "", sep = "__" } = {}) {
|
||||||
return `${Math.random().toString(36).substring(2, 15)}-${Math.random().toString(36).substring(2, 15)}-${name}`
|
const randId = () => Math.random().toString(16).substr(2);
|
||||||
|
return [prefix, randId(), suffix].filter(s => s).join(sep);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,7 +87,7 @@ export class ClusterManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixme: verify
|
// fixme
|
||||||
getClusterForRequest(req: http.IncomingMessage): Cluster {
|
getClusterForRequest(req: http.IncomingMessage): Cluster {
|
||||||
let cluster: Cluster = null
|
let cluster: Cluster = null
|
||||||
|
|
||||||
@ -131,7 +131,6 @@ export class ClusterManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: check feature failures
|
|
||||||
protected async installFeature({ clusterId, name, config }: FeatureInstallRequest) {
|
protected async installFeature({ clusterId, name, config }: FeatureInstallRequest) {
|
||||||
tracker.event("cluster", "install-feature")
|
tracker.event("cluster", "install-feature")
|
||||||
return this.getCluster(clusterId)?.installFeature(name, config)
|
return this.getCluster(clusterId)?.installFeature(name, config)
|
||||||
@ -147,10 +146,6 @@ export class ClusterManager {
|
|||||||
return this.getCluster(clusterId)?.uninstallFeature(name);
|
return this.getCluster(clusterId)?.uninstallFeature(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async getEventsCount(clusterId: ClusterId): Promise<number> {
|
|
||||||
return await this.getCluster(clusterId)?.getEventCount() || 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async refreshCluster(clusterId: ClusterId) {
|
protected async refreshCluster(clusterId: ClusterId) {
|
||||||
await this.getCluster(clusterId)?.refreshStatus();
|
await this.getCluster(clusterId)?.refreshStatus();
|
||||||
}
|
}
|
||||||
@ -161,7 +156,6 @@ export class ClusterManager {
|
|||||||
[ClusterIpcMessage.CLUSTER_STOP]: clusterManager.stopCluster,
|
[ClusterIpcMessage.CLUSTER_STOP]: clusterManager.stopCluster,
|
||||||
[ClusterIpcMessage.CLUSTER_REMOVE]: clusterManager.removeCluster,
|
[ClusterIpcMessage.CLUSTER_REMOVE]: clusterManager.removeCluster,
|
||||||
[ClusterIpcMessage.CLUSTER_REMOVE_WORKSPACE]: clusterManager.removeAllByWorkspace,
|
[ClusterIpcMessage.CLUSTER_REMOVE_WORKSPACE]: clusterManager.removeAllByWorkspace,
|
||||||
[ClusterIpcMessage.CLUSTER_EVENTS]: clusterManager.getEventsCount,
|
|
||||||
[ClusterIpcMessage.CLUSTER_REFRESH]: clusterManager.refreshCluster,
|
[ClusterIpcMessage.CLUSTER_REFRESH]: clusterManager.refreshCluster,
|
||||||
[ClusterIpcMessage.FEATURE_INSTALL]: clusterManager.installFeature,
|
[ClusterIpcMessage.FEATURE_INSTALL]: clusterManager.installFeature,
|
||||||
[ClusterIpcMessage.FEATURE_UPGRADE]: clusterManager.upgradeFeature,
|
[ClusterIpcMessage.FEATURE_UPGRADE]: clusterManager.upgradeFeature,
|
||||||
|
|||||||
@ -48,11 +48,11 @@ export class Cluster implements ClusterModel {
|
|||||||
@observable online: boolean;
|
@observable online: boolean;
|
||||||
@observable accessible: boolean;
|
@observable accessible: boolean;
|
||||||
@observable failureReason: string;
|
@observable failureReason: string;
|
||||||
@observable nodes: number;
|
@observable nodes = 0;
|
||||||
@observable version: string;
|
@observable version: string;
|
||||||
@observable distribution: string;
|
@observable distribution = "unknown";
|
||||||
@observable isAdmin: boolean;
|
@observable isAdmin = false;
|
||||||
@observable eventCount: number; // todo: auto-fetch every 3s and push updates to client (?)
|
@observable eventCount = 0; // todo: auto-fetch every 3s and push updates to client (?)
|
||||||
@observable preferences: ClusterPreferences = {};
|
@observable preferences: ClusterPreferences = {};
|
||||||
@observable features: FeatureStatusMap = {};
|
@observable features: FeatureStatusMap = {};
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ export class Cluster implements ClusterModel {
|
|||||||
return this.preferences.prometheus?.prefix || ""
|
return this.preferences.prometheus?.prefix || ""
|
||||||
}
|
}
|
||||||
|
|
||||||
k8sRequest(path: string, options: RequestPromiseOptions = {}) {
|
protected k8sRequest(path: string, options: RequestPromiseOptions = {}) {
|
||||||
return request(this.kubeProxyUrl + path, {
|
return request(this.kubeProxyUrl + path, {
|
||||||
json: true,
|
json: true,
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
@ -160,7 +160,7 @@ export class Cluster implements ClusterModel {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async getConnectionStatus() {
|
protected async getConnectionStatus(): Promise<ClusterStatus> {
|
||||||
try {
|
try {
|
||||||
const response = await this.k8sRequest("/version")
|
const response = await this.k8sRequest("/version")
|
||||||
this.version = response.gitVersion
|
this.version = response.gitVersion
|
||||||
@ -198,7 +198,7 @@ export class Cluster implements ClusterModel {
|
|||||||
kind: "SelfSubjectAccessReview",
|
kind: "SelfSubjectAccessReview",
|
||||||
spec: { resourceAttributes }
|
spec: { resourceAttributes }
|
||||||
})
|
})
|
||||||
return accessReview.body.status.allowed === true
|
return accessReview.body.status.allowed
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`failed to request selfSubjectAccessReview: ${error.message}`)
|
logger.error(`failed to request selfSubjectAccessReview: ${error.message}`)
|
||||||
return false
|
return false
|
||||||
@ -224,7 +224,7 @@ export class Cluster implements ClusterModel {
|
|||||||
return "vanilla"
|
return "vanilla"
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async getNodeCount() {
|
protected async getNodeCount(): Promise<number> {
|
||||||
try {
|
try {
|
||||||
const response = await this.k8sRequest("/api/v1/nodes")
|
const response = await this.k8sRequest("/api/v1/nodes")
|
||||||
return response.items.length
|
return response.items.length
|
||||||
@ -234,7 +234,7 @@ export class Cluster implements ClusterModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEventCount(): Promise<number> {
|
protected async getEventCount(): Promise<number> {
|
||||||
if (!this.isAdmin) {
|
if (!this.isAdmin) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,7 +80,7 @@ export class ContextHandler {
|
|||||||
return apiTarget
|
return apiTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixme: verify
|
// fixme
|
||||||
public async getApiTargetUrl(): Promise<string> {
|
public async getApiTargetUrl(): Promise<string> {
|
||||||
await this.ensurePort();
|
await this.ensurePort();
|
||||||
return `http://127.0.0.1:${this.proxyPort}${this.clusterUrl.path}`;
|
return `http://127.0.0.1:${this.proxyPort}${this.clusterUrl.path}`;
|
||||||
|
|||||||
@ -66,52 +66,52 @@ export function splitConfig(kubeConfig: KubeConfig): KubeConfig[] {
|
|||||||
return configs;
|
return configs;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dumpConfigYaml(kc: KubeConfig): string {
|
export function dumpConfigYaml(kubeConfig: Partial<KubeConfig>): string {
|
||||||
const config = {
|
const config = {
|
||||||
apiVersion: "v1",
|
apiVersion: "v1",
|
||||||
kind: "Config",
|
kind: "Config",
|
||||||
preferences: {},
|
preferences: {},
|
||||||
'current-context': kc.currentContext,
|
'current-context': kubeConfig.currentContext,
|
||||||
clusters: kc.clusters.map(c => {
|
clusters: kubeConfig.clusters.map(cluster => {
|
||||||
return {
|
return {
|
||||||
name: c.name,
|
name: cluster.name,
|
||||||
cluster: {
|
cluster: {
|
||||||
'certificate-authority-data': c.caData,
|
'certificate-authority-data': cluster.caData,
|
||||||
'certificate-authority': c.caFile,
|
'certificate-authority': cluster.caFile,
|
||||||
server: c.server,
|
server: cluster.server,
|
||||||
'insecure-skip-tls-verify': c.skipTLSVerify
|
'insecure-skip-tls-verify': cluster.skipTLSVerify
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
contexts: kc.contexts.map(c => {
|
contexts: kubeConfig.contexts.map(context => {
|
||||||
return {
|
return {
|
||||||
name: c.name,
|
name: context.name,
|
||||||
context: {
|
context: {
|
||||||
cluster: c.cluster,
|
cluster: context.cluster,
|
||||||
user: c.user,
|
user: context.user,
|
||||||
namespace: c.namespace
|
namespace: context.namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
users: kc.users.map(u => {
|
users: kubeConfig.users.map(user => {
|
||||||
return {
|
return {
|
||||||
name: u.name,
|
name: user.name,
|
||||||
user: {
|
user: {
|
||||||
'client-certificate-data': u.certData,
|
'client-certificate-data': user.certData,
|
||||||
'client-certificate': u.certFile,
|
'client-certificate': user.certFile,
|
||||||
'client-key-data': u.keyData,
|
'client-key-data': user.keyData,
|
||||||
'client-key': u.keyFile,
|
'client-key': user.keyFile,
|
||||||
'auth-provider': u.authProvider,
|
'auth-provider': user.authProvider,
|
||||||
exec: u.exec,
|
exec: user.exec,
|
||||||
token: u.token,
|
token: user.token,
|
||||||
username: u.username,
|
username: user.username,
|
||||||
password: u.password
|
password: user.password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// logger.info("Dumping KubeConfig:", config);
|
logger.debug("Dumping KubeConfig:", config);
|
||||||
// skipInvalid: true makes dump ignore undefined values
|
// skipInvalid: true makes dump ignore undefined values
|
||||||
return yaml.safeDump(config, { skipInvalid: true });
|
return yaml.safeDump(config, { skipInvalid: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,27 @@
|
|||||||
|
import type { KubeConfig } from "@kubernetes/client-node";
|
||||||
import type { ContextHandler } from "./context-handler";
|
import type { ContextHandler } from "./context-handler";
|
||||||
import type { Cluster } from "./cluster"
|
import type { Cluster } from "./cluster"
|
||||||
import { app } from "electron"
|
import { app } from "electron"
|
||||||
|
import path from "path"
|
||||||
import fs from "fs-extra"
|
import fs from "fs-extra"
|
||||||
import { randomFileName } from "../common/utils"
|
|
||||||
import { dumpConfigYaml, loadConfig } from "./k8s"
|
import { dumpConfigYaml, loadConfig } from "./k8s"
|
||||||
import logger from "./logger"
|
import logger from "./logger"
|
||||||
|
|
||||||
export class KubeconfigManager {
|
export class KubeconfigManager {
|
||||||
protected configDir = app.getPath("temp")
|
protected configDir = app.getPath("temp")
|
||||||
protected tempFile: string
|
protected tempFile: string;
|
||||||
|
|
||||||
constructor(protected cluster: Cluster, protected contextHandler: ContextHandler) {
|
constructor(protected cluster: Cluster, protected contextHandler: ContextHandler) {
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async init() {
|
protected async init() {
|
||||||
await this.contextHandler.ensurePort();
|
try {
|
||||||
this.tempFile = await this.createTemporaryKubeconfig();
|
await this.contextHandler.ensurePort();
|
||||||
|
await this.createProxyKubeconfig();
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`Failed to created temp config`, { err })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPath() {
|
getPath() {
|
||||||
@ -27,33 +32,38 @@ export class KubeconfigManager {
|
|||||||
* Creates new "temporary" kubeconfig that point to the kubectl-proxy.
|
* 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.
|
* This way any user of the config does not need to know anything about the auth etc. details.
|
||||||
*/
|
*/
|
||||||
protected async createTemporaryKubeconfig(): Promise<string> {
|
protected async createProxyKubeconfig(): Promise<string> {
|
||||||
fs.ensureDir(this.configDir);
|
fs.ensureDir(this.configDir);
|
||||||
const path = `${this.configDir}/${randomFileName("kubeconfig")}`;
|
const { contextName, kubeConfigPath, id } = this.cluster;
|
||||||
const { contextName, kubeConfigPath } = this.cluster;
|
const tempFile = path.join(this.configDir, `kubeconfig-${id}`);
|
||||||
const kubeConfig = loadConfig(kubeConfigPath);
|
const kubeConfig = loadConfig(kubeConfigPath);
|
||||||
kubeConfig.clusters = [
|
const proxyUser = "proxy";
|
||||||
{
|
const proxyConfig: Partial<KubeConfig> = {
|
||||||
name: contextName,
|
currentContext: contextName,
|
||||||
server: await this.contextHandler.getApiTargetUrl(),
|
clusters: [
|
||||||
skipTLSVerify: true,
|
{
|
||||||
}
|
name: contextName,
|
||||||
];
|
server: await this.contextHandler.getApiTargetUrl(),
|
||||||
kubeConfig.users = [
|
skipTLSVerify: undefined,
|
||||||
{ name: "proxy" },
|
}
|
||||||
];
|
],
|
||||||
kubeConfig.currentContext = contextName;
|
users: [
|
||||||
kubeConfig.contexts = [
|
{ name: proxyUser },
|
||||||
{
|
],
|
||||||
user: "proxy",
|
contexts: [
|
||||||
name: contextName,
|
{
|
||||||
cluster: contextName,
|
user: proxyUser,
|
||||||
namespace: kubeConfig.getContextObject(contextName).namespace,
|
name: contextName,
|
||||||
}
|
cluster: contextName,
|
||||||
];
|
namespace: kubeConfig.getContextObject(contextName).namespace,
|
||||||
logger.info(`Creating temp config for context "${contextName}" at "${path}"`);
|
}
|
||||||
fs.writeFileSync(path, dumpConfigYaml(kubeConfig));
|
]
|
||||||
return path;
|
};
|
||||||
|
const tempConfigYaml = dumpConfigYaml(proxyConfig);
|
||||||
|
fs.writeFileSync(tempFile, tempConfigYaml);
|
||||||
|
logger.info(`Created temp kubeconfig "${contextName}" at "${tempFile}": \n${tempConfigYaml}`);
|
||||||
|
this.tempFile = tempFile;
|
||||||
|
return tempFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlink() {
|
unlink() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user