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_REMOVE = "cluster-remove",
|
||||
CLUSTER_REMOVE_WORKSPACE = "cluster-remove-all-from-workspace",
|
||||
CLUSTER_EVENTS = "cluster-events-count",
|
||||
CLUSTER_REFRESH = "cluster-refresh",
|
||||
CLUSTER_REFRESH = "cluster-refresh", // todo: remove, possibly won't needed
|
||||
FEATURE_INSTALL = "cluster-feature-install",
|
||||
FEATURE_UPGRADE = "cluster-feature-upgrade",
|
||||
FEATURE_REMOVE = "cluster-feature-remove",
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
// Creates system valid random filename
|
||||
// Create random filename
|
||||
|
||||
export function randomFileName(name: string) {
|
||||
return `${Math.random().toString(36).substring(2, 15)}-${Math.random().toString(36).substring(2, 15)}-${name}`
|
||||
export function randomFileName({ prefix = "", suffix = "", sep = "__" } = {}) {
|
||||
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 {
|
||||
let cluster: Cluster = null
|
||||
|
||||
@ -131,7 +131,6 @@ export class ClusterManager {
|
||||
}
|
||||
}
|
||||
|
||||
// todo: check feature failures
|
||||
protected async installFeature({ clusterId, name, config }: FeatureInstallRequest) {
|
||||
tracker.event("cluster", "install-feature")
|
||||
return this.getCluster(clusterId)?.installFeature(name, config)
|
||||
@ -147,10 +146,6 @@ export class ClusterManager {
|
||||
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) {
|
||||
await this.getCluster(clusterId)?.refreshStatus();
|
||||
}
|
||||
@ -161,7 +156,6 @@ export class ClusterManager {
|
||||
[ClusterIpcMessage.CLUSTER_STOP]: clusterManager.stopCluster,
|
||||
[ClusterIpcMessage.CLUSTER_REMOVE]: clusterManager.removeCluster,
|
||||
[ClusterIpcMessage.CLUSTER_REMOVE_WORKSPACE]: clusterManager.removeAllByWorkspace,
|
||||
[ClusterIpcMessage.CLUSTER_EVENTS]: clusterManager.getEventsCount,
|
||||
[ClusterIpcMessage.CLUSTER_REFRESH]: clusterManager.refreshCluster,
|
||||
[ClusterIpcMessage.FEATURE_INSTALL]: clusterManager.installFeature,
|
||||
[ClusterIpcMessage.FEATURE_UPGRADE]: clusterManager.upgradeFeature,
|
||||
|
||||
@ -48,11 +48,11 @@ export class Cluster implements ClusterModel {
|
||||
@observable online: boolean;
|
||||
@observable accessible: boolean;
|
||||
@observable failureReason: string;
|
||||
@observable nodes: number;
|
||||
@observable nodes = 0;
|
||||
@observable version: string;
|
||||
@observable distribution: string;
|
||||
@observable isAdmin: boolean;
|
||||
@observable eventCount: number; // todo: auto-fetch every 3s and push updates to client (?)
|
||||
@observable distribution = "unknown";
|
||||
@observable isAdmin = false;
|
||||
@observable eventCount = 0; // todo: auto-fetch every 3s and push updates to client (?)
|
||||
@observable preferences: ClusterPreferences = {};
|
||||
@observable features: FeatureStatusMap = {};
|
||||
|
||||
@ -149,7 +149,7 @@ export class Cluster implements ClusterModel {
|
||||
return this.preferences.prometheus?.prefix || ""
|
||||
}
|
||||
|
||||
k8sRequest(path: string, options: RequestPromiseOptions = {}) {
|
||||
protected k8sRequest(path: string, options: RequestPromiseOptions = {}) {
|
||||
return request(this.kubeProxyUrl + path, {
|
||||
json: true,
|
||||
timeout: 10000,
|
||||
@ -160,7 +160,7 @@ export class Cluster implements ClusterModel {
|
||||
})
|
||||
}
|
||||
|
||||
protected async getConnectionStatus() {
|
||||
protected async getConnectionStatus(): Promise<ClusterStatus> {
|
||||
try {
|
||||
const response = await this.k8sRequest("/version")
|
||||
this.version = response.gitVersion
|
||||
@ -198,7 +198,7 @@ export class Cluster implements ClusterModel {
|
||||
kind: "SelfSubjectAccessReview",
|
||||
spec: { resourceAttributes }
|
||||
})
|
||||
return accessReview.body.status.allowed === true
|
||||
return accessReview.body.status.allowed
|
||||
} catch (error) {
|
||||
logger.error(`failed to request selfSubjectAccessReview: ${error.message}`)
|
||||
return false
|
||||
@ -224,7 +224,7 @@ export class Cluster implements ClusterModel {
|
||||
return "vanilla"
|
||||
}
|
||||
|
||||
protected async getNodeCount() {
|
||||
protected async getNodeCount(): Promise<number> {
|
||||
try {
|
||||
const response = await this.k8sRequest("/api/v1/nodes")
|
||||
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) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ export class ContextHandler {
|
||||
return apiTarget
|
||||
}
|
||||
|
||||
// fixme: verify
|
||||
// fixme
|
||||
public async getApiTargetUrl(): Promise<string> {
|
||||
await this.ensurePort();
|
||||
return `http://127.0.0.1:${this.proxyPort}${this.clusterUrl.path}`;
|
||||
|
||||
@ -66,52 +66,52 @@ export function splitConfig(kubeConfig: KubeConfig): KubeConfig[] {
|
||||
return configs;
|
||||
}
|
||||
|
||||
export function dumpConfigYaml(kc: KubeConfig): string {
|
||||
export function dumpConfigYaml(kubeConfig: Partial<KubeConfig>): string {
|
||||
const config = {
|
||||
apiVersion: "v1",
|
||||
kind: "Config",
|
||||
preferences: {},
|
||||
'current-context': kc.currentContext,
|
||||
clusters: kc.clusters.map(c => {
|
||||
'current-context': kubeConfig.currentContext,
|
||||
clusters: kubeConfig.clusters.map(cluster => {
|
||||
return {
|
||||
name: c.name,
|
||||
name: cluster.name,
|
||||
cluster: {
|
||||
'certificate-authority-data': c.caData,
|
||||
'certificate-authority': c.caFile,
|
||||
server: c.server,
|
||||
'insecure-skip-tls-verify': c.skipTLSVerify
|
||||
'certificate-authority-data': cluster.caData,
|
||||
'certificate-authority': cluster.caFile,
|
||||
server: cluster.server,
|
||||
'insecure-skip-tls-verify': cluster.skipTLSVerify
|
||||
}
|
||||
}
|
||||
}),
|
||||
contexts: kc.contexts.map(c => {
|
||||
contexts: kubeConfig.contexts.map(context => {
|
||||
return {
|
||||
name: c.name,
|
||||
name: context.name,
|
||||
context: {
|
||||
cluster: c.cluster,
|
||||
user: c.user,
|
||||
namespace: c.namespace
|
||||
cluster: context.cluster,
|
||||
user: context.user,
|
||||
namespace: context.namespace
|
||||
}
|
||||
}
|
||||
}),
|
||||
users: kc.users.map(u => {
|
||||
users: kubeConfig.users.map(user => {
|
||||
return {
|
||||
name: u.name,
|
||||
name: user.name,
|
||||
user: {
|
||||
'client-certificate-data': u.certData,
|
||||
'client-certificate': u.certFile,
|
||||
'client-key-data': u.keyData,
|
||||
'client-key': u.keyFile,
|
||||
'auth-provider': u.authProvider,
|
||||
exec: u.exec,
|
||||
token: u.token,
|
||||
username: u.username,
|
||||
password: u.password
|
||||
'client-certificate-data': user.certData,
|
||||
'client-certificate': user.certFile,
|
||||
'client-key-data': user.keyData,
|
||||
'client-key': user.keyFile,
|
||||
'auth-provider': user.authProvider,
|
||||
exec: user.exec,
|
||||
token: user.token,
|
||||
username: user.username,
|
||||
password: user.password
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// logger.info("Dumping KubeConfig:", config);
|
||||
logger.debug("Dumping KubeConfig:", config);
|
||||
// skipInvalid: true makes dump ignore undefined values
|
||||
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 { Cluster } from "./cluster"
|
||||
import { app } from "electron"
|
||||
import path from "path"
|
||||
import fs from "fs-extra"
|
||||
import { randomFileName } from "../common/utils"
|
||||
import { dumpConfigYaml, loadConfig } from "./k8s"
|
||||
import logger from "./logger"
|
||||
|
||||
export class KubeconfigManager {
|
||||
protected configDir = app.getPath("temp")
|
||||
protected tempFile: string
|
||||
protected tempFile: string;
|
||||
|
||||
constructor(protected cluster: Cluster, protected contextHandler: ContextHandler) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
protected async init() {
|
||||
await this.contextHandler.ensurePort();
|
||||
this.tempFile = await this.createTemporaryKubeconfig();
|
||||
try {
|
||||
await this.contextHandler.ensurePort();
|
||||
await this.createProxyKubeconfig();
|
||||
} catch (err) {
|
||||
logger.error(`Failed to created temp config`, { err })
|
||||
}
|
||||
}
|
||||
|
||||
getPath() {
|
||||
@ -27,33 +32,38 @@ export class KubeconfigManager {
|
||||
* 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 async createTemporaryKubeconfig(): Promise<string> {
|
||||
protected async createProxyKubeconfig(): Promise<string> {
|
||||
fs.ensureDir(this.configDir);
|
||||
const path = `${this.configDir}/${randomFileName("kubeconfig")}`;
|
||||
const { contextName, kubeConfigPath } = this.cluster;
|
||||
const { contextName, kubeConfigPath, id } = this.cluster;
|
||||
const tempFile = path.join(this.configDir, `kubeconfig-${id}`);
|
||||
const kubeConfig = loadConfig(kubeConfigPath);
|
||||
kubeConfig.clusters = [
|
||||
{
|
||||
name: contextName,
|
||||
server: await this.contextHandler.getApiTargetUrl(),
|
||||
skipTLSVerify: true,
|
||||
}
|
||||
];
|
||||
kubeConfig.users = [
|
||||
{ name: "proxy" },
|
||||
];
|
||||
kubeConfig.currentContext = contextName;
|
||||
kubeConfig.contexts = [
|
||||
{
|
||||
user: "proxy",
|
||||
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 proxyUser = "proxy";
|
||||
const proxyConfig: Partial<KubeConfig> = {
|
||||
currentContext: contextName,
|
||||
clusters: [
|
||||
{
|
||||
name: contextName,
|
||||
server: await this.contextHandler.getApiTargetUrl(),
|
||||
skipTLSVerify: undefined,
|
||||
}
|
||||
],
|
||||
users: [
|
||||
{ name: proxyUser },
|
||||
],
|
||||
contexts: [
|
||||
{
|
||||
user: proxyUser,
|
||||
name: contextName,
|
||||
cluster: contextName,
|
||||
namespace: kubeConfig.getContextObject(contextName).namespace,
|
||||
}
|
||||
]
|
||||
};
|
||||
const tempConfigYaml = dumpConfigYaml(proxyConfig);
|
||||
fs.writeFileSync(tempFile, tempConfigYaml);
|
||||
logger.info(`Created temp kubeconfig "${contextName}" at "${tempFile}": \n${tempConfigYaml}`);
|
||||
this.tempFile = tempFile;
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
unlink() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user