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

api-fixes

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2020-07-13 12:00:13 +03:00
parent 002176ac25
commit a2b5ebb1dc
7 changed files with 57 additions and 57 deletions

View File

@ -7,6 +7,7 @@ import { action, observable, reaction, toJS, when } from "mobx";
import Singleton from "./utils/singleton"; import Singleton from "./utils/singleton";
import isEqual from "lodash/isEqual" import isEqual from "lodash/isEqual"
import { getAppVersion } from "./utils/app-version"; import { getAppVersion } from "./utils/app-version";
import logger from "../main/logger";
export interface BaseStoreParams<T = any> { export interface BaseStoreParams<T = any> {
configName: string; configName: string;
@ -57,7 +58,7 @@ export class BaseStore<T = any> extends Singleton {
async load() { async load() {
const { configName, syncEnabled, confOptions = {} } = this.params; 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({ this.storeConfig = await new Config({
projectName: "lens", projectName: "lens",
projectVersion: getAppVersion(), projectVersion: getAppVersion(),
@ -69,7 +70,7 @@ export class BaseStore<T = any> extends Singleton {
...confOptions, ...confOptions,
}); });
const jsonModel = this.storeConfig.store; 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.fromStore(jsonModel);
this.isLoaded = true; this.isLoaded = true;
} }
@ -91,14 +92,14 @@ export class BaseStore<T = any> extends Singleton {
protected onConfigChange(data: T, oldValue: Partial<T>) { protected onConfigChange(data: T, oldValue: Partial<T>) {
if (!isEqual(this.toJSON(), data)) { 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); this.fromStore(data);
} }
} }
protected onModelChange(model: T) { protected onModelChange(model: T) {
if (!isEqual(this.storeModel, model)) { if (!isEqual(this.storeModel, model)) {
console.info(`[STORE]: [SAVE] ${this.name} from runtime update`, { logger.debug(`[STORE]: saving ${this.name} from runtime`, {
data: model, data: model,
oldValue: this.storeModel oldValue: this.storeModel
}); });

View File

@ -1,17 +1,12 @@
// Register custom protocols // Register custom protocols
import path from "path";
import { protocol } from "electron" import { protocol } from "electron"
import logger from "../main/logger"; import path from "path";
export function registerFileProtocol(name: string, basePath: string) { export function registerFileProtocol(name: string, basePath: string) {
protocol.registerFileProtocol(name, (request, callback) => { protocol.registerFileProtocol(name, (request, callback) => {
const filePath = request.url.replace(name + "://", ""); const filePath = request.url.replace(name + "://", "");
const absPath = path.resolve(basePath, filePath); const absPath = path.resolve(basePath, filePath);
callback(absPath); callback({ path: absPath });
}, (error) => {
if (error) {
logger.error(`Failed to register protocol "${name}"`, { basePath, error });
}
}) })
} }

View File

@ -27,10 +27,10 @@ export class Cluster implements ClusterModel {
@observable contextName: string; @observable contextName: string;
@observable workspace: string; @observable workspace: string;
@observable kubeConfigPath: string; @observable kubeConfigPath: string;
@observable url: string; // cluster-api url @observable apiUrl: string; // cluster server url
@observable proxyUrl: string; // lens-proxy url @observable kubeProxyUrl: string; // lens-proxy to kube-api url
@observable webContentUrl: string; @observable kubeAuthProxyUrl: string; // auth-proxy to temp kube-config
@observable proxyPort: number; @observable webContentUrl: string; // page content url for loading in renderer
@observable online: boolean; @observable online: boolean;
@observable accessible: boolean; @observable accessible: boolean;
@observable failureReason: string; @observable failureReason: string;
@ -49,30 +49,28 @@ export class Cluster implements ClusterModel {
@action @action
updateModel(model: ClusterModel) { updateModel(model: ClusterModel) {
Object.assign(this, model); Object.assign(this, model);
this.url = this.getKubeconfig().getCurrentCluster().server; this.apiUrl = this.getKubeconfig().getCurrentCluster().server;
this.contextName = this.preferences.clusterName; this.contextName = this.preferences.clusterName;
} }
@action @action
async init(proxyPort: number) { async init(port: number) {
try { try {
this.proxyPort = proxyPort;
this.contextHandler = new ContextHandler(this); this.contextHandler = new ContextHandler(this);
this.kubeconfigManager = new KubeconfigManager(this, this.contextHandler); const contextPort = await this.contextHandler.ensurePort();
this.proxyUrl = `http://localhost:${proxyPort}`; this.kubeAuthProxyUrl = `http://127.0.0.1:${contextPort}`;
this.webContentUrl = `http://${this.id}.localhost:${proxyPort}`; this.kubeProxyUrl = `http://localhost:${port}${apiKubePrefix}`;
this.webContentUrl = `http://${this.id}.localhost:${port}`;
this.kubeconfigManager = new KubeconfigManager(this);
this.initialized = true; this.initialized = true;
logger.info(`[CLUSTER]: init success`, { logger.info(`[✔] Cluster(${this.id}) init success`, {
id: this.id, serverUrl: this.apiUrl,
url: this.url,
proxyUrl: this.proxyUrl,
webContentUrl: this.webContentUrl, webContentUrl: this.webContentUrl,
kubeProxyUrl: this.kubeProxyUrl,
kubeAuthProxyUrl: this.kubeAuthProxyUrl,
}); });
} catch (err) { } catch (err) {
logger.error(`[CLUSTER]: init error`, { logger.error(`[X] Cluster(${this.id}) init failed: ${err}`);
id: this.id,
error: err.stack,
});
} }
} }
@ -130,14 +128,15 @@ export class Cluster implements ClusterModel {
} }
protected k8sRequest(path: string, options: RequestPromiseOptions = {}) { 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, { return request(apiUrl, {
json: true, json: true,
timeout: 10000, timeout: 10000,
headers: { headers: {
...(options.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 return accessReview.body.status.allowed
} catch (error) { } catch (error) {
logger.error(`failed to request selfSubjectAccessReview: ${error.message}`) logger.error(`failed to request selfSubjectAccessReview: ${error}`)
return false return false
} }
} }
@ -198,8 +197,8 @@ export class Cluster implements ClusterModel {
if (kubernetesVersion.includes("gke")) return "gke" if (kubernetesVersion.includes("gke")) return "gke"
if (kubernetesVersion.includes("eks")) return "eks" if (kubernetesVersion.includes("eks")) return "eks"
if (kubernetesVersion.includes("IKS")) return "iks" if (kubernetesVersion.includes("IKS")) return "iks"
if (this.url.endsWith("azmk8s.io")) return "aks" if (this.apiUrl.endsWith("azmk8s.io")) return "aks"
if (this.url.endsWith("k8s.ondigitalocean.com")) return "digitalocean" if (this.apiUrl.endsWith("k8s.ondigitalocean.com")) return "digitalocean"
if (this.contextName.startsWith("minikube")) return "minikube" if (this.contextName.startsWith("minikube")) return "minikube"
if (kubernetesVersion.includes("+")) return "custom" if (kubernetesVersion.includes("+")) return "custom"
return "vanilla" return "vanilla"

View File

@ -18,7 +18,7 @@ export class ContextHandler {
protected prometheusPath: string protected prometheusPath: string
constructor(protected cluster: Cluster) { constructor(protected cluster: Cluster) {
this.clusterUrl = url.parse(cluster.url); this.clusterUrl = url.parse(cluster.apiUrl);
this.setupPrometheus(cluster.preferences); this.setupPrometheus(cluster.preferences);
} }
@ -80,18 +80,19 @@ export class ContextHandler {
return apiTarget return apiTarget
} }
public async getApiTargetUrl(): Promise<string> {
await this.ensurePort();
return `http://127.0.0.1:${this.proxyPort}${this.clusterUrl.path}`;
}
protected async newApiTarget(timeout: number): Promise<httpProxy.ServerOptions> { protected async newApiTarget(timeout: number): Promise<httpProxy.ServerOptions> {
await this.ensurePort();
return { return {
changeOrigin: true, changeOrigin: true,
target: await this.getApiTargetUrl(),
timeout: timeout, timeout: timeout,
headers: { headers: {
"Host": this.clusterUrl.hostname, "Host": this.clusterUrl.hostname,
},
target: {
protocol: "http://",
host: "127.0.0.1",
port: this.proxyPort,
path: this.clusterUrl.path
} }
} }
} }

View File

@ -1,5 +1,4 @@
import type { KubeConfig } from "@kubernetes/client-node"; import type { KubeConfig } from "@kubernetes/client-node";
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 path from "path"
@ -11,16 +10,21 @@ 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) {
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(); this.init();
} }
protected async init() { protected async init() {
try { try {
await this.contextHandler.ensurePort();
await this.createProxyKubeconfig(); await this.createProxyKubeconfig();
} catch (err) { } 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. * This way any user of the config does not need to know anything about the auth etc. details.
*/ */
protected async createProxyKubeconfig(): Promise<string> { protected async createProxyKubeconfig(): Promise<string> {
fs.ensureDir(this.configDir); const { configDir, cluster } = this;
const { contextName, kubeConfigPath, id } = this.cluster; const { contextName, kubeConfigPath, id } = cluster;
const tempFile = path.join(this.configDir, `kubeconfig-${id}`); const tempFile = path.join(configDir, `kubeconfig-${id}`);
const kubeConfig = loadConfig(kubeConfigPath); const kubeConfig = loadConfig(kubeConfigPath);
const proxyUser = "proxy"; const proxyUser = "proxy";
const proxyConfig: Partial<KubeConfig> = { const proxyConfig: Partial<KubeConfig> = {
@ -43,7 +47,7 @@ export class KubeconfigManager {
clusters: [ clusters: [
{ {
name: contextName, name: contextName,
server: await this.contextHandler.getApiTargetUrl(), server: cluster.kubeAuthProxyUrl,
skipTLSVerify: undefined, skipTLSVerify: undefined,
} }
], ],
@ -59,9 +63,10 @@ export class KubeconfigManager {
} }
] ]
}; };
const tempConfigYaml = dumpConfigYaml(proxyConfig); const configYaml = dumpConfigYaml(proxyConfig);
fs.writeFileSync(tempFile, tempConfigYaml); fs.ensureDir(path.dirname(tempFile));
logger.info(`Created temp kubeconfig "${contextName}" at "${tempFile}": \n${tempConfigYaml}`); fs.writeFileSync(tempFile, configYaml);
logger.debug(`Created temp kubeconfig "${contextName}" at "${tempFile}": \n${configYaml}`);
this.tempFile = tempFile; this.tempFile = tempFile;
return tempFile; return tempFile;
} }

View File

@ -12,7 +12,7 @@ function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster
{ {
'name': cluster.contextName, 'name': cluster.contextName,
'cluster': { 'cluster': {
'server': cluster.url, 'server': cluster.apiUrl,
'certificate-authority-data': secret.data["ca.crt"] 'certificate-authority-data': secret.data["ca.crt"]
} }
} }

View File

@ -10,13 +10,12 @@ export type IMetricsQuery = string | string[] | {
class MetricsRoute extends LensApi { class MetricsRoute extends LensApi {
public async routeMetrics(request: LensApiRequest<IMetricsQuery>) { public async routeMetrics(request: LensApiRequest<IMetricsQuery>) {
const { response, cluster, payload } = request const { response, cluster, payload } = request
const { contextHandler } = cluster; const { contextHandler, kubeProxyUrl } = cluster;
const serverUrl = await contextHandler.getApiTargetUrl();
let metricsUrl: string let metricsUrl: string
let prometheusProvider: PrometheusProvider let prometheusProvider: PrometheusProvider
try { try {
const prometheusPath = await contextHandler.getPrometheusPath() 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() prometheusProvider = await contextHandler.getPrometheusProvider()
} catch { } catch {
this.respondJson(response, {}) this.respondJson(response, {})