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