From a2b5ebb1dc220f73f759c1cc10fc431b95feba8e Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 13 Jul 2020 12:00:13 +0300 Subject: [PATCH] api-fixes Signed-off-by: Roman --- src/common/base-store.ts | 9 +++--- src/common/register-protocol.ts | 9 ++---- src/main/cluster.ts | 47 ++++++++++++++--------------- src/main/context-handler.ts | 15 ++++----- src/main/kubeconfig-manager.ts | 27 ++++++++++------- src/main/routes/kubeconfig-route.ts | 2 +- src/main/routes/metrics-route.ts | 5 ++- 7 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/common/base-store.ts b/src/common/base-store.ts index 4b694200b5..b7254a559f 100644 --- a/src/common/base-store.ts +++ b/src/common/base-store.ts @@ -7,6 +7,7 @@ import { action, observable, reaction, toJS, when } from "mobx"; import Singleton from "./utils/singleton"; import isEqual from "lodash/isEqual" import { getAppVersion } from "./utils/app-version"; +import logger from "../main/logger"; export interface BaseStoreParams { configName: string; @@ -57,7 +58,7 @@ export class BaseStore extends Singleton { async load() { const { configName, syncEnabled, confOptions = {} } = this.params; - // use "await" to make pseudo-async "load" for more future-proof usages + // use "await" to make pseudo-async "load" for more future-proof use-cases this.storeConfig = await new Config({ projectName: "lens", projectVersion: getAppVersion(), @@ -69,7 +70,7 @@ export class BaseStore extends Singleton { ...confOptions, }); const jsonModel = this.storeConfig.store; - console.info(`[STORE]: [LOADED] ${this.storeConfig.path}`, jsonModel); + logger.info(`[STORE]: loaded from ${this.storeConfig.path}`); this.fromStore(jsonModel); this.isLoaded = true; } @@ -91,14 +92,14 @@ export class BaseStore extends Singleton { protected onConfigChange(data: T, oldValue: Partial) { if (!isEqual(this.toJSON(), data)) { - console.info(`[STORE]: [UPDATE] from ${this.name}`, { data, oldValue }); + logger.debug(`[STORE]: received update from ${this.name}`, { data, oldValue }); this.fromStore(data); } } protected onModelChange(model: T) { if (!isEqual(this.storeModel, model)) { - console.info(`[STORE]: [SAVE] ${this.name} from runtime update`, { + logger.debug(`[STORE]: saving ${this.name} from runtime`, { data: model, oldValue: this.storeModel }); diff --git a/src/common/register-protocol.ts b/src/common/register-protocol.ts index 1ddb2ef7ae..ff9d310f57 100644 --- a/src/common/register-protocol.ts +++ b/src/common/register-protocol.ts @@ -1,17 +1,12 @@ // Register custom protocols -import path from "path"; import { protocol } from "electron" -import logger from "../main/logger"; +import path from "path"; export function registerFileProtocol(name: string, basePath: string) { protocol.registerFileProtocol(name, (request, callback) => { const filePath = request.url.replace(name + "://", ""); const absPath = path.resolve(basePath, filePath); - callback(absPath); - }, (error) => { - if (error) { - logger.error(`Failed to register protocol "${name}"`, { basePath, error }); - } + callback({ path: absPath }); }) } diff --git a/src/main/cluster.ts b/src/main/cluster.ts index cc2a95cbdc..24623706b6 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -27,10 +27,10 @@ export class Cluster implements ClusterModel { @observable contextName: string; @observable workspace: string; @observable kubeConfigPath: string; - @observable url: string; // cluster-api url - @observable proxyUrl: string; // lens-proxy url - @observable webContentUrl: string; - @observable proxyPort: number; + @observable apiUrl: string; // cluster server url + @observable kubeProxyUrl: string; // lens-proxy to kube-api url + @observable kubeAuthProxyUrl: string; // auth-proxy to temp kube-config + @observable webContentUrl: string; // page content url for loading in renderer @observable online: boolean; @observable accessible: boolean; @observable failureReason: string; @@ -49,30 +49,28 @@ export class Cluster implements ClusterModel { @action updateModel(model: ClusterModel) { Object.assign(this, model); - this.url = this.getKubeconfig().getCurrentCluster().server; + this.apiUrl = this.getKubeconfig().getCurrentCluster().server; this.contextName = this.preferences.clusterName; } @action - async init(proxyPort: number) { + async init(port: number) { try { - this.proxyPort = proxyPort; this.contextHandler = new ContextHandler(this); - this.kubeconfigManager = new KubeconfigManager(this, this.contextHandler); - this.proxyUrl = `http://localhost:${proxyPort}`; - this.webContentUrl = `http://${this.id}.localhost:${proxyPort}`; + const contextPort = await this.contextHandler.ensurePort(); + this.kubeAuthProxyUrl = `http://127.0.0.1:${contextPort}`; + this.kubeProxyUrl = `http://localhost:${port}${apiKubePrefix}`; + this.webContentUrl = `http://${this.id}.localhost:${port}`; + this.kubeconfigManager = new KubeconfigManager(this); this.initialized = true; - logger.info(`[CLUSTER]: init success`, { - id: this.id, - url: this.url, - proxyUrl: this.proxyUrl, + logger.info(`[✔] Cluster(${this.id}) init success`, { + serverUrl: this.apiUrl, webContentUrl: this.webContentUrl, + kubeProxyUrl: this.kubeProxyUrl, + kubeAuthProxyUrl: this.kubeAuthProxyUrl, }); } catch (err) { - logger.error(`[CLUSTER]: init error`, { - id: this.id, - error: err.stack, - }); + logger.error(`[X] Cluster(${this.id}) init failed: ${err}`); } } @@ -130,14 +128,15 @@ export class Cluster implements ClusterModel { } protected k8sRequest(path: string, options: RequestPromiseOptions = {}) { - const apiUrl = this.proxyUrl + apiKubePrefix + path; + const apiUrl = this.kubeProxyUrl + path; + logger.debug(`[CLUSTER]: getting request to: ${apiUrl}`); return request(apiUrl, { json: true, timeout: 10000, headers: { ...(options.headers || {}), - host: `${this.id}.localhost:${this.proxyPort}`, - } + Host: new URL(this.webContentUrl).host, + }, }) } @@ -181,7 +180,7 @@ export class Cluster implements ClusterModel { }) return accessReview.body.status.allowed } catch (error) { - logger.error(`failed to request selfSubjectAccessReview: ${error.message}`) + logger.error(`failed to request selfSubjectAccessReview: ${error}`) return false } } @@ -198,8 +197,8 @@ export class Cluster implements ClusterModel { if (kubernetesVersion.includes("gke")) return "gke" if (kubernetesVersion.includes("eks")) return "eks" if (kubernetesVersion.includes("IKS")) return "iks" - if (this.url.endsWith("azmk8s.io")) return "aks" - if (this.url.endsWith("k8s.ondigitalocean.com")) return "digitalocean" + if (this.apiUrl.endsWith("azmk8s.io")) return "aks" + if (this.apiUrl.endsWith("k8s.ondigitalocean.com")) return "digitalocean" if (this.contextName.startsWith("minikube")) return "minikube" if (kubernetesVersion.includes("+")) return "custom" return "vanilla" diff --git a/src/main/context-handler.ts b/src/main/context-handler.ts index e0a11ca414..d6310fe5f4 100644 --- a/src/main/context-handler.ts +++ b/src/main/context-handler.ts @@ -18,7 +18,7 @@ export class ContextHandler { protected prometheusPath: string constructor(protected cluster: Cluster) { - this.clusterUrl = url.parse(cluster.url); + this.clusterUrl = url.parse(cluster.apiUrl); this.setupPrometheus(cluster.preferences); } @@ -80,18 +80,19 @@ export class ContextHandler { return apiTarget } - public async getApiTargetUrl(): Promise { - await this.ensurePort(); - return `http://127.0.0.1:${this.proxyPort}${this.clusterUrl.path}`; - } - protected async newApiTarget(timeout: number): Promise { + await this.ensurePort(); return { changeOrigin: true, - target: await this.getApiTargetUrl(), timeout: timeout, headers: { "Host": this.clusterUrl.hostname, + }, + target: { + protocol: "http://", + host: "127.0.0.1", + port: this.proxyPort, + path: this.clusterUrl.path } } } diff --git a/src/main/kubeconfig-manager.ts b/src/main/kubeconfig-manager.ts index a72258d8f4..f888c1dd6f 100644 --- a/src/main/kubeconfig-manager.ts +++ b/src/main/kubeconfig-manager.ts @@ -1,5 +1,4 @@ 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" @@ -11,16 +10,21 @@ export class KubeconfigManager { protected configDir = app.getPath("temp") protected tempFile: string; - constructor(protected cluster: Cluster, protected contextHandler: ContextHandler) { + constructor(protected cluster: Cluster) { + if(!cluster.kubeAuthProxyUrl) { + throw new Error(`Cluster's auth proxy url must be initialized`) + } + if (!cluster.contextHandler.proxyPort) { + throw new Error("Context-handler proxy port must be resolved") + } this.init(); } protected async init() { try { - await this.contextHandler.ensurePort(); await this.createProxyKubeconfig(); } catch (err) { - logger.error(`Failed to created temp config`, { err }) + logger.error(`Failed to created temp config for auth-proxy`, { err }) } } @@ -33,9 +37,9 @@ export class KubeconfigManager { * This way any user of the config does not need to know anything about the auth etc. details. */ protected async createProxyKubeconfig(): Promise { - fs.ensureDir(this.configDir); - const { contextName, kubeConfigPath, id } = this.cluster; - const tempFile = path.join(this.configDir, `kubeconfig-${id}`); + const { configDir, cluster } = this; + const { contextName, kubeConfigPath, id } = cluster; + const tempFile = path.join(configDir, `kubeconfig-${id}`); const kubeConfig = loadConfig(kubeConfigPath); const proxyUser = "proxy"; const proxyConfig: Partial = { @@ -43,7 +47,7 @@ export class KubeconfigManager { clusters: [ { name: contextName, - server: await this.contextHandler.getApiTargetUrl(), + server: cluster.kubeAuthProxyUrl, skipTLSVerify: undefined, } ], @@ -59,9 +63,10 @@ export class KubeconfigManager { } ] }; - const tempConfigYaml = dumpConfigYaml(proxyConfig); - fs.writeFileSync(tempFile, tempConfigYaml); - logger.info(`Created temp kubeconfig "${contextName}" at "${tempFile}": \n${tempConfigYaml}`); + const configYaml = dumpConfigYaml(proxyConfig); + fs.ensureDir(path.dirname(tempFile)); + fs.writeFileSync(tempFile, configYaml); + logger.debug(`Created temp kubeconfig "${contextName}" at "${tempFile}": \n${configYaml}`); this.tempFile = tempFile; return tempFile; } diff --git a/src/main/routes/kubeconfig-route.ts b/src/main/routes/kubeconfig-route.ts index 08a67c13f7..afd1128781 100644 --- a/src/main/routes/kubeconfig-route.ts +++ b/src/main/routes/kubeconfig-route.ts @@ -12,7 +12,7 @@ function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster { 'name': cluster.contextName, 'cluster': { - 'server': cluster.url, + 'server': cluster.apiUrl, 'certificate-authority-data': secret.data["ca.crt"] } } diff --git a/src/main/routes/metrics-route.ts b/src/main/routes/metrics-route.ts index 78ae3735c8..7c7ec7aa28 100644 --- a/src/main/routes/metrics-route.ts +++ b/src/main/routes/metrics-route.ts @@ -10,13 +10,12 @@ export type IMetricsQuery = string | string[] | { class MetricsRoute extends LensApi { public async routeMetrics(request: LensApiRequest) { const { response, cluster, payload } = request - const { contextHandler } = cluster; - const serverUrl = await contextHandler.getApiTargetUrl(); + const { contextHandler, kubeProxyUrl } = cluster; let metricsUrl: string let prometheusProvider: PrometheusProvider try { const prometheusPath = await contextHandler.getPrometheusPath() - metricsUrl = `${serverUrl}/api/v1/namespaces/${prometheusPath}/proxy${cluster.getPrometheusApiPrefix()}/api/v1/query_range` + metricsUrl = `${kubeProxyUrl}/api/v1/namespaces/${prometheusPath}/proxy${cluster.getPrometheusApiPrefix()}/api/v1/query_range` prometheusProvider = await contextHandler.getPrometheusProvider() } catch { this.respondJson(response, {})