1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

refactoring

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2020-07-11 11:32:29 +03:00
parent 8bb3954700
commit e72e28ddab
22 changed files with 133 additions and 218 deletions

View File

@ -13,11 +13,12 @@ export type ClusterId = string;
export interface ClusterModel {
id: ClusterId;
contextName: string;
kubeConfigPath: string;
kubeConfig?: string;
workspace?: string;
preferences?: ClusterPreferences;
kubeConfigPath: string;
/** @deprecated */
kubeConfig?: string; // kube-config yaml
}
export interface ClusterPreferences {

View File

@ -0,0 +1,5 @@
// Clone json-serializable object
export function cloneJsonObject<T = object>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
}

View File

@ -4,3 +4,4 @@ export * from "./base64"
export * from "./camelCase"
export * from "./splitArray"
export * from "./randomFileName"
export * from "./cloneJson"

View File

@ -2,11 +2,8 @@ import { app, remote } from "electron"
import { ensureDirSync, writeFileSync } from "fs-extra"
import * as path from "path"
// todo: move to main/kubeconfig-manager.ts (?)
// Writes kubeconfigs to "embedded" store, i.e. .../Lens/kubeconfigs/
// Write kubeconfigs to "embedded" store, i.e. "/Users/ixrock/Library/Application Support/Lens/kubeconfigs"
export function writeEmbeddedKubeConfig(clusterId: string, kubeConfig: string): string {
// This can be called from main & renderer
const userData = (app || remote.app).getPath("userData");
const kubeConfigBase = path.join(userData, "kubeconfigs")
ensureDirSync(kubeConfigBase)

View File

@ -89,6 +89,7 @@ export class ClusterManager {
}
}
// fixme
getClusterForRequest(req: http.IncomingMessage): Cluster {
let cluster: Cluster = null

View File

@ -1,6 +1,6 @@
import url, { UrlWithStringQuery } from "url"
import type { ClusterId, ClusterModel, ClusterPreferences } from "../common/cluster-store"
import type { FeatureStatusMap } from "./feature"
import { UrlWithStringQuery } from "url"
import { action, observable, toJS } from "mobx";
import { ContextHandler } from "./context-handler"
import { AuthorizationV1Api, CoreV1Api, KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node"
@ -31,16 +31,15 @@ export interface ClusterState extends ClusterModel {
}
export class Cluster implements ClusterModel {
public id: ClusterId;
public kubeCtl: Kubectl
public contextHandler: ContextHandler;
protected kubeconfigManager: KubeconfigManager;
@observable initialized = false;
@observable id: ClusterId;
@observable workspace: string;
@observable kubeConfig?: string;
@observable kubeConfigPath: string;
@observable contextName: string;
@observable kubeConfigPath: string;
@observable port: number;
@observable url: string; // cluster-api url
@observable apiUrl: UrlWithStringQuery; // same as url, but parsed
@ -63,20 +62,23 @@ export class Cluster implements ClusterModel {
updateModel(model: ClusterModel) {
Object.assign(this, model);
if (!this.contextName) {
this.contextName = this.preferences.clusterName;
}
}
@action
// fixme: completely broken
async init() {
try {
// fixme: all broken
this.contextHandler = new ContextHandler(this);
this.contextName = this.contextHandler.contextName;
this.port = await this.contextHandler.resolveProxyPort(); // resolve port before KubeconfigManager
this.webContentUrl = `http://${this.id}.localhost:${this.port}`;
this.kubeAuthProxyUrl = `http://127.0.0.1:${this.port}`;
this.kubeconfigManager = new KubeconfigManager(this);
this.url = this.kubeconfigManager.getCurrentClusterServer();
this.apiUrl = url.parse(this.url);
// this.url = this.kubeconfigManager.getCurrentClusterServer();
// this.apiUrl = url.parse(this.url);
logger.info(`[CLUSTER]: init success`, {
id: this.id,
port: this.port,
@ -102,7 +104,7 @@ export class Cluster implements ClusterModel {
// todo: auto-refresh when preferences changed + by timer?
@action
async refreshCluster() {
this.contextHandler.setClusterPreferences(this.preferences);
this.contextHandler.setupPrometheus(this.preferences);
const connectionStatus = await this.getConnectionStatus()
this.accessible = connectionStatus == ClusterStatus.AccessGranted;

View File

@ -9,29 +9,18 @@ import { getFreePort } from "./port"
import { KubeAuthProxy } from "./kube-auth-proxy"
export class ContextHandler {
public url: string
public proxyPort: number
public contextName: string
protected id: string
protected proxyServer: KubeAuthProxy
protected apiTarget: ServerOptions
protected certData: string
protected authCertData: string
protected proxyTarget: ServerOptions
protected clientCert: string
protected clientKey: string
protected prometheusProvider: string
protected prometheusPath: string
constructor(protected cluster: Cluster) {
this.id = cluster.id
this.url = cluster.url;
this.contextName = cluster.contextName || cluster.preferences.clusterName;
this.setClusterPreferences(cluster.preferences)
this.setupPrometheus(cluster.preferences)
}
public setClusterPreferences(preferences: ClusterPreferences = {}) {
public setupPrometheus(preferences: ClusterPreferences = {}) {
this.prometheusProvider = preferences.prometheusProvider?.type;
this.prometheusPath = null;
if (preferences.prometheus) {
@ -89,6 +78,7 @@ export class ContextHandler {
return apiTarget
}
// fixme
protected async newApiTarget(timeout: number): Promise<ServerOptions> {
return {
changeOrigin: true,
@ -107,27 +97,19 @@ export class ContextHandler {
async resolveProxyPort(): Promise<number> {
if (!this.proxyPort) {
this.proxyPort = await getFreePort()
this.proxyPort = await getFreePort();
}
return this.proxyPort
}
public async withTemporaryKubeconfig(callback: (kubeconfig: string) => Promise<any>) {
try {
await callback(this.cluster.proxyKubeconfigPath())
} catch (error) {
throw(error)
}
}
public async ensureServer() {
if (!this.proxyServer) {
const proxyPort = await this.resolveProxyPort()
await this.resolveProxyPort();
const proxyEnv = Object.assign({}, process.env)
if (this.cluster.preferences.httpsProxy) {
proxyEnv.HTTPS_PROXY = this.cluster.preferences.httpsProxy
}
this.proxyServer = new KubeAuthProxy(this.cluster, proxyPort, proxyEnv)
this.proxyServer = new KubeAuthProxy(this.cluster, this.proxyPort, proxyEnv)
await this.proxyServer.run()
}
}
@ -139,7 +121,7 @@ export class ContextHandler {
}
}
public proxyServerError() {
public proxyServerError(): string {
return this.proxyServer?.lastError || ""
}
}

View File

@ -2,7 +2,7 @@ import fs from "fs";
import path from "path"
import hb from "handlebars"
import { ResourceApplier } from "./resource-applier"
import { KubeConfig, CoreV1Api, Watch } from "@kubernetes/client-node"
import { CoreV1Api, KubeConfig, Watch } from "@kubernetes/client-node"
import { Cluster } from "./cluster";
import logger from "./logger";
@ -23,75 +23,61 @@ export interface FeatureStatus {
export abstract class Feature {
name: string;
config: any;
latestVersion: string;
constructor(config: any) {
if(config) this.config = config;
}
// TODO Return types for these?
async install(cluster: Cluster): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
// Read and process yamls through handlebar
const resources = this.renderTemplates();
// Apply processed manifests
cluster.contextHandler.withTemporaryKubeconfig(async (kubeconfigPath) => {
const resourceApplier = new ResourceApplier(cluster, kubeconfigPath)
try {
await resourceApplier.kubectlApplyAll(resources)
resolve(true)
} catch(error) {
reject(error)
}
});
});
}
abstract async upgrade(cluster: Cluster): Promise<boolean>;
abstract async uninstall(cluster: Cluster): Promise<boolean>;
abstract async featureStatus(kc: KubeConfig): Promise<FeatureStatus>;
constructor(public config: any) {
}
async install(cluster: Cluster): Promise<boolean> {
const resources = this.renderTemplates();
try {
await new ResourceApplier(cluster).kubectlApplyAll(resources);
return true;
} catch (err) {
logger.error("Installing feature error", { err, cluster });
return false
}
}
protected async deleteNamespace(kc: KubeConfig, name: string) {
return new Promise(async (resolve, reject) => {
const client = kc.makeApiClient(CoreV1Api)
const result = await client.deleteNamespace("lens-metrics", 'false', undefined, undefined, undefined, "Foreground");
const nsVersion = result.body.metadata.resourceVersion;
const nsWatch = new Watch(kc);
const req = await nsWatch.watch('/api/v1/namespaces', {resourceVersion: nsVersion, fieldSelector: "metadata.name=lens-metrics"},
(type, obj) => {
if(type === 'DELETED') {
const query: Record<string, string> = {
resourceVersion: nsVersion,
fieldSelector: "metadata.name=lens-metrics",
}
const req = await nsWatch.watch('/api/v1/namespaces', query,
(phase, obj) => {
if (phase === 'DELETED') {
logger.debug(`namespace ${name} finally gone`)
req.abort();
resolve()
}
},
(err) => {
if(err) {
reject(err)
}
(err?: any) => {
if (err) reject(err);
});
});
}
protected renderTemplates(): string[] {
console.log("starting to render resources...");
const resources: string[] = [];
fs.readdirSync(this.manifestPath()).forEach((f) => {
const file = path.join(this.manifestPath(), f);
console.log("processing file:", file)
fs.readdirSync(this.manifestPath()).forEach(filename => {
const file = path.join(this.manifestPath(), filename);
const raw = fs.readFileSync(file);
console.log("raw file loaded");
if(f.endsWith('.hb')) {
console.log("processing HB template");
if (filename.endsWith('.hb')) {
const template = hb.compile(raw.toString());
resources.push(template(this.config));
console.log("HB template done");
} else {
console.log("using as raw, no HB detected");
resources.push(raw.toString());
}
});
@ -101,7 +87,7 @@ export abstract class Feature {
protected manifestPath() {
const devPath = path.join(__dirname, "..", 'src/features', this.name);
if(fs.existsSync(devPath)) {
if (fs.existsSync(devPath)) {
return devPath;
}
return path.join(__dirname, "..", 'features', this.name);

View File

@ -99,7 +99,7 @@ export class HelmReleaseManager {
protected async getResources(name: string, namespace: string, cluster: Cluster) {
const helm = await helmCli.binaryPath()
const kubectl = await cluster.kubeCtl.kubectlPath()
const kubectl = await cluster.kubeCtl.getPath()
const pathToKubeconfig = cluster.proxyKubeconfigPath()
const { stdout } = await promiseExec(`"${helm}" get manifest ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} | "${kubectl}" get -n ${namespace} --kubeconfig ${pathToKubeconfig} -f - -o=json`).catch((error) => {
return { stdout: JSON.stringify({items: []})}

View File

@ -11,7 +11,7 @@ function resolveTilde(filePath: string) {
return filePath;
}
export function loadKubeConfig(pathOrContent?: string): KubeConfig {
export function loadConfig(pathOrContent?: string): KubeConfig {
const kc = new KubeConfig();
if (path.isAbsolute(pathOrContent)) {
kc.loadFromFile(resolveTilde(pathOrContent));
@ -30,7 +30,7 @@ export function loadKubeConfig(pathOrContent?: string): KubeConfig {
*/
export function validateConfig(config: KubeConfig | string): KubeConfig {
if (typeof config == "string") {
config = loadKubeConfig(config);
config = loadConfig(config);
}
logger.debug(`validating kube config: ${JSON.stringify(config)}`)
if (!config.users || config.users.length == 0) {
@ -66,17 +66,6 @@ export function splitConfig(kubeConfig: KubeConfig): KubeConfig[] {
return configs;
}
/**
* Loads KubeConfig from a yaml and breaks it into several configs. Each context per KubeConfig object
*
* @param configPath path to kube config yaml file
*/
export function loadAndSplitConfig(configPath: string): KubeConfig[] {
const allConfigs = new KubeConfig();
allConfigs.loadFromFile(configPath);
return splitConfig(allConfigs);
}
export function dumpConfigYaml(kc: KubeConfig): string {
const config = {
apiVersion: "v1",
@ -122,8 +111,7 @@ export function dumpConfigYaml(kc: KubeConfig): string {
})
}
console.log("dumping kc:", config);
// logger.info("Dumping KubeConfig:", config);
// skipInvalid: true makes dump ignore undefined values
return yaml.safeDump(config, { skipInvalid: true });
}

View File

@ -25,7 +25,7 @@ export class KubeAuthProxy {
if (this.proxyProcess) {
return;
}
const proxyBin = await this.kubectl.kubectlPath()
const proxyBin = await this.kubectl.getPath()
let args = [
"proxy",
"-p", this.port.toString(),

View File

@ -1,13 +1,11 @@
import type { Cluster } from "./cluster"
import { app } from "electron"
import fs from "fs-extra"
import { KubeConfig } from "@kubernetes/client-node"
import { randomFileName } from "../common/utils"
import { dumpConfigYaml, loadKubeConfig } from "./k8s"
import { dumpConfigYaml, loadConfig } from "./k8s"
import logger from "./logger"
export class KubeconfigManager {
public config: KubeConfig;
protected configDir = app.getPath("temp")
protected tempFile: string
@ -19,16 +17,6 @@ export class KubeconfigManager {
return this.tempFile;
}
getCurrentClusterServer() {
return this.config.getCurrentCluster().server;
}
protected loadConfig() {
const { kubeConfigPath, kubeConfig } = this.cluster;
this.config = loadKubeConfig(kubeConfigPath || kubeConfig);
return this.config;
}
/**
* 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.
@ -36,12 +24,12 @@ export class KubeconfigManager {
protected createTemporaryKubeconfig(): string {
fs.ensureDir(this.configDir);
const path = `${this.configDir}/${randomFileName("kubeconfig")}`;
const { contextName, kubeAuthProxyUrl } = this.cluster;
const kubeConfig = this.loadConfig();
const { contextName, contextHandler, kubeConfigPath } = this.cluster;
const kubeConfig = loadConfig(kubeConfigPath);
kubeConfig.clusters = [
{
name: contextName,
server: kubeAuthProxyUrl,
server: `http://127.0.0.1:${contextHandler.proxyPort}`, // fixme: extract
skipTLSVerify: true,
}
];
@ -54,10 +42,11 @@ export class KubeconfigManager {
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));
logger.info(`Created temp kube-config file for context "${this.cluster.contextName}" at "${path}"`);
return path;
}

View File

@ -98,7 +98,7 @@ export class Kubectl {
this.path = path.join(this.dirname, binaryName)
}
public async kubectlPath(): Promise<string> {
public async getPath(): Promise<string> {
try {
await this.ensureKubectl()
return this.path

View File

@ -4,7 +4,7 @@ import { Socket } from "net";
import * as url from "url";
import * as WebSocket from "ws"
import { ContextHandler } from "./context-handler";
import * as shell from "./node-shell-session"
import * as nodeShell from "./node-shell-session"
import { ClusterManager } from "./cluster-manager"
import { Router } from "./router"
import { apiPrefix } from "../common/vars";
@ -84,12 +84,13 @@ export class LensProxy {
if (req.method === "GET" && (!res.statusCode || res.statusCode >= 500)) {
const retryCounterKey = `${req.headers.host}${req.url}`
const retryCount = this.retryCounters.get(retryCounterKey) || 0
const timeoutMs = retryCount * 250
if (retryCount < 20) {
logger.debug("Retrying proxy request to url: " + retryCounterKey)
setTimeout(() => {
this.retryCounters.set(retryCounterKey, retryCount + 1)
this.handleRequest(proxy, req, res)
}, (250 * retryCount))
}, timeoutMs)
}
}
}
@ -102,23 +103,13 @@ export class LensProxy {
return proxy;
}
protected createWsListener() {
protected createWsListener(): WebSocket.Server {
const ws = new WebSocket.Server({ noServer: true })
ws.on("connection", ((con: WebSocket, req: http.IncomingMessage) => {
return ws.on("connection", (async (socket: WebSocket, req: http.IncomingMessage) => {
const cluster = this.clusterManager.getClusterForRequest(req)
const contextHandler = cluster.contextHandler
const nodeParam = url.parse(req.url, true).query["node"]?.toString();
contextHandler.withTemporaryKubeconfig((kubeconfigPath) => {
return new Promise<boolean>(async (resolve, reject) => {
const shellSession = await shell.open(con, kubeconfigPath, cluster, nodeParam)
shellSession.on("exit", () => {
resolve(true)
})
})
})
}))
return ws
await nodeShell.open(socket, cluster, nodeParam);
}));
}
protected async getProxyTarget(req: http.IncomingMessage, contextHandler: ContextHandler): Promise<httpProxy.ServerOptions> {
@ -146,7 +137,7 @@ export class LensProxy {
if (proxyTarget) {
proxy.web(req, res, proxyTarget)
} else {
this.router.route(cluster, req, res); // todo: handle not-found route when isBoom==true?
this.router.route(cluster, req, res); // todo: handle "not-found" if isBoom==true?
}
}

View File

@ -13,15 +13,15 @@ export class NodeShellSession extends ShellSession {
protected podId: string
protected kc: KubeConfig
constructor(socket: WebSocket, pathToKubeconfig: string, cluster: Cluster, nodeName: string) {
super(socket, pathToKubeconfig, cluster)
constructor(socket: WebSocket, cluster: Cluster, nodeName: string) {
super(socket, cluster)
this.nodeName = nodeName
this.podId = `node-shell-${uuid()}`
this.kc = cluster.proxyKubeconfig()
}
public async open() {
const shell = await this.kubectl.kubectlPath()
const shell = await this.kubectl.getPath()
let args = []
if (this.createNodeShellPod(this.podId, this.nodeName)) {
await this.waitForRunningPod(this.podId).catch((error) => {
@ -133,15 +133,9 @@ export class NodeShellSession extends ShellSession {
}
}
export async function open(socket: WebSocket, pathToKubeconfig: string, cluster: Cluster, nodeName?: string): Promise<ShellSession> {
return new Promise(async (resolve, reject) => {
let shell = null
if (nodeName) {
shell = new NodeShellSession(socket, pathToKubeconfig, cluster, nodeName)
} else {
shell = new ShellSession(socket, pathToKubeconfig, cluster)
}
shell.open()
resolve(shell)
})
export async function open(socket: WebSocket, cluster: Cluster, nodeName?: string): Promise<ShellSession> {
if (nodeName) {
return new NodeShellSession(socket, cluster, nodeName)
}
return new ShellSession(socket, cluster);
}

View File

@ -1,6 +1,8 @@
import logger from "./logger"
import { createServer, AddressInfo } from "net"
// todo: replace with https://github.com/http-party/node-portfinder ?
const getNextAvailablePort = () => {
logger.debug("getNextAvailablePort() start")
const server = createServer()

View File

@ -1,14 +1,14 @@
import { LensApiRequest } from "./router"
import * as resourceApplier from "./resource-applier"
import { ResourceApplier } from "./resource-applier"
import { LensApi } from "./lens-api"
class ResourceApplierApi extends LensApi {
public async applyResource(request: LensApiRequest) {
const { response, cluster, payload } = request
try {
const resource = await resourceApplier.apply(cluster, cluster.proxyKubeconfigPath(), payload)
const resource = await new ResourceApplier(cluster).apply(payload);
this.respondJson(response, [resource], 200)
} catch(error) {
} catch (error) {
this.respondText(response, error, 422)
}
}

View File

@ -1,47 +1,31 @@
import type { Cluster } from "./cluster";
import { KubernetesObject } from "@kubernetes/client-node"
import { exec } from "child_process";
import fs from "fs";
import * as yaml from "js-yaml";
import path from "path";
import * as tempy from "tempy";
import logger from "./logger"
import { Cluster } from "./cluster";
import { tracker } from "../common/tracker";
type KubeObject = {
status: {};
metadata?: {
resourceVersion: number;
annotations?: {
"kubectl.kubernetes.io/last-applied-configuration": string;
};
};
}
import { cloneJsonObject } from "../common/utils";
export class ResourceApplier {
protected kubeconfigPath: string;
protected cluster: Cluster
constructor(cluster: Cluster, pathToKubeconfig: string) {
this.kubeconfigPath = pathToKubeconfig
this.cluster = cluster
constructor(protected cluster: Cluster) {
}
public async apply(resource: any): Promise<string> {
this.sanitizeObject((resource as KubeObject))
try {
tracker.event("resource", "apply")
return await this.kubectlApply(yaml.safeDump(resource))
} catch(error) {
throw (error)
}
async apply(resource: KubernetesObject | any): Promise<string> {
resource = this.sanitizeObject(resource);
tracker.event("resource", "apply")
return await this.kubectlApply(yaml.safeDump(resource));
}
protected async kubectlApply(content: string): Promise<string> {
const kubectl = await this.cluster.kubeCtl.kubectlPath()
const { kubeCtl, kubeConfigPath } = this.cluster;
const kubectlPath = await kubeCtl.getPath()
return new Promise<string>((resolve, reject) => {
const fileName = tempy.file({name: "resource.yaml"})
const fileName = tempy.file({ name: "resource.yaml" })
fs.writeFileSync(fileName, content)
const cmd = `"${kubectl}" apply --kubeconfig ${this.kubeconfigPath} -o json -f ${fileName}`
const cmd = `"${kubectlPath}" apply --kubeconfig ${kubeConfigPath} -o json -f ${fileName}`
logger.debug("shooting manifests with: " + cmd);
const execEnv: NodeJS.ProcessEnv = Object.assign({}, process.env)
const httpsProxy = this.cluster.preferences?.httpsProxy
@ -62,17 +46,18 @@ export class ResourceApplier {
}
public async kubectlApplyAll(resources: string[]): Promise<string> {
const kubectl = await this.cluster.kubeCtl.kubectlPath()
return new Promise<string>((resolve, reject) => {
const { kubeCtl, kubeConfigPath } = this.cluster;
const kubectlPath = await kubeCtl.getPath()
return new Promise((resolve, reject) => {
const tmpDir = tempy.directory()
// Dump each resource into tmpDir
for (const i in resources) {
fs.writeFileSync(path.join(tmpDir, `${i}.yaml`), resources[i])
}
const cmd = `"${kubectl}" apply --kubeconfig ${this.kubeconfigPath} -o json -f ${tmpDir}`
resources.forEach((resource, index) => {
fs.writeFileSync(path.join(tmpDir, `${index}.yaml`), resource);
})
const cmd = `"${kubectlPath}" apply --kubeconfig ${kubeConfigPath} -o json -f ${tmpDir}`
console.log("shooting manifests with:", cmd);
exec(cmd, (error, stdout, stderr) => {
if(error) {
if (error) {
reject("Error applying manifests:" + error);
}
if (stderr != "") {
@ -84,18 +69,14 @@ export class ResourceApplier {
})
}
protected sanitizeObject(resource: KubeObject) {
delete resource['status']
if (resource['metadata']) {
if (resource['metadata']['annotations'] && resource['metadata']['annotations']['kubectl.kubernetes.io/last-applied-configuration']) {
delete resource['metadata']['annotations']['kubectl.kubernetes.io/last-applied-configuration']
}
delete resource['metadata']['resourceVersion']
protected sanitizeObject(resource: KubernetesObject | any) {
resource = cloneJsonObject(resource);
delete resource.status;
delete resource.metadata?.resourceVersion;
const annotations = resource.metadata?.annotations;
if (annotations) {
delete annotations['kubectl.kubernetes.io/last-applied-configuration'];
}
return resource;
}
}
export async function apply(cluster: Cluster, pathToKubeconfig: string, resource: any) {
const resourceApplier = new ResourceApplier(cluster, pathToKubeconfig)
return await resourceApplier.apply(resource)
}

View File

@ -2,6 +2,7 @@ import { LensApiRequest } from "../router"
import { LensApi } from "../lens-api"
import requestPromise from "request-promise-native"
import { PrometheusClusterQuery, PrometheusIngressQuery, PrometheusNodeQuery, PrometheusPodQuery, PrometheusProvider, PrometheusPvcQuery, PrometheusQueryOpts } from "../prometheus/provider-registry"
import { apiPrefix } from "../../common/vars";
export type IMetricsQuery = string | string[] | {
[metricName: string]: string;
@ -11,6 +12,7 @@ class MetricsRoute extends LensApi {
public async routeMetrics(request: LensApiRequest) {
const { response, cluster } = request
const serverUrl = `http://127.0.0.1:${cluster.port}${apiPrefix.KUBE_BASE}` // fixme: extract
const query: IMetricsQuery = request.payload;
const headers: Record<string, string> = {
"Host": `${cluster.id}.localhost:${cluster.port}`,
@ -25,7 +27,7 @@ class MetricsRoute extends LensApi {
let prometheusProvider: PrometheusProvider
try {
const prometheusPath = await cluster.contextHandler.getPrometheusPath()
metricsUrl = `${cluster.kubeAuthProxyUrl}/api/v1/namespaces/${prometheusPath}/proxy${cluster.getPrometheusApiPrefix()}/api/v1/query_range`
metricsUrl = `${serverUrl}/api/v1/namespaces/${prometheusPath}/proxy${cluster.getPrometheusApiPrefix()}/api/v1/query_range`
prometheusProvider = await cluster.contextHandler.getPrometheusProvider()
} catch {
this.respondJson(response, {})

View File

@ -37,7 +37,7 @@ class PortForward {
public async start() {
this.localPort = await getFreePort()
const kubectlBin = await bundledKubectl.kubectlPath()
const kubectlBin = await bundledKubectl.getPath()
const args = [
"--kubeconfig", this.kubeConfig,
"port-forward",

View File

@ -25,10 +25,10 @@ export class ShellSession extends EventEmitter {
protected running = false;
protected clusterId: string;
constructor(socket: WebSocket, pathToKubeconfig: string, cluster: Cluster) {
constructor(socket: WebSocket, cluster: Cluster) {
super()
this.websocket = socket
this.kubeconfigPath = pathToKubeconfig
this.kubeconfigPath = cluster.kubeConfigPath
this.kubectl = new Kubectl(cluster.version)
this.preferences = cluster.preferences || {}
this.clusterId = cluster.id

View File

@ -4,40 +4,33 @@ import path from "path"
import { app, remote } from "electron"
import { migration } from "../migration-wrapper";
import { ensureDirSync } from "fs-extra"
import { KubeConfig } from "@kubernetes/client-node";
import { writeEmbeddedKubeConfig } from "../../common/utils/kubeconfig"
import { ClusterModel } from "../../common/cluster-store";
export default migration({
version: "3.6.0-beta.1",
run(store, log: (...args: any[]) => void) {
const migratingClusters: ClusterModel[] = []
const migratedClusters: ClusterModel[] = []
const storedClusters: ClusterModel[] = store.get("clusters");
const kubeConfigBase = path.join((app || remote.app).getPath("userData"), "kubeconfigs")
ensureDirSync(kubeConfigBase)
const storedClusters: ClusterModel[] = store.get("clusters")
if (!storedClusters) return
if (!storedClusters) return;
ensureDirSync(kubeConfigBase);
log("Number of clusters to migrate: ", storedClusters.length)
for (const cluster of storedClusters) {
try {
// take the embedded kubeconfig and dump it into a file
cluster.kubeConfigPath = writeEmbeddedKubeConfig(cluster.id, cluster.kubeConfig)
const kc = new KubeConfig()
kc.loadFromFile(cluster.kubeConfigPath)
cluster.contextName = kc.getCurrentContext()
delete cluster.kubeConfig
migratingClusters.push(cluster)
migratedClusters.push(cluster)
} catch (error) {
log(`Failed to migrate Kubeconfig for cluster "${cluster.id}"`, error)
}
}
// "overwrite" the cluster configs
if (migratingClusters.length > 0) {
store.set("clusters", migratingClusters)
if (migratedClusters.length > 0) {
store.set("clusters", migratedClusters)
}
}
})