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 isEqual from "lodash/isEqual"
import { getAppVersion } from "./utils/app-version";
import logger from "../main/logger";
export interface BaseStoreParams<T = any> {
configName: string;
@ -57,7 +58,7 @@ export class BaseStore<T = any> 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<T = any> 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<T = any> extends Singleton {
protected onConfigChange(data: T, oldValue: Partial<T>) {
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
});

View File

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

View File

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

View File

@ -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<string> {
await this.ensurePort();
return `http://127.0.0.1:${this.proxyPort}${this.clusterUrl.path}`;
}
protected async newApiTarget(timeout: number): Promise<httpProxy.ServerOptions> {
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
}
}
}

View File

@ -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<string> {
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<KubeConfig> = {
@ -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;
}

View File

@ -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"]
}
}

View File

@ -10,13 +10,12 @@ export type IMetricsQuery = string | string[] | {
class MetricsRoute extends LensApi {
public async routeMetrics(request: LensApiRequest<IMetricsQuery>) {
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, {})