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:
parent
002176ac25
commit
a2b5ebb1dc
@ -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
|
||||
});
|
||||
|
||||
@ -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 });
|
||||
})
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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"]
|
||||
}
|
||||
}
|
||||
|
||||
@ -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, {})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user