1
0
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:
Roman 2020-07-12 12:29:12 +03:00
parent 40da87c0ef
commit e7fc6b4a35
7 changed files with 80 additions and 76 deletions

View File

@ -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",

View File

@ -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);
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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}`;

View File

@ -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 });
}

View File

@ -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() {